From 1fdc150116cad39aae5c5da407c3312b47a59e3a Mon Sep 17 00:00:00 2001 From: Unit 193 Date: Fri, 17 Dec 2021 07:06:30 -0500 Subject: New upstream version 3.3.3+dfsg1. --- .../guiSQLiteStudio/common/datawidgetmapper.cpp | 2 +- .../guiSQLiteStudio/common/dbcombobox.cpp | 26 + SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h | 23 + .../guiSQLiteStudio/common/extactioncontainer.cpp | 9 +- .../guiSQLiteStudio/common/extlineedit.cpp | 2 +- .../common/widgetstateindicator.cpp | 169 ++-- .../guiSQLiteStudio/common/widgetstateindicator.h | 17 +- .../completer/completeritemdelegate.cpp | 4 +- .../guiSQLiteStudio/completer/completerwindow.cpp | 6 +- SQLiteStudio3/guiSQLiteStudio/configmapper.cpp | 12 +- SQLiteStudio3/guiSQLiteStudio/configmapper.h | 4 +- .../constraints/columndefaultpanel.cpp | 8 +- .../constraints/columnforeignkeypanel.cpp | 2 +- .../constraints/columngeneratedpanel.cpp | 227 +++++ .../constraints/columngeneratedpanel.h | 44 + .../constraints/columngeneratedpanel.ui | 92 ++ .../constraints/columnprimarykeypanel.cpp | 23 +- .../constraints/constraintcheckpanel.cpp | 21 +- .../constraints/constraintpanel.cpp | 3 + .../constraints/tableforeignkeypanel.cpp | 3 +- .../constraints/tableforeignkeypanel.ui | 24 +- .../constraints/tablepkanduniquepanel.cpp | 47 +- .../guiSQLiteStudio/datagrid/sqlqueryitem.cpp | 162 +-- .../guiSQLiteStudio/datagrid/sqlqueryitem.h | 17 +- .../datagrid/sqlqueryitemdelegate.cpp | 473 ++++++--- .../datagrid/sqlqueryitemdelegate.h | 45 +- .../guiSQLiteStudio/datagrid/sqlquerymodel.cpp | 521 +++++++++- .../guiSQLiteStudio/datagrid/sqlquerymodel.h | 56 +- .../datagrid/sqlquerymodelcolumn.cpp | 56 +- .../guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h | 17 +- .../guiSQLiteStudio/datagrid/sqlqueryview.cpp | 96 +- .../guiSQLiteStudio/datagrid/sqlqueryview.h | 6 + .../guiSQLiteStudio/datagrid/sqltablemodel.cpp | 268 ++--- .../guiSQLiteStudio/datagrid/sqltablemodel.h | 10 +- SQLiteStudio3/guiSQLiteStudio/dataview.cpp | 20 +- SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp | 6 +- SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp | 24 +- SQLiteStudio3/guiSQLiteStudio/dbobjlistmodel.cpp | 2 +- SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp | 94 +- SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h | 8 +- .../guiSQLiteStudio/dbtree/dbtreeitem.cpp | 10 +- .../guiSQLiteStudio/dbtree/dbtreeitemdelegate.cpp | 6 +- .../guiSQLiteStudio/dbtree/dbtreemodel.cpp | 56 +- SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.h | 10 +- SQLiteStudio3/guiSQLiteStudio/debugconsole.cpp | 2 +- .../guiSQLiteStudio/dialogs/aboutdialog.cpp | 2 +- .../guiSQLiteStudio/dialogs/columndialog.cpp | 101 +- .../guiSQLiteStudio/dialogs/columndialog.h | 7 +- .../guiSQLiteStudio/dialogs/columndialog.ui | 78 +- .../dialogs/columndialogconstraintsmodel.cpp | 17 +- .../dialogs/columndialogconstraintsmodel.h | 1 + .../guiSQLiteStudio/dialogs/configdialog.cpp | 38 +- .../guiSQLiteStudio/dialogs/configdialog.ui | 686 ++----------- .../guiSQLiteStudio/dialogs/constraintdialog.cpp | 6 + .../guiSQLiteStudio/dialogs/constraintdialog.h | 1 + .../guiSQLiteStudio/dialogs/cssdebugdialog.cpp | 7 +- .../guiSQLiteStudio/dialogs/dbconverterdialog.cpp | 218 ---- .../guiSQLiteStudio/dialogs/dbconverterdialog.h | 52 - .../guiSQLiteStudio/dialogs/dbconverterdialog.ui | 144 --- SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp | 68 +- .../dialogs/errorsconfirmdialog.cpp | 3 +- .../guiSQLiteStudio/dialogs/exportdialog.cpp | 10 +- .../guiSQLiteStudio/dialogs/importdialog.cpp | 2 + .../guiSQLiteStudio/dialogs/indexdialog.cpp | 133 +-- .../guiSQLiteStudio/dialogs/indexdialog.h | 35 +- .../dialogs/indexexprcolumndialog.cpp | 2 +- .../dialogs/newconstraintdialog.cpp | 59 +- .../guiSQLiteStudio/dialogs/newconstraintdialog.h | 6 +- .../guiSQLiteStudio/dialogs/newversiondialog.cpp | 33 +- .../guiSQLiteStudio/dialogs/newversiondialog.h | 7 +- .../guiSQLiteStudio/dialogs/newversiondialog.ui | 110 +- .../guiSQLiteStudio/dialogs/populatedialog.cpp | 3 +- .../guiSQLiteStudio/dialogs/triggerdialog.cpp | 30 +- SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp | 60 ++ SQLiteStudio3/guiSQLiteStudio/extendedpalette.h | 30 + SQLiteStudio3/guiSQLiteStudio/formmanager.cpp | 4 +- SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro | 14 +- SQLiteStudio3/guiSQLiteStudio/icon.cpp | 6 +- SQLiteStudio3/guiSQLiteStudio/icon.h | 4 +- SQLiteStudio3/guiSQLiteStudio/iconmanager.h | 29 +- SQLiteStudio3/guiSQLiteStudio/icons.qrc | 24 +- SQLiteStudio3/guiSQLiteStudio/img/act_search.png | Bin 781 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/img/brick_folder.png | Bin 735 -> 0 bytes .../guiSQLiteStudio/img/configure_constraint.png | Bin 297 -> 0 bytes .../guiSQLiteStudio/img/database_connected.png | Bin 725 -> 0 bytes .../guiSQLiteStudio/img/database_file.png | Bin 701 -> 0 bytes .../guiSQLiteStudio/img/database_network.png | Bin 619 -> 0 bytes .../guiSQLiteStudio/img/directory_with_db.png | Bin 663 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/img/disk_small.png | Bin 0 -> 409 bytes SQLiteStudio3/guiSQLiteStudio/img/function.png | Bin 372 -> 218 bytes SQLiteStudio3/guiSQLiteStudio/img/generated.png | Bin 0 -> 736 bytes SQLiteStudio3/guiSQLiteStudio/img/info_balloon.png | Bin 745 -> 0 bytes .../guiSQLiteStudio/img/lightning_small.png | Bin 0 -> 372 bytes .../guiSQLiteStudio/img/load_full_value.png | Bin 0 -> 326 bytes .../guiSQLiteStudio/img/load_full_values.png | Bin 0 -> 564 bytes SQLiteStudio3/guiSQLiteStudio/img/open_forum.png | Bin 1510 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/img/quit.png | Bin 0 -> 660 bytes SQLiteStudio3/guiSQLiteStudio/img/tip.png | Bin 743 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/img/user.png | Bin 744 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/img/user_unknown.png | Bin 614 -> 0 bytes SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp | 79 +- SQLiteStudio3/guiSQLiteStudio/mainwindow.h | 26 +- SQLiteStudio3/guiSQLiteStudio/mdiwindow.h | 2 +- .../guiSQLiteStudio/multieditor/multieditor.cpp | 7 +- .../guiSQLiteStudio/qhexedit2/qhexedit_p.cpp | 2 +- .../guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp | 36 +- .../guiSQLiteStudio/qtscriptsyntaxhighlighter.h | 3 + SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h | 2 +- .../guiSQLiteStudio/selectabledbobjmodel.cpp | 29 +- SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp | 87 +- SQLiteStudio3/guiSQLiteStudio/sqleditor.h | 6 +- .../guiSQLiteStudio/sqlitesyntaxhighlighter.cpp | 48 +- .../guiSQLiteStudio/sqlitesyntaxhighlighter.h | 4 +- SQLiteStudio3/guiSQLiteStudio/sqlview.cpp | 5 - SQLiteStudio3/guiSQLiteStudio/sqlview.h | 1 - SQLiteStudio3/guiSQLiteStudio/statusfield.cpp | 73 +- SQLiteStudio3/guiSQLiteStudio/statusfield.h | 15 +- SQLiteStudio3/guiSQLiteStudio/style.cpp | 48 + SQLiteStudio3/guiSQLiteStudio/style.h | 29 + SQLiteStudio3/guiSQLiteStudio/taskbar.cpp | 2 +- SQLiteStudio3/guiSQLiteStudio/themetuner.cpp | 21 +- SQLiteStudio3/guiSQLiteStudio/themetuner.h | 14 +- .../translations/guiSQLiteStudio_ru.ts | 584 +++++------ .../translations/guiSQLiteStudio_zh_CN.ts | 1053 ++++++++++---------- SQLiteStudio3/guiSQLiteStudio/uiconfig.h | 33 +- SQLiteStudio3/guiSQLiteStudio/uiutils.cpp | 31 +- SQLiteStudio3/guiSQLiteStudio/uiutils.h | 4 +- .../guiSQLiteStudio/windows/collationseditor.cpp | 9 +- .../guiSQLiteStudio/windows/constrainttabmodel.cpp | 28 +- .../guiSQLiteStudio/windows/constrainttabmodel.h | 1 + .../guiSQLiteStudio/windows/editorwindow.cpp | 59 +- .../guiSQLiteStudio/windows/editorwindow.h | 4 +- .../guiSQLiteStudio/windows/functionseditor.cpp | 13 +- .../windows/functionseditormodel.cpp | 10 +- .../windows/sqliteextensioneditor.cpp | 9 +- .../windows/sqliteextensioneditor.h | 10 +- .../windows/tableconstraintsmodel.cpp | 10 +- .../windows/tablestructuremodel.cpp | 63 +- .../guiSQLiteStudio/windows/tablestructuremodel.h | 4 +- .../guiSQLiteStudio/windows/tablewindow.cpp | 99 +- .../guiSQLiteStudio/windows/tablewindow.h | 7 + .../guiSQLiteStudio/windows/tablewindow.ui | 15 + .../guiSQLiteStudio/windows/viewwindow.cpp | 68 +- SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h | 3 +- .../guiSQLiteStudio/windows/viewwindow.ui | 25 +- 145 files changed, 4191 insertions(+), 3253 deletions(-) create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.h create mode 100644 SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.ui delete mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp delete mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h delete mode 100644 SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui create mode 100644 SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/extendedpalette.h delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/act_search.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/brick_folder.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/configure_constraint.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/database_connected.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/database_file.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/database_network.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/directory_with_db.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/disk_small.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/generated.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/info_balloon.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/lightning_small.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/load_full_value.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/load_full_values.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/open_forum.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/img/quit.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/tip.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/user.png delete mode 100644 SQLiteStudio3/guiSQLiteStudio/img/user_unknown.png create mode 100644 SQLiteStudio3/guiSQLiteStudio/style.cpp create mode 100644 SQLiteStudio3/guiSQLiteStudio/style.h (limited to 'SQLiteStudio3/guiSQLiteStudio') diff --git a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp index 35e99c8..7e115ed 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/datawidgetmapper.cpp @@ -122,7 +122,7 @@ void DataWidgetMapper::submit() idx = model->index(currentIndex, entry->columnIndex); value = entry->widget->property(entry->propertyName.toLatin1().constData()); - qDebug() << "copying from form view for idx" << idx << "value:" << value; + //qDebug() << "copying from form view for idx" << idx << "value:" << value; model->setData(idx, value, Qt::EditRole); } } diff --git a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp new file mode 100644 index 0000000..303a37a --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.cpp @@ -0,0 +1,26 @@ +#include "dbcombobox.h" +#include "dblistmodel.h" +#include "db/db.h" + +DbComboBox::DbComboBox(QWidget* parent) : QComboBox(parent) +{ + dbComboModel = new DbListModel(this); + dbComboModel->setCombo(this); + setModel(dbComboModel); + setEditable(false); +} + +DbListModel* DbComboBox::getModel() const +{ + return dbComboModel; +} + +void DbComboBox::setCurrentDb(Db* db) +{ + setCurrentIndex(dbComboModel->getIndexForDb(db)); +} + +Db* DbComboBox::currentDb() const +{ + return dbComboModel->getDb(currentIndex()); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h new file mode 100644 index 0000000..39814a6 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/common/dbcombobox.h @@ -0,0 +1,23 @@ +#ifndef DBCOMBOBOX_H +#define DBCOMBOBOX_H + +#include + +class QComboBox; +class DbListModel; +class Db; + +class DbComboBox : public QComboBox +{ + public: + explicit DbComboBox(QWidget* parent = nullptr); + + DbListModel* getModel() const; + void setCurrentDb(Db* db); + Db* currentDb() const; + + private: + DbListModel* dbComboModel = nullptr; +}; + +#endif // DBCOMBOBOX_H diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp index c67cc73..1203582 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/extactioncontainer.cpp @@ -15,8 +15,13 @@ ExtActionContainer::ExtActionContainer() { actionIdMapper = new QSignalMapper(); - // We need to explicitly cast QSignalMapper::mapped to tell which overloaded version of function we want - QObject::connect(actionIdMapper, static_cast(&QSignalMapper::mapped), + QObject::connect(actionIdMapper, +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + &QSignalMapper::mappedInt, +#else + // We need to explicitly cast QSignalMapper::mapped to tell which overloaded version of function we want + static_cast(&QSignalMapper::mapped), +#endif [=](int action) {refreshShortcut(action);}); instances << this; } diff --git a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp index ccbc586..2ac6531 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/extlineedit.cpp @@ -99,7 +99,7 @@ void ExtLineEdit::handleTextChanged() return; // Text width - int newWidth = fontMetrics().width(txt); + int newWidth = fontMetrics().horizontalAdvance(txt); // Text margins QMargins margins = textMargins(); diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp index c6823e4..b5b0264 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.cpp @@ -9,10 +9,10 @@ #include #include #include -#include -#include #include #include +#include +#include QHash WidgetStateIndicator::instances; @@ -54,7 +54,6 @@ void WidgetStateIndicator::initEffects() { initGlowEffects(); initHighlightingEffects(); - initAnimations(); } void WidgetStateIndicator::initGlowEffects() @@ -73,27 +72,6 @@ void WidgetStateIndicator::initHighlightingEffects() highlightingEffect->setEnabled(false); } -void WidgetStateIndicator::initAnimations() -{ - animation = new QSequentialAnimationGroup(this); - animation->setLoopCount(-1); - - // Animation of glow efect - QPropertyAnimation* varAnim = new QPropertyAnimation(glowEffect, "blurRadius"); - varAnim->setStartValue(3.0); - varAnim->setEndValue(14.0); - varAnim->setEasingCurve(QEasingCurve::InOutCubic); - varAnim->setDuration(300); - animation->addAnimation(varAnim); - - varAnim = new QPropertyAnimation(glowEffect, "blurRadius"); - varAnim->setStartValue(14.0); - varAnim->setEndValue(3.0); - varAnim->setEasingCurve(QEasingCurve::InOutCubic); - varAnim->setDuration(300); - animation->addAnimation(varAnim); -} - void WidgetStateIndicator::initPositionMode() { if (dynamic_cast(widget)) @@ -106,8 +84,15 @@ void WidgetStateIndicator::initPositionMode() void WidgetStateIndicator::finalInit() { - label->setFixedSize(label->pixmap()->size()); - labelParent->setFixedSize(label->pixmap()->size()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + QPixmap pixmap = label->pixmap(Qt::ReturnByValue); + label->setFixedSize(pixmap.size()); + labelParent->setFixedSize(pixmap.size()); +#else + const QPixmap* pixmap = label->pixmap(); + label->setFixedSize(pixmap->size()); + labelParent->setFixedSize(pixmap->size()); +#endif widgetVisible = widget->isVisible(); labelParent->setVisible(false); } @@ -120,7 +105,6 @@ void WidgetStateIndicator::setMessage(const QString& msg) else message = paraTpl.arg(msg); - label->setToolTip(message); if (!msg.isNull()) label->setCursor(Qt::WhatsThisCursor); else @@ -129,7 +113,7 @@ void WidgetStateIndicator::setMessage(const QString& msg) void WidgetStateIndicator::clearMessage() { - message = QString::null; + message = QString(); label->setToolTip(message); label->unsetCursor(); } @@ -151,13 +135,10 @@ void WidgetStateIndicator::setMode(WidgetStateIndicator::Mode mode) updateMode(); } -void WidgetStateIndicator::show(const QString& msg, bool animated) +void WidgetStateIndicator::show(const QString& msg) { visibilityRequested = true; setMessage(msg); - if (animated && animation->state() != QAbstractAnimation::Running) - animation->start(); - updateVisibility(); } @@ -165,9 +146,6 @@ void WidgetStateIndicator::hide() { visibilityRequested = false; clearMessage(); - if (animation->state() == QAbstractAnimation::Running) - animation->stop(); - updateVisibility(); } @@ -186,28 +164,28 @@ void WidgetStateIndicator::release() deleteLater(); } -void WidgetStateIndicator::info(const QString& msg, bool animated) +void WidgetStateIndicator::info(const QString& msg) { setMode(Mode::INFO); - show(msg, animated); + show(msg); } -void WidgetStateIndicator::warn(const QString& msg, bool animated) +void WidgetStateIndicator::warn(const QString& msg) { setMode(Mode::WARNING); - show(msg, animated); + show(msg); } -void WidgetStateIndicator::error(const QString& msg, bool animated) +void WidgetStateIndicator::error(const QString& msg) { setMode(Mode::ERROR); - show(msg, animated); + show(msg); } -void WidgetStateIndicator::hint(const QString& msg, bool animated) +void WidgetStateIndicator::hint(const QString& msg) { setMode(Mode::HINT); - show(msg, animated); + show(msg); } bool WidgetStateIndicator::exists(QWidget* widget) @@ -226,47 +204,11 @@ WidgetStateIndicator* WidgetStateIndicator::getInstance(QWidget* widget) bool WidgetStateIndicator::eventFilter(QObject* obj, QEvent* ev) { if (obj == widget) - { - switch (ev->type()) - { - case QEvent::Move: - case QEvent::Resize: - case QEvent::Scroll: - updatePosition(); - break; - case QEvent::Show: - widgetVisible = true; - updateVisibility(); - break; - case QEvent::Hide: - widgetVisible = false; - updateVisibility(); - break; - case QEvent::EnabledChange: - updateVisibility(); - break; - default: - break; - } - } + return eventFilterFromWidget(ev); else if (obj == windowParent) - { - switch (ev->type()) - { - case QEvent::ParentChange: - detectWindowParent(); - break; - default: - break; - } - } + return eventFilterFromParentWidget(ev); else if (obj == label) - { - if (ev->type() == QEvent::Enter) - highlightingEffect->setEnabled(true); - else if (ev->type() == QEvent::Leave) - highlightingEffect->setEnabled(false); - } + return eventFilterFromIndicatorLabel(ev); return false; } @@ -328,7 +270,7 @@ void WidgetStateIndicator::updatePositionGroupBox() QFont font = gb->font(); QFontMetrics fm(font); QString txt = gb->title(); - QPoint diff(fm.width(txt), 2); + QPoint diff(fm.horizontalAdvance(txt), 2); labelParent->move(xy + diff); } @@ -390,6 +332,67 @@ bool WidgetStateIndicator::shouldShow() return true; } + +bool WidgetStateIndicator::eventFilterFromWidget(QEvent* ev) +{ + switch (ev->type()) + { + case QEvent::Move: + case QEvent::Resize: + case QEvent::Scroll: + updatePosition(); + break; + case QEvent::Show: + widgetVisible = true; + updateVisibility(); + break; + case QEvent::Hide: + widgetVisible = false; + updateVisibility(); + break; + case QEvent::EnabledChange: + updateVisibility(); + break; + default: + break; + } + return false; +} + +bool WidgetStateIndicator::eventFilterFromParentWidget(QEvent* ev) +{ + switch (ev->type()) + { + case QEvent::ParentChange: + detectWindowParent(); + break; + default: + break; + } + return false; +} + +bool WidgetStateIndicator::eventFilterFromIndicatorLabel(QEvent* ev) +{ + switch (ev->type()) + { + case QEvent::Enter: + { + highlightingEffect->setEnabled(true); + QEnterEvent* e = dynamic_cast(ev); + QToolTip::showText(e->globalPos(), message); + break; + } + case QEvent::Leave: + highlightingEffect->setEnabled(false); + QToolTip::hideText(); + break; + default: + break; + } + return false; +} + WidgetStateIndicator::PositionMode WidgetStateIndicator::getPositionMode() const { return positionMode; diff --git a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h index eee49d5..28c6e0b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h +++ b/SQLiteStudio3/guiSQLiteStudio/common/widgetstateindicator.h @@ -7,7 +7,6 @@ class QLabel; class QGraphicsDropShadowEffect; class QGraphicsColorizeEffect; -class QSequentialAnimationGroup; class GUI_API_EXPORT WidgetStateIndicator : public QObject { @@ -32,14 +31,14 @@ class GUI_API_EXPORT WidgetStateIndicator : public QObject ~WidgetStateIndicator(); void setMode(Mode mode); - void show(const QString& msg = QString(), bool animated = true); + void show(const QString& msg = QString()); void hide(); void setVisible(bool visible, const QString& msg = QString()); void release(); - void info(const QString& msg, bool animated = true); - void warn(const QString& msg, bool animated = true); - void error(const QString& msg, bool animated = true); - void hint(const QString& msg, bool animated = true); + void info(const QString& msg); + void warn(const QString& msg); + void error(const QString& msg); + void hint(const QString& msg); static bool exists(QWidget* widget); static WidgetStateIndicator* getInstance(QWidget* widget); @@ -57,7 +56,6 @@ class GUI_API_EXPORT WidgetStateIndicator : public QObject void initEffects(); void initGlowEffects(); void initHighlightingEffects(); - void initAnimations(); void initPositionMode(); void finalInit(); void setMessage(const QString& msg); @@ -66,15 +64,18 @@ class GUI_API_EXPORT WidgetStateIndicator : public QObject QWidget* findParentWindow(QWidget* w); bool shouldHide(); bool shouldShow(); + bool eventFilterFromWidget(QEvent *ev); + bool eventFilterFromParentWidget(QEvent *ev); + bool eventFilterFromIndicatorLabel(QEvent *ev); QWidget* labelParent = nullptr; QLabel* label = nullptr; Mode mode = Mode::ERROR; QWidget* widget = nullptr; QString message; + QString processedMessage; QGraphicsColorizeEffect* highlightingEffect = nullptr; QGraphicsDropShadowEffect* glowEffect = nullptr; - QSequentialAnimationGroup* animation = nullptr; bool widgetVisible = false; bool visibilityRequested = false; QWidget* windowParent = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completeritemdelegate.cpp b/SQLiteStudio3/guiSQLiteStudio/completer/completeritemdelegate.cpp index 3268960..7b17ffe 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completeritemdelegate.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completeritemdelegate.cpp @@ -124,13 +124,13 @@ void CompleterItemDelegate::paintPrefix(QPainter* painter, const QFontMetrics& m QString value = text + "."; painter->drawText(QPoint(x, y), value); - x += metrics.width(value); + x += metrics.horizontalAdvance(value); } void CompleterItemDelegate::paintValue(QPainter* painter, const QFontMetrics& metrics, int& x, int y, const QString& text) const { painter->drawText(QPoint(x, y), text); - x += metrics.width(text); + x += metrics.horizontalAdvance(text); } void CompleterItemDelegate::paintLabel(QPainter* painter, int& x, int y, const QString& text, bool emptyValue) const diff --git a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp index 87ad64c..89a0848 100644 --- a/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/completer/completerwindow.cpp @@ -42,12 +42,12 @@ void CompleterWindow::init() void CompleterWindow::reset() { model->clear(); - ui->status->showMessage(QString::null); + ui->status->showMessage(QString()); } void CompleterWindow::setData(const CompletionHelper::Results& completionResults) { - ui->status->showMessage(QString::null); + ui->status->showMessage(QString()); model->setData(completionResults.expectedTokens); filter = completionResults.partialToken; wrappedFilter = completionResults.wrappedToken; @@ -89,7 +89,7 @@ void CompleterWindow::shringFilterBy(int chars) void CompleterWindow::extendFilterBy(const QString& text) { - if (filter.isEmpty() && text.size() == 1 && isWrapperChar(text[0], db->getDialect())) + if (filter.isEmpty() && text.size() == 1 && isWrapperChar(text[0])) { wrappedFilter = true; updateFilter(); diff --git a/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp b/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp index f4098bc..b36d985 100644 --- a/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/configmapper.cpp @@ -309,7 +309,7 @@ void ConfigMapper::applyConfigToWidget(QWidget* widget, const QHashsetEnabled(value); - QWidget* dependWidget = configEntryToWidgets[cfg]; + QWidget* dependWidget = configEntryToWidgets.value(cfg); boolDependencyToDependingWidget[dependWidget] = widget; } @@ -682,7 +682,7 @@ void ConfigMapper::updateConfigComboModel(const QVariant& value) if (!specialConfigEntryToWidgets.contains(key)) return; - QWidget* w = specialConfigEntryToWidgets[key]; + QWidget* w = specialConfigEntryToWidgets.value(key); ConfigComboBox* ccb = dynamic_cast(w); if (!w) return; @@ -709,7 +709,7 @@ void ConfigMapper::notifiableConfigKeyChanged() return; } - loadToWidget(key, configEntryToWidgets[key]); + loadToWidget(key, configEntryToWidgets.value(key)); } void ConfigMapper::bindToConfig(QWidget* topLevelWidget) @@ -742,7 +742,7 @@ void ConfigMapper::removeMainCfgEntry(CfgMain* cfgMain) QWidget* ConfigMapper::getBindWidgetForConfig(CfgEntry* key) const { if (configEntryToWidgets.contains(key)) - return configEntryToWidgets[key]; + return configEntryToWidgets.value(key); return nullptr; } diff --git a/SQLiteStudio3/guiSQLiteStudio/configmapper.h b/SQLiteStudio3/guiSQLiteStudio/configmapper.h index 47c1087..ca70918 100644 --- a/SQLiteStudio3/guiSQLiteStudio/configmapper.h +++ b/SQLiteStudio3/guiSQLiteStudio/configmapper.h @@ -107,8 +107,8 @@ class GUI_API_EXPORT ConfigMapper : public QObject QList internalCustomConfigWidgets; bool realTimeUpdates = false; QHash widgetToConfigEntry; - QHash configEntryToWidgets; - QHash specialConfigEntryToWidgets; + QMultiHash configEntryToWidgets; + QMultiHash specialConfigEntryToWidgets; bool updatingEntry = false; QList extraWidgets; QList widgetsToIgnore; // main mapper will ignore plugin's forms, they have their own mappers diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp index 5759f76..bb70c81 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp @@ -152,7 +152,7 @@ void ColumnDefaultPanel::storeExpr(SqliteCreateTable::Column::Constraint* constr return; } - Parser parser(db->getDialect()); + Parser parser; SqliteExpr* newExpr = parser.parseExpr(text); newExpr->setParent(constraint.data()); constr->expr = newExpr; @@ -162,7 +162,7 @@ void ColumnDefaultPanel::storeLiteral(SqliteCreateTable::Column::Constraint* con { QString text = ui->exprEdit->toPlainText(); - Parser parser(db->getDialect()); + Parser parser; SqliteCreateTablePtr createTable = parser.parse("CREATE TABLE tab (col DEFAULT "+text+");"); if (!createTable || createTable->columns.size() == 0 || createTable->columns.first()->constraints.size() == 0) { @@ -240,7 +240,7 @@ void ColumnDefaultPanel::readConstraint() } else if (!constr->id.isNull()) { - ui->exprEdit->setPlainText(wrapObjIfNeeded(constr->id, db->getDialect(), true)); + ui->exprEdit->setPlainText(wrapObjIfNeeded(constr->id, true)); currentMode = Mode::LITERAL; } else if (!constr->ctime.isNull()) @@ -265,7 +265,7 @@ void ColumnDefaultPanel::updateVirtualSql() { static QString sql = QStringLiteral("CREATE TABLE tab (col DEFAULT %1)"); ui->exprEdit->setDb(db); - ui->exprEdit->setVirtualSqlExpression(sql.arg(db->getDialect() == Dialect::Sqlite3 ? "(%1)" : "%1")); + ui->exprEdit->setVirtualSqlExpression(sql.arg("(%1)")); } QString ColumnDefaultPanel::getTempTable() diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp index af79331..ea5cb7d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp @@ -227,7 +227,7 @@ void ColumnForeignKeyPanel::storeConfiguration() constr->foreignKey->initially = sqliteInitially(ui->initiallyCombo->currentText()); // Name - constr->name = QString::null; + constr->name = QString(); if (ui->namedCheckBox->isChecked()) constr->name = ui->nameEdit->text(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.cpp new file mode 100644 index 0000000..308b868 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.cpp @@ -0,0 +1,227 @@ +#include "columngeneratedpanel.h" +#include "ui_columngeneratedpanel.h" +#include "parser/parser.h" +#include "parser/keywords.h" +#include "parser/lexer.h" +#include "uiutils.h" +#include "schemaresolver.h" +#include + +ColumnGeneratedPanel::ColumnGeneratedPanel(QWidget *parent) : + ConstraintPanel(parent), + ui(new Ui::ColumnGeneratedPanel) +{ + ui->setupUi(this); + init(); +} + +ColumnGeneratedPanel::~ColumnGeneratedPanel() +{ + delete ui; +} + +void ColumnGeneratedPanel::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +bool ColumnGeneratedPanel::validate() +{ + if (!ui->exprEdit->isSyntaxChecked()) + { + setValidState(ui->exprEdit, false, tr("Enter the column value generating expression.")); + return false; + } + + // First check if we already validated this text. + // This method is called twice, by both errors checking and syntax highlighting, + // because signal for textChange() is connected with call to updateValidation(). + QString text = ui->exprEdit->toPlainText(); + if (!lastValidatedText.isNull() && lastValidatedText == text) + return lastValidationResult; + + lastValidatedText = text; + + bool nameOk = true; + if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty()) + nameOk = false; + + bool exprOk = !ui->exprEdit->toPlainText().trimmed().isEmpty() && + !ui->exprEdit->haveErrors(); + + QString exprError; + if (exprOk) + { + // Everything looks fine, so lets do the final check - if the value is correct for use as generated column expression in SQLite. + static QString tempDdlExprTpl = QStringLiteral("CREATE TEMP TABLE %1 (aaa, %2 GENERATED ALWAYS AS (%3));"); + static QString dropTempDdl = QStringLiteral("DROP TABLE %1;"); + + SqliteCreateTable::Column* columnStmt = dynamic_cast(constraint->parentStatement()); + SqliteCreateTable* createTableStmt = dynamic_cast(columnStmt->parentStatement()); + + QStringList preparedColNames; + QString thisColumnName = columnStmt->name; + for (SqliteCreateTable::Column* column : createTableStmt->columns) + { + if (column->name == thisColumnName) + continue; + + preparedColNames << wrapObjIfNeeded(column->name); + } + preparedColNames << wrapObjIfNeeded(thisColumnName); + + QString tableName = getTempTable(); + QString tempDdl = tempDdlExprTpl.arg(tableName, preparedColNames.join(", "), ui->exprEdit->toPlainText()); + SqlQueryPtr res = db->exec(tempDdl); + if (res->isError()) + { + exprOk = false; + exprError = tr("Invalid value generating expression: %1.") + .arg(res->getErrorText()); + } + + db->exec(dropTempDdl.arg(tableName)); + } else + exprError = tr("Invalid value generating expression."); + + setValidState(ui->exprEdit, exprOk, exprError); + setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint.")); + + lastValidationResult = (exprOk && nameOk); + return lastValidationResult; +} + +bool ColumnGeneratedPanel::validateOnly() +{ + ui->exprEdit->checkSyntaxNow(); + return validate(); +} + +void ColumnGeneratedPanel::constraintAvailable() +{ + if (constraint.isNull()) + return; + + readConstraint(); + updateVirtualSql(); + validateOnly(); +} + +void ColumnGeneratedPanel::storeConfiguration() +{ + if (constraint.isNull()) + return; + + SqliteCreateTable::Column::Constraint* constr = dynamic_cast(constraint.data()); + constr->type = SqliteCreateTable::Column::Constraint::GENERATED; + storeExpr(constr); + + if (ui->typeCheck->isChecked()) + constr->generatedType = SqliteCreateTable::Column::Constraint::generatedTypeFrom(ui->typeCombo->currentText()); + else + constr->generatedType = SqliteCreateTable::Column::Constraint::GeneratedType::null; + + constr->generatedKw = ui->generatedKwCheck->isChecked(); + + if (ui->namedCheck->isChecked()) + constr->name = ui->namedEdit->text(); +} + +void ColumnGeneratedPanel::storeExpr(SqliteCreateTable::Column::Constraint* constr) +{ + QString text = ui->exprEdit->toPlainText(); + clear(constr); + + Parser parser; + SqliteExpr* newExpr = parser.parseExpr(text); + newExpr->setParent(constraint.data()); + constr->expr = newExpr; +} + +void ColumnGeneratedPanel::clear(SqliteCreateTable::Column::Constraint* constr) +{ + if (constr->expr) + { + delete constr->expr; + constr->expr = nullptr; + } + constr->generatedKw = false; + constr->generatedType = SqliteCreateTable::Column::Constraint::GeneratedType::null; +} + +void ColumnGeneratedPanel::init() +{ + setFocusProxy(ui->exprEdit); + ui->exprEdit->setShowLineNumbers(false); + + connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation())); + connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation())); + connect(ui->exprEdit, SIGNAL(textChanged()), this, SIGNAL(updateValidation())); + connect(ui->exprEdit, SIGNAL(errorsChecked(bool)), this, SIGNAL(updateValidation())); + + connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState())); + connect(ui->typeCheck, SIGNAL(toggled(bool)), this, SLOT(updateState())); + + ui->typeCombo->addItems(getGeneratedColumnTypes()); + + updateState(); +} + +void ColumnGeneratedPanel::readConstraint() +{ + SqliteCreateTable::Column::Constraint* constr = dynamic_cast(constraint.data()); + + constr->rebuildTokens(); + if (constr->expr) + ui->exprEdit->setPlainText(constr->expr->detokenize()); + + QString typeValue = SqliteCreateTable::Column::Constraint::toString(constr->generatedType); + switch (constr->generatedType) { + case SqliteCreateTable::Column::Constraint::GeneratedType::STORED: + ui->typeCheck->setChecked(true); + break; + case SqliteCreateTable::Column::Constraint::GeneratedType::VIRTUAL: + ui->typeCheck->setChecked(true); + break; + case SqliteCreateTable::Column::Constraint::GeneratedType::null: + ui->typeCheck->setChecked(false); + typeValue = SqliteCreateTable::Column::Constraint::toString(SqliteCreateTable::Column::Constraint::GeneratedType::VIRTUAL); + break; + } + ui->typeCombo->setCurrentText(typeValue); + + ui->generatedKwCheck->setChecked(constr->generatedKw); + + if (!constr->name.isNull()) + { + ui->namedCheck->setChecked(true); + ui->namedEdit->setText(constr->name); + } +} + +void ColumnGeneratedPanel::updateVirtualSql() +{ + static QString sql = QStringLiteral("CREATE TABLE tab (col GENERATED ALWAYS AS (%1))"); + ui->exprEdit->setDb(db); + ui->exprEdit->setVirtualSqlExpression(sql); +} + +QString ColumnGeneratedPanel::getTempTable() +{ + SchemaResolver resolver(db); + return resolver.getUniqueName("sqlitestudio_temp_table"); +} + +void ColumnGeneratedPanel::updateState() +{ + ui->namedEdit->setEnabled(ui->namedCheck->isChecked()); + ui->typeCombo->setEnabled(ui->typeCheck->isChecked()); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.h new file mode 100644 index 0000000..171dab8 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.h @@ -0,0 +1,44 @@ +#ifndef COLUMNGENERATEDPANEL_H +#define COLUMNGENERATEDPANEL_H + +#include "constraintpanel.h" +#include "guiSQLiteStudio_global.h" +#include + +namespace Ui { + class ColumnGeneratedPanel; +} + +class GUI_API_EXPORT ColumnGeneratedPanel : public ConstraintPanel +{ + Q_OBJECT + + public: + explicit ColumnGeneratedPanel(QWidget *parent = 0); + ~ColumnGeneratedPanel(); + + bool validate(); + bool validateOnly(); + + protected: + void changeEvent(QEvent *e); + void constraintAvailable(); + void storeConfiguration(); + + private: + void init(); + void readConstraint(); + void updateVirtualSql(); + QString getTempTable(); + void storeExpr(SqliteCreateTable::Column::Constraint* constr); + void clear(SqliteCreateTable::Column::Constraint* constr); + + Ui::ColumnGeneratedPanel *ui = nullptr; + QString lastValidatedText; + bool lastValidationResult = false; + + private slots: + void updateState(); +}; + +#endif // COLUMNGENERATEDPANEL_H diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.ui new file mode 100644 index 0000000..3d0bcfc --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columngeneratedpanel.ui @@ -0,0 +1,92 @@ + + + ColumnGeneratedPanel + + + + 0 + 0 + 400 + 263 + + + + Form + + + + + + Generating code: + + + + + + + + + + + + + + + Explicit type: + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Use "GENERATED ALWAYS" keywords + + + + + + + + + + + + + Named constraint: + + + + + + + + + + + + + + SqlEditor + QPlainTextEdit +
sqleditor.h
+
+
+ + +
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp index f8975d8..fa7b7c3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp @@ -39,6 +39,9 @@ void ColumnPrimaryKeyPanel::init() connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation())); connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation())); + connect(ui->sortOrderCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation())); + connect(ui->sortOrderCombo, SIGNAL(currentTextChanged(QString)), this, SIGNAL(updateValidation())); + connect(ui->autoIncrCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation())); connect(ui->sortOrderCheck, SIGNAL(toggled(bool)), this, SLOT(updateState())); connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState())); connect(ui->conflictCheck, SIGNAL(toggled(bool)), this, SLOT(updateState())); @@ -48,8 +51,7 @@ void ColumnPrimaryKeyPanel::init() void ColumnPrimaryKeyPanel::readConstraint() { SqliteCreateTable::Column::Constraint* constr = dynamic_cast(constraint.data()); - if (constraint->dialect == Dialect::Sqlite3) - ui->autoIncrCheck->setChecked(constr->autoincrKw); + ui->autoIncrCheck->setChecked(constr->autoincrKw); if (constr->sortOrder != SqliteSortOrder::null) { @@ -77,7 +79,6 @@ void ColumnPrimaryKeyPanel::updateState() ui->conflictCombo->setEnabled(ui->conflictCheck->isChecked()); } - bool ColumnPrimaryKeyPanel::validate() { bool nameOk = true; @@ -86,7 +87,16 @@ bool ColumnPrimaryKeyPanel::validate() setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint.")); - return nameOk; + bool sortOk = true; + if (ui->autoIncrCheck->isChecked() && ui->sortOrderCombo->isEnabled() && + ui->sortOrderCombo->currentText().toUpper() == "DESC") + { + sortOk = false; + } + + setValidState(ui->sortOrderCombo, sortOk, tr("Descending order is not allowed with AUTOINCREMENT.")); + + return nameOk && sortOk; } void ColumnPrimaryKeyPanel::constraintAvailable() @@ -94,8 +104,6 @@ void ColumnPrimaryKeyPanel::constraintAvailable() if (constraint.isNull()) return; - ui->autoIncrCheck->setVisible(constraint->dialect == Dialect::Sqlite3); - readConstraint(); } @@ -107,8 +115,7 @@ void ColumnPrimaryKeyPanel::storeConfiguration() SqliteCreateTable::Column::Constraint* constr = dynamic_cast(constraint.data()); constr->type = SqliteCreateTable::Column::Constraint::PRIMARY_KEY; - if (constraint->dialect == Dialect::Sqlite3) - constr->autoincrKw = ui->autoIncrCheck->isChecked(); + constr->autoincrKw = ui->autoIncrCheck->isChecked(); if (ui->sortOrderCheck->isChecked() && ui->sortOrderCombo->currentIndex() > -1) constr->sortOrder = sqliteSortOrder(ui->sortOrderCombo->currentText()); diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp index adb5e2b..8a71777 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp @@ -54,11 +54,8 @@ void ConstraintCheckPanel::constraintAvailable() if (constraint.isNull()) return; - if (constraint->dialect == Dialect::Sqlite3) - { - ui->onConflictCheck->setVisible(false); - ui->onConflictCombo->setVisible(false); - } + ui->onConflictCheck->setVisible(false); + ui->onConflictCombo->setVisible(false); readConstraint(); updateVirtualSql(); @@ -76,14 +73,11 @@ void ConstraintCheckPanel::storeConfiguration() newExpr->setParent(constraint.data()); storeExpr(newExpr); - QString name = QString::null; + QString name = QString(); if (ui->namedCheck->isChecked()) name = ui->namedEdit->text(); storeName(name); - - if (constraint->dialect == Dialect::Sqlite2 && ui->onConflictCheck->isChecked()) - storeConflictAlgo(sqliteConflictAlgo(ui->onConflictCombo->currentText())); } void ConstraintCheckPanel::init() @@ -116,13 +110,6 @@ void ConstraintCheckPanel::readConstraint() ui->namedCheck->setChecked(true); ui->namedEdit->setText(name); } - - SqliteConflictAlgo onConflict = readConflictAlgo(); - if (constraint->dialect == Dialect::Sqlite2 && onConflict != SqliteConflictAlgo::null) - { - ui->onConflictCheck->setChecked(true); - ui->onConflictCombo->setCurrentText(sqliteConflictAlgo(onConflict)); - } } void ConstraintCheckPanel::updateVirtualSql() @@ -157,7 +144,7 @@ void ConstraintCheckPanel::updateVirtualSql() SqliteExprPtr ConstraintCheckPanel::parseExpression(const QString& sql) { - Parser parser(db->getDialect()); + Parser parser; SqliteExpr *expr = parser.parseExpr(sql); return SqliteExprPtr(expr); } diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp index 031fbfe..4f3e350 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp @@ -6,6 +6,7 @@ #include "constraints/tablecheckpanel.h" #include "constraints/columncheckpanel.h" #include "constraints/columncollatepanel.h" +#include "constraints/columngeneratedpanel.h" #include "constraints/columndefaultpanel.h" #include "constraints/columnforeignkeypanel.h" #include "constraints/columnnotnullpanel.h" @@ -81,6 +82,8 @@ ConstraintPanel* ConstraintPanel::produce(SqliteCreateTable::Column::Constraint* return new ColumnDefaultPanel(); case SqliteCreateTable::Column::Constraint::COLLATE: return new ColumnCollatePanel(); + case SqliteCreateTable::Column::Constraint::GENERATED: + return new ColumnGeneratedPanel(); case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return new ColumnForeignKeyPanel(); case SqliteCreateTable::Column::Constraint::NULL_: diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp index 8bc7926..cecf5f4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp @@ -72,7 +72,6 @@ bool TableForeignKeyPanel::validate() void TableForeignKeyPanel::setDb(Db* value) { ConstraintPanel::setDb(value); - ui->sqlite2Warn->setVisible(value->getDialect() == Dialect::Sqlite2); } void TableForeignKeyPanel::constraintAvailable() @@ -361,7 +360,7 @@ void TableForeignKeyPanel::storeConfiguration() constr->foreignKey->initially = sqliteInitially(ui->initiallyCombo->currentText()); // Name - constr->name = QString::null; + constr->name = QString(); if (ui->namedCheckBox->isChecked()) constr->name = ui->nameEdit->text(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui index 7ecbe06..a0e38ec 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui @@ -36,26 +36,6 @@ - - - - - 8 - true - - - - SQLite 2 does not support foreign keys officially, -but it's okay to use them anyway. - - - Qt::AlignCenter - - - 5 - - - @@ -110,8 +90,8 @@ but it's okay to use them anyway. 0 0 - 362 - 25 + 358 + 30 diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp index 7c1c359..65c4a34 100644 --- a/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp @@ -78,7 +78,7 @@ void TablePrimaryKeyAndUniquePanel::buildColumn(SqliteCreateTable::Column* colum connect(check, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation())); QComboBox* collation = nullptr; - if (!constraint.isNull() && constraint->dialect == Dialect::Sqlite3) + if (!constraint.isNull()) { collation = new QComboBox(); collation->setMaximumWidth(ui->colHdrCollation->width()); @@ -125,7 +125,7 @@ void TablePrimaryKeyAndUniquePanel::updateColumnState(int colIdx) item = columnsLayout->itemAtPosition(colIdx, 1)->widget(); qobject_cast(item)->setEnabled(enable); - if (!constraint.isNull() && constraint->dialect == Dialect::Sqlite3) + if (!constraint.isNull()) { item = columnsLayout->itemAtPosition(colIdx, 2)->widget(); qobject_cast(item)->setEnabled(enable); @@ -182,7 +182,7 @@ void TablePrimaryKeyAndUniquePanel::storeConfiguration() SqliteCreateTable::Constraint* constr = dynamic_cast(constraint.data()); // Name - constr->name = QString::null; + constr->name = QString(); if (ui->namedCheckBox->isChecked()) constr->name = ui->namedLineEdit->text(); @@ -210,21 +210,13 @@ void TablePrimaryKeyAndUniquePanel::storeConfiguration() name = check->property(UI_PROP_COLUMN).toString(); - if (constr->dialect == Dialect::Sqlite3) - { - combo = dynamic_cast(columnsLayout->itemAtPosition(i, 1)->widget()); - collate = combo->currentText(); - if (collate.isEmpty()) - collate = QString::null; + combo = dynamic_cast(columnsLayout->itemAtPosition(i, 1)->widget()); + collate = combo->currentText(); + if (collate.isEmpty()) + collate = QString(); - combo = dynamic_cast(columnsLayout->itemAtPosition(i, 2)->widget()); - sortOrder = sqliteSortOrder(combo->currentText()); - } - else - { - combo = dynamic_cast(columnsLayout->itemAtPosition(i, 1)->widget()); - sortOrder = sqliteSortOrder(combo->currentText()); - } + combo = dynamic_cast(columnsLayout->itemAtPosition(i, 2)->widget()); + sortOrder = sqliteSortOrder(combo->currentText()); idxCol = new SqliteIndexedColumn(name, collate, sortOrder); idxCol->setParent(constr); @@ -266,24 +258,11 @@ void TablePrimaryKeyAndUniquePanel::readConstraint() check = dynamic_cast(columnsLayout->itemAtPosition(idx, 0)->widget()); check->setChecked(true); - if (constr->dialect == Dialect::Sqlite3) - { - combo = dynamic_cast(columnsLayout->itemAtPosition(idx, 1)->widget()); - combo->setCurrentText(idxCol->collate); + combo = dynamic_cast(columnsLayout->itemAtPosition(idx, 1)->widget()); + combo->setCurrentText(idxCol->collate); - combo = dynamic_cast(columnsLayout->itemAtPosition(idx, 2)->widget()); - combo->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); - } - else - { - combo = dynamic_cast(columnsLayout->itemAtPosition(idx, 1)->widget()); - combo->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); - } - } - - if (constr->dialect == Dialect::Sqlite2) - { - ui->colHdrCollation->setVisible(false); + combo = dynamic_cast(columnsLayout->itemAtPosition(idx, 2)->widget()); + combo->setCurrentText(sqliteSortOrder(idxCol->sortOrder)); } } diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp index b8ebf45..ebca434 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.cpp @@ -6,6 +6,8 @@ #include "sqlqueryview.h" #include #include +#include +#include SqlQueryItem::SqlQueryItem(QObject *parent) : QObject(parent) @@ -46,14 +48,14 @@ void SqlQueryItem::setUncommitted(bool uncommitted) QStandardItem::setData(QVariant(uncommitted), DataRole::UNCOMMITTED); if (!uncommitted) { - setOldValue(QVariant()); + clearOldValue(); setCommittingError(false); } } void SqlQueryItem::rollback() { - setValue(getOldValue(), true, true); + setValue(getOldValue(), getOldValueLimited(), true); setUncommitted(false); setDeletedRow(false); } @@ -66,6 +68,24 @@ bool SqlQueryItem::isCommittingError() const void SqlQueryItem::setCommittingError(bool isError) { QStandardItem::setData(QVariant(isError), DataRole::COMMITTING_ERROR); + if (!isError) + setCommittingErrorMessage(QString()); +} + +void SqlQueryItem::setCommittingError(bool isError, const QString& msg) +{ + setCommittingErrorMessage(msg); + setCommittingError(isError); +} + +QString SqlQueryItem::getCommittingErrorMessage() const +{ + return QStandardItem::data(DataRole::COMMITTING_ERROR_MESSAGE).toString(); +} + +void SqlQueryItem::setCommittingErrorMessage(const QString& value) +{ + QStandardItem::setData(QVariant(value), DataRole::COMMITTING_ERROR_MESSAGE); } bool SqlQueryItem::isNewRow() const @@ -96,7 +116,7 @@ bool SqlQueryItem::isDeletedRow() const void SqlQueryItem::setDeletedRow(bool isDeleted) { if (isDeleted && !getOldValue().isValid()) - setOldValue(getValue()); + rememberOldValue(); QStandardItem::setData(QVariant(isDeleted), DataRole::DELETED); } @@ -108,6 +128,13 @@ QVariant SqlQueryItem::getValue() const void SqlQueryItem::setValue(const QVariant &value, bool limited, bool loadedFromDb) { + if (!valueSettingLock.tryLock()) + { + // Triggered recursively by catching "itemChanged" event, + // that was caused by the QStandardItem::setData below. + return; + } + QVariant newValue = adjustVariantType(value); QVariant origValue = getValue(); @@ -124,7 +151,7 @@ void SqlQueryItem::setValue(const QVariant &value, bool limited, bool loadedFrom isUncommitted(); if (modified && !getOldValue().isValid()) - setOldValue(origValue); + rememberOldValue(); // This is a workaround for an issue in Qt, that uses operator== to compare values in QStandardItem::setData(). // If the old value is null and the new value is empty, then operator == returns true, which is a lie. @@ -138,46 +165,12 @@ void SqlQueryItem::setValue(const QVariant &value, bool limited, bool loadedFrom // Value for display (in a cell) will always be limited, for performance reasons setValueForDisplay("x"); // the same trick as with the DataRole::VALUE - if (!limited) - { - int theLimit = SqlQueryModel::getCellDataLengthLimit(); - switch (value.type()) - { - case QVariant::ByteArray: - { - QByteArray newBytes = newValue.toByteArray(); - if (newBytes.size() > theLimit) - { - newBytes.resize(theLimit); - setValueForDisplay(newBytes); - } - else - setValueForDisplay(newValue); - - break; - } - case QVariant::String: - { - QString newString = newValue.toString(); - if (newString.size() > theLimit) - { - newString.resize(theLimit); - setValueForDisplay(newString); - } - else - setValueForDisplay(newValue); - - break; - } - default: - setValueForDisplay(newValue); - } - } - else - setValueForDisplay(newValue); + setValueForDisplay(newValue); if (modified && getModel()) getModel()->itemValueEdited(this); + + valueSettingLock.unlock(); } bool SqlQueryItem::isLimitedValue() const @@ -195,6 +188,16 @@ void SqlQueryItem::setOldValue(const QVariant& value) QStandardItem::setData(value, DataRole::OLD_VALUE); } +bool SqlQueryItem::getOldValueLimited() const +{ + return QStandardItem::data(DataRole::OLD_VALUE_LIMITED).toBool(); +} + +void SqlQueryItem::setOldValueLimited(bool value) +{ + QStandardItem::setData(value, DataRole::OLD_VALUE_LIMITED); +} + QVariant SqlQueryItem::getValueForDisplay() const { return QStandardItem::data(DataRole::VALUE_FOR_DISPLAY); @@ -240,20 +243,27 @@ QString SqlQueryItem::getToolTip() const static const QString hdrRowTmp = "%2 %3"; static const QString constrRowTmp = "%2%3"; static const QString emptyRow = ""; + static const QString topErrorRowTmp = "%2%3"; if (!index().isValid()) - return QString::null; + return QString(); SqlQueryModelColumn* col = getColumn(); if (!col) - return QString::null; // happens when simple execution method was performed + return QString(); // happens when simple execution method was performed QStringList rows; - rows << hdrRowTmp.arg(ICONS.COLUMN.getPath()).arg(tr("Column:", "data view tooltip")).arg(col->column); - rows << rowTmp.arg(tr("Data type:", "data view")).arg(col->dataType.toString()); + if (isCommittingError()) + { + rows << topErrorRowTmp.arg(ICONS.STATUS_WARNING.getPath(), tr("Committing error:", "data view tooltip"), getCommittingErrorMessage()); + rows << emptyRow; + } + + rows << hdrRowTmp.arg(ICONS.COLUMN.getPath(), tr("Column:", "data view tooltip"), col->column); + rows << rowTmp.arg(tr("Data type:", "data view"), col->dataType.toString()); if (!col->table.isNull()) { - rows << rowTmp.arg(tr("Table:", "data view tooltip")).arg(col->table); + rows << rowTmp.arg(tr("Table:", "data view tooltip"), col->table); RowId rowId = getRowId(); QString rowIdStr; @@ -277,20 +287,32 @@ QString SqlQueryItem::getToolTip() const } rowIdStr = "[" + values.join(", ") + "]"; } - rows << rowTmp.arg("ROWID:").arg(rowIdStr); + rows << rowTmp.arg("ROWID:", rowIdStr); } if (col->constraints.size() > 0) { rows << emptyRow; - rows << hdrRowTmp.arg(ICONS.COLUMN_CONSTRAINT.getPath()).arg(tr("Constraints:", "data view tooltip")).arg(""); + rows << hdrRowTmp.arg(ICONS.COLUMN_CONSTRAINT.getPath(), tr("Constraints:", "data view tooltip"), ""); for (SqlQueryModelColumn::Constraint* constr : col->constraints) - rows << constrRowTmp.arg(constr->getIcon()->toUrl()).arg(constr->getTypeString()).arg(constr->getDetails()); + rows << constrRowTmp.arg(constr->getIcon()->toUrl(), constr->getTypeString(), constr->getDetails()); } return tableTmp.arg(rows.join("")); } +void SqlQueryItem::rememberOldValue() +{ + setOldValue(getValue()); + setOldValueLimited(isLimitedValue()); +} + +void SqlQueryItem::clearOldValue() +{ + setOldValue(QVariant()); + setOldValueLimited(false); +} + SqlQueryModelColumn* SqlQueryItem::getColumn() const { return QStandardItem::data(DataRole::COLUMN).value(); @@ -352,14 +374,14 @@ QVariant SqlQueryItem::data(int role) const { QVariant value = getValue(); if (value.isNull()) - return QBrush(CFG_UI.Colors.DataNullFg.get()); + return QApplication::style()->standardPalette().dark(); break; } case Qt::BackgroundRole: { if (isDeletedRow()) - return QBrush(CFG_UI.Colors.DataDeletedBg.get()); + return QApplication::style()->standardPalette().dark(); break; } @@ -396,25 +418,6 @@ QVariant SqlQueryItem::data(int role) const QString SqlQueryItem::loadFullData() { SqlQueryModelColumn* col = getColumn(); - - // Yes, this function won't be called in case of trying to edit the cell - it's handled in the Editor. - // However this function can be called from the FormView, to display full contents of the read-only property. - // I'll keep it for some time just in case. To be removed in future. -// if (col->editionForbiddenReason.size() > 0) -// { -// qWarning() << "Tried to load full cell which is not editable. This should be already handled in Editor class when invoking edition action."; -// return tr("This cell is not editable, because: %1").arg(SqlQueryModelColumn::resolveMessage(col->editionForbiddenReason.values().first())); -// } - - // This should not happen anymore (since WITHOUT ROWID tables should be handled properly now, - // but we will keep this here for a while, just in case. -// if (isJustInsertedWithOutRowId()) -// { -// QString msg = tr("When inserted new row to the WITHOUT ROWID table, using DEFAULT value for PRIMARY KEY, " -// "the table has to be reloaded in order to edit the new row."); -// return tr("This cell is not editable, because: %1").arg(msg); -// } - SqlQueryModel *model = getModel(); Db* db = model->getDb(); if (!db->isOpen()) @@ -423,31 +426,36 @@ QString SqlQueryItem::loadFullData() return tr("Cannot load the data for a cell that refers to the already closed database."); } - Dialect dialect = db->getDialect(); - // Query QString query; QHash queryArgs; - QString column = wrapObjIfNeeded(col->column, dialect); if (col->editionForbiddenReason.size() > 0) { static_qstring(tpl, "SELECT %1 FROM (%2) LIMIT 1 OFFSET %3"); // The query + QString colName = !col->alias.isNull() ? col->alias : col->column; + if (colName.isNull()) + colName = col->displayName; + + QString column = wrapObjIfNeeded(colName); query = tpl.arg(column, model->getQuery(), QString::number(index().row())); } else { static_qstring(tpl, "SELECT %1 FROM %2 WHERE %3"); + // Column + QString column = wrapObjIfNeeded(col->column); + // Db and table - QString source = wrapObjIfNeeded(col->table, dialect); + QString source = wrapObjIfNeeded(col->table); if (!col->database.isNull()) - source.prepend(wrapObjIfNeeded(col->database, dialect)+"."); + source.prepend(wrapObjIfNeeded(col->database)+"."); // ROWID RowIdConditionBuilder rowIdBuilder; - rowIdBuilder.setRowId(getRowId(), dialect); + rowIdBuilder.setRowId(getRowId()); QString rowId = rowIdBuilder.build(); queryArgs = rowIdBuilder.getQueryArgs(); @@ -461,7 +469,7 @@ QString SqlQueryItem::loadFullData() return results->getErrorText(); setValue(results->getSingleCell(), false, true); - return QString::null; + return QString(); } QVariant SqlQueryItem::getFullValue() diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h index 805d275..6ffddfc 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitem.h @@ -27,7 +27,9 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem DELETED = 1008, OLD_VALUE = 1009, JUST_INSERTED_WITHOUT_ROWID = 1010, - VALUE_FOR_DISPLAY = 1011 + VALUE_FOR_DISPLAY = 1011, + COMMITTING_ERROR_MESSAGE = 1012, + OLD_VALUE_LIMITED = 1013 }; }; @@ -45,6 +47,10 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem bool isCommittingError() const; void setCommittingError(bool isError); + void setCommittingError(bool isError, const QString& msg); + + QString getCommittingErrorMessage() const; + void setCommittingErrorMessage(const QString& value); bool isNewRow() const; void setNewRow(bool isNew); @@ -62,12 +68,15 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem QVariant getOldValue() const; void setOldValue(const QVariant& value); + bool getOldValueLimited() const; + void setOldValueLimited(bool value); + QVariant getValueForDisplay() const; void setValueForDisplay(const QVariant& value); /** * @brief loadFullData Reloads entire value of the cell from database. - * @return QString::null on sucess, or error string on failure. + * @return QString() on sucess, or error string on failure. */ QString loadFullData(); @@ -91,6 +100,10 @@ class GUI_API_EXPORT SqlQueryItem : public QObject, public QStandardItem void setLimitedValue(bool limited); QVariant adjustVariantType(const QVariant& value); QString getToolTip() const; + void rememberOldValue(); + void clearOldValue(); + + QMutex valueSettingLock; }; #endif // SQLQUERYITEM_H diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp index 64d37e4..1ebf06f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.cpp @@ -18,12 +18,19 @@ #include #include #include +#include +#include +#include +#include bool SqlQueryItemDelegate::warnedAboutHugeContents = false; SqlQueryItemDelegate::SqlQueryItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { + fullValueButtonOption.icon = ICONS.LOAD_FULL_VALUE; + fullValueButtonOption.iconSize = QSize(LOAD_FULL_VALUE_ICON_SIZE, LOAD_FULL_VALUE_ICON_SIZE); + fullValueButtonOption.state = QStyle::State_Enabled; } void SqlQueryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const @@ -33,10 +40,115 @@ void SqlQueryItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem & if (item->isUncommitted()) { - painter->setPen(item->isCommittingError() ? CFG_UI.Colors.DataUncommittedError.get() : CFG_UI.Colors.DataUncommitted.get()); + painter->setPen(item->isCommittingError() ? QColor(Qt::red) : QColor(Qt::blue)); painter->setBrush(Qt::NoBrush); painter->drawRect(option.rect.x(), option.rect.y(), option.rect.width()-1, option.rect.height()-1); } + + if (item->isLimitedValue()) + { + QStyleOptionViewItem opt = option; + initStyleOption(&opt, index); + QString text = displayText(item->getValue(), opt.locale); + int textWidth = opt.fontMetrics.horizontalAdvance(text); + int margin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, opt.widget) + 1; // from QCommonStyle source code + if (opt.rect.width() >= (textWidth + LOAD_FULL_VALUE_BUTTON_SIZE + margin)) + { + QStyleOptionButton button = fullValueButtonOption; + button.rect = getLoadFullValueButtonRegion(opt.rect); + button.state = QStyle::State_Enabled | QStyle::State_MouseOver; + if (lmbPressedOnButton) + button.state |= QStyle::State_Sunken | QStyle::State_Active; + + QApplication::style()->drawControl( + (mouseOverFullDataButton == index) ? QStyle::CE_PushButton : QStyle::CE_PushButtonLabel, + &button, painter); + } + } +} + +bool SqlQueryItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) +{ + switch (event->type()) + { + case QEvent::MouseButtonDblClick: + if (isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) + return true; + + break; + case QEvent::MouseButtonPress: + { + if (isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) + { + lmbPressedOnButton = true; + return true; + } + + break; + } + case QEvent::MouseButtonRelease: + if (lmbPressedOnButton && isOverFullValueButton(option.rect, dynamic_cast(event)) && isLimited(index)) + { + lmbPressedOnButton = false; + getItem(index)->loadFullData(); + return true; + } + lmbPressedOnButton = false; + break; + case QEvent::MouseMove: + { + bool isOverButton = isOverFullValueButton(option.rect, dynamic_cast(event)); + if (mouseOverFullDataButton.isValid() != isOverButton) + { + mouseOverFullDataButton = isOverButton ? index : QModelIndex(); + dynamic_cast(model)->getView()->update(index); + } + + if (!isOverButton && showingFullButtonTooltip) + { + QToolTip::hideText(); + showingFullButtonTooltip = false; + } + break; + } + default: + break; + } + + return QStyledItemDelegate::editorEvent(event, model, option, index); +} + +bool SqlQueryItemDelegate::shouldLoadFullData(const QRect& rect, QMouseEvent* event, const QModelIndex& index) +{ + return shouldLoadFullData(rect, event->x(), event->y(), index); +} + +bool SqlQueryItemDelegate::shouldLoadFullData(const QRect& rect, int x, int y, const QModelIndex& index) +{ + return isOverFullValueButton(rect, x, y) && isLimited(index); +} + +void SqlQueryItemDelegate::mouseLeftIndex(const QModelIndex& index) +{ + if (mouseOverFullDataButton == index) + mouseOverFullDataButton = QModelIndex(); +} + +bool SqlQueryItemDelegate::isLimited(const QModelIndex& index) +{ + return index.data(SqlQueryItem::DataRole::LIMITED_VALUE).toBool(); +} + +bool SqlQueryItemDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index) +{ + if (shouldLoadFullData(option.rect, event->x(), event->y(), index)) + { + QToolTip::showText(view->mapToGlobal(event->pos() - QPoint(0, 15)), tr("Load remaining part of the value")); + showingFullButtonTooltip = true; + return true; + } + + return QStyledItemDelegate::helpEvent(event, view, option, index); } QWidget* SqlQueryItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const @@ -98,32 +210,12 @@ void SqlQueryItemDelegate::setEditorData(QWidget* editor, const QModelIndex& ind void SqlQueryItemDelegate::setEditorDataForFk(QComboBox* cb, const QModelIndex& index) const { - const SqlQueryModel* queryModel = dynamic_cast(index.model()); - SqlQueryItem* item = queryModel->itemFromIndex(index); - QVariant modelData = item->getValue(); - - SqlQueryModel* cbModel = dynamic_cast(cb->model()); - SqlQueryItem* foundItem = cbModel->findAnyInColumn(0, SqlQueryItem::DataRole::VALUE, modelData); - int idx = -1; - if (foundItem) - idx = foundItem->index().row(); - - if (idx == -1 && modelData.isValid()) - { - idx = 0; - QList values; - values << modelData; - for (int i = 1; i < cbModel->columnCount(); i++) - values << QVariant(); - - cbModel->insertCustomRow(values, idx); - - SqlQueryView* view = dynamic_cast(cb->view()); - view->resizeColumnsToContents(); - view->setMinimumWidth(view->horizontalHeader()->length()); - } - cb->setCurrentIndex(idx); - cb->lineEdit()->selectAll(); + UNUSED(cb); + UNUSED(index); + // There used to be code here, but it's empty now. + // All necessary data population happens in the fkDataReady(). + // Keeping this method just for this comment and for consistency across different kind of cell editors + // (i.e. each editor has method to copy value from model to editor and another to copy from editor to model). } void SqlQueryItemDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const @@ -148,31 +240,59 @@ void SqlQueryItemDelegate::setModelDataForFk(QComboBox* cb, QAbstractItemModel* if (cbModel->isExecutionInProgress() || !cbModel->isAllDataLoaded()) return; + int idx = cb->currentIndex(); + QModelIndex cbCol0Index = cbModel->index(idx, 0); QString cbText = cb->currentText(); + bool customValue = false; + + if (cb->currentIndex() > -1 && !cbModel->itemFromIndex(cbCol0Index)) + { + // Inserted QStandardItem by QComboBox, meaning custom value (out of dropdown model) + // With Qt 5.15 (maybe earlier) QComboBox started inserting QStandardItems and setting them as currentIndex. + // Here we're extracting this inserted value and remembering this is the custom value. + cbText = cbModel->data(cbCol0Index).toString(); + customValue = true; + } + + // Regardless if its preselected value or custom value, we need to honor empty=null setting if (CFG_UI.General.KeepNullWhenEmptyValue.get() && model->data(index, Qt::EditRole).isNull() && cbText.isEmpty()) return; - int idx = cb->currentIndex(); - if (idx < 0 || idx >= cbModel->rowCount()) + SqlQueryModel* dataModel = dynamic_cast(model); + SqlQueryItem* theItem = dataModel->itemFromIndex(index); + + // If the item of the main data model cannot be found, we use plain QStandardItem method to set item's value. + // It's a safety circuit breaker. Shouldn't happend and if does so, it prints Critical debug log. + if (!theItem) { + qCritical() << "Confirmed FK edition, but there is no SqlQueryItem for which this was triggered!" << index; model->setData(index, cbText, Qt::EditRole); return; } + // Out of index? So it's custom value. Set it and it's done. + // If we deal with custom value inserted as item, we also just set it and that's it. + if (idx < 0 || idx >= cbModel->rowCount() || customValue) + { + theItem->setValue(cbText); + return; + } + + // Otherwise we will have at least 2 columns. 1st column is hidden and is meta-column holding 1/0 (1 for value matching current cell value) QList row = cbModel->getRow(idx); - if (!row[0]) + if (row.size() < 2 || !row[1]) { // This happens when inexisting value is confirmed with "Enter" key, - // cause rowCount() is apparently incremented, but items not yet. - model->setData(index, cbText, Qt::EditRole); + // and rowCount() is apparently incremented, but items not yet. + // Very likely this was addressed in recent Qt versions (5.15 or a bit earlier) + // which resulted in value insertion and the "customValue" flag above in this method. + qCritical() << "Confirmed FK edition, but there is no combo item in the row for index" << idx << ", the item is" << theItem << ", CB row count is" << cbModel->rowCount(); + theItem->setValue(cbText); return; } - QVariant comboData = row[0]->getValue(); - if (cbText != comboData.toString()) - comboData = cbText; - - model->setData(index, comboData, Qt::EditRole); + QVariant comboData = row[1]->getFullValue(); + theItem->setValue(comboData); } void SqlQueryItemDelegate::setModelDataForLineEdit(QLineEdit* editor, QAbstractItemModel* model, const QModelIndex& index) const @@ -237,24 +357,22 @@ QWidget* SqlQueryItemDelegate::getEditor(int type, QWidget* parent) const editor->setMaxLength(std::numeric_limits::max()); editor->setFrame(editor->style()->styleHint(QStyle::SH_ItemView_DrawDelegateFrame, 0, editor)); return editor; - } QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const { - static_qstring(sql, "SELECT %1 FROM %2%3"); - static_qstring(srcColTpl, "%1 AS %2"); - static_qstring(dbColTpl, "%1.%2 AS %3"); + static_qstring(sql, "SELECT %4, %1 FROM %2%3"); + static_qstring(currValueTpl, "(%1 == %2) AS %3"); + static_qstring(currNullValueTpl, "(%1 IS NULL) AS %2"); + static_qstring(dbColTpl, "%1 AS %2"); static_qstring(conditionTpl, "%1.%2 = %3.%4"); static_qstring(conditionPrefixTpl, " WHERE %1"); - static_qstring(cellLimitTpl, "substr(%2, 0, %1)"); QStringList selectedCols; - QStringList fkConfitionTables; + QStringList fkConditionTables; QStringList fkConditionCols; QStringList srcCols; Db* db = item->getModel()->getDb(); - Dialect dialect = db->getDialect(); SchemaResolver resolver(db); QList fkList = item->getColumn()->getFkConstraints(); @@ -262,39 +380,46 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const QString src; QString fullSrcCol; QString col; + QString firstSrcCol; + QStringList usedNames; for (SqlQueryModelColumn::ConstraintFk* fk : fkList) { - col = wrapObjIfNeeded(fk->foreignColumn, dialect); - src = wrapObjIfNeeded(fk->foreignTable, dialect); + col = wrapObjIfNeeded(fk->foreignColumn); + src = wrapObjIfNeeded(fk->foreignTable); if (i == 0) { - selectedCols << dbColTpl.arg(src, col, - wrapObjIfNeeded(item->getColumn()->column, dialect)); + firstSrcCol = col; + fullSrcCol = src + "." + col; + selectedCols << dbColTpl.arg(fullSrcCol, wrapObjIfNeeded(item->getColumn()->column)); } + if (fkConditionTables.contains(src, Qt::CaseInsensitive)) + continue; + srcCols = resolver.getTableColumns(src); for (const QString& srcCol : srcCols) { if (fk->foreignColumn.compare(srcCol, Qt::CaseInsensitive) == 0) continue; // Exclude matching column. We don't want the same column several times. - fullSrcCol = src + "." + wrapObjIfNeeded(srcCol, dialect); - selectedCols << srcColTpl.arg(cellLimitTpl.arg(CELL_LENGTH_LIMIT).arg(fullSrcCol), wrapObjName(fullSrcCol, dialect)); + fullSrcCol = src + "." + wrapObjIfNeeded(srcCol); + selectedCols << fullSrcCol; + usedNames << srcCol; } fkConditionCols << col; - fkConfitionTables << src; + fkConditionTables << src; i++; } QStringList conditions; - QString firstSrc = wrapObjIfNeeded(fkConfitionTables.first(), dialect); - QString firstCol = wrapObjIfNeeded(fkConditionCols.first(), dialect); - for (i = 1; i < fkConfitionTables.size(); i++) + QString firstSrc = fkConditionTables.first(); + QString firstCol = fkConditionCols.first(); + for (i = 1; i < fkConditionTables.size(); i++) { - src = wrapObjIfNeeded(fkConfitionTables[i], dialect); - col = wrapObjIfNeeded(fkConditionCols[i], dialect); + src = fkConditionTables[i]; + col = fkConditionCols[i]; conditions << conditionTpl.arg(firstSrc, firstCol, src, col); } @@ -303,7 +428,19 @@ QString SqlQueryItemDelegate::getSqlForFkEditor(SqlQueryItem* item) const conditionsStr = conditionPrefixTpl.arg(conditions.join(", ")); } - return sql.arg(selectedCols.join(", "), fkConfitionTables.join(", "), conditionsStr); + // Current value column (will be 1 for row which matches current cell value) + QVariant fullValue = item->getFullValue(); + QString currValueColName = generateUniqueName("curr", usedNames); + QString currValueExpr = fullValue.isNull() ? + currNullValueTpl.arg(firstSrcCol, currValueColName) : + currValueTpl.arg(firstSrcCol, wrapValueIfNeeded(fullValue), currValueColName); + + return sql.arg( + selectedCols.join(", "), + fkConditionTables.join(", "), + conditionsStr, + currValueExpr + ); } qlonglong SqlQueryItemDelegate::getRowCountForFkEditor(Db* db, const QString& query, bool* isError) const @@ -318,42 +455,31 @@ qlonglong SqlQueryItemDelegate::getRowCountForFkEditor(Db* db, const QString& qu return result->getSingleCell().toLongLong(); } -void SqlQueryItemDelegate::fkDataReady() +QRect SqlQueryItemDelegate::getLoadFullValueButtonRegion(const QRect& cell) { - SqlQueryModel* model = dynamic_cast(sender()); - SqlQueryView* queryView = model->getView(); - - queryView->resizeColumnsToContents(); - queryView->resizeRowsToContents(); - - int wd = queryView->horizontalHeader()->length(); - if (model->rowCount() > 10) // 10 is default visible item count for combobox - wd += queryView->verticalScrollBar()->sizeHint().width(); - - queryView->setMinimumWidth(wd); - - // Set selected combo value to initial value from the cell - QComboBox* cb = modelToFkCombo[model]; - QVariant valueFromQueryModel = modelToFkInitialValue[model]; + int x = cell.left() + cell.width() - LOAD_FULL_VALUE_BUTTON_SIZE - LOAD_FULL_VALUE_BUTTON_SIDE_MARGIN; + int y = cell.top() + (cell.height() / 2) - (LOAD_FULL_VALUE_BUTTON_SIZE / 2); + return QRect(x, y, LOAD_FULL_VALUE_BUTTON_SIZE, LOAD_FULL_VALUE_BUTTON_SIZE); +} - if (model->rowCount() > 0) - { - QModelIndex startIdx = model->index(0, 0); - QModelIndex endIdx = model->index(model->rowCount() - 1, 0); - QModelIndexList idxList = model->findIndexes(startIdx, endIdx, SqlQueryItem::DataRole::VALUE, valueFromQueryModel, 1); +bool SqlQueryItemDelegate::isOverFullValueButton(const QRect& cell, QMouseEvent* event) +{ + return isOverFullValueButton(cell, event->x(), event->y()); +} - if (idxList.size() > 0) - cb->setCurrentIndex(idxList.first().row()); - else - cb->setCurrentText(valueFromQueryModel.toString()); - } - else - cb->setCurrentText(valueFromQueryModel.toString()); +bool SqlQueryItemDelegate::isOverFullValueButton(const QRect& cell, int x, int y) +{ + QRect buttonRect = getLoadFullValueButtonRegion(cell); + return buttonRect.contains(x, y); } -void SqlQueryItemDelegate::fkDataFailed(const QString &errorText) +int SqlQueryItemDelegate::getFkViewHeaderWidth(SqlQueryView* fkView, bool includeScrollBar) const { - notifyWarn(tr("Cannot edit this cell. Details: %1").arg(errorText)); + int wd = fkView->horizontalHeader()->length(); + if (includeScrollBar && fkView->verticalScrollBar()->isVisible()) + wd += fkView->verticalScrollBar()->width(); + + return wd; } QWidget* SqlQueryItemDelegate::getFkEditor(SqlQueryItem* item, QWidget* parent, const SqlQueryModel* model) const @@ -381,49 +507,164 @@ QWidget* SqlQueryItemDelegate::getFkEditor(SqlQueryItem* item, QWidget* parent, cb->setEditable(true); // Prepare combo dropdown view. - SqlQueryView* queryView = new SqlQueryView(); - queryView->setSimpleBrowserMode(true); - connect(queryView->horizontalHeader(), &QHeaderView::sectionResized, [queryView](int, int, int) + SqlQueryView* comboView = new SqlQueryView(); + comboView->setSimpleBrowserMode(true); + comboView->setMaximumWidth(QGuiApplication::primaryScreen()->size().width()); + + fkViewParentItemSize = model->getView()->horizontalHeader()->sectionSize(item->index().column()); + connect(comboView->horizontalHeader(), &QHeaderView::sectionResized, [this, comboView, model](int, int, int) { - int wd = queryView->horizontalHeader()->length(); - if (queryView->verticalScrollBar()->isVisible()) - wd += queryView->verticalScrollBar()->width(); + if (!model->isAllDataLoaded()) + return; - queryView->setMinimumWidth(wd); + updateComboViewGeometry(comboView, false); }); - SqlQueryModel* queryModel = new SqlQueryModel(queryView); - queryModel->setView(queryView); + SqlQueryModel* comboModel = new SqlQueryModel(comboView); + comboModel->setView(comboView); // Mapping of model to cb, so we can update combo when data arrives. - modelToFkInitialValue[queryModel] = item->getValue(); - modelToFkCombo[queryModel] = cb; - connect(cb, &QComboBox::destroyed, [this, queryModel](QObject*) + modelToFkInitialValue[comboModel] = item->getFullValue(); + modelToFkCombo[comboModel] = cb; + connect(cb, &QComboBox::destroyed, [this, comboModel](QObject*) + { + modelToFkCombo.remove(comboModel); + modelToFkInitialValue.remove(comboModel); + }); + + connect(cb, QOverload::of(&QComboBox::currentIndexChanged), [this, comboModel](int idx) { - modelToFkCombo.remove(queryModel); - modelToFkInitialValue.remove(queryModel); + if (idx > -1 && idx < comboModel->getTotalRowsReturned() && comboModel->isAllDataLoaded()) + comboModel->itemFromIndex(idx, 1)->loadFullData(); }); // When execution is done, update combo. - connect(queryModel, SIGNAL(executionSuccessful()), this, SLOT(fkDataReady())); - connect(queryModel, SIGNAL(executionFailed(QString)), this, SLOT(fkDataFailed(QString))); + connect(comboModel, SIGNAL(executionSuccessful()), this, SLOT(fkDataReady())); + connect(comboModel, SIGNAL(executionFailed(QString)), this, SLOT(fkDataFailed(QString))); // Setup combo, model, etc. - cb->setModel(queryModel); - cb->setView(queryView); - cb->setModelColumn(0); + cb->setModel(comboModel); + cb->setView(comboView); + cb->setModelColumn(1); + cb->view()->viewport()->installEventFilter(new FkComboFilter(this, comboView, cb)); + cb->view()->viewport()->installEventFilter(new FkComboShowFilter(this, comboView, cb)); + cb->view()->verticalScrollBar()->installEventFilter(new FkComboShowFilter(this, comboView, cb)); + + comboModel->setHardRowLimit(MAX_ROWS_FOR_FK); + comboModel->setCellDataLengthLimit(FK_CELL_LENGTH_LIMIT); + comboModel->setDb(db); + comboModel->setQuery(sql); + comboModel->setAsyncMode(false); + comboModel->executeQuery(); + + comboView->verticalHeader()->setVisible(false); + comboView->horizontalHeader()->setVisible(true); + comboView->setSelectionMode(QAbstractItemView::SingleSelection); + comboView->setSelectionBehavior(QAbstractItemView::SelectRows); - queryModel->setHardRowLimit(MAX_ROWS_FOR_FK); - queryModel->setDb(db); - queryModel->setQuery(sql); - queryModel->setAsyncMode(false); - queryModel->executeQuery(); + return cb; +} - queryView->verticalHeader()->setVisible(false); - queryView->horizontalHeader()->setVisible(true); - queryView->setSelectionMode(QAbstractItemView::SingleSelection); - queryView->setSelectionBehavior(QAbstractItemView::SelectRows); +void SqlQueryItemDelegate::fkDataReady() +{ + SqlQueryModel* model = dynamic_cast(sender()); + SqlQueryView* queryView = model->getView(); - return cb; + queryView->horizontalHeader()->setSectionHidden(0, true); + queryView->resizeColumnsToContents(); + queryView->resizeRowsToContents(); + + updateComboViewGeometry(queryView, true); + + // Set selected combo value to initial value from the cell + QComboBox* cb = modelToFkCombo[model]; + QVariant valueFromQueryModel = modelToFkInitialValue[model]; + + if (model->rowCount() > 0) + { + QModelIndex startIdx = model->index(0, 0); + QModelIndex endIdx = model->index(model->rowCount() - 1, 0); + QModelIndexList idxList = model->findIndexes(startIdx, endIdx, SqlQueryItem::DataRole::VALUE, 1, 1); + + if (idxList.size() > 0) + { + model->itemFromIndex(idxList.first().row(), 1)->loadFullData(); + cb->setCurrentIndex(idxList.first().row()); + } + else + { + cb->setCurrentIndex(-1); + cb->setEditText(valueFromQueryModel.toString()); + } + } + else + cb->setEditText(valueFromQueryModel.toString()); + + cb->lineEdit()->selectAll(); +} + +void SqlQueryItemDelegate::fkDataFailed(const QString &errorText) +{ + notifyWarn(tr("Cannot edit this cell. Details: %1").arg(errorText)); } +void SqlQueryItemDelegate::updateComboViewGeometry(SqlQueryView* comboView, bool initial) const +{ + int wd = getFkViewHeaderWidth(comboView, true); + comboView->setMinimumWidth(qMin(qMax(fkViewParentItemSize, wd), comboView->maximumWidth())); + + if (initial && wd < comboView->minimumWidth()) + { + // First time, upon showing up + int currentSize = comboView->horizontalHeader()->sectionSize(1); + int gap = comboView->minimumWidth() - wd; + comboView->horizontalHeader()->resizeSection(1, currentSize + gap); + } + + QWidget* container = comboView->parentWidget(); + if (container->width() > comboView->minimumWidth()) + { + container->setMaximumWidth(comboView->minimumWidth()); + container->resize(comboView->minimumWidth(), container->height()); + } +} + + +SqlQueryItemDelegate::FkComboFilter::FkComboFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent) + : QObject(parent), delegate(delegate), comboView(comboView) +{ +} + +bool SqlQueryItemDelegate::FkComboFilter::eventFilter(QObject* obj, QEvent* event) +{ + UNUSED(obj); + if (event->type() == QEvent::MouseButtonRelease) + { + QMouseEvent* e = dynamic_cast(event); + QModelIndex idx = comboView->indexAt(QPoint(e->pos())); + if (!idx.isValid()) + return false; + + SqlQueryItem* item = comboView->getModel()->itemFromIndex(idx); + if (shouldLoadFullData(comboView->visualRect(idx), e, idx)) + { + item->loadFullData(); + return true; + } + } + return false; +} + +SqlQueryItemDelegate::FkComboShowFilter::FkComboShowFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent) + : QObject(parent), delegate(delegate), comboView(comboView) +{ +} + +bool SqlQueryItemDelegate::FkComboShowFilter::eventFilter(QObject* obj, QEvent* event) +{ + UNUSED(obj); + if (event->type() == QEvent::Show) + delegate->updateComboViewGeometry(comboView, true); + + return false; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h index a35ef9a..dbd2192 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryitemdelegate.h @@ -10,6 +10,7 @@ class SqlQueryItem; class QComboBox; class QStandardItemModel; class SqlQueryModel; +class SqlQueryView; class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate { @@ -18,12 +19,44 @@ class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate explicit SqlQueryItemDelegate(QObject *parent = 0); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); + bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index); QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; QString displayText(const QVariant & value, const QLocale & locale) const; void setEditorData(QWidget * editor, const QModelIndex & index) const; void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + void mouseLeftIndex(const QModelIndex& index); private: + class FkComboFilter : public QObject + { + public: + explicit FkComboFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent = 0); + bool eventFilter(QObject *obj, QEvent *event); + + private: + const SqlQueryItemDelegate* delegate = nullptr; + SqlQueryView* comboView = nullptr; + }; + + class FkComboShowFilter : public QObject + { + public: + explicit FkComboShowFilter(const SqlQueryItemDelegate* delegate, SqlQueryView* comboView, QObject* parent = 0); + bool eventFilter(QObject *obj, QEvent *event); + + private: + const SqlQueryItemDelegate* delegate = nullptr; + SqlQueryView* comboView = nullptr; + }; + + static QRect getLoadFullValueButtonRegion(const QRect& cell); + static bool isOverFullValueButton(const QRect& cell, QMouseEvent* event); + static bool isOverFullValueButton(const QRect& cell, int x, int y); + static bool shouldLoadFullData(const QRect& rect, QMouseEvent* event, const QModelIndex& index); + static bool shouldLoadFullData(const QRect& rect, int x, int y, const QModelIndex& index); + static bool isLimited(const QModelIndex &index); + SqlQueryItem* getItem(const QModelIndex &index) const; QWidget* getEditor(int type, QWidget* parent) const; QWidget* getFkEditor(SqlQueryItem* item, QWidget* parent, const SqlQueryModel *model) const; @@ -33,14 +66,24 @@ class GUI_API_EXPORT SqlQueryItemDelegate : public QStyledItemDelegate void setModelDataForLineEdit(QLineEdit* editor, QAbstractItemModel* model, const QModelIndex& index) const; QString getSqlForFkEditor(SqlQueryItem* item) const; qlonglong getRowCountForFkEditor(Db* db, const QString& query, bool *isError) const; + int getFkViewHeaderWidth(SqlQueryView* fkView, bool includeScrollBar) const; + void updateComboViewGeometry(SqlQueryView* comboView, bool initial) const; + QStyleOptionButton fullValueButtonOption; QSet editorsWithAsyncExecution; + QModelIndex mouseOverFullDataButton; + bool showingFullButtonTooltip = false; + bool lmbPressedOnButton = false; + mutable int fkViewParentItemSize = 0; mutable QHash modelToFkCombo; mutable QHash modelToFkInitialValue; static bool warnedAboutHugeContents; + static const int LOAD_FULL_VALUE_BUTTON_SIZE = 18; + static const int LOAD_FULL_VALUE_BUTTON_SIDE_MARGIN = 2; + static const int LOAD_FULL_VALUE_ICON_SIZE = 12; static const qlonglong MAX_ROWS_FOR_FK = 10000L; - static const int CELL_LENGTH_LIMIT = 30; + static const int FK_CELL_LENGTH_LIMIT = 30; static const int HUGE_CONTENTS_WARNING_LIMIT = 32767; // pow(2, 16) / 2 - 1 private slots: diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp index ff025df..dd3f655 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.cpp @@ -12,6 +12,9 @@ #include "datagrid/sqlqueryrownummodel.h" #include "services/dbmanager.h" #include "querygenerator.h" +#include "parser/lexer.h" +#include "common/compatibility.h" +#include "mainwindow.h" #include #include #include @@ -100,14 +103,14 @@ void SqlQueryModel::executeQueryInternal() { if (!db || !db->isValid()) { - notifyWarn("Cannot execute query on undefined or invalid database."); + notifyWarn(tr("Cannot execute query on undefined or invalid database.")); internalExecutionStopped(); return; } - if (query.isEmpty()) + if (isEmptyQuery()) { - notifyWarn("Cannot execute empty query."); + notifyWarn(tr("Cannot execute empty query.")); internalExecutionStopped(); return; } @@ -138,6 +141,23 @@ void SqlQueryModel::executeQueryInternal() queryExecutor->exec(); } +bool SqlQueryModel::isEmptyQuery() const +{ + if (query.trimmed().isEmpty()) + return true; + + TokenList tokens = Lexer::tokenize(query); + auto foundIter = std::find_if(tokens.begin(), tokens.end(), [](const TokenPtr& token) + { + return token->isMeaningful(); + }); + + if (foundIter != tokens.end()) + return false; + + return true; +} + void SqlQueryModel::internalExecutionStopped() { reloading = false; @@ -189,6 +209,12 @@ int SqlQueryModel::getCellDataLengthLimit() return cellDataLengthLimit; } +void SqlQueryModel::setCellDataLengthLimit(int value) +{ + cellDataLengthLimit = value; + queryExecutor->setDataLengthLimit(value); +} + QModelIndexList SqlQueryModel::findIndexes(int role, const QVariant& value, int hits) const { QModelIndex startIdx = index(0, 0); @@ -258,6 +284,26 @@ QList > SqlQueryModel::groupItemsByRows(const QList> SqlQueryModel::groupColumnsByTable(const QVector& columns) +{ + QHash> columnsByTable; + AliasedTable table; + for (SqlQueryModelColumn* col : columns) + { + if (!col->column.isNull()) + { + table.setDatabase(col->database.toLower()); + table.setTable(col->table.toLower()); + table.setTableAlias(col->tableAlias.toLower()); + columnsByTable[table] << col; + } + else + columnsByTable[AliasedTable()] << col; + } + + return columnsByTable; +} + QHash > SqlQueryModel::groupItemsByTable(const QList& items) { QHash> itemsByTable; @@ -325,7 +371,7 @@ void SqlQueryModel::commit(const QList& items) commitInternal(filterOutCommittedItems(items)); } -bool SqlQueryModel::commitRow(const QList& itemsInRow) +bool SqlQueryModel::commitRow(const QList& itemsInRow, QList& successfulCommitHandlers) { const SqlQueryItem* item = itemsInRow.at(0); if (!item) @@ -334,11 +380,11 @@ bool SqlQueryModel::commitRow(const QList& itemsInRow) return true; } if (item->isNewRow()) - return commitAddedRow(getRow(item->row())); // we need to get all items again, in case of selective commit + return commitAddedRow(getRow(item->row()), successfulCommitHandlers); // we need to get all items again, in case of selective commit else if (item->isDeletedRow()) - return commitDeletedRow(getRow(item->row())); // we need to get all items again, in case of selective commit + return commitDeletedRow(getRow(item->row()), successfulCommitHandlers); // we need to get all items again, in case of selective commit else - return commitEditedRow(itemsInRow); + return commitEditedRow(itemsInRow, successfulCommitHandlers); } void SqlQueryModel::rollbackRow(const QList& itemsInRow) @@ -357,6 +403,127 @@ void SqlQueryModel::rollbackRow(const QList& itemsInRow) rollbackEditedRow(itemsInRow); } +void SqlQueryModel::refreshGeneratedColumns(const QList& items) +{ + QHash resultValues; + refreshGeneratedColumns(items, resultValues, RowId()); + for (auto resultIt = resultValues.begin(); resultIt != resultValues.end(); resultIt++) + { + SqlQueryItem* item = resultIt.key(); + item->setValue(resultIt.value(), false, true); + item->setTextAlignment(findValueAlignment(resultIt.value(), item->getColumn())); + } +} + +void SqlQueryModel::refreshGeneratedColumns(const QList& items, QHash& values, const RowId& insertedRowId) +{ + // Find out which columns are generated + int colIdx = 0; + QVector generatedColumns; + QHash generatedColumnIdx; + for (const SqlQueryModelColumnPtr& column : columns) + { + if (column->isGenerated()) + { + generatedColumns << column.data(); + generatedColumnIdx[column.data()] = colIdx; + } + colIdx++; + } + if (generatedColumns.isEmpty()) + return; + + QHash> columnsByTable = groupColumnsByTable(generatedColumns); + + // Filter out deleted items - we won't update generated values for them + QList insertedOrAlteredItems = filter(items, [&](SqlQueryItem* item) -> bool + { + if (item->isNewRow()) + return !insertedRowId.isEmpty(); + + return !item->isDeletedRow(); + }); + + SelectCellsQueryBuilder builder; + QHash> itemsByTable = groupItemsByTable(insertedOrAlteredItems); + for (auto itemsIt = itemsByTable.begin(); itemsIt != itemsByTable.end(); itemsIt++) + { + const AliasedTable table = itemsIt.key(); + QVector tableColumns = columnsByTable[table]; + if (tableColumns.isEmpty()) + continue; + + builder.setDatabase(wrapObjIfNeeded(table.getDatabase())); + builder.setTable(wrapObjIfNeeded(table.getTable())); + + QHash> itemsPerRowId; + for (SqlQueryItem* item : itemsIt.value()) + { + RowId rowId = insertedRowId.isEmpty() ? item->getRowId() : insertedRowId; + builder.addRowId(rowId); + for (SqlQueryModelColumn* tableCol : tableColumns) + itemsPerRowId[rowId] << itemFromIndex(item->row(), generatedColumnIdx[tableCol]); + } + + for (SqlQueryModelColumn* tableCol : tableColumns) + builder.addColumn(tableCol->column); + + unite(values, readCellValues(builder, itemsPerRowId)); + builder.clear(); + } +} + +QHash SqlQueryModel::readCellValues(SelectCellsQueryBuilder& queryBuilder, const QHash>& itemsPerRowId) +{ + QHash values; + + // Executing query + SqlQueryPtr results = db->exec(queryBuilder.build(), queryBuilder.getQueryArgs(), Db::Flag::PRELOAD); + + // Handling error + if (results->isError()) + { + qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. Error from database was:" + << results->getErrorText(); + + for (SqlQueryItem* item : concatSet(itemsPerRowId.values())) + values[item] = QVariant(); + + return values; + } + + if (!results->hasNext()) + { + qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. There were no result rows."; + + for (SqlQueryItem* item : concatSet(itemsPerRowId.values())) + values[item] = QVariant(); + + return values; + } + + // Reading a row + while (results->hasNext()) + { + SqlResultsRowPtr row = results->next(); + if (row->valueList().size() != queryBuilder.getColumnCount()) + { + qCritical() << "Could not load cell values for table" << queryBuilder.getTable() << ", so defaulting them with NULL. Number of columns from results was invalid:" + << row->valueList().size() << ", while expected:" << queryBuilder.getColumnCount(); + + for (SqlQueryItem* item : concatSet(itemsPerRowId.values())) + values[item] = QVariant(); + + return values; + } + + for (SqlQueryItem* item : itemsPerRowId[queryBuilder.readRowId(row)]) + values[item] = row->value(item->getColumn()->column); + } + + return values; +} + void SqlQueryModel::rollback() { QList items = findItems(SqlQueryItem::DataRole::UNCOMMITTED, true); @@ -399,14 +566,13 @@ void SqlQueryModel::commitInternal(const QList& items) int step = 1; rowsDeletedSuccessfullyInTheCommit.clear(); + QList successfulCommitHandlers; // list of lambdas to execute after all rows were committed successfully bool ok = true; for (const QList& itemsInRow : groupedItems) { - if (!commitRow(itemsInRow)) - { + if (!commitRow(itemsInRow, successfulCommitHandlers)) ok = false; - break; - } + emit committingStepFinished(step++); } @@ -431,6 +597,13 @@ void SqlQueryModel::commitInternal(const QList& items) } else { + // Call all successfull commit handler to refresh cell metadata, etc. + for (CommitSuccessfulHandler& handler : successfulCommitHandlers) + handler(); + + // Refresh generated columns of altered rows + refreshGeneratedColumns(itemsLeft); + // Committed successfully for (SqlQueryItem* item : itemsLeft) { @@ -438,7 +611,8 @@ void SqlQueryModel::commitInternal(const QList& items) item->setNewRow(false); } - qSort(rowsDeletedSuccessfullyInTheCommit); + // Physically delete rows + std::sort(rowsDeletedSuccessfullyInTheCommit.begin(), rowsDeletedSuccessfullyInTheCommit.end()); int removeOffset = 0; for (int row : rowsDeletedSuccessfullyInTheCommit) removeRow(row - removeOffset++); // deleting row decrements all rows below @@ -585,13 +759,14 @@ int SqlQueryModel::getCurrentPage(bool includeOneBeingLoaded) const return result < 0 ? 0 : result; } -bool SqlQueryModel::commitAddedRow(const QList& itemsInRow) +bool SqlQueryModel::commitAddedRow(const QList& itemsInRow, QList& successfulCommitHandlers) { UNUSED(itemsInRow); + UNUSED(successfulCommitHandlers); return false; } -bool SqlQueryModel::commitEditedRow(const QList& itemsInRow) +bool SqlQueryModel::commitEditedRow(const QList& itemsInRow, QList& successfulCommitHandlers) { if (itemsInRow.size() == 0) { @@ -599,8 +774,6 @@ bool SqlQueryModel::commitEditedRow(const QList& itemsInRow) return true; } - Dialect dialect = db->getDialect(); - QHash> itemsByTable = groupItemsByTable(itemsInRow); // Values @@ -631,15 +804,15 @@ bool SqlQueryModel::commitEditedRow(const QList& itemsInRow) // RowId queryBuilder.clear(); rowId = items.first()->getRowId(); - queryBuilder.setRowId(rowId, dialect); + queryBuilder.setRowId(rowId); newRowId = getNewRowId(rowId, items); // if any of item updates any of rowid columns, then this will be different than initial rowid // Database and table - queryBuilder.setTable(wrapObjIfNeeded(table.getTable(), dialect)); + queryBuilder.setTable(wrapObjIfNeeded(table.getTable())); if (!table.getDatabase().isNull()) { QString tableDb = getDatabaseForCommit(table.getDatabase()); - queryBuilder.setDatabase(wrapObjIfNeeded(tableDb, dialect)); + queryBuilder.setDatabase(wrapObjIfNeeded(tableDb)); } for (SqlQueryItem* item : items) @@ -647,12 +820,14 @@ bool SqlQueryModel::commitEditedRow(const QList& itemsInRow) col = item->getColumn(); if (col->editionForbiddenReason.size() > 0 || item->isJustInsertedWithOutRowId()) { - notifyError(tr("Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it.")); + QString errMsg = tr("Tried to commit a cell which is not editable (yet modified and waiting for commit)! This is a bug. Please report it."); + item->setCommittingError(true, errMsg); + notifyError(errMsg); return false; } // Column - queryBuilder.addColumn(wrapObjIfNeeded(col->column, dialect)); + queryBuilder.addColumn(wrapObjIfNeeded(col->column)); } // Completing query @@ -670,23 +845,31 @@ bool SqlQueryModel::commitEditedRow(const QList& itemsInRow) SqlQueryPtr results = db->exec(query, queryArgs); if (results->isError()) { + QString errMsg = tr("An error occurred while committing the data: %1").arg(results->getErrorText()); for (SqlQueryItem* item : items) - item->setCommittingError(true); + item->setCommittingError(true, errMsg); - notifyError(tr("An error occurred while committing the data: %1").arg(results->getErrorText())); + notifyError(errMsg); return false; } // After successful commit, check if RowId was modified and upadate it accordingly if (rowId != newRowId) - updateRowIdForAllItems(table, rowId, newRowId); + { + // ...and do it with deferred lambda, so only after all rows were successully committed + successfulCommitHandlers << [this, table, rowId, newRowId]() + { + updateRowIdForAllItems(table, rowId, newRowId); + }; + } } return true; } -bool SqlQueryModel::commitDeletedRow(const QList& itemsInRow) +bool SqlQueryModel::commitDeletedRow(const QList& itemsInRow, QList& successfulCommitHandlers) { + UNUSED(successfulCommitHandlers); if (itemsInRow.size() == 0) { qCritical() << "No items passed to SqlQueryModel::commitDeletedRow()."; @@ -766,7 +949,7 @@ bool SqlQueryModel::loadData(SqlQueryPtr results) view->horizontalHeader()->show(); // Read columns first. It will be needed later. - readColumns(); + bool rowsLimited = readColumns(); // Load data SqlResultsRowPtr row; @@ -782,7 +965,7 @@ bool SqlQueryModel::loadData(SqlQueryPtr results) if (!row) break; - rowList << loadRow(row); + rowList << loadRow(row, results); if ((rowIdx % 50) == 0) { @@ -794,6 +977,12 @@ bool SqlQueryModel::loadData(SqlQueryPtr results) rowIdx++; } + if (rowsLimited && rowIdx >= columnRatioBasedRowLimit) + { + NOTIFY_MANAGER->info(tr("Number of rows per page was decreased to %1 due to number of columns (%2) in the data view.") + .arg(columnRatioBasedRowLimit).arg(columns.size())); + } + rowIdx = 0; for (const QList& row : rowList) insertRow(rowIdx++, row); @@ -802,8 +991,11 @@ bool SqlQueryModel::loadData(SqlQueryPtr results) return true; } -QList SqlQueryModel::loadRow(SqlResultsRowPtr row) +QList SqlQueryModel::loadRow(SqlResultsRowPtr row, SqlQueryPtr results) { + QStringList columnNames = results->getColumnNames(); + BiStrHash typeColumnToResColumn = queryExecutor->getTypeColumns(); + QList itemList; SqlQueryItem* item = nullptr; RowId rowId; @@ -812,7 +1004,7 @@ QList SqlQueryModel::loadRow(SqlResultsRowPtr row) { item = new SqlQueryItem(); rowId = getRowIdValue(row, colIdx); - updateItem(item, value, colIdx, rowId); + updateItem(item, value, colIdx, rowId, row, columnNames, typeColumnToResColumn); itemList << item; colIdx++; } @@ -847,15 +1039,54 @@ RowId SqlQueryModel::getRowIdValue(SqlResultsRowPtr row, int columnIdx) return rowId; } + +void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId, SqlResultsRowPtr row, + const QStringList& columnNames, const BiStrHash& typeColumnToResColumn) +{ + if (columnIndex >= columnNames.size()) + { + updateItem(item, value, columnIndex, rowId); + return; + } + + QString colName = columnNames[columnIndex]; + if (typeColumnToResColumn.isEmpty() || !typeColumnToResColumn.containsRight(colName)) + { + updateItem(item, value, columnIndex, rowId); + return; + } + + QString colTypeColumnName = typeColumnToResColumn.valueByRight(colName); + QString colTypeStr = row->value(colTypeColumnName).toString(); + SqliteDataType sqliteDataType = toSqliteDataType(colTypeStr); + + switch (sqliteDataType) + { + case SqliteDataType::INTEGER: + case SqliteDataType::REAL: + updateItem(item, value, columnIndex, rowId, Qt::AlignRight); + break; + case SqliteDataType::_NULL: + case SqliteDataType::TEXT: + case SqliteDataType::BLOB: + updateItem(item, value, columnIndex, rowId, Qt::AlignLeft); + break; + case SqliteDataType::UNKNOWN: + updateItem(item, value, columnIndex, rowId); + break; + } +} + void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId) { SqlQueryModelColumnPtr column = columns[columnIndex]; - Qt::Alignment alignment; + Qt::Alignment alignment = findValueAlignment(value, column.data()); + updateItem(item, value, columnIndex, rowId, alignment); +} - if (column->isNumeric() && isNumeric(value)) - alignment = Qt::AlignRight|Qt::AlignVCenter; - else - alignment = Qt::AlignLeft|Qt::AlignVCenter; +void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId, Qt::Alignment alignment) +{ + SqlQueryModelColumnPtr column = columns[columnIndex]; // This should be equal at most, unless we have UTF-8 string, than there might be more bytes. // If less, than it's not limited. @@ -868,6 +1099,14 @@ void SqlQueryModel::updateItem(SqlQueryItem* item, const QVariant& value, int co item->setRowId(rowId); } +Qt::Alignment SqlQueryModel::findValueAlignment(const QVariant& value, SqlQueryModelColumn* column) +{ + if ((column->isNumeric() || column->isNull()) && isNumeric(value)) + return Qt::AlignRight|Qt::AlignVCenter; + else + return Qt::AlignLeft|Qt::AlignVCenter; +} + RowId SqlQueryModel::getNewRowId(const RowId& currentRowId, const QList items) { if (currentRowId.size() > 1) @@ -941,7 +1180,7 @@ bool SqlQueryModel::supportsModifyingQueriesInMenu() const return false; } -void SqlQueryModel::readColumns() +bool SqlQueryModel::readColumns() { columns.clear(); tableToRowIdColumn.clear(); @@ -980,15 +1219,15 @@ void SqlQueryModel::readColumns() // Rows limit to avoid out of memory problems columnRatioBasedRowLimit = -1; int rowsPerPage = getRowsPerPage(); - if (!columns.isEmpty()) - columnRatioBasedRowLimit = 150000 / columns.size(); + if (!columns.isEmpty() && CFG_UI.General.LimitRowsForManyColumns.get()) + columnRatioBasedRowLimit = 50000 / columns.size(); - if (columnRatioBasedRowLimit > -1 && columnRatioBasedRowLimit < rowsPerPage) - NOTIFY_MANAGER->info(tr("Number of rows per page was decreased to %1 due to number of columns (%2) in the data view.") - .arg(columnRatioBasedRowLimit).arg(columns.size())); + bool rowsLimited = (columnRatioBasedRowLimit > -1 && columnRatioBasedRowLimit < rowsPerPage); // We have fresh info about columns structureOutOfDate = false; + + return rowsLimited; } void SqlQueryModel::readColumnDetails() @@ -1037,6 +1276,8 @@ void SqlQueryModel::readColumnDetails() modelConstraint = SqlQueryModelColumn::Constraint::create(constrPtr); if (modelConstraint) modelColumn->constraints << modelConstraint; + + modelColumn->postProcessConstraints(); } // Table constraints @@ -1060,7 +1301,6 @@ QHash SqlQueryModel::readTableDetails QHash results; SqliteQueryPtr query; SqliteCreateTablePtr createTable; - Dialect dialect = db->getDialect(); SchemaResolver resolver(getDb()); QString database; AliasedTable table; @@ -1092,7 +1332,7 @@ QHash SqlQueryModel::readTableDetails { // Column details TableDetails::ColumnDetails columnDetails; - columnName = stripObjName(columnStmt->name, dialect); + columnName = stripObjName(columnStmt->name); // Column type if (columnStmt->type) @@ -1229,9 +1469,14 @@ void SqlQueryModel::handleExecFailed(int code, QString errorMessage) void SqlQueryModel::resultsCountingFinished(quint64 rowsAffected, quint64 rowsReturned, int totalPages) { + // TotalPages provided by QueryExecutor is wrong if there are tons of columns in results, as row limit is applied + // to prevent memory exhaustion. QueryExecutor is not aware of the limit at the moment of execution, so it calculates + // total number of pages incorrectly. + UNUSED(totalPages); + this->rowsAffected = rowsAffected; this->totalRowsReturned = rowsReturned; - this->totalPages = totalPages; + this->totalPages = (int)qCeil(((double)totalRowsReturned) / ((double)getRowsPerPage())); detachDatabases(); emit totalRowsAndPagesAvailable(); emit storeExecutionInHistory(); @@ -1356,9 +1601,10 @@ void SqlQueryModel::storeStep1NumbersFromExecution() if (!queryExecutor->getSkipRowCounting()) { - totalPages = queryExecutor->getTotalPages(); if (!queryExecutor->isRowCountingRequired()) totalRowsReturned = queryExecutor->getTotalRowsReturned(); + + totalPages = (int)qCeil(((double)totalRowsReturned) / ((double)getRowsPerPage())); } } @@ -1572,7 +1818,7 @@ int SqlQueryModel::getRowsPerPage() const if (hardRowLimit > -1) rowsPerPage = hardRowLimit; - if (columnRatioBasedRowLimit > -1 && columnRatioBasedRowLimit < rowsPerPage) + if (CFG_UI.General.LimitRowsForManyColumns.get() && columnRatioBasedRowLimit > -1 && columnRatioBasedRowLimit < rowsPerPage) rowsPerPage = columnRatioBasedRowLimit; return rowsPerPage; @@ -1611,7 +1857,7 @@ void SqlQueryModel::setDesiredColumnWidth(int colIdx, int width) return; } - Column column(columnModel->database, columnModel->table, columnModel->column); + AliasedColumn column(columnModel->database, columnModel->table, columnModel->column, columnModel->displayName); columnWidths[column] = width; } @@ -1621,7 +1867,7 @@ int SqlQueryModel::getDesiredColumnWidth(int colIdx) if (!columnModel) return -1; - Column column(columnModel->database, columnModel->table, columnModel->column); + AliasedColumn column(columnModel->database, columnModel->table, columnModel->column, columnModel->displayName); if (!columnWidths.contains(column)) return -1; @@ -1683,11 +1929,20 @@ void SqlQueryModel::deleteSelectedRows() { QList selectedItems = view->getSelectedItems(); QSet rows; + QSet newRows; for (SqlQueryItem* item : selectedItems) - rows << item->index().row(); + { + int row = item->index().row(); + if (item->isNewRow()) + newRows << row; + + rows << row; + } - QList rowList = rows.toList(); - qSort(rowList); + QList rowList = rows.values(); + QList newRowList = newRows.values(); + sSort(rowList); + sSort(newRowList); QList newItemsToDelete; int cols = columnCount(); @@ -1707,8 +1962,25 @@ void SqlQueryModel::deleteSelectedRows() } } - for (SqlQueryItem* item : newItemsToDelete) - removeRow(item->index().row()); + if (newItemsToDelete.size() > 0) + { + QStringList rowNumbers; + int rowBase = getRowsPerPage() * getCurrentPage(); + for (int row : newRowList) + rowNumbers << QString::number(rowBase + row + 1); // +1 for visual representation of row, which in code is 0-based + + QMessageBox::StandardButton userResponse = QMessageBox::question(MAINWINDOW, tr("Delete rows"), + tr("You're about to delete newly inserted rows that are not committed yet. Row numbers: %1\n" + "Such deletion will be permanent. Are you sure you want to delete them?") + .arg(rowNumbers.join(", "))); + + if (userResponse == QMessageBox::Yes) + { + for (SqlQueryItem* item : newItemsToDelete) + removeRow(item->index().row()); + } + } + emit commitStatusChanged(getUncommittedItems().size() > 0); } @@ -1832,6 +2104,39 @@ void SqlQueryModel::loadFullDataForEntireRow(int row) } } +void SqlQueryModel::loadFullDataForEntireColumn(int column) +{ + int rowCnt = rowCount(); + SqlQueryItem *item = nullptr; + for (int row = 0; row < rowCnt; row++) + { + item = itemFromIndex(row, column); + if (!item) + continue; + + if (!item->isLimitedValue()) + continue; + + item->loadFullData(); + } +} + +bool SqlQueryModel::doesColumnHaveLimitedValues(int column) const +{ + int rowCnt = rowCount(); + SqlQueryItem *item = nullptr; + for (int row = 0; row < rowCnt; row++) + { + item = itemFromIndex(row, column); + if (!item) + continue; + + if (item->isLimitedValue()) + return true; + } + return false; +} + void SqlQueryModel::CommitUpdateQueryBuilder::clear() { database.clear(); @@ -1889,3 +2194,115 @@ QStringList SqlQueryModel::CommitUpdateQueryBuilder::getAssignmentArgs() const { return assignmentArgs; } + +void SqlQueryModel::SelectCellsQueryBuilder::addRowId(const RowId& rowId) +{ + if (includedRowIds.contains(rowId)) + return; + + static_qstring(argTempalate, ":rowIdArg%1"); + + QStringList parts; + QString arg; + QHashIterator it(rowId); + while (it.hasNext()) + { + it.next(); + arg = argTempalate.arg(argSquence++); + queryArgs[arg] = it.value(); + parts << wrapObjIfNeeded(it.key()) + " = " + arg; + } + conditions << parts.join(" AND "); + + if (rowIdColumns.isEmpty()) + { + rowIdColumns = toSet(rowId.keys()); + for (const QString& col : rowIdColumns) + columns << wrapObjIfNeeded(col); + } + + includedRowIds << rowId; +} + +QString SqlQueryModel::SelectCellsQueryBuilder::build() +{ + QString conditionsString = conditions.join(" OR "); + + QString dbAndTable; + if (!database.isNull()) + dbAndTable += database + "."; + + dbAndTable += table; + + QStringList selectColumns; + for (const QString& col : columns) + { + // Explicit "ROWID" alias, because - if ROWID column + // is the INTEGER PRIMARY KEY column - SQLite reports + // column name as its table column name and it does not + // match the RowId columns when requested in #readRowId(). + if (col.toUpper() == "ROWID") + selectColumns << "ROWID AS ROWID"; + else + selectColumns << col; + } + + + static_qstring(sql, "SELECT %1 FROM %2 WHERE %3;"); + return sql.arg( + selectColumns.join(", "), + dbAndTable, + conditionsString + ); +} + +void SqlQueryModel::SelectCellsQueryBuilder::clear() +{ + database = QString(); + table = QString(); + rowIdColumns.clear(); + columns.clear(); + conditions.clear(); + queryArgs.clear(); + includedRowIds.clear(); + argSquence = 0; +} + +void SqlQueryModel::SelectCellsQueryBuilder::setDatabase(const QString& database) +{ + this->database = database; +} + +void SqlQueryModel::SelectCellsQueryBuilder::setTable(const QString& table) +{ + this->table = table; +} + +void SqlQueryModel::SelectCellsQueryBuilder::addColumn(const QString& column) +{ + this->columns << column; +} + +RowId SqlQueryModel::SelectCellsQueryBuilder::readRowId(SqlResultsRowPtr row) const +{ + RowId rowId; + for (const QString& column : rowIdColumns) + rowId[column] = row->value(column); + + return rowId; +} + +int SqlQueryModel::SelectCellsQueryBuilder::getColumnCount() const +{ + return columns.size(); +} + +QString SqlQueryModel::SelectCellsQueryBuilder::getTable() const +{ + return table; +} + +QString SqlQueryModel::SelectCellsQueryBuilder::getDatabase() const +{ + return database; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h index 6c17740..9a89205 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodel.h @@ -31,13 +31,14 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel }; Q_DECLARE_FLAGS(Features, Feature) + typedef std::function CommitSuccessfulHandler; + friend class SqlQueryItemDelegate; explicit SqlQueryModel(QObject *parent = 0); virtual ~SqlQueryModel(); static void staticInit(); - static int getCellDataLengthLimit(); QString getQuery() const; void setQuery(const QString &value); @@ -63,6 +64,8 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel QVariant headerData(int section, Qt::Orientation orientation, int role) const; bool isExecutionInProgress() const; void loadFullDataForEntireRow(int row); + void loadFullDataForEntireColumn(int column); + bool doesColumnHaveLimitedValues(int column) const; StrHash attachDependencyTables(); void detachDependencyTables(); @@ -143,6 +146,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel void gotoPage(int newPage); bool canReload(); virtual bool supportsModifyingQueriesInMenu() const; + Qt::Alignment findValueAlignment(const QVariant& value, SqlQueryModelColumn* column); QueryExecutor::SortList getSortOrder() const; void setSortOrder(const QueryExecutor::SortList& newSortOrder); @@ -159,6 +163,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel static QList> groupItemsByRows(const QList& items); static QHash > groupItemsByTable(const QList& items); + static QHash > groupColumnsByTable(const QVector& columns); bool getSimpleExecutionMode() const; void setSimpleExecutionMode(bool value); @@ -178,6 +183,9 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel void setDesiredColumnWidth(int colIdx, int width); int getDesiredColumnWidth(int colIdx); + void setCellDataLengthLimit(int value); + int getCellDataLengthLimit(); + protected: class CommitUpdateQueryBuilder : public RowIdConditionBuilder { @@ -199,6 +207,29 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel QStringList assignmentArgs; }; + class SelectCellsQueryBuilder : public RowIdConditionBuilder + { + public: + void addRowId(const RowId& rowId); + QString build(); + void clear(); + void setDatabase(const QString& database); + void setTable(const QString& table); + QString getDatabase() const; + QString getTable() const; + void addColumn(const QString& column); + RowId readRowId(SqlResultsRowPtr row) const; + int getColumnCount() const; + + protected: + QSet rowIdColumns; + QString database; + QString table; + QSet columns; + QSet includedRowIds; + int argSquence = 0; + }; + /** * @brief commitAddedRow Inserts new row to a table. * @param itemsInRow All cells for the new row. @@ -209,7 +240,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel * and insert them into the actual database table. It also has to update items in the model, * so they are no longer "new" and have the same data as inserted into the database. */ - virtual bool commitAddedRow(const QList& itemsInRow); + virtual bool commitAddedRow(const QList& itemsInRow, QList& successfulCommitHandlers); /** * @brief commitEditedRow Updates table row with new values. @@ -220,7 +251,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel * unless the cell doesn't referr to the table, but in that case the cell should not be editable for user anyway. * Important thing to pay attention to is that the item list passed in arguments contains only modified items. */ - virtual bool commitEditedRow(const QList& itemsInRow); + virtual bool commitEditedRow(const QList& itemsInRow, QList& successfulCommitHandlers); /** * @brief commitDeletedRow Deletes row from the table. @@ -230,7 +261,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel * Inheriting class can reimplement this, so for example model specialized for single table can delete rows. * The method implementation should delete the row from the database. */ - virtual bool commitDeletedRow(const QList& itemsInRow); + virtual bool commitDeletedRow(const QList& itemsInRow, QList& successfulCommitHandlers); /** * @brief rollbackAddedRow @@ -258,10 +289,15 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel SqlQueryModelColumnPtr getColumnModel(const QString& table, const QString& column); QList getTableColumnModels(const QString& database, const QString& table); QList getTableColumnModels(const QString& table); + void updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId, SqlResultsRowPtr row, + const QStringList& columnNames, const BiStrHash& typeColumnToResColumn); void updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId); + void updateItem(SqlQueryItem* item, const QVariant& value, int columnIndex, const RowId& rowId, Qt::Alignment alignment); RowId getNewRowId(const RowId& currentRowId, const QList items); void updateRowIdForAllItems(const AliasedTable& table, const RowId& rowId, const RowId& newRowId); QHash toValuesGroupedByColumns(const QList& items); + void refreshGeneratedColumns(const QList& items); + void refreshGeneratedColumns(const QList& items, QHash& values, const RowId& insertedRowId); QueryExecutor* queryExecutor = nullptr; Db* db = nullptr; @@ -282,7 +318,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel * Having this set to 10000 gives about 290 MB of memory consumption * while having 30 columns and 1000 result rows loaded, all with 10000 bytes. */ - static const int cellDataLengthLimit = 100; + int cellDataLengthLimit = 100; private: struct TableDetails @@ -304,9 +340,9 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel */ bool loadData(SqlQueryPtr results); - QList loadRow(SqlResultsRowPtr row); + QList loadRow(SqlResultsRowPtr row, SqlQueryPtr results); RowId getRowIdValue(SqlResultsRowPtr row, int columnIdx); - void readColumns(); + bool readColumns(); void readColumnDetails(); void updateColumnsHeader(); void updateColumnHeaderLabels(); @@ -316,8 +352,9 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel QList getTablesForColumns(); QList getColumnEditionEnabledList(); QList toItemList(const QModelIndexList& indexes) const; - bool commitRow(const QList& itemsInRow); + bool commitRow(const QList& itemsInRow, QList& successfulCommitHandlers); void rollbackRow(const QList& itemsInRow); + QHash readCellValues(SelectCellsQueryBuilder& queryBuilder, const QHash >& itemsPerRowId); void storeStep1NumbersFromExecution(); void storeStep2NumbersFromExecution(); void restoreNumbersToQueryExecutor(); @@ -333,6 +370,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel int getInsertRowIndex(); void notifyItemEditionEnded(const QModelIndex& idx); int getRowsPerPage() const; + bool isEmptyQuery() const; QString query; QHash queryParams; @@ -400,7 +438,7 @@ class GUI_API_EXPORT SqlQueryModel : public QStandardItemModel QueryExecutor::SortList sortOrder; QHash columnMap; - QHash columnWidths; + QHash columnWidths; QHash> tableToRowIdColumn; QStringList headerColumns; int rowNumBase = 0; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp index 688e05a..8171154 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.cpp @@ -6,6 +6,7 @@ SqlQueryModelColumn::SqlQueryModelColumn(const QueryExecutor::ResultColumnPtr& r { displayName = resultColumn->displayName; column = resultColumn->column; + alias = resultColumn->alias; table = resultColumn->table; tableAlias = resultColumn->tableAlias; database = resultColumn->database.isEmpty() ? "main": resultColumn->database; @@ -80,9 +81,17 @@ QString SqlQueryModelColumn::resolveMessage(SqlQueryModelColumn::EditionForbidde return QObject::tr("Cannot edit columns that are result of %1 statement.").arg("SELECT DISTINCT"); case EditionForbiddenReason::COMMON_TABLE_EXPRESSION: return QObject::tr("Cannot edit columns that are result of common table expression statement (%1).").arg("WITH ... SELECT ..."); + case EditionForbiddenReason::GENERATED_COLUMN: + return QObject::tr("Cannot edit table generated columns."); } qCritical() << "Reached null text message for SqlQueryModel::EditionForbiddenReason. This should not happen!"; - return QString::null; + return QString(); +} + +void SqlQueryModelColumn::postProcessConstraints() +{ + if (isGenerated()) + editionForbiddenReason << EditionForbiddenReason::GENERATED_COLUMN; } bool SqlQueryModelColumn::isNumeric() @@ -90,6 +99,11 @@ bool SqlQueryModelColumn::isNumeric() return dataType.isNumeric(); } +bool SqlQueryModelColumn::isNull() +{ + return dataType.isNull(); +} + bool SqlQueryModelColumn::canEdit() { return editionForbiddenReason.size() == 0; @@ -98,11 +112,11 @@ bool SqlQueryModelColumn::canEdit() QString SqlQueryModelColumn::getEditionForbiddenReason() { if (canEdit()) - return QString::null; + return QString(); // We sort reasons to get most significant reason at first position. - QList list = editionForbiddenReason.toList(); - qSort(list); + QList list = editionForbiddenReason.values(); + std::sort(list.begin(), list.end()); return resolveMessage(list[0]); } @@ -157,6 +171,11 @@ bool SqlQueryModelColumn::isCollate() const return getConstraints().size() > 0; } +bool SqlQueryModelColumn::isGenerated() const +{ + return getConstraints().size() > 0; +} + QList SqlQueryModelColumn::getFkConstraints() const { return getConstraints(); @@ -305,6 +324,14 @@ SqlQueryModelColumn::Constraint* SqlQueryModelColumn::Constraint::create(SqliteC constr->type = Type::COLLATE; break; } + case SqliteCreateTable::Column::Constraint::GENERATED: + { + ConstraintGenerated* generate = new ConstraintGenerated(); + generate->generatedType = columnConstraint->generatedType; + constr = generate; + constr->type = Type::GENERATED; + break; + } case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: { if (columnConstraint->foreignKey->indexedColumns.size() == 0) @@ -392,7 +419,7 @@ QString SqlQueryModelColumn::ConstraintUnique::getDetails() const if (onConflict != SqliteConflictAlgo::null) return "("+QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict))+")"; - return QString::null; + return QString(); } Icon* SqlQueryModelColumn::ConstraintUnique::getIcon() const @@ -410,7 +437,7 @@ QString SqlQueryModelColumn::ConstraintNotNull::getDetails() const if (onConflict != SqliteConflictAlgo::null) return "("+QObject::tr("on conflict: %1", "data view tooltip").arg(sqliteConflictAlgo(onConflict))+")"; - return QString::null; + return QString(); } Icon* SqlQueryModelColumn::ConstraintNotNull::getIcon() const @@ -468,3 +495,20 @@ Icon* SqlQueryModelColumn::ConstraintCollate::getIcon() const { return ICONS.CONSTRAINT_COLLATION; } + +QString SqlQueryModelColumn::ConstraintGenerated::getTypeString() const +{ + return "GENERATED"; +} + +QString SqlQueryModelColumn::ConstraintGenerated::getDetails() const +{ + return "("+QObject::tr("generated column type: %1", "data view tooltip") + .arg(SqliteCreateTable::Column::Constraint::toString(generatedType))+")"; +} + +Icon* SqlQueryModelColumn::ConstraintGenerated::getIcon() const +{ + return generatedType == SqliteCreateTable::Column::Constraint::GeneratedType::STORED ? + ICONS.CONSTRAINT_GENERATED_STORED : ICONS.CONSTRAINT_GENERATED_VIRTUAL; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h index e0f11e0..8e05fd5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlquerymodelcolumn.h @@ -28,7 +28,8 @@ class GUI_API_EXPORT SqlQueryModelColumn EXPRESSION, SMART_EXECUTION_FAILED, DISTINCT_RESULTS, - COMMON_TABLE_EXPRESSION + COMMON_TABLE_EXPRESSION, + GENERATED_COLUMN }; struct Constraint @@ -41,6 +42,7 @@ class GUI_API_EXPORT SqlQueryModelColumn CHECK, DEFAULT, COLLATE, + GENERATED, FOREIGN_KEY, null }; @@ -131,6 +133,15 @@ class GUI_API_EXPORT SqlQueryModelColumn QString collationName; }; + struct ConstraintGenerated : public Constraint + { + QString getTypeString() const; + QString getDetails() const; + Icon* getIcon() const; + + SqliteCreateTable::Column::Constraint::GeneratedType generatedType = SqliteCreateTable::Column::Constraint::GeneratedType::null; + }; + SqlQueryModelColumn(const QueryExecutor::ResultColumnPtr& resultColumn); virtual ~SqlQueryModelColumn(); @@ -138,7 +149,9 @@ class GUI_API_EXPORT SqlQueryModelColumn static EditionForbiddenReason convert(QueryExecutor::EditionForbiddenReason reason); static EditionForbiddenReason convert(QueryExecutor::ColumnEditionForbiddenReason reason); static QString resolveMessage(EditionForbiddenReason reason); + void postProcessConstraints(); bool isNumeric(); + bool isNull(); bool canEdit(); QString getEditionForbiddenReason(); bool isPk() const; @@ -149,11 +162,13 @@ class GUI_API_EXPORT SqlQueryModelColumn bool isFk() const; bool isDefault() const; bool isCollate() const; + bool isGenerated() const; QList getFkConstraints() const; ConstraintDefault* getDefaultConstraint() const; QString displayName; QString column; + QString alias; QString table; QString database; QString tableAlias; diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp index 4364986..9d3ea96 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.cpp @@ -107,6 +107,7 @@ void SqlQueryView::createActions() createAction(INSERT_ROW, ICONS.INSERT_ROW, tr("Insert row"), this, SIGNAL(requestForRowInsert()), this); createAction(INSERT_MULTIPLE_ROWS, ICONS.INSERT_ROWS, tr("Insert multiple rows"), this, SIGNAL(requestForMultipleRowInsert()), this); createAction(DELETE_ROW, ICONS.DELETE_ROW, tr("Delete selected row"), this, SIGNAL(requestForRowDelete()), this); + createAction(LOAD_FULL_VALUES, ICONS.LOAD_FULL_VALUES, tr("Load full values"), this, SLOT(loadFullValuesForColumn()), this); actionMap[RESET_SORTING]->setEnabled(false); } @@ -121,8 +122,6 @@ void SqlQueryView::setupDefShortcuts() void SqlQueryView::setupActionsForMenu(SqlQueryItem* currentItem, const QList& selectedItems) { - UNUSED(currentItem); - // Selected items count int selCount = selectedItems.size(); @@ -136,7 +135,7 @@ void SqlQueryView::setupActionsForMenu(SqlQueryItem* currentItem, const QListgetColumn()->editionForbiddenReason.size() > 0) editableSelCount--; - bool currentItemEditable = (getCurrentItem()->getColumn()->editionForbiddenReason.size() == 0); + bool currentItemEditable = (currentItem && currentItem->getColumn()->editionForbiddenReason.size() == 0); // Uncommitted & selected items count int uncommittedSelCount = 0; @@ -173,7 +172,7 @@ void SqlQueryView::setupActionsForMenu(SqlQueryItem* currentItem, const QListaddSeparator(); } - if (selCount == 1 && selectedItems.first() == currentItem) + if (selCount == 1 && currentItem && selectedItems.first() == currentItem) addFkActionsToContextMenu(currentItem); if (selCount > 0) @@ -210,6 +209,8 @@ void SqlQueryView::setupHeaderMenu() headerContextMenu = new QMenu(horizontalHeader()); headerContextMenu->addAction(actionMap[SORT_DIALOG]); headerContextMenu->addAction(actionMap[RESET_SORTING]); + headerContextMenu->addSeparator(); + headerContextMenu->addAction(actionMap[LOAD_FULL_VALUES]); } QList SqlQueryView::getSelectedItems() @@ -223,7 +224,7 @@ QList SqlQueryView::getSelectedItems() if (idxList.size() == 0) return items; - qSort(idxList); + std::sort(idxList.begin(), idxList.end()); const SqlQueryModel* model = dynamic_cast(idxList.first().model()); for (const QModelIndex& idx : idxList) items << model->itemFromIndex(idx); @@ -318,6 +319,11 @@ void SqlQueryView::generateDelete() MAINWINDOW->openSqlEditor(getModel()->getDb(), sql); } +void SqlQueryView::loadFullValuesForColumn() +{ + getModel()->loadFullDataForEntireColumn(headerContextMenuSection); +} + bool SqlQueryView::editInEditorIfNecessary(SqlQueryItem* item) { if (item->getColumn()->dataType.getType() == DataType::BLOB) @@ -340,11 +346,24 @@ void SqlQueryView::paste(const QList >& data) return; } + if (getModel()->isStructureOutOfDate()) + { + notifyWarn(tr("Cannot paste data. Details: %1").arg(tr("Structure of at least one table used has changed since last data was loaded. Reload the data to proceed."))); + return; + } + + QSet warnedColumns; + bool warnedRowDeletion = false; if (data.size() == 1 && data[0].size() == 1) { QVariant theValue = data[0][0]; for (SqlQueryItem* item : selectedItems) + { + if (!validatePasting(warnedColumns, warnedRowDeletion, item)) + continue; + item->setValue(theValue, false, false); + } return; } @@ -377,21 +396,47 @@ void SqlQueryView::paste(const QList >& data) qDebug() << "Tried to paste more columns than available in the grid."; break; } - item = getModel()->itemFromIndex(rowIdx, colIdx); + item = getModel()->itemFromIndex(rowIdx, colIdx++); - // Set value to the cell - item->setValue(cell, false, false); + // Set value to the cell, if possible + if (!validatePasting(warnedColumns, warnedRowDeletion, item)) + continue; - // Go to next cell - colIdx++; + item->setValue(cell, false, false); } - // Go to next row, first cell + // Go to next row, first column rowIdx++; colIdx = topLeft->column(); } } +bool SqlQueryView::validatePasting(QSet& warnedColumns, bool& warnedRowDeletion, SqlQueryItem* item) +{ + if (item->isDeletedRow()) + { + if (!warnedRowDeletion) + { + warnedRowDeletion = true; + notifyWarn(tr("Cannot paste to a cell. Details: %1").arg(tr("The row is marked for deletion."))); + } + return false; + } + + if (!item->getColumn()->canEdit()) + { + QString colName = item->getColumn()->displayName; + if (!warnedColumns.contains(colName)) + { + warnedColumns << colName; + notifyWarn(tr("Cannot paste to column %1. Details: %2").arg(colName).arg(item->getColumn()->getEditionForbiddenReason())); + } + return false; + } + + return true; +} + void SqlQueryView::addFkActionsToContextMenu(SqlQueryItem* currentItem) { QList fkList = currentItem->getColumn()->getFkConstraints(); @@ -430,9 +475,8 @@ void SqlQueryView::goToReferencedRow(const QString& table, const QString& column static_qstring(sqlTpl, "SELECT * FROM %1 WHERE %2 = %3"); - Dialect dialect = db->getDialect(); - QString wrappedTable = wrapObjIfNeeded(table, dialect); - QString wrappedColumn = wrapObjIfNeeded(column, dialect); + QString wrappedTable = wrapObjIfNeeded(table); + QString wrappedColumn = wrapObjIfNeeded(column); QString valueStr = wrapValueIfNeeded(value.toString()); EditorWindow* win = MAINWINDOW->openSqlEditor(db, sqlTpl.arg(wrappedTable, wrappedColumn, valueStr)); if (!win) @@ -464,7 +508,8 @@ void SqlQueryView::copy(bool withHeader) // Header if (withHeader) { - for (SqlQueryModelColumnPtr col : getModel()->getColumns().mid(0, groupedItems.first().size())) + int leftMostColumn = groupedItems.first().first()->column(); + for (SqlQueryModelColumnPtr col : getModel()->getColumns().mid(leftMostColumn, groupedItems.first().size())) { theDataRow << col->displayName; cells << col->displayName; @@ -540,6 +585,20 @@ void SqlQueryView::scrollContentsBy(int dx, int dy) emit scrolledBy(dx, dy); } +void SqlQueryView::mouseMoveEvent(QMouseEvent* event) +{ + QAbstractItemView::mouseMoveEvent(event); + + QModelIndex idx = indexAt(QPoint(event->x(), event->y())); + if (idx != indexUnderCursor) + { + if (indexUnderCursor.isValid()) + itemDelegate->mouseLeftIndex(indexUnderCursor); + + indexUnderCursor = idx; + } +} + void SqlQueryView::updateCommitRollbackActions(bool enabled) { actionMap[COMMIT]->setEnabled(enabled); @@ -570,6 +629,11 @@ void SqlQueryView::headerContextMenuRequested(const QPoint& pos) if (simpleBrowserMode) return; + headerContextMenuSection = horizontalHeader()->visualIndexAt(pos.x()); + + bool hasLimitedValues = getModel()->doesColumnHaveLimitedValues(headerContextMenuSection); + actionMap[LOAD_FULL_VALUES]->setEnabled(hasLimitedValues); + headerContextMenu->popup(horizontalHeader()->mapToGlobal(pos)); } @@ -712,7 +776,7 @@ void SqlQueryView::setNull() if (selItem->getColumn()->editionForbiddenReason.size() > 0) continue; - selItem->setValue(QVariant(QString::null), false, false); + selItem->setValue(QVariant(QString()), false, false); } } diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h index 98e2783..5e50ed1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqlqueryview.h @@ -58,6 +58,7 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer OPEN_VALUE_EDITOR, SORT_DIALOG, RESET_SORTING, + LOAD_FULL_VALUES, GENERATE_SELECT, GENERATE_INSERT, GENERATE_UPDATE, @@ -85,6 +86,7 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer protected: void scrollContentsBy(int dx, int dy); + void mouseMoveEvent(QMouseEvent *event); private: class Header : public QHeaderView @@ -104,6 +106,7 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer void setupHeaderMenu(); bool editInEditorIfNecessary(SqlQueryItem* item); void paste(const QList>& data); + bool validatePasting(QSet& warnedColumns, bool& warnedRowDeletion, SqlQueryItem* item); void addFkActionsToContextMenu(SqlQueryItem* currentItem); void goToReferencedRow(const QString& table, const QString& column, const QVariant& value); void copy(bool withHeaders); @@ -122,6 +125,8 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer bool simpleBrowserMode = false; bool ignoreColumnWidthChanges = false; int beforeExecutionHorizontalPosition = -1; + int headerContextMenuSection = -1; + QModelIndex indexUnderCursor; private slots: void updateCommitRollbackActions(bool enabled); @@ -136,6 +141,7 @@ class GUI_API_EXPORT SqlQueryView : public QTableView, public ExtActionContainer void generateInsert(); void generateUpdate(); void generateDelete(); + void loadFullValuesForColumn(); public slots: void executionStarted(); diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp index ed9a3a4..50db973 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.cpp @@ -3,6 +3,7 @@ #include "sqlqueryitem.h" #include "services/notifymanager.h" #include "uiconfig.h" +#include "common/unused.h" #include #include #include @@ -45,7 +46,7 @@ SqlQueryModel::Features SqlTableModel::features() const return INSERT_ROW|DELETE_ROW|FILTERING; } -bool SqlTableModel::commitAddedRow(const QList& itemsInRow) +bool SqlTableModel::commitAddedRow(const QList& itemsInRow, QList& successfulCommitHandlers) { QList modelColumns = getTableColumnModels(table); if (modelColumns.size() != itemsInRow.size()) @@ -76,10 +77,11 @@ bool SqlTableModel::commitAddedRow(const QList& itemsInRow) // Handle error if (result->isError()) { + QString errMsg = tr("Error while committing new row: %1").arg(result->getErrorText()); for (SqlQueryItem* item : itemsInRow) - item->setCommittingError(true); + item->setCommittingError(true, errMsg); - notifyError(tr("Error while committing new row: %1").arg(result->getErrorText())); + notifyError(errMsg); return false; } @@ -99,11 +101,16 @@ bool SqlTableModel::commitAddedRow(const QList& itemsInRow) else rowId = result->getInsertRowId(); - updateRowAfterInsert(itemsInRow, modelColumns, rowId); + // After all items are committed successfully, update data/metadata for inserted rows/items + successfulCommitHandlers << [this, itemsInRow, modelColumns, rowId]() + { + updateRowAfterInsert(itemsInRow, modelColumns, rowId); + }; + return true; } -bool SqlTableModel::commitDeletedRow(const QList& itemsInRow) +bool SqlTableModel::commitDeletedRow(const QList& itemsInRow, QList& successfulCommitHandlers) { if (itemsInRow.size() == 0) { @@ -125,11 +132,9 @@ bool SqlTableModel::commitDeletedRow(const QList& itemsInRow) if (rowId.isEmpty()) return false; - Dialect dialect = db->getDialect(); - CommitDeleteQueryBuilder queryBuilder; - queryBuilder.setTable(wrapObjIfNeeded(table, dialect)); - queryBuilder.setRowId(rowId, dialect); + queryBuilder.setTable(wrapObjIfNeeded(table)); + queryBuilder.setRowId(rowId); QString sql = queryBuilder.build(); QHash args = queryBuilder.getQueryArgs(); @@ -137,11 +142,15 @@ bool SqlTableModel::commitDeletedRow(const QList& itemsInRow) SqlQueryPtr result = db->exec(sql, args); if (result->isError()) { - notifyError(tr("Error while deleting row from table %1: %2").arg(table).arg(result->getErrorText())); + QString errMsg = tr("Error while deleting row from table %1: %2").arg(table, result->getErrorText()); + for (SqlQueryItem* item : itemsInRow) + item->setCommittingError(true, errMsg); + + notifyError(errMsg); return false; } - if (!SqlQueryModel::commitDeletedRow(itemsInRow)) + if (!SqlQueryModel::commitDeletedRow(itemsInRow, successfulCommitHandlers)) qCritical() << "Could not delete row from SqlQueryView while committing row deletion."; return true; @@ -157,10 +166,9 @@ void SqlTableModel::applyFilter(const QString& value, FilterValueProcessor value return; } - Dialect dialect = db->getDialect(); QStringList conditions; for (SqlQueryModelColumnPtr column : columns) - conditions << wrapObjIfNeeded(column->column, dialect)+" "+valueProc(value); + conditions << wrapObjIfNeeded(column->column)+" "+valueProc(value); setQuery(sql.arg(getDataSource(), conditions.join(" OR "))); executeQuery(); @@ -182,14 +190,13 @@ void SqlTableModel::applyFilter(const QStringList& values, FilterValueProcessor return; } - Dialect dialect = db->getDialect(); QStringList conditions; for (int i = 0, total = columns.size(); i < total; ++i) { if (values[i].isEmpty()) continue; - conditions << wrapObjIfNeeded(columns[i]->column, dialect)+" "+valueProc(values[i]); + conditions << wrapObjIfNeeded(columns[i]->column)+" "+valueProc(values[i]); } setQuery(sql.arg(getDataSource(), conditions.join(" AND "))); @@ -286,107 +293,35 @@ QString SqlTableModel::generateDeleteQueryForItems(const QList& i void SqlTableModel::updateRowAfterInsert(const QList& itemsInRow, const QList& modelColumns, RowId rowId) { - Dialect dialect = db->getDialect(); - // Update cells with data just like it was entered. Only DEFAULT and PRIMARY KEY AUTOINCREMENT will have special values. // If the DEFAULT is not an explicit literal, but an expression and db is SQLite3, we have to read the inserted value from DB. QHash columnsToReadFromDb; - Parser parser(dialect); - SqliteExpr* expr = nullptr; + Parser parser; QHash values; SqlQueryItem* item = nullptr; int i = 0; for (const SqlQueryModelColumnPtr& modelColumn : modelColumns) { item = itemsInRow[i++]; -// qDebug() << "Item is for column" << item->getColumn()->column << ", column iterated:" << modelColumn->column; - if (item->getValue().isNull()) - { - if (modelColumn->isDefault()) - { - if (dialect == Dialect::Sqlite3) - { - expr = parser.parseExpr(modelColumn->getDefaultConstraint()->defaultValue); - if (expr && expr->mode != SqliteExpr::Mode::LITERAL_VALUE) - { - if (isWithOutRowIdTable && rowId.isEmpty()) - { - qWarning() << "Inserted expression as DEFAULT value for table WITHOUT ROWID and actually no ROWID." - << "This is currently unsupported to refresh such cell value instantly."; - values[item] = QVariant(); - } - else - columnsToReadFromDb[modelColumn] = item; - - continue; - } - } - values[item] = modelColumn->getDefaultConstraint()->defaultValue; - continue; - } - - // If this is the PK AUTOINCR column we use RowId as value, because it was skipped when setting values to items - if (modelColumn->isPk() && modelColumn->isAutoIncr()) - { - values[item] = rowId["ROWID"]; - continue; - } - } + if (processNullValueAfterInsert(item, values[item], modelColumn, columnsToReadFromDb, rowId, parser)) + continue; values[item] = item->getValue(); } // Reading values for DEFAULT values being an expression if (columnsToReadFromDb.size() > 0) - { - // Preparing query - static_qstring(limitedColTpl, "substr(%1, 1, %2)"); - SelectColumnsQueryBuilder queryBuilder; - queryBuilder.setTable(wrapObjIfNeeded(table, dialect)); - queryBuilder.setRowId(rowId, dialect); - QList columnKeys = columnsToReadFromDb.keys(); - for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) - queryBuilder.addColumn(limitedColTpl.arg(wrapObjIfNeeded(modelColumn->column, dialect), QString::number(cellDataLengthLimit))); + processDefaultValueAfterInsert(columnsToReadFromDb, values, rowId); - // Executing query - SqlQueryPtr defColValues = db->exec(queryBuilder.build(), queryBuilder.getQueryArgs(), Db::Flag::PRELOAD); - - // Handling error - if (defColValues->isError()) - { - qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. Error from database was:" - << defColValues->getErrorText(); - - for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) - values[columnsToReadFromDb[modelColumn]] = QVariant(); - } - else if (!defColValues->hasNext()) - { - qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. There were no result rows."; - - for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) - values[columnsToReadFromDb[modelColumn]] = QVariant(); - } - else - { - // Reading a row - SqlResultsRowPtr row = defColValues->next(); - if (row->valueList().size() != columnKeys.size()) - { - qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. Number of columns from results was invalid:" - << row->valueList().size() << ", while expected:" << columnKeys.size(); - - for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) - values[columnsToReadFromDb[modelColumn]] = QVariant(); - } - else - { - int colIdx = 0; - for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) - values[columnsToReadFromDb[modelColumn]] = row->value(colIdx++); - } - } + // Reading values for GENERATED columns + i = 0; + QList generatedColumnItems; + for (const SqlQueryModelColumnPtr& modelColumn : modelColumns) + { + if (modelColumn->isGenerated()) + generatedColumnItems << itemsInRow[i++]; } + refreshGeneratedColumns(generatedColumnItems, values, rowId); // Update cell data with results int colIdx = 0; @@ -401,32 +336,128 @@ void SqlTableModel::updateRowAfterInsert(const QList& itemsInRow, } } +bool SqlTableModel::processNullValueAfterInsert(SqlQueryItem* item, QVariant& value, const SqlQueryModelColumnPtr& modelColumn, + QHash& columnsToReadFromDb, RowId rowId, Parser& parser) +{ +// qDebug() << "Item is for column" << item->getColumn()->column << ", column iterated:" << modelColumn->column; + if (!item->getValue().isNull()) + return false; + + // If this is the PK AUTOINCR column we use RowId as value, because it was skipped when setting values to items + if (modelColumn->isPk() && modelColumn->isAutoIncr()) + { + value = rowId["ROWID"]; + return true; + } + + if (!CFG_UI.General.UseDefaultValueForNull.get() || !modelColumn->isDefault()) + return false; + + SqliteExpr* expr = parser.parseExpr(modelColumn->getDefaultConstraint()->defaultValue); + if (expr && expr->mode != SqliteExpr::Mode::LITERAL_VALUE) + { + if (isWithOutRowIdTable && rowId.isEmpty()) + { + qWarning() << "Inserted expression as DEFAULT value for table WITHOUT ROWID and actually no ROWID." + << "This is currently unsupported to refresh such cell value instantly."; + value = QVariant(); + } + else + columnsToReadFromDb[modelColumn] = item; + + return true; + } + + if (expr) + value = expr->literalValue; + else + value = modelColumn->getDefaultConstraint()->defaultValue; + + if (value.userType() == QVariant::String) + value = stripString(value.toString()); + + return true; +} + +void SqlTableModel::processDefaultValueAfterInsert(QHash& columnsToReadFromDb, QHash& values, RowId rowId) +{ + // Preparing query + static_qstring(limitedColTpl, "substr(%1, 1, %2)"); + SelectColumnsQueryBuilder queryBuilder; + queryBuilder.setTable(wrapObjIfNeeded(table)); + queryBuilder.setRowId(rowId); + QList columnKeys = columnsToReadFromDb.keys(); + for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) + queryBuilder.addColumn(limitedColTpl.arg(wrapObjIfNeeded(modelColumn->column), QString::number(cellDataLengthLimit))); + + // Executing query + SqlQueryPtr defColValues = db->exec(queryBuilder.build(), queryBuilder.getQueryArgs(), Db::Flag::PRELOAD); + + // Handling error + if (defColValues->isError()) + { + qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. Error from database was:" + << defColValues->getErrorText(); + + for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) + values[columnsToReadFromDb[modelColumn]] = QVariant(); + + return; + } + + + if (!defColValues->hasNext()) + { + qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. There were no result rows."; + + for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) + values[columnsToReadFromDb[modelColumn]] = QVariant(); + + return; + } + + + // Reading a row + SqlResultsRowPtr row = defColValues->next(); + if (row->valueList().size() != columnKeys.size()) + { + qCritical() << "Could not load inserted values for DEFAULT expression in the table, so filling them with NULL. Number of columns from results was invalid:" + << row->valueList().size() << ", while expected:" << columnKeys.size(); + + for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) + values[columnsToReadFromDb[modelColumn]] = QVariant(); + + return; + } + + int colIdx = 0; + for (const SqlQueryModelColumnPtr& modelColumn : columnKeys) + values[columnsToReadFromDb[modelColumn]] = row->value(colIdx++); +} + QString SqlTableModel::getDatabasePrefix() { if (database.isNull()) - return "main."; + return ""; // not "main.", because the "main." doesn't work for TEMP tables, such as sqlite_temp_master - return wrapObjIfNeeded(database, db->getDialect()) + "."; + return wrapObjIfNeeded(database) + "."; } QString SqlTableModel::getDataSource() { - return getDatabasePrefix() + wrapObjIfNeeded(table, db->getDialect()); + return getDatabasePrefix() + wrapObjIfNeeded(table); } QString SqlTableModel::getInsertSql(const QList& modelColumns, QStringList& colNameList, QStringList& sqlValues, QList& args) { - Dialect dialect = db->getDialect(); - - QString sql = "INSERT INTO "+wrapObjIfNeeded(table, dialect); + UNUSED(modelColumns); + UNUSED(args); + QString sql = "INSERT INTO "+wrapObjIfNeeded(table); if (colNameList.size() == 0) { // There are all null values passed to the query. We need to use Sqlite3 special syntax, or find at least one default value - if (dialect == Dialect::Sqlite2) - updateColumnsAndValuesWithDefaultValues(modelColumns, colNameList, sqlValues, args); - else // Sqlite3 has default values syntax for that case - sql += " DEFAULT VALUES"; + sql += " DEFAULT VALUES"; } else sql += " ("+colNameList.join(", ")+") VALUES ("+sqlValues.join(", ")+")"; @@ -437,12 +468,13 @@ QString SqlTableModel::getInsertSql(const QList& modelCo void SqlTableModel::updateColumnsAndValues(const QList& itemsInRow, const QList& modelColumns, QStringList& colNameList, QStringList& sqlValues, QList& args) { - Dialect dialect = db->getDialect(); - SqlQueryItem* item = nullptr; int i = 0; for (SqlQueryModelColumnPtr modelColumn : modelColumns) { + if (!modelColumn->canEdit()) + continue; + item = itemsInRow[i++]; if (item->getValue().isNull()) { @@ -456,7 +488,7 @@ void SqlTableModel::updateColumnsAndValues(const QList& itemsInRo continue; } - colNameList << wrapObjIfNeeded(modelColumn->column, dialect); + colNameList << wrapObjIfNeeded(modelColumn->column); sqlValues << ":arg" + QString::number(i); args << item->getFullValue(); } @@ -465,14 +497,12 @@ void SqlTableModel::updateColumnsAndValues(const QList& itemsInRo void SqlTableModel::updateColumnsAndValuesWithDefaultValues(const QList& modelColumns, QStringList& colNameList, QStringList& sqlValues, QList& args) { - Dialect dialect = db->getDialect(); - // First try to find the one with DEFAULT value for (SqlQueryModelColumnPtr modelColumn : modelColumns) { if (modelColumn->isDefault()) { - colNameList << wrapObjIfNeeded(modelColumn->column, dialect); + colNameList << wrapObjIfNeeded(modelColumn->column); sqlValues << ":defValue"; args << modelColumn->getDefaultConstraint()->defaultValue; return; @@ -484,15 +514,15 @@ void SqlTableModel::updateColumnsAndValuesWithDefaultValues(const QListisPk() && modelColumn->isAutoIncr()) { - QString colName = wrapObjIfNeeded(modelColumn->column, dialect); - QString tableName = wrapObjIfNeeded(table, dialect); + QString colName = wrapObjIfNeeded(modelColumn->column); + QString tableName = wrapObjIfNeeded(table); SqlQueryPtr results = db->exec("SELECT max("+colName+") FROM "+tableName); qint64 rowid = 0; QVariant cellValue = results->getSingleCell(); if (!cellValue.isNull()) rowid = cellValue.toLongLong(); - colNameList << wrapObjIfNeeded(modelColumn->column, dialect); + colNameList << wrapObjIfNeeded(modelColumn->column); sqlValues << ":defValue"; args << rowid; return; @@ -501,7 +531,7 @@ void SqlTableModel::updateColumnsAndValuesWithDefaultValues(const QListcolumn, dialect); + colNameList << wrapObjIfNeeded(modelColumns[0]->column); sqlValues << ":defValue"; args << QVariant(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h index b904343..3a1ab36 100644 --- a/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/datagrid/sqltablemodel.h @@ -28,8 +28,8 @@ class GUI_API_EXPORT SqlTableModel : public SqlQueryModel bool supportsModifyingQueriesInMenu() const; protected: - bool commitAddedRow(const QList& itemsInRow); - bool commitDeletedRow(const QList& itemsInRow); + bool commitAddedRow(const QList& itemsInRow, QList& successfulCommitHandlers); + bool commitDeletedRow(const QList& itemsInRow, QList& successfulCommitHandlers); private: class CommitDeleteQueryBuilder : public CommitUpdateQueryBuilder @@ -59,6 +59,12 @@ class GUI_API_EXPORT SqlTableModel : public SqlQueryModel QString getInsertSql(const QList& modelColumns, QStringList& colNameList, QStringList& sqlValues, QList& args); void updateRowAfterInsert(const QList& itemsInRow, const QList& modelColumns, RowId rowId); + bool processNullValueAfterInsert(SqlQueryItem* item, QVariant& value, const SqlQueryModelColumnPtr& modelColumn, + QHash& columnsToReadFromDb, RowId rowId, + Parser& parser); + void processDefaultValueAfterInsert(QHash& columnsToReadFromDb, QHash& values, + RowId rowId); + QString getDatabasePrefix(); QString getDataSource(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp index 9012c1c..568954c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dataview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dataview.cpp @@ -311,9 +311,13 @@ void DataView::resizeColumnsInitiallyToContents() } if (wd > CFG_UI.General.MaxInitialColumnWith.get()) + { gridView->setColumnWidth(i, CFG_UI.General.MaxInitialColumnWith.get()); + } else if (wd < 60) + { gridView->setColumnWidth(i, 60); + } } gridView->setIgnoreColumnWidthChanges(false); } @@ -522,7 +526,7 @@ void DataView::coverForGridCommit(int total) void DataView::updateGridCommitCover(int value) { - if (!widgetCover->isVisible()) + if (!widgetCover->isVisible() || (value % 10) != 0) return; widgetCover->setProgress(value); @@ -544,6 +548,9 @@ void DataView::adjustColumnWidth(SqlQueryItem* item) return; int col = item->column(); + if (model->getDesiredColumnWidth(col) > -1) + return; + gridView->resizeColumnToContents(col); if (gridView->columnWidth(col) > CFG_UI.General.MaxInitialColumnWith.get()) gridView->setColumnWidth(col, CFG_UI.General.MaxInitialColumnWith.get()); @@ -606,6 +613,9 @@ void DataView::goToPage(const QString& pageStr) page--; // Converting from visual page representation to logical + if (page == model->getCurrentPage(true)) + return; + // We need to get this synchronized against event loop, cause changeing action status (probably) calls event loop update, // so this method was sometimes called twice at the time (until setResultsNavigationState() call below), // but the page in results model wasn't updated yet. We cannot simply move setResultsNavigationState() below gotoPage(), @@ -616,9 +626,6 @@ void DataView::goToPage(const QString& pageStr) if (!manualPageChangeMutex.tryLock()) return; - if (page == model->getCurrentPage(true)) - return; - setNavigationState(false); model->gotoPage(page); manualPageChangeMutex.unlock(); @@ -643,8 +650,8 @@ void DataView::updateResultsCount(int resultsCount) QString msg = QObject::tr("Total rows loaded: %1").arg(resultsCount); rowCountLabel->setText(msg); formViewRowCountLabel->setText(msg); - rowCountLabel->setToolTip(QString::null); - formViewRowCountLabel->setToolTip(QString::null); + rowCountLabel->setToolTip(QString()); + formViewRowCountLabel->setToolTip(QString()); } else { @@ -1007,6 +1014,7 @@ void DataView::createFilteringActions() actionMap[FILTER]->setIcon(actionMap[FILTER_STRING]->icon()); + gridView->getHeaderContextMenu()->addSeparator(); gridView->getHeaderContextMenu()->addAction(actionMap[FILTER_PER_COLUMN]); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp index b6203da..a03675f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dblistmodel.cpp @@ -114,19 +114,19 @@ void DbListModel::sort() case DbListModel::SortMode::LikeDbTree: { DbTreeComparer comparer; - qSort(dbList.begin(), dbList.end(), comparer); + std::sort(dbList.begin(), dbList.end(), comparer); break; } case DbListModel::SortMode::Alphabetical: { AlphaComparer comparer; - qSort(dbList.begin(), dbList.end(), comparer); + std::sort(dbList.begin(), dbList.end(), comparer); break; } case DbListModel::SortMode::AlphabeticalCaseInsensitive: { AlphaComparer comparer(Qt::CaseInsensitive); - qSort(dbList.begin(), dbList.end(), comparer); + std::sort(dbList.begin(), dbList.end(), comparer); break; } case DbListModel::SortMode::ConnectionOrder: diff --git a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp index 7dd45fd..05b614b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbobjectdialogs.cpp @@ -52,12 +52,12 @@ void DbObjectDialogs::editIndex(const QString& index) void DbObjectDialogs::addTriggerOnTable(const QString& table) { - addTrigger(table, QString::null); + addTrigger(table, QString()); } void DbObjectDialogs::addTriggerOnView(const QString& view) { - addTrigger(QString::null, view); + addTrigger(QString(), view); } void DbObjectDialogs::addTrigger(const QString& table, const QString& view) @@ -161,8 +161,7 @@ bool DbObjectDialogs::dropObject(const QString& database, const QString& name) static const QString dropSql2 = "DROP %1 %2;"; static const QString dropSql3 = "DROP %1 %2.%3;"; - Dialect dialect = db->getDialect(); - QString dbName = wrapObjIfNeeded(database, dialect); + QString dbName = wrapObjIfNeeded(database); Type type = getObjectType(database, name); QString title; @@ -206,11 +205,7 @@ bool DbObjectDialogs::dropObject(const QString& database, const QString& name) SqlQueryPtr results; - QString finalSql; - if (dialect == Dialect::Sqlite3) - finalSql = dropSql3.arg(typeForSql, dbName, wrapObjIfNeeded(name, dialect)); - else - finalSql = dropSql2.arg(typeForSql, wrapObjIfNeeded(name, dialect)); + QString finalSql = dropSql3.arg(typeForSql, dbName, wrapObjIfNeeded(name)); results = db->exec(finalSql); if (results->isError()) @@ -273,7 +268,6 @@ bool DbObjectDialogs::dropObjects(const QHash& objects) static const QString dropSql2 = "DROP %1 IF EXISTS %2;"; static const QString dropSql3 = "DROP %1 IF EXISTS %2.%3;"; - Dialect dialect = db->getDialect(); QStringList names = concat(objects.values()); QHash> groupedObjects = groupObjects(objects); @@ -298,16 +292,13 @@ bool DbObjectDialogs::dropObjects(const QHash& objects) QHash typeToNames; for (QHash>::const_iterator dbIt = groupedObjects.begin(); dbIt != groupedObjects.end(); ++dbIt) { - dbName = wrapObjIfNeeded(dbIt.key(), dialect); + dbName = wrapObjIfNeeded(dbIt.key()); typeToNames = dbIt.value(); for (QHash::const_iterator typeIt = typeToNames.begin(); typeIt != typeToNames.end(); ++typeIt) { for (const QString& name : typeIt.value()) { - if (dialect == Dialect::Sqlite3) - finalSql = dropSql3.arg(typeIt.key(), dbName, wrapObjIfNeeded(name, dialect)); - else - finalSql = dropSql2.arg(typeIt.key(), wrapObjIfNeeded(name, dialect)); + finalSql = dropSql3.arg(typeIt.key(), dbName, wrapObjIfNeeded(name)); results = db->exec(finalSql); if (results->isError()) @@ -339,8 +330,7 @@ DbObjectDialogs::Type DbObjectDialogs::getObjectType(const QString& database, co static const QString typeSql = "SELECT type FROM %1.sqlite_master WHERE name = ?;"; static const QStringList types = {"table", "index", "trigger", "view"}; - Dialect dialect = db->getDialect(); - QString dbName = wrapObjIfNeeded(database, dialect); + QString dbName = wrapObjIfNeeded(database); SqlQueryPtr results = db->exec(typeSql.arg(dbName), {name}); if (results->isError()) { diff --git a/SQLiteStudio3/guiSQLiteStudio/dbobjlistmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dbobjlistmodel.cpp index 00914a1..d5c72ee 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbobjlistmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbobjlistmodel.cpp @@ -110,7 +110,7 @@ QString DbObjListModel::typeString() const case ObjectType::null: break; } - return QString::null; + return QString(); } bool DbObjListModel::getIncludeSystemObjects() const { diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp index ba8ccc1..85a7047 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.cpp @@ -26,10 +26,10 @@ #include "windows/editorwindow.h" #include "uiconfig.h" #include "themetuner.h" -#include "dialogs/dbconverterdialog.h" #include "querygenerator.h" #include "dialogs/execfromfiledialog.h" #include "dialogs/fileexecerrorsdialog.h" +#include "common/compatibility.h" #include #include #include @@ -44,6 +44,7 @@ #include #include #include +#include #include CFG_KEYS_DEFINE(DbTree) @@ -104,6 +105,7 @@ void DbTree::init() connect(this, &DbTree::updateFileExecProgress, this, &DbTree::setFileExecProgress, Qt::QueuedConnection); connect(this, &DbTree::fileExecCoverToBeClosed, this, &DbTree::hideFileExecCover, Qt::QueuedConnection); connect(this, &DbTree::fileExecErrors, this, &DbTree::showFileExecErrors, Qt::QueuedConnection); + connect(this, SIGNAL(schemaNeedsRefreshing(Db*)), this, SLOT(refreshSchema(Db*)), Qt::QueuedConnection); treeModel = new DbTreeModel(); treeModel->setTreeView(ui->treeView); @@ -144,7 +146,6 @@ void DbTree::createActions() createAction(DISCONNECT_FROM_DB, ICONS.DATABASE_DISCONNECT, tr("&Disconnect from the database"), this, SLOT(disconnectFromDb()), this); createAction(IMPORT_INTO_DB, ICONS.IMPORT, tr("Import"), this, SLOT(import()), this); createAction(EXPORT_DB, ICONS.DATABASE_EXPORT, tr("&Export the database"), this, SLOT(exportDb()), this); - createAction(CONVERT_DB, ICONS.CONVERT_DB, tr("Con&vert database type"), this, SLOT(convertDb()), this); createAction(VACUUM_DB, ICONS.VACUUM_DB, tr("Vac&uum"), this, SLOT(vacuumDb()), this); createAction(INTEGRITY_CHECK, ICONS.INTEGRITY_CHECK, tr("&Integrity check"), this, SLOT(integrityCheck()), this); createAction(ADD_TABLE, ICONS.TABLE_ADD, tr("Create a &table"), this, SLOT(addTable()), this); @@ -207,7 +208,7 @@ void DbTree::updateActionStates(const QStandardItem *item) enabled << DELETE_DB << EDIT_DB; if (dbTreeItem->getDb()->isOpen()) { - enabled << DISCONNECT_FROM_DB << ADD_TABLE << ADD_VIEW << IMPORT_INTO_DB << EXPORT_DB << REFRESH_SCHEMA << CONVERT_DB + enabled << DISCONNECT_FROM_DB << IMPORT_INTO_DB << EXPORT_DB << REFRESH_SCHEMA << VACUUM_DB << INTEGRITY_CHECK; isDbOpen = true; } @@ -340,8 +341,14 @@ void DbTree::updateActionStates(const QStandardItem *item) } if (treeModel->rowCount() > 0) + { enabled << SELECT_ALL; // if there's at least 1 item, enable this + // Table/view always enabled, as long as there is at least 1 db on the list. #4017 + if (treeModel->findFirstItemOfType(DbTreeItem::Type::DB)) + enabled << ADD_TABLE << ADD_VIEW; + } + enabled << REFRESH_SCHEMAS; for (int action : actionMap.keys()) @@ -413,7 +420,6 @@ void DbTree::setupActionsForMenu(DbTreeItem* currItem, QMenu* contextMenu) actions += ActionEntry(REFRESH_SCHEMA); actions += ActionEntry(IMPORT_INTO_DB); actions += ActionEntry(EXPORT_DB); - actions += ActionEntry(CONVERT_DB); actions += ActionEntry(VACUUM_DB); actions += ActionEntry(INTEGRITY_CHECK); actions += ActionEntry(EXEC_SQL_FROM_FILE); @@ -715,7 +721,7 @@ bool DbTree::areDbTreeItemsValidForItem(QList srcItems, const DbTre {DbTreeItem::Type::INDEX, DbTreeItem::Type::INDEXES} }; - if (!forPasting && srcTypes.toSet().size() == 1 && srcDbs.size() == 1 && dstItem && + if (!forPasting && toSet(srcTypes).size() == 1 && srcDbs.size() == 1 && dstItem && *(srcDbs.begin()) == dstItem->getDb() && reorderingTypeToParent[srcTypes.first()] == dstType) return true; @@ -794,7 +800,7 @@ Db* DbTree::getSelectedOpenDb() TableWindow* DbTree::openTable(DbTreeItem* item) { - QString database = QString::null; // TODO implement this when named databases (attached) are handled by dbtree. + QString database = QString(); // TODO implement this when named databases (attached) are handled by dbtree. Db* db = item->getDb(); return openTable(db, database, item->text()); } @@ -807,7 +813,7 @@ TableWindow* DbTree::openTable(Db* db, const QString& database, const QString& t void DbTree::editIndex(DbTreeItem* item) { - //QString database = QString::null; // TODO implement this when named databases (attached) are handled by dbtree. + //QString database = QString(); // TODO implement this when named databases (attached) are handled by dbtree. Db* db = item->getDb(); DbObjectDialogs dialogs(db); @@ -816,7 +822,7 @@ void DbTree::editIndex(DbTreeItem* item) ViewWindow* DbTree::openView(DbTreeItem* item) { - QString database = QString::null; // TODO implement this when named databases (attached) are handled by dbtree. + QString database = QString(); // TODO implement this when named databases (attached) are handled by dbtree. Db* db = item->getDb(); return openView(db, database, item->text()); } @@ -827,25 +833,21 @@ ViewWindow* DbTree::openView(Db* db, const QString& database, const QString& vie return dialogs.editView(database, view); } -TableWindow* DbTree::newTable(DbTreeItem* item) +TableWindow* DbTree::newTable(Db* db) { - Db* db = item->getDb(); - DbObjectDialogs dialogs(db); return dialogs.addTable(); } -ViewWindow* DbTree::newView(DbTreeItem* item) +ViewWindow* DbTree::newView(Db* db) { - Db* db = item->getDb(); - DbObjectDialogs dialogs(db); return dialogs.addView(); } void DbTree::editTrigger(DbTreeItem* item) { - //QString database = QString::null; // TODO implement this when named databases (attached) are handled by dbtree. + //QString database = QString(); // TODO implement this when named databases (attached) are handled by dbtree. Db* db = item->getDb(); DbObjectDialogs dialogs(db); @@ -1161,7 +1163,7 @@ void DbTree::editDb() void DbTree::removeDb() { - QList dbList = getSelectedDatabases().toList(); + QList dbList = getSelectedDatabases().values(); if (dbList.isEmpty()) return; @@ -1247,11 +1249,17 @@ void DbTree::exportDb() void DbTree::addTable() { Db* db = getSelectedOpenDb(); + if (!db || !db->isValid()) + { + DbTreeItem* item = treeModel->findFirstItemOfType(DbTreeItem::Type::DB); + if (item) + db = item->getDb(); + } + if (!db || !db->isValid()) return; - DbTreeItem* item = ui->treeView->currentItem(); - newTable(item); + newTable(db); } void DbTree::editTable() @@ -1267,7 +1275,7 @@ void DbTree::editTable() return; } - openTable(db, QString::null, table); // TODO put database name when supported + openTable(db, QString(), table); // TODO put database name when supported } void DbTree::delTable() @@ -1339,11 +1347,17 @@ void DbTree::delTrigger() void DbTree::addView() { Db* db = getSelectedOpenDb(); + if (!db || !db->isValid()) + { + DbTreeItem* item = treeModel->findFirstItemOfType(DbTreeItem::Type::DB); + if (item) + db = item->getDb(); + } + if (!db || !db->isValid()) return; - DbTreeItem* item = ui->treeView->currentItem(); - newView(item); + newView(db); } void DbTree::editView() @@ -1463,17 +1477,6 @@ void DbTree::delColumn() delColumn(item); } -void DbTree::convertDb() -{ - Db* db = getSelectedDb(); - if (!db || !db->isValid()) - return; - - DbConverterDialog dialog(this); - dialog.setDb(db); - dialog.exec(); -} - void DbTree::vacuumDb() { Db* db = getSelectedDb(); @@ -1569,11 +1572,10 @@ void DbTree::eraseTableData() return; static_qstring(DELETE_SQL, "DELETE FROM %1;"); - Dialect dialect = db->getDialect(); SqlQueryPtr res; for (const QString& table : tables) { - res = db->exec(DELETE_SQL.arg(wrapObjIfNeeded(table, dialect))); + res = db->exec(DELETE_SQL.arg(wrapObjIfNeeded(table))); if (res->isError()) { notifyError(tr("An error occurred while trying to delete data from table '%1': %2").arg(table, res->getErrorText())); @@ -1898,7 +1900,7 @@ void DbTree::execFromFileAsync(const QString& path, Db* db, bool ignoreErrors, c int executed = 0; bool ok = true; - QTime timer; + QElapsedTimer timer; timer.start(); QList> errors = executeFileQueries(db, stream, executed, attemptedExecutions, ok, ignoreErrors, fileSize); int millis = timer.elapsed(); @@ -1935,8 +1937,11 @@ QList> DbTree::executeFileQueries(Db* db, QTextStream& s } } - if (sql.trimmed().isEmpty()) + if (shouldSkipQueryFromFileExecution(sql)) + { + sql.clear();; continue; + } results = db->exec(sql); attemptedExecutions++; @@ -1961,6 +1966,18 @@ QList> DbTree::executeFileQueries(Db* db, QTextStream& s return errors; } +bool DbTree::shouldSkipQueryFromFileExecution(const QString& sql) +{ + if (sql.trimmed().isEmpty()) + return true; + + QString upper = sql.toUpper().trimmed().split("\n").last().trimmed(); + return (upper.startsWith("BEGIN") || + upper.startsWith("COMMIT") || + upper.startsWith("ROLLBACK") || + upper.startsWith("END")); +} + void DbTree::handleFileQueryExecution(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis) { bool doCommit = ok ? true : ignoreErrors; @@ -1968,17 +1985,20 @@ void DbTree::handleFileQueryExecution(Db* db, int executed, int attemptedExecuti { if (!db->commit()) { - db->rollback(); notifyError(tr("Could not execute SQL, because application has failed to commit the transaction: %1").arg(db->getErrorText())); + db->rollback(); } else if (!ok) // committed with errors { notifyInfo(tr("Finished executing %1 queries in %2 seconds. %3 were not executed due to errors.") .arg(executed).arg(millis / 1000.0).arg(attemptedExecutions - executed)); + + emit schemaNeedsRefreshing(db); } else { notifyInfo(tr("Finished executing %1 queries in %2 seconds.").arg(executed).arg(millis / 1000.0)); + emit schemaNeedsRefreshing(db); } } else diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h index f72ebda..62ef0df 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtree.h @@ -60,7 +60,6 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer DISCONNECT_FROM_DB, IMPORT_INTO_DB, EXPORT_DB, - CONVERT_DB, VACUUM_DB, INTEGRITY_CHECK, ADD_TABLE, @@ -133,10 +132,10 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer void setActionEnabled(int action, bool enabled); TableWindow* openTable(DbTreeItem* item); TableWindow* openTable(Db* db, const QString& database, const QString& table); - TableWindow* newTable(DbTreeItem* item); + TableWindow* newTable(Db* db); ViewWindow* openView(DbTreeItem* item); ViewWindow* openView(Db* db, const QString& database, const QString& view); - ViewWindow* newView(DbTreeItem* item); + ViewWindow* newView(Db* db); void editIndex(DbTreeItem* item); void editTrigger(DbTreeItem* item); void delSelectedObject(); @@ -156,6 +155,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer bool execQueryFromFile(Db* db, const QString& sql); void handleFileQueryExecution(Db* db, int executed, int attemptedExecutions, bool ok, bool ignoreErrors, int millis); QList> executeFileQueries(Db* db, QTextStream& stream, int& executed, int& attemptedExecutions, bool& ok, bool ignoreErrors, qint64 fileSize); + bool shouldSkipQueryFromFileExecution(const QString& sql); static bool areDbTreeItemsValidForItem(QList srcItems, const DbTreeItem* dstItem, bool forPasting = false); static bool areUrlsValidForItem(const QList& srcUrls, const DbTreeItem* dstItem); @@ -210,7 +210,6 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer void addColumn(); void editColumn(); void delColumn(); - void convertDb(); void vacuumDb(); void integrityCheck(); void createSimilarTable(); @@ -241,6 +240,7 @@ class GUI_API_EXPORT DbTree : public QDockWidget, public ExtActionContainer void updateFileExecProgress(int value); void fileExecCoverToBeClosed(); void fileExecErrors(const QList>& errors, bool rolledBack); + void schemaNeedsRefreshing(Db* db); }; int qHash(DbTree::Action action); diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitem.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitem.cpp index ead5e3d..6514aa8 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitem.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitem.cpp @@ -85,7 +85,7 @@ QString DbTreeItem::getTable() const { const DbTreeItem* item = getParentItem(Type::TABLE); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -93,7 +93,7 @@ QString DbTreeItem::getTable() const QString DbTreeItem::getColumn() const { if (getType() != Type::COLUMN) - return QString::null; + return QString(); return text(); } @@ -102,7 +102,7 @@ QString DbTreeItem::getIndex() const { const DbTreeItem* item = getParentItem(Type::INDEX); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -111,7 +111,7 @@ QString DbTreeItem::getTrigger() const { const DbTreeItem* item = getParentItem(Type::TRIGGER); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -120,7 +120,7 @@ QString DbTreeItem::getView() const { const DbTreeItem* item = getParentItem(Type::VIEW); if (!item) - return QString::null; + return QString(); return item->text(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitemdelegate.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitemdelegate.cpp index ef691d2..552bd91 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitemdelegate.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreeitemdelegate.cpp @@ -133,7 +133,7 @@ void DbTreeItemDelegate::paintSystemIndexLabel(QPainter* painter, const QStyleOp if (!db || !db->isValid()) return; - if (!isSystemIndex(item->text(), db->getDialect())) + if (!isSystemIndex(item->text())) return; paintLabel(painter, option, index, item, tr("(system index)", "database tree label")); @@ -147,7 +147,7 @@ void DbTreeItemDelegate::paintLabel(QPainter *painter, const QStyleOptionViewIte painter->save(); // Colors - painter->setPen(CFG_UI.Colors.DbTreeLabelsFg.get()); + painter->setPen(QApplication::style()->standardPalette().link().color()); // Font opt.font = CFG_UI.Fonts.DbTreeLabel.get(); @@ -155,7 +155,7 @@ void DbTreeItemDelegate::paintLabel(QPainter *painter, const QStyleOptionViewIte painter->setFont(opt.font); // Coords - int x = option.rect.x() + option.fontMetrics.width(item->text()) + 15 + option.decorationSize.width(); + int x = option.rect.x() + option.fontMetrics.horizontalAdvance(item->text()) + 15 + option.decorationSize.width(); int y = opt.rect.y() + (opt.rect.height() - opt.fontMetrics.descent() - opt.fontMetrics.ascent()) / 2 + opt.fontMetrics.ascent(); // Paint diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp index 123c4df..436ab50 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.cpp @@ -14,6 +14,7 @@ #include "dialogs/versionconvertsummarydialog.h" #include "db/invaliddb.h" #include "services/notifymanager.h" +#include "common/compatibility.h" #include #include #include @@ -202,6 +203,7 @@ QList DbTreeModel::childsToConfig(QStandardItem *item) group->referencedDbName = dbTreeItem->text(); group->order = i; group->open = dbTreeItem->getDb()->isOpen(); + group->dbExpanded = treeView->isExpanded(dbTreeItem->index()); groups += group; break; } @@ -260,10 +262,7 @@ void DbTreeModel::restoreGroup(const Config::DbGroupPtr& group, QList* dbLi // Instead of that, we just check if the database is already open (by DbManager) // and call proper handler to refresh database's schema and create tree nodes. if (db->isOpen()) - { - dbConnected(db); - treeView->expand(item->index()); - } + dbConnected(db, group->dbExpanded); } else { @@ -394,7 +393,7 @@ QVariant DbTreeModel::data(const QModelIndex &index, int role) const QString DbTreeModel::getToolTip(DbTreeItem* item) const { if (!item) - return QString::null; + return QString(); switch (item->getType()) { @@ -405,7 +404,7 @@ QString DbTreeModel::getToolTip(DbTreeItem* item) const default: break; } - return QString::null; + return QString(); } QString DbTreeModel::getDbToolTip(DbTreeItem* item) const @@ -552,13 +551,13 @@ QList DbTreeModel::refreshSchemaTables(const QStringList &table StrHash> DbTreeModel::refreshSchemaTableColumns(const StrHash &columns) { QStringList sortedColumns; - bool sort = CFG_UI.General.SortColumns.get(); + bool doSort = CFG_UI.General.SortColumns.get(); StrHash> items; for (const QString& key : columns.keys()) { sortedColumns = columns[key]; - if (sort) - qSort(sortedColumns); + if (doSort) + ::sSort(sortedColumns); for (const QString& column : sortedColumns) items[key] += DbTreeItemFactory::createColumn(column, this); @@ -681,7 +680,23 @@ void DbTreeModel::restoreExpandedState(const QHash& expandedState restoreExpandedState(expandedState, child); } -void DbTreeModel::dbConnected(Db* db) +DbTreeItem* DbTreeModel::findFirstItemOfType(DbTreeItem::Type type, QStandardItem* parentItem) +{ + DbTreeItem* child = nullptr; + for (int i = 0; i < parentItem->rowCount(); i++) + { + child = dynamic_cast(parentItem->child(i)); + if (child->getType() == type) + return child; + + child = findFirstItemOfType(type, child); + if (child) + return child; + } + return nullptr; +} + +void DbTreeModel::dbConnected(Db* db, bool expandItem) { QStandardItem* item = findItem(DbTreeItem::Type::DB, db); if (!item) @@ -690,12 +705,15 @@ void DbTreeModel::dbConnected(Db* db) return; } refreshSchema(db, item); - treeView->expand(item->index()); - if (CFG_UI.General.ExpandTables.get()) - treeView->expand(item->index().child(0, 0)); // also expand tables + if (expandItem) + { + treeView->expand(item->index()); + if (CFG_UI.General.ExpandTables.get()) + treeView->expand(item->model()->index(0, 0, item->index())); // also expand tables - if (CFG_UI.General.ExpandViews.get()) - treeView->expand(item->index().child(1, 0)); // also expand views + if (CFG_UI.General.ExpandViews.get()) + treeView->expand(item->model()->index(1, 0, item->index())); // also expand views + } } void DbTreeModel::dbDisconnected(Db* db) @@ -798,6 +816,11 @@ DbTreeItem *DbTreeModel::findItem(DbTreeItem::Type type, Db* db) return findItem(root(), type, db); } +DbTreeItem* DbTreeModel::findFirstItemOfType(DbTreeItem::Type type) +{ + return findFirstItemOfType(type, root()); +} + DbTreeItem *DbTreeModel::findItemBySignature(const QString &signature) { QStringList parts = signature.split("_"); @@ -953,7 +976,7 @@ bool DbTreeModel::pasteData(const QMimeData* data, int row, int column, const QM DbTreeItem* dstItem = nullptr; if (parent.isValid()) { - QModelIndex idx = parent.child(row, column); + QModelIndex idx = index(row, column, parent); if (idx.isValid()) dstItem = dynamic_cast(itemFromIndex(idx)); else // drop on top of the parent @@ -1268,6 +1291,7 @@ void DbTreeModel::dbObjectsMoveFinished(bool success, Db* srcDb, Db* dstDb) if (!success) { interruptableFinished(dbOrganizer); + DBTREE->refreshSchema(srcDb); return; } diff --git a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.h b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.h index 9ba0d82..86dc8a6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/dbtree/dbtreemodel.h @@ -27,6 +27,7 @@ class GUI_API_EXPORT DbTreeModel : public QStandardItemModel void connectDbManagerSignals(); DbTreeItem* findItem(DbTreeItem::Type type, const QString &name); DbTreeItem* findItem(DbTreeItem::Type type, Db* db); + DbTreeItem* findFirstItemOfType(DbTreeItem::Type type); DbTreeItem* findItemBySignature(const QString& signature); QList findItems(DbTreeItem::Type type); void move(QStandardItem* itemToMove, QStandardItem* newParentItem, int newRow = -1); @@ -69,14 +70,15 @@ class GUI_API_EXPORT DbTreeModel : public QStandardItemModel void collectExpandedState(QHash& state, QStandardItem* parentItem = nullptr); QStandardItem* refreshSchemaDb(Db* db); QList refreshSchemaTables(const QStringList &tables, const QStringList& virtualTables, bool sort); - StrHash > refreshSchemaTableColumns(const StrHash& columns); - StrHash > refreshSchemaIndexes(const StrHash& indexes, bool sort); - StrHash > refreshSchemaTriggers(const StrHash& triggers, bool sort); + StrHash> refreshSchemaTableColumns(const StrHash& columns); + StrHash> refreshSchemaIndexes(const StrHash& indexes, bool sort); + StrHash> refreshSchemaTriggers(const StrHash& triggers, bool sort); QList refreshSchemaViews(const QStringList &views, bool sort); void populateChildItemsWithDb(QStandardItem* parentItem, Db* db); void refreshSchemaBuild(QStandardItem* dbItem, QList tables, StrHash > indexes, StrHash > triggers, QList views, StrHash > allTableColumns); void restoreExpandedState(const QHash& expandedState, QStandardItem* parentItem); + DbTreeItem* findFirstItemOfType(DbTreeItem::Type type, QStandardItem* parentItem); QString getToolTip(DbTreeItem *item) const; QString getDbToolTip(DbTreeItem *item) const; QString getTableToolTip(DbTreeItem *item) const; @@ -111,7 +113,7 @@ class GUI_API_EXPORT DbTreeModel : public QStandardItemModel void dbAdded(Db* db); void dbUpdated(const QString &oldName, Db* db); void dbRemoved(Db* db); - void dbConnected(Db* db); + void dbConnected(Db* db, bool expandItem = true); void dbDisconnected(Db* db); void dbUnloaded(Db* db); void dbLoaded(Db* db); diff --git a/SQLiteStudio3/guiSQLiteStudio/debugconsole.cpp b/SQLiteStudio3/guiSQLiteStudio/debugconsole.cpp index 033eb1c..47387e6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/debugconsole.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/debugconsole.cpp @@ -51,7 +51,7 @@ void DebugConsole::initFormats() fatFormat.setFontUnderline(true); QFontMetrics fm(ui->textEdit->font()); - int indent = fm.width(QString("X").repeated(25)); + int indent = fm.horizontalAdvance(QString("X").repeated(25)); ui->textEdit->document()->setIndentWidth(indent); blockFormat.setIndent(1); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp index 82d5e14..7e084fb 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/aboutdialog.cpp @@ -120,7 +120,7 @@ QString AboutDialog::readFile(const QString& path) if (!file.open(QIODevice::ReadOnly)) { qCritical() << "Error opening" << file.fileName(); - return QString::null; + return QString(); } QString contents = QString::fromLatin1(file.readAll()).toHtmlEscaped(); file.close(); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp index 8bf1698..ebf9253 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.cpp @@ -62,6 +62,7 @@ void ColumnDialog::init() connect(ui->fkButton, SIGNAL(clicked()), this, SLOT(configureFk())); connect(ui->checkButton, SIGNAL(clicked()), this, SLOT(configureCheck())); connect(ui->defaultButton, SIGNAL(clicked()), this, SLOT(configureDefault())); + connect(ui->generatedButton, SIGNAL(clicked()), this, SLOT(configureGenerated())); connect(ui->notNullButton, SIGNAL(clicked()), this, SLOT(configureNotNull())); connect(ui->collateButton, SIGNAL(clicked()), this, SLOT(configureCollate())); connect(ui->uniqueButton, SIGNAL(clicked()), this, SLOT(configureUnique())); @@ -95,6 +96,7 @@ void ColumnDialog::createActions() createAction(ADD_CHECK, ICONS.CONSTRAINT_CHECK_ADD, tr("Add a check constraint", "column dialog"), this, SLOT(addCheck()), ui->constraintsToolbar); createAction(ADD_NOT_NULL, ICONS.CONSTRAINT_NOT_NULL_ADD, tr("Add a not null constraint", "column dialog"), this, SLOT(addNotNull()), ui->constraintsToolbar); createAction(ADD_COLLATE, ICONS.CONSTRAINT_COLLATION_ADD, tr("Add a collate constraint", "column dialog"), this, SLOT(addCollate()), ui->constraintsToolbar); + createAction(ADD_GENERATED, ICONS.CONSTRAINT_GENERATED_ADD, tr("Add a generated value constraint", "column dialog"), this, SLOT(addGenerated()), ui->constraintsToolbar); createAction(ADD_DEFAULT, ICONS.CONSTRAINT_DEFAULT_ADD, tr("Add a default constraint", "column dialog"), this, SLOT(addDefault()), ui->constraintsToolbar); } @@ -128,6 +130,7 @@ void ColumnDialog::updateState() ui->notNullButton->setEnabled(ui->notNullCheck->isChecked()); ui->checkButton->setEnabled(ui->checkCheck->isChecked()); ui->collateButton->setEnabled(ui->collateCheck->isChecked()); + ui->generatedButton->setEnabled(ui->generatedCheck->isChecked()); ui->defaultButton->setEnabled(ui->defaultCheck->isChecked()); updateConstraintsToolbarState(); } @@ -135,6 +138,9 @@ void ColumnDialog::updateState() void ColumnDialog::addConstraint(ConstraintDialog::Constraint mode) { NewConstraintDialog dialog(mode, column.data(), db, this); + for (ConstraintDialog::Constraint constraint : disabledConstraints) + dialog.disableMode(constraint); + if (dialog.exec() != QDialog::Accepted) return; @@ -160,6 +166,7 @@ void ColumnDialog::setupConstraintCheckBoxes() ui->notNullCheck->setIcon(ICONS.CONSTRAINT_NOT_NULL); ui->checkCheck->setIcon(ICONS.CONSTRAINT_CHECK); ui->collateCheck->setIcon(ICONS.CONSTRAINT_COLLATION); + ui->generatedCheck->setIcon(ICONS.CONSTRAINT_GENERATED); ui->defaultCheck->setIcon(ICONS.CONSTRAINT_DEFAULT); connect(ui->pkCheck, SIGNAL(clicked(bool)), this, SLOT(pkToggled(bool))); @@ -168,6 +175,7 @@ void ColumnDialog::setupConstraintCheckBoxes() connect(ui->notNullCheck, SIGNAL(clicked(bool)), this, SLOT(notNullToggled(bool))); connect(ui->checkCheck, SIGNAL(clicked(bool)), this, SLOT(checkToggled(bool))); connect(ui->collateCheck, SIGNAL(clicked(bool)), this, SLOT(collateToggled(bool))); + connect(ui->generatedCheck, SIGNAL(clicked(bool)), this, SLOT(generatedToggled(bool))); connect(ui->defaultCheck, SIGNAL(clicked(bool)), this, SLOT(defaultToggled(bool))); for (QCheckBox* cb : { @@ -177,6 +185,7 @@ void ColumnDialog::setupConstraintCheckBoxes() ui->notNullCheck, ui->checkCheck, ui->collateCheck, + ui->generatedCheck, ui->defaultCheck }) { @@ -293,15 +302,7 @@ void ColumnDialog::updateConstraintState(SqliteCreateTable::Column::Constraint* } QString errMsg = tr("Correct the constraint's configuration."); - if (db->getDialect() == Dialect::Sqlite2 && isUnofficialSqlite2Constraint(constraint)) - { - QString tooltip = tr("This constraint is not officially supported by SQLite 2,\nbut it's okay to use it."); - setValidStateWihtTooltip(toolButton, tooltip, result, errMsg); - } - else - { - setValidState(toolButton, result, errMsg); - } + setValidState(toolButton, result, errMsg); if (!result) { @@ -326,6 +327,8 @@ QCheckBox* ColumnDialog::getCheckBoxForConstraint(SqliteCreateTable::Column::Con return ui->defaultCheck; case SqliteCreateTable::Column::Constraint::COLLATE: return ui->collateCheck; + case SqliteCreateTable::Column::Constraint::GENERATED: + return ui->generatedCheck; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return ui->fkCheck; case SqliteCreateTable::Column::Constraint::NULL_: @@ -352,6 +355,8 @@ QToolButton* ColumnDialog::getToolButtonForConstraint(SqliteCreateTable::Column: return ui->defaultButton; case SqliteCreateTable::Column::Constraint::COLLATE: return ui->collateButton; + case SqliteCreateTable::Column::Constraint::GENERATED: + return ui->generatedButton; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return ui->fkButton; case SqliteCreateTable::Column::Constraint::NULL_: @@ -362,26 +367,6 @@ QToolButton* ColumnDialog::getToolButtonForConstraint(SqliteCreateTable::Column: return nullptr; } -bool ColumnDialog::isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Constraint* constraint) -{ - switch (constraint->type) - { - case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: - case SqliteCreateTable::Column::Constraint::COLLATE: - return true; - case SqliteCreateTable::Column::Constraint::PRIMARY_KEY: - case SqliteCreateTable::Column::Constraint::NOT_NULL: - case SqliteCreateTable::Column::Constraint::UNIQUE: - case SqliteCreateTable::Column::Constraint::CHECK: - case SqliteCreateTable::Column::Constraint::DEFAULT: - case SqliteCreateTable::Column::Constraint::NULL_: - case SqliteCreateTable::Column::Constraint::NAME_ONLY: - case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: - break; - } - return false; -} - void ColumnDialog::updateTypeValidations() { QString scaleErrorMsg = tr("Scale is not allowed for INTEGER PRIMARY KEY columns."); @@ -503,6 +488,11 @@ void ColumnDialog::addNotNull() addConstraint(ConstraintDialog::NOTNULL); } +void ColumnDialog::addGenerated() +{ + addConstraint(ConstraintDialog::GENERATED); +} + void ColumnDialog::addDefault() { addConstraint(ConstraintDialog::DEFAULT); @@ -538,6 +528,11 @@ void ColumnDialog::configureNotNull() configureConstraint(SqliteCreateTable::Column::Constraint::NOT_NULL); } +void ColumnDialog::configureGenerated() +{ + configureConstraint(SqliteCreateTable::Column::Constraint::GENERATED); +} + void ColumnDialog::configureDefault() { configureConstraint(SqliteCreateTable::Column::Constraint::DEFAULT); @@ -569,6 +564,11 @@ void ColumnDialog::collateToggled(bool enabled) constraintToggled(SqliteCreateTable::Column::Constraint::COLLATE, enabled); } +void ColumnDialog::generatedToggled(bool enabled) +{ + constraintToggled(SqliteCreateTable::Column::Constraint::GENERATED, enabled); +} + void ColumnDialog::notNullToggled(bool enabled) { constraintToggled(SqliteCreateTable::Column::Constraint::NOT_NULL, enabled); @@ -596,6 +596,7 @@ void ColumnDialog::updateValidations() ui->notNullCheck, ui->checkCheck, ui->collateCheck, + ui->generatedCheck, ui->defaultCheck }) { @@ -609,6 +610,7 @@ void ColumnDialog::updateValidations() ui->notNullButton, ui->checkButton, ui->collateButton, + ui->generatedButton, ui->defaultButton }) { @@ -663,6 +665,47 @@ QToolBar* ColumnDialog::getToolBar(int toolbar) const return nullptr; } +void ColumnDialog::disableConstraint(ConstraintDialog::Constraint constraint) +{ + disabledConstraints << constraint; + switch (constraint) { + case ConstraintDialog::PK: + ui->pkCheck->setEnabled(false); + actionMap[ADD_PK]->setEnabled(false); + break; + case ConstraintDialog::FK: + ui->fkCheck->setEnabled(false); + actionMap[ADD_FK]->setEnabled(false); + break; + case ConstraintDialog::UNIQUE: + ui->uniqueCheck->setEnabled(false); + actionMap[ADD_UNIQUE]->setEnabled(false); + break; + case ConstraintDialog::NOTNULL: + ui->notNullCheck->setEnabled(false); + actionMap[ADD_NOT_NULL]->setEnabled(false); + break; + case ConstraintDialog::CHECK: + ui->checkCheck->setEnabled(false); + actionMap[ADD_CHECK]->setEnabled(false); + break; + case ConstraintDialog::COLLATE: + ui->collateCheck->setEnabled(false); + actionMap[ADD_COLLATE]->setEnabled(false); + break; + case ConstraintDialog::GENERATED: + ui->generatedCheck->setEnabled(false); + actionMap[ADD_GENERATED]->setEnabled(false); + break; + case ConstraintDialog::DEFAULT: + ui->defaultCheck->setEnabled(false); + actionMap[ADD_DEFAULT]->setEnabled(false); + break; + case ConstraintDialog::UNKNOWN: + break; + } +} + void ColumnDialog::updateDataType() { if (!column) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h index 47615e8..c9faf23 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.h @@ -34,6 +34,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer ADD_CHECK, ADD_DEFAULT, ADD_NOT_NULL, + ADD_GENERATED, ADD_COLLATE }; @@ -48,6 +49,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer void setColumn(SqliteCreateTable::Column* value); SqliteCreateTable::Column* getModifiedColumn(); QToolBar* getToolBar(int toolbar) const; + void disableConstraint(ConstraintDialog::Constraint constraint); protected: void changeEvent(QEvent *e); @@ -66,7 +68,6 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer void updateConstraintState(SqliteCreateTable::Column::Constraint* constraint); QCheckBox* getCheckBoxForConstraint(SqliteCreateTable::Column::Constraint* constraint); QToolButton* getToolButtonForConstraint(SqliteCreateTable::Column::Constraint* constraint); - bool isUnofficialSqlite2Constraint(SqliteCreateTable::Column::Constraint* constraint); void updateTypeValidations(); void updateTypeForAutoIncr(); bool hasAutoIncr() const; @@ -77,6 +78,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer QCheckBox* modeCheckBox = nullptr; Db* db = nullptr; bool integerTypeEnforced = false; + QSet disabledConstraints; private slots: void updateConstraintsToolbarState(); @@ -93,12 +95,14 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer void addCheck(); void addCollate(); void addNotNull(); + void addGenerated(); void addDefault(); void configurePk(); void configureFk(); void configureUnique(); void configureCheck(); void configureCollate(); + void configureGenerated(); void configureNotNull(); void configureDefault(); void pkToggled(bool enabled); @@ -106,6 +110,7 @@ class GUI_API_EXPORT ColumnDialog : public QDialog, public ExtActionContainer void uniqueToggled(bool enabled); void checkToggled(bool enabled); void collateToggled(bool enabled); + void generatedToggled(bool enabled); void notNullToggled(bool enabled); void defaultToggled(bool enabled); void switchMode(bool advanced); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui index 1ac6cbb..6094ab1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialog.ui @@ -6,8 +6,8 @@ 0 0 - 424 - 360 + 467 + 393 @@ -109,31 +109,31 @@ - - + + - Unique + Configure - - + + - Configure + Generated value - - + + - Foreign Key + Not NULL - - + + - Configure + Foreign Key @@ -144,17 +144,24 @@ - - + + - Not NULL + Check condition - - + + - Check condition + Unique + + + + + + + Configure @@ -165,43 +172,50 @@ - - + + - Default + Configure - - + + Configure - - + + Configure - - + + Configure - - + + + + Default + + + + + Configure - + Configure diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp index 853b680..1107ef5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.cpp @@ -147,6 +147,9 @@ QIcon ColumnDialogConstraintsModel::getIcon(int rowIdx) const return ICONS.CONSTRAINT_DEFAULT; case SqliteCreateTable::Column::Constraint::COLLATE: return ICONS.CONSTRAINT_COLLATION; + case SqliteCreateTable::Column::Constraint::GENERATED: + return (constr->generatedType == SqliteCreateTable::Column::Constraint::GeneratedType::STORED) ? + ICONS.CONSTRAINT_GENERATED_STORED : ICONS.CONSTRAINT_GENERATED_VIRTUAL; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return ICONS.CONSTRAINT_FOREIGN_KEY; case SqliteCreateTable::Column::Constraint::NULL_: @@ -180,6 +183,8 @@ QString ColumnDialogConstraintsModel::getType(int rowIdx) const return "DEFAULT"; case SqliteCreateTable::Column::Constraint::COLLATE: return "COLLATE"; + case SqliteCreateTable::Column::Constraint::GENERATED: + return "GENERATED"; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return "FOREIGN KEY"; case SqliteCreateTable::Column::Constraint::NULL_: @@ -187,7 +192,7 @@ QString ColumnDialogConstraintsModel::getType(int rowIdx) const case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: break; } - return QString::null; + return QString(); } QString ColumnDialogConstraintsModel::getDetails(int rowIdx) const @@ -207,6 +212,8 @@ QString ColumnDialogConstraintsModel::getDetails(int rowIdx) const return getDefaultDetails(constr); case SqliteCreateTable::Column::Constraint::COLLATE: return getCollateDetails(constr); + case SqliteCreateTable::Column::Constraint::GENERATED: + return getGeneratedDetails(constr); case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return getFkDetails(constr); case SqliteCreateTable::Column::Constraint::NULL_: @@ -214,7 +221,7 @@ QString ColumnDialogConstraintsModel::getDetails(int rowIdx) const case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: break; } - return QString::null; + return QString(); } QString ColumnDialogConstraintsModel::getPkDetails(SqliteCreateTable::Column::Constraint* constr) const @@ -241,6 +248,12 @@ QString ColumnDialogConstraintsModel::getCheckDetails(SqliteCreateTable::Column: return getConstrDetails(constr, idx); } +QString ColumnDialogConstraintsModel::getGeneratedDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "AS", Qt::CaseInsensitive); + return getConstrDetails(constr, idx); +} + QString ColumnDialogConstraintsModel::getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const { int idx = constr->tokens.indexOf(Token::KEYWORD, "DEFAULT", Qt::CaseInsensitive); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h index f37933a..aed7a28 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/columndialogconstraintsmodel.h @@ -44,6 +44,7 @@ class GUI_API_EXPORT ColumnDialogConstraintsModel : public QAbstractTableModel QString getNotNullDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getUniqueDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getCheckDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getGeneratedDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getCollateDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getFkDetails(SqliteCreateTable::Column::Constraint* constr) const; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp index 63af58a..be45873 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.cpp @@ -22,6 +22,9 @@ #include "datatype.h" #include "uiutils.h" #include "translations.h" +#include "plugins/uiconfiguredplugin.h" +#include "dbtree/dbtree.h" +#include "common/compatibility.h" #include #include #include @@ -37,8 +40,6 @@ #include #include #include -#include -#include #define GET_FILTER_STRING(Widget, WidgetType, Method) \ if (qobject_cast(Widget))\ @@ -117,7 +118,7 @@ QString ConfigDialog::getFilterString(QWidget *widget) GET_FILTER_STRING2(widget, QListWidget); GET_FILTER_STRING2(widget, QTableWidget); - return QString::null; + return QString(); } QString ConfigDialog::getFilterString(QComboBox *widget) @@ -201,7 +202,9 @@ void ConfigDialog::init() for (CfgEntry* cfg : entries) connect(cfg, SIGNAL(changed(QVariant)), this, SLOT(markRequiresSchemasRefresh())); - ui->activeStyleCombo->addItems(QStyleFactory::keys()); + QStringList styles = QStyleFactory::keys(); + styles.sort(Qt::CaseInsensitive); + ui->activeStyleCombo->addItems(styles); connect(ui->stackedWidget, SIGNAL(currentChanged(int)), this, SLOT(pageSwitched())); @@ -384,7 +387,7 @@ QList ConfigDialog::getDefaultEditorsForType(DataType: sortedPlugins << editorWithPrio; } - qSort(sortedPlugins.begin(), sortedPlugins.end(), [=](const PluginWithPriority& p1, const PluginWithPriority& p2) -> bool + sSort(sortedPlugins, [=](const PluginWithPriority& p1, const PluginWithPriority& p2) -> bool { return p1.first < p2.first; }); @@ -446,7 +449,7 @@ QList ConfigDialog::updateCustomDataTypeEditors(const ui->dataEditorsAvailableList->addItem(item); } - qSort(enabledPlugins.begin(), enabledPlugins.end(), [=](MultiEditorWidgetPlugin* p1, MultiEditorWidgetPlugin* p2) -> bool + sSort(enabledPlugins, [=](MultiEditorWidgetPlugin* p1, MultiEditorWidgetPlugin* p2) -> bool { return editorsOrder.indexOf(p1->getName()) < editorsOrder.indexOf(p2->getName()); }); @@ -920,9 +923,9 @@ void ConfigDialog::updateBuiltInPluginsVisibility() { it.next(); if (PLUGINS->isBuiltIn(it.value())) - ui->pluginsList->setItemHidden(it.key(), hideBuiltIn); + it.key()->setHidden(hideBuiltIn); else - ui->pluginsList->setItemHidden(it.key(), false); + it.key()->setHidden(false); } } @@ -1053,7 +1056,7 @@ void ConfigDialog::refreshFormattersPage() pluginTitles << plugin->getTitle(); } sortedPluginNames = pluginNames; - qSort(sortedPluginNames); + sSort(sortedPluginNames); combo = new QComboBox(ui->formatterPluginsTree); for (int i = 0, total = pluginNames.size(); i < total; ++i) @@ -1092,6 +1095,7 @@ void ConfigDialog::refreshFormattersPage() void ConfigDialog::applyStyle(QWidget *widget, QStyle *style) { widget->setStyle(style); + widget->setPalette(style->standardPalette()); for (QObject* child : widget->children()) { if (!qobject_cast(child)) @@ -1250,7 +1254,7 @@ void ConfigDialog::initPluginsPage() categoryRow = 0; QList pluginTypes = PLUGINS->getPluginTypes(); - qSort(pluginTypes.begin(), pluginTypes.end(), PluginType::nameLessThan); + sSort(pluginTypes, PluginType::nameLessThan); for (PluginType* pluginType : pluginTypes) { category = new QTreeWidgetItem({pluginType->getTitle()}); @@ -1270,7 +1274,7 @@ void ConfigDialog::initPluginsPage() itemRow = 0; pluginNames = pluginType->getAllPluginNames(); - qSort(pluginNames); + sSort(pluginNames); for (const QString& pluginName : pluginNames) { builtIn = PLUGINS->isBuiltIn(pluginName); @@ -1388,10 +1392,10 @@ void ConfigDialog::initDataEditors() ui->dataEditorsAvailableList->setSpacing(1); QHash editorsOrder = CFG_UI.General.DataEditorsOrder.get(); - QSet dataTypeSet = editorsOrder.keys().toSet(); - dataTypeSet += DataType::getAllNames().toSet(); - QStringList dataTypeList = dataTypeSet.toList(); - qSort(dataTypeList); + QSet dataTypeSet = toSet(editorsOrder.keys()); + dataTypeSet += toSet(DataType::getAllNames()); + QStringList dataTypeList = dataTypeSet.values(); + sSort(dataTypeList); QListWidgetItem* item = nullptr; for (const QString& type : dataTypeList) @@ -1452,7 +1456,7 @@ void ConfigDialog::initShortcuts() categories << cat; } - qSort(categories.begin(), categories.end(), [](CfgCategory* cat1, CfgCategory* cat2) -> bool + sSort(categories, [](CfgCategory* cat1, CfgCategory* cat2) -> bool { return cat1->getTitle().compare(cat2->getTitle()) < 0; }); @@ -1503,7 +1507,7 @@ void ConfigDialog::initShortcuts(CfgCategory *cfgCategory) int itemRow = 0; QStringList entryNames = cfgCategory->getEntries().keys(); - qSort(entryNames); + sSort(entryNames); for (const QString& entryName : entryNames) { // Title diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui index fe0b772..4452f63 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/configdialog.ui @@ -148,18 +148,6 @@ :/icons/img/config_font.png:/icons/img/config_font.png - - - Colors - - - colorsPage - - - - :/icons/img/config_colors.png:/icons/img/config_colors.png - - @@ -229,7 +217,7 @@ - 3 + 1 @@ -407,8 +395,8 @@ 0 0 - 577 - 472 + 564 + 540 @@ -418,29 +406,6 @@ Data browsing and editing - - - - <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 - - - - - - - <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - - - 999999 - - - General.PopulateHistorySize - - - @@ -460,7 +425,7 @@ - + <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> @@ -479,7 +444,14 @@ - + + + + Number of data rows per page: + + + + <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> @@ -489,14 +461,43 @@ - - + + + + <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> + - Number of data rows per page: + Use DEFAULT value (if defined), when committing NULL value + + + General.UseDefaultValueForNull - + + + + <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> + + + 999999 + + + General.PopulateHistorySize + + + + + + + <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 + + + + <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> @@ -509,7 +510,7 @@ - + <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> @@ -522,16 +523,16 @@ - - + + - <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> + <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> - Use DEFAULT value (if defined), when committing NULL value + Limit number of rows for in case of dozens of columns - General.UseDefaultValueForNull + General.LimitRowsForManyColumns @@ -934,6 +935,16 @@ + + + + Allow multiple instances of the application at the same time + + + General.AllowMultipleSessions + + + @@ -1021,12 +1032,12 @@ false - - 150 - 16 + + 150 + false @@ -1073,8 +1084,8 @@ 0 0 - 335 - 237 + 339 + 264 @@ -1585,8 +1596,8 @@ p, li { white-space: pre-wrap; } 0 0 - 196 - 263 + 206 + 298 @@ -1689,542 +1700,6 @@ p, li { white-space: pre-wrap; } - - - - - - QFrame::NoFrame - - - 0 - - - true - - - - - 0 - 0 - 247 - 701 - - - - - - - SQL editor colors - - - - - - Current line background - - - - - - - <p>SQL strings are enclosed with single quote characters.</p> - - - String foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorKeywordFg - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorStringFg - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorCommentFg - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorForeground - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorCurrentLineBg - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorLineNumAreaBg - - - - - - - <p>Bind parameters are placeholders for values yet to be provided by the user. They have one of the forms:</p><ul><li>:param_name</li><li>$param_name</li><li>@param_name</li><li>?</li></ul> - - - Bind parameter foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorParenthesisBg - - - - - - - Highlighted parenthesis background - - - - - - - <p>BLOB values are binary values represented as hexadecimal numbers, like:</p><ul><li>X'12B4'</li><li>x'46A2F4'</li></ul> - - - BLOB value foreground - - - - - - - Regular foreground - - - - - - - Line numbers area background - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorBlobFg - - - - - - - Keyword foreground - - - - - - - Number foreground - - - - - - - Comment foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorNumberFg - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorBindParamFg - - - - - - - <p>Valid objects are name of tables, indexes, triggers, or views that exist in the SQLite database.</p> - - - Valid objects foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.SqlEditorValidObject - - - - - - - - - - Data view colors - - - - - - <p>Any data changes will be outlined with this color, until they're committed to the database.</p> - - - Uncommitted data outline color - - - - - - - - 50 - 16777215 - - - - - - - Colors.DataUncommitted - - - - - - - <p>In case of error while committing data changes, the problematic cell will be outlined with this color.</p> - - - Commit error outline color - - - - - - - - 50 - 16777215 - - - - - - - Colors.DataUncommittedError - - - - - - - NULL value foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.DataNullFg - - - - - - - Deleted row background - - - - - - - - 50 - 16777215 - - - - - - - Colors.DataDeletedBg - - - - - - - - - - Database list colors - - - - - - <p>Additional labels are those which tell you SQLite version, number of objects deeper in the tree, etc.</p> - - - Additional labels foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.DbTreeLabelsFg - - - - - - - - - - Status field colors - - - - - - Information message foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.StatusFieldInfoFg - - - - - - - Warning message foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.StatusFieldWarnFg - - - - - - - Error message foreground - - - - - - - - 50 - 16777215 - - - - - - - Colors.StatusFieldErrorFg - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - @@ -2255,11 +1730,6 @@ p, li { white-space: pre-wrap; } - - ColorButton - QPushButton -
common/colorbutton.h
-
FontEdit QWidget @@ -2298,26 +1768,6 @@ p, li { white-space: pre-wrap; } previewComboBox previewTextEdit fontsScrollArea - scrollArea - sqlEditorKeywordFgButton - sqlEditorStringFgButton - sqlEditorCommentFgButton - sqlEditorRegularFgButton - sqlEditorCurrLineBgButton - sqlEditorLineNumAreaBgButton - sqlEditorParBgButton - sqlEditorBlobFgButton - sqlEditorNumberFgButton - sqlEditorBindParamFgButton - sqlEditorValidObjectsButton - dataViewUncommittedButton - dataViewErrorButton - dataViewNullFgButton - dataViewDeletedRowBgButton - dbTreeLabelsButton - statusFieldInfoButton - statusFieldWarnButton - statusFieldErrorButton diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp index 0094ad0..0333cbc 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.cpp @@ -136,6 +136,8 @@ ConstraintDialog::Constraint ConstraintDialog::getSelectedConstraint(SqliteCreat return CHECK; case SqliteCreateTable::Column::Constraint::DEFAULT: return DEFAULT; + case SqliteCreateTable::Column::Constraint::GENERATED: + return GENERATED; case SqliteCreateTable::Column::Constraint::COLLATE: return COLLATE; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: @@ -185,6 +187,10 @@ void ConstraintDialog::updateDefinitionHeader() ui->titleIcon->setPixmap(ICONS.CONSTRAINT_CHECK); ui->titleLabel->setText(tr("Check", "table constraints")); break; + case ConstraintDialog::GENERATED: + ui->titleIcon->setPixmap(ICONS.CONSTRAINT_GENERATED); + ui->titleLabel->setText(tr("Generated", "table constraints")); + break; case ConstraintDialog::COLLATE: ui->titleIcon->setPixmap(ICONS.CONSTRAINT_COLLATION); ui->titleLabel->setText(tr("Collate", "table constraints")); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h index fe24c0f..ef3e9f5 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/constraintdialog.h @@ -32,6 +32,7 @@ class GUI_API_EXPORT ConstraintDialog : public QDialog NOTNULL, CHECK, COLLATE, + GENERATED, DEFAULT, UNKNOWN }; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/cssdebugdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/cssdebugdialog.cpp index 99439e8..6e45138 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/cssdebugdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/cssdebugdialog.cpp @@ -2,6 +2,7 @@ #include "ui_cssdebugdialog.h" #include "mainwindow.h" #include "themetuner.h" +#include "uiconfig.h" #include #include @@ -12,7 +13,10 @@ CssDebugDialog::CssDebugDialog(QWidget *parent) : ui->setupUi(this); connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); - appliedCss = MAINWINDOW->styleSheet(); + appliedCss = CFG_UI.General.CustomCss.get(); + if (appliedCss.isEmpty()) + appliedCss = MAINWINDOW->styleSheet(); + ui->cssEdit->setPlainText(appliedCss); updateState(); @@ -33,6 +37,7 @@ void CssDebugDialog::buttonClicked(QAbstractButton* button) else if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) { appliedCss = ui->cssEdit->toPlainText(); + CFG_UI.General.CustomCss.set(appliedCss); MAINWINDOW->setStyleSheet(appliedCss); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp deleted file mode 100644 index 8af131e..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include "dbconverterdialog.h" -#include "ui_dbconverterdialog.h" -#include "common/global.h" -#include "dblistmodel.h" -#include "db/db.h" -#include "common/utils_sql.h" -#include "dbversionconverter.h" -#include "services/dbmanager.h" -#include "iconmanager.h" -#include "uiutils.h" -#include "versionconvertsummarydialog.h" -#include "mainwindow.h" -#include "errorsconfirmdialog.h" -#include "parser/ast/sqlitecreatetable.h" -#include "services/pluginmanager.h" -#include "plugins/dbplugin.h" -#include "db/sqlquery.h" -#include "services/notifymanager.h" -#include "common/widgetcover.h" -#include -#include -#include - -DbConverterDialog::DbConverterDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::DbConverterDialog) -{ - init(); -} - -DbConverterDialog::~DbConverterDialog() -{ - delete ui; - safe_delete(converter); -} - -void DbConverterDialog::setDb(Db* db) -{ - ui->srcDbCombo->setCurrentText(db->getName()); - srcDb = db; - srcDbChanged(); -} - -void DbConverterDialog::init() -{ - ui->setupUi(this); - limitDialogWidth(this); - - widgetCover = new WidgetCover(this); - widgetCover->setVisible(false); - widgetCover->initWithInterruptContainer(); - - ui->trgFileButton->setIcon(ICONS.OPEN_FILE); - - converter = new DbVersionConverter(); - - dbListModel = new DbListModel(this); - ui->srcDbCombo->setModel(dbListModel); - - connect(ui->srcDbCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(srcDbChanged(int))); - connect(ui->trgVersionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState())); - connect(ui->trgFileEdit, SIGNAL(textChanged(QString)), this, SLOT(updateState())); - connect(ui->trgNameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateState())); - connect(converter, SIGNAL(conversionFailed(QString)), this, SLOT(processingFailed(QString))); - connect(converter, SIGNAL(conversionSuccessful()), this, SLOT(processingSuccessful())); - connect(converter, SIGNAL(conversionAborted()), this, SLOT(processingAborted())); - connect(widgetCover, SIGNAL(cancelClicked()), converter, SLOT(interrupt())); -} - -void DbConverterDialog::srcDbChanged() -{ - dontUpdateState = true; - ui->srcDbVersionCombo->clear(); - ui->trgVersionCombo->clear(); - if (srcDb) - { - // Source version - QList dialects = converter->getSupportedVersions(); - QStringList versionNames = converter->getSupportedVersionNames(); - Dialect dialect = srcDb->getDialect(); - int idx = dialects.indexOf(dialect); - QString type = versionNames[idx]; - ui->srcDbVersionCombo->addItem(type); - ui->srcDbVersionCombo->setCurrentText(type); - - // Target version - QString oldTrgVersion = ui->trgVersionCombo->currentText(); - versionNames.removeAt(idx); - ui->trgVersionCombo->addItems(versionNames); - if (versionNames.contains(oldTrgVersion)) - ui->trgVersionCombo->setCurrentText(oldTrgVersion); - else if (versionNames.size() > 0) - ui->trgVersionCombo->setCurrentIndex(0); - - // File - QString trgFile = srcDb->getPath() + "_new"; - int i = 0; - while (QFileInfo(trgFile).exists()) - { - trgFile = srcDb->getPath() + "_new" + QString::number(i++); - } - - ui->trgFileEdit->setText(trgFile); - - // Name - QString generatedName = generateUniqueName(srcDb->getName() + "_new", DBLIST->getDbNames()); - ui->trgNameEdit->setText(generatedName); - } - else - { - ui->srcDbVersionCombo->setCurrentText(""); - ui->trgFileEdit->setText(""); - ui->trgVersionCombo->setCurrentText(""); - ui->trgNameEdit->setText(""); - } - dontUpdateState = false; - updateState(); -} - -bool DbConverterDialog::validate() -{ - bool srcDbOk = (srcDb != nullptr); - setValidState(ui->srcDbCombo, srcDbOk, tr("Select source database")); - - QString dstDbPath = ui->trgFileEdit->text(); - QFileInfo dstDbFi(dstDbPath); - bool dstDbOk = (!dstDbFi.exists() || dstDbFi.isWritable()) && dstDbFi != QFileInfo(srcDb->getPath()); - bool dstExists = dstDbFi.exists(); - setValidState(ui->trgFileEdit, dstDbOk, tr("Enter valid and writable file path.")); - if (dstExists && dstDbOk) - setValidStateInfo(ui->trgFileEdit, tr("Entered file exists and will be overwritten.")); - - QString name = ui->trgNameEdit->text(); - bool nameOk = !name.isEmpty() && !DBLIST->getDbNames().contains(name); - setValidState(ui->trgNameEdit, nameOk, tr("Enter a not empty, unique name (as in the list of databases on the left).")); - - bool dstDialectOk = ui->trgVersionCombo->currentIndex() > -1; - QString msg; - if (!dstDialectOk && ui->trgVersionCombo->count() == 0) - msg = tr("No valid target dialect available. Conversion not possible."); - else - msg = tr("Select valid target dialect."); - - setValidState(ui->trgVersionCombo, dstDialectOk, msg); - - return (srcDbOk && nameOk && dstDbOk && dstDialectOk); -} - -void DbConverterDialog::accept() -{ - if (!validate()) - return; - - QStringList versionNames = converter->getSupportedVersionNames(); - QList dialects = converter->getSupportedVersions(); - QString trgDialectName = ui->trgVersionCombo->currentText(); - int idx = versionNames.indexOf(trgDialectName); - if (idx == -1) - { - qCritical() << "Could not find target dialect on list of supported dialects in DbConverterDialog::accept()"; - return; - } - - Dialect srcDialect = srcDb->getDialect(); - Dialect trgDialect = dialects[idx]; - QString trgFile = ui->trgFileEdit->text(); - QString trgName = ui->trgNameEdit->text(); - widgetCover->show(); - converter->convert(srcDialect, trgDialect, srcDb, trgFile, trgName, &DbConverterDialog::confirmConversion, &DbConverterDialog::confirmConversionErrors); -} - -void DbConverterDialog::srcDbChanged(int index) -{ - srcDb = dbListModel->getDb(index); - srcDbChanged(); -} - -void DbConverterDialog::updateState() -{ - if (dontUpdateState) - return; - - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(validate()); -} - -void DbConverterDialog::processingFailed(const QString& errorMessage) -{ - widgetCover->hide(); - notifyError(errorMessage); -} - -void DbConverterDialog::processingSuccessful() -{ - notifyInfo(tr("Database %1 has been successfully converted and now is available under new name: %2").arg(srcDb->getName(), ui->trgNameEdit->text())); - QDialog::accept(); -} - -void DbConverterDialog::processingAborted() -{ - widgetCover->hide(); -} - -bool DbConverterDialog::confirmConversion(const QList >& diffs) -{ - VersionConvertSummaryDialog dialog(MAINWINDOW); - dialog.setWindowTitle(tr("SQL statements conversion")); - dialog.setSides(diffs); - return dialog.exec() == QDialog::Accepted; -} - -bool DbConverterDialog::confirmConversionErrors(const QSet& errors) -{ - ErrorsConfirmDialog dialog(MAINWINDOW); - dialog.setTopLabel(tr("Following error occurred while converting SQL statements to the target SQLite version:")); - dialog.setBottomLabel(tr("Would you like to ignore those errors and proceed?")); - dialog.setErrors(errors); - return dialog.exec() == QDialog::Accepted; -} diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h deleted file mode 100644 index 4267a1b..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef DBCONVERTERDIALOG_H -#define DBCONVERTERDIALOG_H - -#include "guiSQLiteStudio_global.h" -#include - -class DbListModel; -class Db; -class DbVersionConverter; -class WidgetCover; - -namespace Ui { - class DbConverterDialog; -} - -class GUI_API_EXPORT DbConverterDialog : public QDialog -{ - Q_OBJECT - - public: - explicit DbConverterDialog(QWidget *parent = 0); - ~DbConverterDialog(); - - void setDb(Db* db); - - private: - void init(); - void srcDbChanged(); - bool validate(); - - static bool confirmConversion(const QList >& diffs); - static bool confirmConversionErrors(const QSet& errors); - - Ui::DbConverterDialog *ui = nullptr; - DbListModel* dbListModel = nullptr; - Db* srcDb = nullptr; - DbVersionConverter* converter = nullptr; - bool dontUpdateState = false; - WidgetCover* widgetCover = nullptr; - - public slots: - void accept(); - - private slots: - void srcDbChanged(int index); - void updateState(); - void processingFailed(const QString& errorMessage); - void processingSuccessful(); - void processingAborted(); -}; - -#endif // DBCONVERTERDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui deleted file mode 100644 index f6bf009..0000000 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbconverterdialog.ui +++ /dev/null @@ -1,144 +0,0 @@ - - - DbConverterDialog - - - - 0 - 0 - 400 - 251 - - - - Convert database - - - - - - Source database - - - - - - - - - Source database version: - - - - - - - false - - - - - - - - - - Target database - - - - - - Target version: - - - - - - - This is the file that will be created as a result of the conversion. - - - - - - - Target file: - - - - - - - Name of the new database: - - - - - - - - - - - - - - - - - This is the name that the converted database will be added to SQLiteStudio with. - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - DbConverterDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - DbConverterDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp index 183f8dd..1e56258 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/dbdialog.cpp @@ -8,6 +8,7 @@ #include "services/dbmanager.h" #include "common/global.h" #include "iconmanager.h" +#include "sqleditor.h" #include "common/unused.h" #include "db/sqlquery.h" #include @@ -199,16 +200,13 @@ void DbDialog::addOption(const DbPluginOption& option, int& row) } QLabel* label = new QLabel(option.label, this); - label->setAlignment(Qt::AlignVCenter|Qt::AlignRight); + label->setAlignment(Qt::AlignTop|Qt::AlignRight); QWidget* editor = nullptr; QWidget* editorHelper = nullptr; // TODO, based on plugins for Url handlers editor = getEditor(option, editorHelper); Q_ASSERT(editor != nullptr); - if (!option.toolTip.isNull()) - editor->setToolTip(option.toolTip); - optionWidgets << label << editor; optionKeyToWidget[option.key] = editor; @@ -240,6 +238,16 @@ QWidget *DbDialog::getEditor(const DbPluginOption& opt, QWidget*& editorHelper) editorHelper = nullptr; switch (opt.type) { + case DbPluginOption::SQL: + { + SqlEditor* sqlEdit = new SqlEditor(this); + editor = sqlEdit; + sqlEdit->setShowLineNumbers(false); + sqlEdit->setPlainText(opt.defaultValue.toString()); + sqlEdit->setMaximumHeight(sqlEdit->fontMetrics().height() * 5); + connect(sqlEdit, SIGNAL(textChanged()), this, SLOT(propertyChanged())); + break; + } case DbPluginOption::STRING: { editor = new QLineEdit(this); @@ -260,8 +268,20 @@ QWidget *DbDialog::getEditor(const DbPluginOption& opt, QWidget*& editorHelper) QComboBox* cb = new QComboBox(this); editor = cb; cb->setEditable(!opt.choiceReadOnly); - cb->addItems(opt.choiceValues); - cb->setCurrentText(opt.defaultValue.toString()); + if (opt.choiceDataValues.isEmpty()) + { + cb->addItems(opt.choiceValues); + cb->setCurrentText(opt.defaultValue.toString()); + } + else + { + for (auto it = opt.choiceDataValues.begin(); it != opt.choiceDataValues.end(); ++it) + { + cb->addItem(it.key(), it.value()); + if (it.value() == opt.defaultValue) + cb->setCurrentText(it.key()); + } + } connect(cb, SIGNAL(currentIndexChanged(QString)), this, SLOT(propertyChanged())); break; } @@ -330,6 +350,9 @@ QWidget *DbDialog::getEditor(const DbPluginOption& opt, QWidget*& editorHelper) le->setText(opt.defaultValue.toString()); } + if (!opt.toolTip.isNull()) + editor->setToolTip(opt.toolTip); + return editor; } @@ -338,6 +361,9 @@ QVariant DbDialog::getValueFrom(DbPluginOption::Type type, QWidget *editor) QVariant value; switch (type) { + case DbPluginOption::SQL: + value = dynamic_cast(editor)->toPlainText(); + break; case DbPluginOption::STRING: case DbPluginOption::PASSWORD: case DbPluginOption::FILE: @@ -353,8 +379,17 @@ QVariant DbDialog::getValueFrom(DbPluginOption::Type type, QWidget *editor) value = dynamic_cast(editor)->value(); break; case DbPluginOption::CHOICE: - value = dynamic_cast(editor)->currentText(); + { + QComboBox* cb = dynamic_cast(editor); + QVariant data = cb->currentData(); + if (data.isValid()) + { + value = data; + break; + } + value = cb->currentText(); break; + } case DbPluginOption::CUSTOM_PATH_BROWSE: break; // should not happen ever default: @@ -369,6 +404,9 @@ void DbDialog::setValueFor(DbPluginOption::Type type, QWidget *editor, const QVa { switch (type) { + case DbPluginOption::SQL: + dynamic_cast(editor)->setPlainText(value.toString()); + break; case DbPluginOption::STRING: case DbPluginOption::FILE: case DbPluginOption::PASSWORD: @@ -384,8 +422,20 @@ void DbDialog::setValueFor(DbPluginOption::Type type, QWidget *editor, const QVa dynamic_cast(editor)->setValue(value.toDouble()); break; case DbPluginOption::CHOICE: - dynamic_cast(editor)->setCurrentText(value.toString()); + { + QComboBox* cb = dynamic_cast(editor); + if (value.isValid()) + { + int idx = cb->findData(value); + if (idx > -1) + { + cb->setCurrentIndex(idx); + break; + } + } + cb->setCurrentText(value.toString()); break; + } case DbPluginOption::CUSTOM_PATH_BROWSE: break; // should not happen ever default: @@ -413,7 +463,7 @@ QHash DbDialog::collectOptions() if (ui->typeCombo->currentIndex() < 0) return options; - for (const QString key : optionKeyToWidget.keys()) + for (const QString& key : optionKeyToWidget.keys()) options[key] = getValueFrom(optionKeyToType[key], optionKeyToWidget[key]); DbPlugin* plugin = nullptr; diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp index c0a73f3..9f53ef1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/errorsconfirmdialog.cpp @@ -1,6 +1,7 @@ #include "errorsconfirmdialog.h" #include "ui_errorsconfirmdialog.h" #include "iconmanager.h" +#include "common/compatibility.h" ErrorsConfirmDialog::ErrorsConfirmDialog(QWidget *parent) : QDialog(parent), @@ -31,7 +32,7 @@ void ErrorsConfirmDialog::setErrors(const QHash>& errors) void ErrorsConfirmDialog::setErrors(const QSet& errors) { ui->list->clear(); - ui->list->addItems(errors.toList()); + ui->list->addItems(errors.values()); for (int i = 0, total = ui->list->count(); i < total; ++i) ui->list->item(i)->setIcon(ICONS.STATUS_ERROR); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp index 7b6a4d0..7dda03e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/exportdialog.cpp @@ -14,6 +14,7 @@ #include "schemaresolver.h" #include "common/widgetcover.h" #include "services/notifymanager.h" +#include "themetuner.h" #include "uiconfig.h" #include #include @@ -49,6 +50,7 @@ ExportDialog::~ExportDialog() void ExportDialog::init() { ui->setupUi(this); + THEME_TUNER->darkThemeFix(this); limitDialogWidth(this); #ifdef Q_OS_MACX @@ -531,7 +533,7 @@ void ExportDialog::updateDbObjTree() ui->dbObjectsTree->expand(root); QModelIndex child; - for (int i = 0; (child = root.child(i, 0)).isValid(); i++) + for (int i = 0; (child = selectableDbListModel->index(i, 0, root)).isValid(); i++) ui->dbObjectsTree->expand(child); } dbObjectsSelectAll(); @@ -718,7 +720,7 @@ void ExportDialog::exportTable(const ExportManager::StandardExportConfig& stdCon EXPORT_MANAGER->configure(format, stdConfig); // TODO when dbnames are fully supported, pass the dbname below - EXPORT_MANAGER->exportTable(db, QString::null, ui->exportTableNameCombo->currentText()); + EXPORT_MANAGER->exportTable(db, QString(), ui->exportTableNameCombo->currentText()); } void ExportDialog::exportQuery(const ExportManager::StandardExportConfig& stdConfig, const QString& format) @@ -741,7 +743,7 @@ ExportManager::StandardExportConfig ExportDialog::getExportConfig() const stdConfig.intoClipboard = clipboard; if (clipboard) - stdConfig.outputFileName = QString::null; + stdConfig.outputFileName = QString(); else if (outputFileSupported) stdConfig.outputFileName = ui->exportFileEdit->text(); @@ -790,7 +792,7 @@ QModelIndex ExportDialog::setupNewDbObjTreeRoot(const QModelIndex& root) if (item->getType() == DbTreeItem::Type::DB) return newRoot; - newRoot = newRoot.child(0, 0); + newRoot = selectableDbListModel->index(0, 0, newRoot); } return newRoot; } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp index ddb443d..565feb4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/importdialog.cpp @@ -13,6 +13,7 @@ #include "formmanager.h" #include "common/utils.h" #include "uiconfig.h" +#include "themetuner.h" #include #include #include @@ -100,6 +101,7 @@ void ImportDialog::readStdConfig() void ImportDialog::init() { ui->setupUi(this); + THEME_TUNER->darkThemeFix(this); limitDialogWidth(this); #ifdef Q_OS_MACX diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp index e3e7701..ebf9beb 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.cpp @@ -9,10 +9,11 @@ #include "uiconfig.h" #include "services/config.h" #include "uiutils.h" -#include "sqlite3.h" +#include "db/sqlite3.h" #include "indexexprcolumndialog.h" #include "windows/editorwindow.h" #include "services/codeformatter.h" +#include "common/compatibility.h" #include #include #include @@ -64,7 +65,7 @@ void IndexDialog::init() { qCritical() << "Created IndexDialog for null or closed database."; notifyError(tr("Tried to open index dialog for closed or inexisting database.")); - reject(); + preReject(); return; } @@ -91,7 +92,7 @@ void IndexDialog::init() connect(columnStateSignalMapping, SIGNAL(mapped(QString)), this, SLOT(updateColumnState(QString))); SchemaResolver resolver(db); - ui->tableCombo->addItem(QString::null); + ui->tableCombo->addItem(QString()); ui->tableCombo->addItems(resolver.getTables()); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateTable(QString))); connect(ui->tableCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateValidation())); @@ -99,29 +100,15 @@ void IndexDialog::init() if (existingIndex) ui->tableCombo->setEnabled(false); - if (db->getDialect() == Dialect::Sqlite3) - { - connect(ui->partialIndexCheck, SIGNAL(toggled(bool)), this, SLOT(updatePartialConditionState())); - connect(ui->partialIndexEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); - connect(ui->partialIndexEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); - ui->partialIndexEdit->setVirtualSqlExpression("SELECT %1"); - updatePartialConditionState(); - ui->columnsTable->setColumnHidden(2, false); - } - else - { - ui->partialIndexCheck->setVisible(false); - ui->partialIndexEdit->setVisible(false); - ui->columnsTable->setColumnHidden(2, true); - ui->addExprColumnButton->setVisible(false); - ui->editExprColumnButton->setVisible(false); - ui->delExprColumnButton->setVisible(false); - } + connect(ui->partialIndexCheck, SIGNAL(toggled(bool)), this, SLOT(updatePartialConditionState())); + connect(ui->partialIndexEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateValidation())); + connect(ui->partialIndexEdit, SIGNAL(textChanged()), this, SLOT(updateValidation())); + ui->partialIndexEdit->setVirtualSqlExpression("SELECT %1"); + updatePartialConditionState(); + ui->columnsTable->setColumnHidden(2, false); readCollations(); - ui->ddlEdit->setSqliteVersion(db->getVersion()); - if (index.isNull()) createIndex = SqliteCreateIndexPtr::create(); else @@ -146,8 +133,9 @@ void IndexDialog::readIndex() SqliteQueryPtr parsedObject = resolver.getParsedObject(index, SchemaResolver::INDEX); if (!parsedObject.dynamicCast()) { + createIndex = SqliteCreateIndexPtr::create(); notifyError(tr("Could not process index %1 correctly. Unable to open an index dialog.").arg(index)); - reject(); + preReject(); return; } @@ -160,9 +148,6 @@ void IndexDialog::buildColumns() clearColumns(); ui->columnsTable->setRowCount(0); - totalColumns = tableColumns.size(); - ui->columnsTable->setRowCount(totalColumns); - int row = 0; for (const QString& column : tableColumns) buildColumn(column, row++); @@ -235,8 +220,12 @@ void IndexDialog::buildColumn(const QString& name, int row) IndexDialog::Column* IndexDialog::buildColumn(SqliteOrderBy* orderBy, int row) { - SqliteExpr* expr = dynamic_cast(orderBy->expr->clone()); - return buildColumn(expr, row); + Column* column = orderBy->isSimpleColumn() ? + new Column(orderBy->getColumnName(), ui->columnsTable) : + new Column(dynamic_cast(orderBy->expr->clone()), ui->columnsTable); + + buildColumn(column, row); + return column; } IndexDialog::Column* IndexDialog::buildColumn(SqliteExpr* expr, int row) @@ -248,6 +237,9 @@ IndexDialog::Column* IndexDialog::buildColumn(SqliteExpr* expr, int row) void IndexDialog::buildColumn(Column* column, int row) { + totalColumns++; + ui->columnsTable->setRowCount(totalColumns); + QString key = column->getKey(); columns[key] = column; columnsByRow << column; @@ -262,20 +254,17 @@ void IndexDialog::buildColumn(Column* column, int row) layout->setContentsMargins(margins); column->getCheckParent()->setLayout(layout); - column->setCheck(new QCheckBox(key)); + column->setCheck(new QCheckBox(column->getKey())); column->getCheckParent()->layout()->addWidget(column->getCheck()); columnStateSignalMapping->setMapping(column->getCheck(), key); connect(column->getCheck(), SIGNAL(toggled(bool)), columnStateSignalMapping, SLOT(map())); connect(column->getCheck(), SIGNAL(toggled(bool)), this, SLOT(updateValidation())); - if (db->getDialect() == Dialect::Sqlite3) - { - column->setCollation(new QComboBox()); - column->getCollation()->setEditable(true); - column->getCollation()->lineEdit()->setPlaceholderText(tr("default", "index dialog")); - column->getCollation()->setModel(&collations); - } + column->setCollation(new QComboBox()); + column->getCollation()->setEditable(true); + column->getCollation()->lineEdit()->setPlaceholderText(tr("default", "index dialog")); + column->getCollation()->setModel(&collations); column->setSort(new QComboBox()); column->getSort()->setToolTip(tr("Sort order", "table constraints")); @@ -286,8 +275,6 @@ void IndexDialog::buildColumn(Column* column, int row) column->prepareForNewRow(); column->assignToNewRow(row); - totalColumns++; - updateColumnState(key); } @@ -297,8 +284,7 @@ void IndexDialog::updateColumnState(const QString& columnKey) bool enabled = col->getCheck()->isChecked(); col->getSort()->setEnabled(enabled); - if (col->hasCollation()) - col->getCollation()->setEnabled(enabled); + col->getCollation()->setEnabled(enabled); } void IndexDialog::updatePartialConditionState() @@ -482,9 +468,11 @@ void IndexDialog::applyColumnValues() int row = 0; int totalRows = tableColumns.size(); bool orderChanged = false; + QStringList orderedIndexKeys; for (SqliteOrderBy* idxCol : createIndex->indexedColumns) { key = getKey(idxCol); + orderedIndexKeys << key; if (idxCol->isSimpleColumn()) { @@ -502,14 +490,13 @@ void IndexDialog::applyColumnValues() column->getCheck()->setChecked(true); updateColumnState(key); column->getSort()->setCurrentText(sqliteSortOrder(idxCol->order)); - if (column->hasCollation()) - column->getCollation()->setCurrentText(idxCol->getCollation()); + column->getCollation()->setCurrentText(idxCol->getCollation()); // Setting proper order - int currentRow = columnsByRow.indexOf(column); - if (currentRow != row) + int intendedRow = columnsByRow.indexOf(column); + if (intendedRow != row) { - columnsByRow.move(currentRow, row); + columnsByRow.move(intendedRow, row); orderChanged = true; } @@ -587,7 +574,7 @@ void IndexDialog::rebuildCreateIndex() else idxCol = addIndexedColumn(column->getName()); - if (column->hasCollation() && !column->getCollation()->currentText().isEmpty()) + if (!column->getCollation()->currentText().isEmpty()) addCollation(idxCol, column->getCollation()->currentText()); if (column->getSort()->currentIndex() > 0) @@ -599,7 +586,7 @@ void IndexDialog::rebuildCreateIndex() if (createIndex->where) delete createIndex->where; - Parser parser(db->getDialect()); + Parser parser; SqliteExpr* expr = parser.parseExpr(ui->partialIndexEdit->toPlainText()); if (expr) @@ -624,8 +611,6 @@ void IndexDialog::queryDuplicates() static QString countColNameTpl = QStringLiteral("count(%1)"); static QString countConditionTpl = QStringLiteral("count(%1) > 1"); - Dialect dialect = db->getDialect(); - QStringList cols; QStringList grpCols; QStringList countCols; @@ -636,10 +621,10 @@ void IndexDialog::queryDuplicates() if (!columns[column]->getCheck()->isChecked()) continue; - wrappedCol = wrapObjIfNeeded(column, dialect); + wrappedCol = wrapObjIfNeeded(column); cols << wrappedCol; grpCols << wrappedCol; - countColName = wrapObjIfNeeded(countColNameTpl.arg(column), dialect); + countColName = wrapObjIfNeeded(countColNameTpl.arg(column)); cols << countTpl.arg(wrappedCol, countColName); countCols << countConditionTpl.arg(wrappedCol); } @@ -650,7 +635,7 @@ void IndexDialog::queryDuplicates() QString sqlCols = cols.join(", "); QString sqlGrpCols = grpCols.join(", "); QString sqlCntCols = countCols.join(" AND "); - QString sqlTable = wrapObjIfNeeded(ui->tableCombo->currentText(), dialect); + QString sqlTable = wrapObjIfNeeded(ui->tableCombo->currentText()); editor->setContents(queryTpl.arg(sqlCols, sqlTable, sqlGrpCols, sqlCntCols)); editor->execute(); } @@ -679,7 +664,15 @@ QString IndexDialog::getKey(SqliteOrderBy* col) const if (col->isSimpleColumn()) return col->getColumnName(); - return col->expr->tokens.filterWhiteSpaces(false).detokenize(); + return buildKey(col->expr); +} + +QString IndexDialog::buildKey(SqliteExpr* expr) +{ + if (expr->mode == SqliteExpr::Mode::COLLATE && expr->expr1) + return expr->expr1->tokens.filterWhiteSpaces(false).detokenize().trimmed(); + + return expr->tokens.filterWhiteSpaces(false).detokenize().trimmed(); } QStringList IndexDialog::getExistingColumnExprs(const QString& exceptThis) const @@ -711,15 +704,18 @@ QStringList IndexDialog::getTableColumns() const return cols; } +void IndexDialog::preReject() +{ + preRejected = true; +} + void IndexDialog::accept() { rebuildCreateIndex(); - Dialect dialect = db->getDialect(); - QStringList sqls; if (existingIndex) - sqls << QString("DROP INDEX %1").arg(wrapObjIfNeeded(originalCreateIndex->index, dialect)); + sqls << QString("DROP INDEX %1").arg(wrapObjIfNeeded(originalCreateIndex->index)); sqls << createIndex->detokenize(); @@ -767,6 +763,14 @@ void IndexDialog::accept() } } +int IndexDialog::exec() +{ + if (preRejected) + return Rejected; + + return QDialog::exec(); +} + IndexDialog::Column::Column(const QString& name, QTableWidget* table) { this->name = name; @@ -795,8 +799,7 @@ void IndexDialog::Column::prepareForNewRow() { column1Contrainer = defineContainer(checkParent); column2Contrainer = defineContainer(sort); - if (collation) - column3Contrainer = defineContainer(collation); + column3Contrainer = defineContainer(collation); } QCheckBox* IndexDialog::Column::getCheck() const @@ -839,11 +842,6 @@ void IndexDialog::Column::setCollation(QComboBox* cb) collation = cb; } -bool IndexDialog::Column::hasCollation() const -{ - return collation != nullptr; -} - QString IndexDialog::Column::getName() const { return name; @@ -851,6 +849,11 @@ QString IndexDialog::Column::getName() const SqliteExpr* IndexDialog::Column::getExpr() const { + // If column's expression contains collation at top level, + // the EXPR for processing is inner expr of collation. + if (expr->mode == SqliteExpr::Mode::COLLATE) + return expr->expr1; + return expr; } @@ -868,7 +871,7 @@ bool IndexDialog::Column::isExpr() const QString IndexDialog::Column::getKey() const { if (expr) - return expr->tokens.filterWhiteSpaces(false).detokenize(); + return IndexDialog::buildKey(expr); else return name; } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h index f29b651..2e586d4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexdialog.h @@ -36,7 +36,14 @@ class GUI_API_EXPORT IndexDialog : public QDialog class Column { public: + /** + * @brief Constructor for a real column in the table + */ Column(const QString& name, QTableWidget* table); + + /** + * @brief Constructor for an expression column + */ Column(SqliteExpr* expr, QTableWidget* table); ~Column(); @@ -50,11 +57,32 @@ class GUI_API_EXPORT IndexDialog : public QDialog void setSort(QComboBox* cb); QComboBox* getCollation() const; void setCollation(QComboBox* cb); - bool hasCollation() const; + /** + * @brief Gets name of a real column in the table + * @return Name of table's column, or null string if it's expression column. + */ QString getName() const; + + /** + * @brief Gets expression of an expression column. + * @return Either the expression, or null if this is a real table column. + * + * If an expression assigned to the column is a collate expression, + * this method will extract inner expression and return that. + * + * This is done this way, because after parsing SqliteOrderBy, + * the whole expression (including outer COLLATE expression) is passed + * to the column, whereas COLLATE is used for dropdowns, not for actual expression. + */ SqliteExpr* getExpr() const; + void setExpr(SqliteExpr* expr); + + /** + * @brief Tells whether this is an expression column or real table column. + * @return true if it's expression column, or false if it's real table. + */ bool isExpr() const; QString getKey() const; @@ -73,6 +101,8 @@ class GUI_API_EXPORT IndexDialog : public QDialog SqliteExpr* expr = nullptr; }; + static QString buildKey(SqliteExpr* expr); + void init(); void readIndex(); void readCollations(); @@ -92,6 +122,7 @@ class GUI_API_EXPORT IndexDialog : public QDialog QString getKey(SqliteOrderBy* col) const; QStringList getExistingColumnExprs(const QString& exceptThis = QString()) const; QStringList getTableColumns() const; + void preReject(); bool existingIndex = false; Db* db = nullptr; @@ -105,6 +136,7 @@ class GUI_API_EXPORT IndexDialog : public QDialog StrHash columns; QList columnsByRow; int totalColumns = 0; + bool preRejected = false; Ui::IndexDialog *ui = nullptr; private slots: @@ -124,6 +156,7 @@ class GUI_API_EXPORT IndexDialog : public QDialog public slots: void accept(); + int exec(); }; #endif // INDEXDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp index f041294..3fcbdb9 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/indexexprcolumndialog.cpp @@ -44,7 +44,7 @@ void IndexExprColumnDialog::setOkEnabled(bool enabled) SqliteExpr* IndexExprColumnDialog::parseExpr() { - Parser parser(db->getDialect()); + Parser parser; return parser.parseExpr(ui->sqlEditor->toPlainText()); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp index 36b400b..f34ff2e 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.cpp @@ -50,6 +50,24 @@ SqliteStatement* NewConstraintDialog::getConstraint() return constrStatement; } +void NewConstraintDialog::disableMode(ConstraintDialog::Constraint constraintType) +{ + switch (constraintType) { + case ConstraintDialog::PK: + case ConstraintDialog::FK: + case ConstraintDialog::UNIQUE: + case ConstraintDialog::CHECK: + case ConstraintDialog::NOTNULL: + case ConstraintDialog::COLLATE: + case ConstraintDialog::DEFAULT: + case ConstraintDialog::GENERATED: + modeToButton[constraintType]->setEnabled(false); + break; + case ConstraintDialog::UNKNOWN: + break; + } +} + void NewConstraintDialog::changeEvent(QEvent *e) { QDialog::changeEvent(e); @@ -84,36 +102,32 @@ void NewConstraintDialog::init() void NewConstraintDialog::initTable() { - addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createTablePk())); - if (createTable->dialect == Dialect::Sqlite3) - addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createTableFk())); - - addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createTableUnique())); - addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createTableCheck())); + modeToButton[ConstraintDialog::Constraint::PK] = addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createTablePk())); + modeToButton[ConstraintDialog::Constraint::FK] = addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createTableFk())); + modeToButton[ConstraintDialog::Constraint::UNIQUE] = addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createTableUnique())); + modeToButton[ConstraintDialog::Constraint::CHECK] = addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createTableCheck())); } void NewConstraintDialog::initColumn() { - addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createColumnPk())); - if (createTable->dialect == Dialect::Sqlite3) - addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createColumnFk())); - - addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createColumnUnique())); - addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createColumnCheck())); - addButton(ICONS.CONSTRAINT_NOT_NULL, tr("Not NULL", "new constraint dialog"), SLOT(createColumnNotNull())); - if (createTable->dialect == Dialect::Sqlite3) - addButton(ICONS.CONSTRAINT_COLLATION, tr("Collate", "new constraint dialog"), SLOT(createColumnCollate())); - - addButton(ICONS.CONSTRAINT_DEFAULT, tr("Default", "new constraint dialog"), SLOT(createColumnDefault())); + modeToButton[ConstraintDialog::Constraint::PK] = addButton(ICONS.CONSTRAINT_PRIMARY_KEY, tr("Primary Key", "new constraint dialog"), SLOT(createColumnPk())); + modeToButton[ConstraintDialog::Constraint::FK] = addButton(ICONS.CONSTRAINT_FOREIGN_KEY, tr("Foreign Key", "new constraint dialog"), SLOT(createColumnFk())); + modeToButton[ConstraintDialog::Constraint::UNIQUE] = addButton(ICONS.CONSTRAINT_UNIQUE, tr("Unique", "new constraint dialog"), SLOT(createColumnUnique())); + modeToButton[ConstraintDialog::Constraint::CHECK] = addButton(ICONS.CONSTRAINT_CHECK, tr("Check", "new constraint dialog"), SLOT(createColumnCheck())); + modeToButton[ConstraintDialog::Constraint::NOTNULL] = addButton(ICONS.CONSTRAINT_NOT_NULL, tr("Not NULL", "new constraint dialog"), SLOT(createColumnNotNull())); + modeToButton[ConstraintDialog::Constraint::COLLATE] = addButton(ICONS.CONSTRAINT_COLLATION, tr("Collate", "new constraint dialog"), SLOT(createColumnCollate())); + modeToButton[ConstraintDialog::Constraint::GENERATED] = addButton(ICONS.CONSTRAINT_GENERATED, tr("Generated", "new constraint dialog"), SLOT(createColumnGenerated())); + modeToButton[ConstraintDialog::Constraint::DEFAULT] = addButton(ICONS.CONSTRAINT_DEFAULT, tr("Default", "new constraint dialog"), SLOT(createColumnDefault())); } -void NewConstraintDialog::addButton(const Icon& icon, const QString text, const char* slot) +QCommandLinkButton* NewConstraintDialog::addButton(const Icon& icon, const QString text, const char* slot) { QCommandLinkButton* btn = new QCommandLinkButton(); btn->setIcon(icon); btn->setText(text); connect(btn, SIGNAL(clicked()), this, slot); ui->container->layout()->addWidget(btn); + return btn; } int NewConstraintDialog::createColumnConstraint(ConstraintDialog::Constraint constraintType) @@ -142,6 +156,9 @@ int NewConstraintDialog::createColumnConstraint(ConstraintDialog::Constraint con case ConstraintDialog::DEFAULT: constraint->type = SqliteCreateTable::Column::Constraint::DEFAULT; break; + case ConstraintDialog::GENERATED: + constraint->type = SqliteCreateTable::Column::Constraint::GENERATED; + break; case ConstraintDialog::UNKNOWN: break; } @@ -172,6 +189,7 @@ int NewConstraintDialog::createTableConstraint(ConstraintDialog::Constraint cons case ConstraintDialog::NOTNULL: case ConstraintDialog::COLLATE: case ConstraintDialog::DEFAULT: + case ConstraintDialog::GENERATED: case ConstraintDialog::UNKNOWN: break; } @@ -258,6 +276,11 @@ void NewConstraintDialog::createColumnCollate() createColumnConstraint(ConstraintDialog::COLLATE); } +void NewConstraintDialog::createColumnGenerated() +{ + createColumnConstraint(ConstraintDialog::GENERATED); +} + int NewConstraintDialog::exec() { if (predefinedConstraintType == ConstraintDialog::UNKNOWN) diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h index e374312..daa226c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newconstraintdialog.h @@ -12,6 +12,7 @@ namespace Ui { class NewConstraintDialog; } +class QCommandLinkButton; class GUI_API_EXPORT NewConstraintDialog : public QDialog { @@ -25,6 +26,7 @@ class GUI_API_EXPORT NewConstraintDialog : public QDialog ~NewConstraintDialog(); SqliteStatement* getConstraint(); + void disableMode(ConstraintDialog::Constraint constraintType); int exec(); protected: @@ -34,7 +36,7 @@ class GUI_API_EXPORT NewConstraintDialog : public QDialog void init(); void initTable(); void initColumn(); - void addButton(const Icon& icon, const QString text, const char* slot); + QCommandLinkButton* addButton(const Icon& icon, const QString text, const char* slot); int createColumnConstraint(ConstraintDialog::Constraint constraintType); int createTableConstraint(ConstraintDialog::Constraint constraintType); int editConstraint(); @@ -47,6 +49,7 @@ class GUI_API_EXPORT NewConstraintDialog : public QDialog QPointer createTable; QPointer columnStmt; ConstraintDialog* constraintDialog = nullptr; + QHash modeToButton; private slots: void createTablePk(); @@ -60,6 +63,7 @@ class GUI_API_EXPORT NewConstraintDialog : public QDialog void createColumnNotNull(); void createColumnDefault(); void createColumnCollate(); + void createColumnGenerated(); }; #endif // NEWCONSTRAINTDIALOG_H diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp index 020cfef..7ba2c0b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.cpp @@ -5,7 +5,9 @@ #include "sqlitestudio.h" #include "ui_newversiondialog.h" #include "services/config.h" +#include #include +#include NewVersionDialog::NewVersionDialog(QWidget *parent) : QDialog(parent), @@ -19,22 +21,10 @@ NewVersionDialog::~NewVersionDialog() delete ui; } -void NewVersionDialog::setUpdates(const QList& updates) +void NewVersionDialog::setUpdate(const QString& version, const QString& url) { - QTableWidgetItem* item = nullptr; - int row = 0; - ui->updateList->setRowCount(updates.size()); - for (const UpdateManager::UpdateEntry& entry : updates) - { - item = new QTableWidgetItem(entry.compontent); - ui->updateList->setItem(row, 0, item); - - item = new QTableWidgetItem(entry.version); - ui->updateList->setItem(row, 1, item); - - row++; - } - ui->updateList->resizeColumnsToContents(); + downloadUrl = url; + ui->versionLabel->setText(version); } void NewVersionDialog::init() @@ -42,16 +32,23 @@ void NewVersionDialog::init() ui->setupUi(this); connect(ui->abortButton, SIGNAL(clicked()), this, SLOT(reject())); - connect(ui->updateButton, SIGNAL(clicked()), this, SLOT(installUpdates())); + connect(ui->updateButton, SIGNAL(clicked()), this, SLOT(downloadUpdates())); + connect(ui->homepageButton, SIGNAL(clicked()), this, SLOT(openHomePage())); connect(ui->checkOnStartupCheck, &QCheckBox::clicked, [=](bool checked) { CFG_CORE.General.CheckUpdatesOnStartup.set(checked); }); } -void NewVersionDialog::installUpdates() +void NewVersionDialog::downloadUpdates() +{ + QDesktopServices::openUrl(QUrl(downloadUrl)); + close(); +} + +void NewVersionDialog::openHomePage() { - UPDATES->update(); + QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getHomePage())); close(); } diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h index fc62d6d..a1b1569 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.h @@ -19,7 +19,7 @@ class GUI_API_EXPORT NewVersionDialog : public QDialog explicit NewVersionDialog(QWidget *parent = 0); ~NewVersionDialog(); - void setUpdates(const QList& updates); + void setUpdate(const QString& version, const QString& url); protected: void showEvent(QShowEvent*); @@ -27,10 +27,13 @@ class GUI_API_EXPORT NewVersionDialog : public QDialog private: void init(); + QString downloadUrl; + Ui::NewVersionDialog *ui = nullptr; private slots: - void installUpdates(); + void downloadUpdates(); + void openHomePage(); }; #endif // PORTABLE_CONFIG diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui index d5468b3..12794ce 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/newversiondialog.ui @@ -23,7 +23,7 @@ - New updates are available! + New version is available! Qt::AlignCenter @@ -31,73 +31,50 @@ - - - Qt::NoFocus - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::SelectRows - - - QAbstractItemView::ScrollPerPixel - - - false - - - true - - - false - - - 20 - - - 20 - - - - Component - - - - - Update version - - + + + + 16 + 75 + true + + + + 0.0.0 + + + Qt::AlignCenter + - - - - - - Check for updates on startup - - - - + + + Download new version! + + + + :/icons/img/get_update.png:/icons/img/get_update.png + + + + 24 + 24 + + + + New version package will be downloaded. It will be up to you to install it whenever you're ready. + - + - Update to new version! + Open SQLiteStudio home page. - :/icons/img/get_update.png:/icons/img/get_update.png + :/icons/img/sqlitestudio.svg:/icons/img/sqlitestudio.svg @@ -106,7 +83,7 @@ - This application will be closed and the update installer will start to download and install all the updates. + Read release notes && download package yourself. @@ -126,10 +103,23 @@ - Don't install the update and close this window. + Just close this window. + + + + + + + Check for updates on startup + + + + + + diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp index 7f4ec20..aae0d58 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/populatedialog.cpp @@ -10,6 +10,7 @@ #include "uiutils.h" #include "services/populatemanager.h" #include "common/widgetcover.h" +#include "common/compatibility.h" #include #include #include @@ -42,7 +43,7 @@ void PopulateDialog::init() ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Populate", "populate dialog button")); plugins = PLUGINS->getLoadedPlugins(); - qSort(plugins.begin(), plugins.end(), [](PopulatePlugin* p1, PopulatePlugin* p2) -> bool + sSort(plugins, [](PopulatePlugin* p1, PopulatePlugin* p2) -> bool { return p1->getTitle().compare(p2->getTitle()) < 0; }); diff --git a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp index 255694c..86862e2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/dialogs/triggerdialog.cpp @@ -107,12 +107,6 @@ void TriggerDialog::init() SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::null), SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_ROW) }); - if (db->getDialect() == Dialect::Sqlite2) - { - ui->scopeCombo->addItems({ - SqliteCreateTrigger::scopeToString(SqliteCreateTrigger::Scope::FOR_EACH_STATEMENT) - }); - } // Event combo - default values ui->whenCombo->addItems(tableEventNames + viewEventNames); @@ -223,7 +217,6 @@ void TriggerDialog::readTrigger() void TriggerDialog::setupVirtualSqls() { - Dialect dialect = db->getDialect(); static QString preconditionVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 WHEN %3 BEGIN SELECT 1; END;"); static QString codeVirtSql = QStringLiteral("CREATE TRIGGER %1 BEFORE INSERT ON %2 BEGIN %3 END;"); ui->codeEdit->setVirtualSqlCompleteSemicolon(true); @@ -232,14 +225,14 @@ void TriggerDialog::setupVirtualSqls() if (createTrigger) // if false, then there was a parsing error in parseDdl(). { ui->preconditionEdit->setVirtualSqlExpression( - preconditionVirtSql.arg(wrapObjIfNeeded(trigger, dialect), - wrapObjIfNeeded(createTrigger->table, dialect), + preconditionVirtSql.arg(wrapObjIfNeeded(trigger), + wrapObjIfNeeded(createTrigger->table), "%1")); ui->codeEdit->setVirtualSqlExpression( codeVirtSql.arg( - wrapObjIfNeeded(trigger, dialect), - wrapObjIfNeeded(createTrigger->table, dialect), + wrapObjIfNeeded(trigger), + wrapObjIfNeeded(createTrigger->table), "%1")); } } @@ -247,12 +240,12 @@ void TriggerDialog::setupVirtualSqls() { ui->preconditionEdit->setVirtualSqlExpression( preconditionVirtSql.arg("trig", - wrapObjIfNeeded(getTargetObjectName(), dialect), + wrapObjIfNeeded(getTargetObjectName()), "%1")); ui->codeEdit->setVirtualSqlExpression( codeVirtSql.arg("trig", - wrapObjIfNeeded(getTargetObjectName(), dialect), + wrapObjIfNeeded(getTargetObjectName()), "%1")); } else @@ -292,12 +285,11 @@ void TriggerDialog::rebuildTrigger() */ static const QString tempDdl = QStringLiteral("CREATE TRIGGER %1%2 %3%4 ON %5%6%7 BEGIN %8 END;"); - Dialect dialect = db->getDialect(); - QString trigName = wrapObjIfNeeded(ui->nameEdit->text(), dialect); + QString trigName = wrapObjIfNeeded(ui->nameEdit->text()); QString when = ui->whenCombo->currentText(); QString action = ui->actionCombo->currentText(); QString columns = ""; - QString target = wrapObjIfNeeded(getTargetObjectName(), dialect); + QString target = wrapObjIfNeeded(getTargetObjectName()); QString scope = ui->scopeCombo->currentText(); QString precondition = ""; QString queries = ui->codeEdit->toPlainText(); @@ -308,7 +300,7 @@ void TriggerDialog::rebuildTrigger() { QStringList colNames; for (const QString& colName : selectedColumns) - colNames << wrapObjIfNeeded(colName, dialect); + colNames << wrapObjIfNeeded(colName); columns = " "+colNames.join(", "); } @@ -394,11 +386,9 @@ void TriggerDialog::accept() { rebuildTrigger(); - Dialect dialect = db->getDialect(); - QStringList sqls; if (existingTrigger) - sqls << QString("DROP TRIGGER %1").arg(wrapObjIfNeeded(originalTriggerName, dialect)); + sqls << QString("DROP TRIGGER %1").arg(wrapObjIfNeeded(originalTriggerName)); sqls << ddl; diff --git a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp new file mode 100644 index 0000000..8357810 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.cpp @@ -0,0 +1,60 @@ +#include "extendedpalette.h" +#include +#include + +ExtendedPalette::ExtendedPalette() +{ + +} + +QBrush ExtendedPalette::editorString() const +{ + return editorStringBrush; +} + +void ExtendedPalette::setEditorString(const QBrush &value) +{ + editorStringBrush = value; +} + +QBrush ExtendedPalette::editorLineBase() const +{ + return editorLineBaseBrush; +} + +void ExtendedPalette::setEditorLineBase(const QBrush &value) +{ + editorLineBaseBrush = value; +} + +void ExtendedPalette::styleChanged(QStyle *style, const QString &themeName) +{ + QPalette stdPalette = style->standardPalette(); + bool isDark = stdPalette.base().color().lightness() < 128; + + static const QColor stdAltColor = QColor(Qt::green); + if (stdPalette.text().color().lightness() >= 128) + editorStringBrush = QBrush(stdAltColor.lighter()); + else + editorStringBrush = QBrush(stdAltColor.darker()); + + if (themeName.toLower() == "macintosh" && isDark) + editorLineBaseBrush = QBrush(stdPalette.alternateBase().color().darker(300)); + else + editorLineBaseBrush = stdPalette.alternateBase(); + + if (isDark) + mdiAreaBaseBrush = stdPalette.alternateBase(); + else + mdiAreaBaseBrush = QBrush("#8a8a8a"); +} + +QBrush ExtendedPalette::mdiAreaBase() const +{ + return mdiAreaBaseBrush; +} + +void ExtendedPalette::setMdiAreaBase(const QBrush& value) +{ + mdiAreaBaseBrush = value; +} diff --git a/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h new file mode 100644 index 0000000..e6e5aef --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/extendedpalette.h @@ -0,0 +1,30 @@ +#ifndef EXTENDEDPALETTE_H +#define EXTENDEDPALETTE_H + +#include + +class QStyle; + +class ExtendedPalette +{ + public: + ExtendedPalette(); + + QBrush editorString() const; + void setEditorString(const QBrush &value); + + QBrush editorLineBase() const; + void setEditorLineBase(const QBrush &value); + + void styleChanged(QStyle* style, const QString& themeName); + + QBrush mdiAreaBase() const; + void setMdiAreaBase(const QBrush& value); + + private: + QBrush editorStringBrush; + QBrush editorLineBaseBrush; + QBrush mdiAreaBaseBrush; +}; + +#endif // EXTENDEDPALETTE_H diff --git a/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp b/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp index 735fb5d..bd5f6c0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/formmanager.cpp @@ -170,7 +170,7 @@ QString FormManager::getWidgetName(const QString& path) if (!file.open(QIODevice::ReadOnly)) { qWarning() << "Could not open" << path << "for reading. Form file ignored."; - return QString::null; + return QString(); } QString contents = file.readAll(); @@ -180,7 +180,7 @@ QString FormManager::getWidgetName(const QString& path) if (!match.hasMatch()) { qWarning() << "Could not match widget in" << path << " document. File ignored."; - return QString::null; + return QString(); } QString widgetName = match.captured(1); diff --git a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro index d603f87..59a56d0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro +++ b/SQLiteStudio3/guiSQLiteStudio/guiSQLiteStudio.pro @@ -26,7 +26,7 @@ portable: { TARGET = guiSQLiteStudio TEMPLATE = lib -CONFIG += c++11 +CONFIG += c++17 QMAKE_CXXFLAGS += -pedantic DEFINES += GUISQLITESTUDIO_LIBRARY @@ -43,6 +43,9 @@ TRANSLATIONS += translations/guiSQLiteStudio_ro_RO.ts \ translations/guiSQLiteStudio_pl.ts SOURCES +=\ + common/dbcombobox.cpp \ + constraints/columngeneratedpanel.cpp \ + extendedpalette.cpp \ mainwindow.cpp \ iconmanager.cpp \ dbtree/dbtreemodel.cpp \ @@ -50,6 +53,7 @@ SOURCES +=\ dbtree/dbtree.cpp \ dbtree/dbtreeview.cpp \ actionentry.cpp \ + style.cpp \ uiutils.cpp \ dbtree/dbtreeitemdelegate.cpp \ dbtree/dbtreeitemfactory.cpp \ @@ -168,7 +172,6 @@ SOURCES +=\ uicustomicon.cpp \ uiurlbutton.cpp \ common/configcombobox.cpp \ - dialogs/dbconverterdialog.cpp \ dialogs/dbdialog.cpp \ uidebug.cpp \ debugconsole.cpp \ @@ -193,12 +196,16 @@ SOURCES +=\ dialogs/fileexecerrorsdialog.cpp HEADERS += mainwindow.h \ + common/dbcombobox.h \ + constraints/columngeneratedpanel.h \ + extendedpalette.h \ iconmanager.h \ dbtree/dbtreemodel.h \ dbtree/dbtreeitem.h \ dbtree/dbtree.h \ dbtree/dbtreeview.h \ actionentry.h \ + style.h \ uiutils.h \ dbtree/dbtreeitemdelegate.h \ dbtree/dbtreeitemfactory.h \ @@ -320,7 +327,6 @@ HEADERS += mainwindow.h \ uicustomicon.h \ uiurlbutton.h \ common/configcombobox.h \ - dialogs/dbconverterdialog.h \ dialogs/configdialog.h \ dialogs/dbdialog.h \ uidebug.h \ @@ -348,6 +354,7 @@ HEADERS += mainwindow.h \ dialogs/fileexecerrorsdialog.h FORMS += mainwindow.ui \ + constraints/columngeneratedpanel.ui \ dbtree/dbtree.ui \ statusfield.ui \ completer/completerwindow.ui \ @@ -384,7 +391,6 @@ FORMS += mainwindow.ui \ dialogs/importdialog.ui \ dialogs/populatedialog.ui \ dialogs/populateconfigdialog.ui \ - dialogs/dbconverterdialog.ui \ dialogs/dbdialog.ui \ debugconsole.ui \ dialogs/aboutdialog.ui \ diff --git a/SQLiteStudio3/guiSQLiteStudio/icon.cpp b/SQLiteStudio3/guiSQLiteStudio/icon.cpp index bdc21ce..e1fc552 100644 --- a/SQLiteStudio3/guiSQLiteStudio/icon.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/icon.cpp @@ -318,10 +318,14 @@ QString Icon::getIconNameForAttribute(Icon::Attributes attr) return "sort_ind_asc"; case SORT_DESC: return "sort_ind_desc"; + case DISK: + return "disk_small"; + case LIGHTENING: + return "lightning_small"; default: qWarning() << "Unhandled icon attribute:" << attr; } - return QString::null; + return QString(); } QIcon Icon::mergeAttribute(const QIcon* icon, Icon::Attributes attr) diff --git a/SQLiteStudio3/guiSQLiteStudio/icon.h b/SQLiteStudio3/guiSQLiteStudio/icon.h index d6512c9..b54e730 100644 --- a/SQLiteStudio3/guiSQLiteStudio/icon.h +++ b/SQLiteStudio3/guiSQLiteStudio/icon.h @@ -35,7 +35,9 @@ class GUI_API_EXPORT Icon QUESTION, ERROR, SORT_ASC, - SORT_DESC + SORT_DESC, + DISK, + LIGHTENING }; Icon(const QString& name, const QString& fileName); diff --git a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h index 6034de1..6bff0e0 100644 --- a/SQLiteStudio3/guiSQLiteStudio/iconmanager.h +++ b/SQLiteStudio3/guiSQLiteStudio/iconmanager.h @@ -18,7 +18,6 @@ class GUI_API_EXPORT IconManager : public QObject public: DEF_ICONS(Icons, iconEnums, - DEF_ICON(ABORT24, "abort24") DEF_ICON(ACT_ABORT, "act_abort") DEF_ICON(ACT_CLEAR, "act_clear") DEF_ICON(ACT_COPY, "act_copy") @@ -27,7 +26,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(ACT_DELETE, "act_delete") DEF_ICON(ACT_PASTE, "act_paste") DEF_ICON(ACT_REDO, "act_redo") - DEF_ICON(ACT_SEARCH, "act_search") DEF_ICON(ACT_SELECT_ALL, "act_select_all") DEF_ICON(ACT_UNDO, "act_undo") DEF_ICON(APPLY_FILTER, "apply_filter") @@ -55,7 +53,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(COMPLETER_PRAGMA, "completer_pragma") DEF_ICON(COMPLETER_STRING, "completer_string") DEF_ICON(CONFIGURE, "configure") - DEF_ICON(CONFIGURE_CONSTRAINT, "configure_constraint") DEF_ICON(CONSTRAINT_CHECK, "check") DEF_ICO2(CONSTRAINT_CHECK_ADD, CONSTRAINT_CHECK, PLUS) DEF_ICON(CONSTRAINT_COLLATION, "collation") @@ -64,27 +61,27 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICO2(CONSTRAINT_DEFAULT_ADD, CONSTRAINT_DEFAULT, PLUS) DEF_ICON(CONSTRAINT_FOREIGN_KEY, "fk") DEF_ICO2(CONSTRAINT_FOREIGN_KEY_ADD, CONSTRAINT_FOREIGN_KEY, PLUS) + DEF_ICON(CONSTRAINT_GENERATED, "generated") + DEF_ICO2(CONSTRAINT_GENERATED_STORED, CONSTRAINT_GENERATED, DISK) + DEF_ICO2(CONSTRAINT_GENERATED_VIRTUAL, CONSTRAINT_GENERATED, LIGHTENING) + DEF_ICO2(CONSTRAINT_GENERATED_ADD, CONSTRAINT_GENERATED, PLUS) DEF_ICON(CONSTRAINT_NOT_NULL, "not_null") DEF_ICO2(CONSTRAINT_NOT_NULL_ADD, CONSTRAINT_NOT_NULL, PLUS) DEF_ICON(CONSTRAINT_PRIMARY_KEY, "pk") DEF_ICO2(CONSTRAINT_PRIMARY_KEY_ADD, CONSTRAINT_PRIMARY_KEY, PLUS) DEF_ICON(CONSTRAINT_UNIQUE, "unique") DEF_ICO2(CONSTRAINT_UNIQUE_ADD, CONSTRAINT_UNIQUE, PLUS) - DEF_ICON(CONVERT_DB, "convert_db") DEF_ICON(VACUUM_DB, "vacuum_db") DEF_ICON(INTEGRITY_CHECK, "integrity_check") DEF_ICON(DATABASE, "database") DEF_ICO2(DATABASE_ADD, DATABASE, PLUS) DEF_ICON(DATABASE_CONNECT, "database_connect") - DEF_ICON(DATABASE_CONNECTED, "database_connected") DEF_ICO2(DATABASE_DEL, DATABASE, MINUS) DEF_ICON(DATABASE_DISCONNECT, "database_disconnect") DEF_ICO2(DATABASE_EDIT, DATABASE, EDIT) DEF_ICON(DATABASE_EXPORT, "database_export") DEF_ICON(DATABASE_EXPORT_WIZARD, "database_export_wizard") - DEF_ICON(DATABASE_FILE, "database_file") DEF_ICON(DATABASE_IMPORT_WIZARD, "database_import_wizard") - DEF_ICON(DATABASE_NETWORK, "database_network") DEF_ICON(DATABASE_OFFLINE, "database_offline") DEF_ICO2(DATABASE_INVALID, DATABASE_OFFLINE, WARNING) DEF_ICON(DATABASE_ONLINE, "database_online") @@ -103,9 +100,7 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICO2(DIRECTORY_EDIT, DIRECTORY, EDIT) DEF_ICON(DIRECTORY_OPEN, "directory_open") DEF_ICON(DIRECTORY_OPEN_WITH_DB, "directory_open_with_db") - DEF_ICON(DIRECTORY_WITH_DB, "directory_with_db") - DEF_ICON(DOCK_LAYOUT_HORIZONTAL, "dock_layout_horizontal") - DEF_ICON(DOCK_LAYOUT_VERTICAL, "dock_layout_vertical") + DEF_ICO3(DONATE, TEST_CONN_OK) DEF_ICON(ERASE, "erase") DEF_ICON(ERASE_TABLE_DATA, "erase_table_data") DEF_ICON(EXEC_QUERY, "exec_query") @@ -116,7 +111,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(EXTENSION, "brick") DEF_ICON(EXTENSION_ADD, "brick_add") DEF_ICON(EXTENSION_DELETE, "brick_delete") - DEF_ICON(EXTENSION_EDITOR, "brick_folder") DEF_ICON(EXTENSION_ERROR, "brick_error") DEF_ICON(FEATURE_REQUEST, "feature_request") DEF_ICON(FONT_BROWSE, "font_browse") @@ -138,7 +132,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(INDICATOR_HINT, "indicator_hint") DEF_ICON(INDICATOR_INFO, "indicator_info") DEF_ICON(INDICATOR_WARN, "indicator_warn") - DEF_ICON(INFO_BALLOON, "info_balloon") DEF_ICON(INDEX_EXPR_ADD, "tag_hash_add") DEF_ICON(INDEX_EXPR_EDIT, "tag_hash_edit") DEF_ICON(INDEX_EXPR_DEL, "tag_hash_del") @@ -147,15 +140,15 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICO3(INSERT_FN_ARG, INSERT_ROW) DEF_ICO3(INSERT_DATATYPE, INSERT_ROW) DEF_ICON(KEYWORD, "keyword") - DEF_ICON(KEYBOARD, "keyboard") - DEF_ICON(LOADING, "loading") DEF_ICON(LICENSES, "licenses") + DEF_ICON(LOADING, "loading") + DEF_ICON(LOAD_FULL_VALUE, "load_full_value") + DEF_ICON(LOAD_FULL_VALUES, "load_full_values") DEF_ICON(MOVE_DOWN, "move_down") DEF_ICON(MOVE_UP, "move_up") DEF_ICO3(NEW_COLLATION, INSERT_ROW) DEF_ICO3(NEW_FUNCTION, INSERT_ROW) DEF_ICON(OPEN_FILE, "open_sql_file") - DEF_ICON(OPEN_FORUM, "open_forum") DEF_ICON(OPEN_SQL_EDITOR, "open_sql_editor") DEF_ICO3(OPEN_SQL_FILE, OPEN_FILE) DEF_ICON(OPEN_VALUE_EDITOR, "open_value_editor") @@ -166,6 +159,7 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICO3(MOVE_LEFT, PAGE_PREV) DEF_ICO3(MOVE_RIGHT, PAGE_NEXT) DEF_ICON(PLUS, "plus") + DEF_ICON(QUIT, "quit") DEF_ICON(RELOAD, "reload") DEF_ICON(RENAME_FN_ARG, "rename_fn_arg") DEF_ICO3(RENAME_DATATYPE, RENAME_FN_ARG) @@ -200,8 +194,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(SORT_COUNT_19, "sort_cnt_19") DEF_ICON(SORT_COUNT_20, "sort_cnt_20") DEF_ICON(SORT_COUNT_20_PLUS, "sort_cnt_20p") - DEF_ICON(SORT_INDICATOR_ASC, "sort_ind_asc") - DEF_ICON(SORT_INDICATOR_DESC, "sort_ind_desc") DEF_ICON(SORT_RESET, "sort_reset") DEF_ICON(SQLITE_DOCS, "sqlite_docs") DEF_ICON(SQLITESTUDIO_APP, "sqlitestudio") @@ -229,7 +221,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICON(TABS_ON_TOP, "tabs_on_top") DEF_ICON(TEST_CONN_ERROR, "test_conn_error") DEF_ICON(TEST_CONN_OK, "test_conn_ok") - DEF_ICON(TIP, "tip") DEF_ICON(TRIGGER, "trigger") DEF_ICO2(TRIGGER_ADD, TRIGGER, PLUS) DEF_ICON(TRIGGER_COLUMNS, "trigger_columns") @@ -237,8 +228,6 @@ class GUI_API_EXPORT IconManager : public QObject DEF_ICO2(TRIGGER_DEL, TRIGGER, MINUS) DEF_ICO2(TRIGGER_EDIT, TRIGGER, EDIT) DEF_ICON(TRIGGERS, "triggers") - DEF_ICON(USER, "user") - DEF_ICON(USER_UNKNOWN, "user_unknown") DEF_ICON(USER_MANUAL, "user_manual") DEF_ICON(VIEW, "view") DEF_ICO2(VIEW_ADD, VIEW, PLUS) diff --git a/SQLiteStudio3/guiSQLiteStudio/icons.qrc b/SQLiteStudio3/guiSQLiteStudio/icons.qrc index b656f25..67c9351 100644 --- a/SQLiteStudio3/guiSQLiteStudio/icons.qrc +++ b/SQLiteStudio3/guiSQLiteStudio/icons.qrc @@ -8,7 +8,6 @@ img/act_delete.png img/act_paste.png img/act_redo.png - img/act_search.png img/act_select_all.png img/act_undo.png img/apply_filter_re.png @@ -31,14 +30,10 @@ img/completer_other.png img/completer_pragma.png img/completer_string.png - img/configure_constraint.png img/configure.png img/database_connect.png - img/database_connected.png img/database_disconnect.png img/database_export.png - img/database_file.png - img/database_network.png img/database_reload.png img/database.png img/ddl_history.png @@ -49,7 +44,6 @@ img/denied_small.png img/directory_open_with_db.png img/directory_open.png - img/directory_with_db.png img/directory.png img/edit_small.png img/erase.png @@ -70,7 +64,6 @@ img/indicator_hint.png img/indicator_info.png img/indicator_warn.png - img/info_balloon.png img/info_small.png img/insert_row.png img/insert_rows.png @@ -162,19 +155,14 @@ img/vacuum_db.png img/bug.png img/feature_request.png - img/user_unknown.png - img/user.png - img/open_forum.png img/user_manual.png img/sqlite_docs.png - img/tip.png img/licenses.png img/homepage.png img/bug_list.png img/get_update.png img/sqlitestudio.svg img/sqlitestudio_16.png - img/keyboard.png img/config_general.png img/config_colors.png img/config_font.png @@ -193,8 +181,6 @@ img/plus.png img/erase_table_data.png img/delete.png - img/dock_layout_horizontal.png - img/dock_layout_vertical.png img/tag_hash_add.png img/tag_hash_del.png img/tag_hash_edit.png @@ -209,7 +195,15 @@ img/brick_add.png img/brick_delete.png img/brick_error.png - img/brick_folder.png img/execute_sql_from_file.png + img/dock_layout_horizontal.png + img/dock_layout_vertical.png + img/keyboard.png + img/disk_small.png + img/generated.png + img/lightning_small.png + img/load_full_value.png + img/load_full_values.png + img/quit.png diff --git a/SQLiteStudio3/guiSQLiteStudio/img/act_search.png b/SQLiteStudio3/guiSQLiteStudio/img/act_search.png deleted file mode 100644 index 7d57954..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/act_search.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/brick_folder.png b/SQLiteStudio3/guiSQLiteStudio/img/brick_folder.png deleted file mode 100644 index 5dea976..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/brick_folder.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/configure_constraint.png b/SQLiteStudio3/guiSQLiteStudio/img/configure_constraint.png deleted file mode 100644 index 40a7bf9..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/configure_constraint.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/database_connected.png b/SQLiteStudio3/guiSQLiteStudio/img/database_connected.png deleted file mode 100644 index 71bc905..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/database_connected.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/database_file.png b/SQLiteStudio3/guiSQLiteStudio/img/database_file.png deleted file mode 100644 index a36f74b..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/database_file.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/database_network.png b/SQLiteStudio3/guiSQLiteStudio/img/database_network.png deleted file mode 100644 index 03f6f90..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/database_network.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/directory_with_db.png b/SQLiteStudio3/guiSQLiteStudio/img/directory_with_db.png deleted file mode 100644 index 33c6d88..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/directory_with_db.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/disk_small.png b/SQLiteStudio3/guiSQLiteStudio/img/disk_small.png new file mode 100644 index 0000000..df83a60 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/disk_small.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/function.png b/SQLiteStudio3/guiSQLiteStudio/img/function.png index 18fa585..f2a57a6 100644 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/function.png and b/SQLiteStudio3/guiSQLiteStudio/img/function.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/generated.png b/SQLiteStudio3/guiSQLiteStudio/img/generated.png new file mode 100644 index 0000000..efc599d Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/generated.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/info_balloon.png b/SQLiteStudio3/guiSQLiteStudio/img/info_balloon.png deleted file mode 100644 index f14f2a3..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/info_balloon.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/lightning_small.png b/SQLiteStudio3/guiSQLiteStudio/img/lightning_small.png new file mode 100644 index 0000000..e1b6e78 Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/lightning_small.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/load_full_value.png b/SQLiteStudio3/guiSQLiteStudio/img/load_full_value.png new file mode 100644 index 0000000..a7bd87b Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/load_full_value.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/load_full_values.png b/SQLiteStudio3/guiSQLiteStudio/img/load_full_values.png new file mode 100644 index 0000000..7ae440a Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/load_full_values.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/open_forum.png b/SQLiteStudio3/guiSQLiteStudio/img/open_forum.png deleted file mode 100644 index 6aa2603..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/open_forum.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/quit.png b/SQLiteStudio3/guiSQLiteStudio/img/quit.png new file mode 100644 index 0000000..2bc51ac Binary files /dev/null and b/SQLiteStudio3/guiSQLiteStudio/img/quit.png differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/tip.png b/SQLiteStudio3/guiSQLiteStudio/img/tip.png deleted file mode 100644 index 845e110..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/tip.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/user.png b/SQLiteStudio3/guiSQLiteStudio/img/user.png deleted file mode 100644 index 2d44726..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/user.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/img/user_unknown.png b/SQLiteStudio3/guiSQLiteStudio/img/user_unknown.png deleted file mode 100644 index 09f73c2..0000000 Binary files a/SQLiteStudio3/guiSQLiteStudio/img/user_unknown.png and /dev/null differ diff --git a/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp index 2b6463a..57c7d35 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/mainwindow.cpp @@ -38,7 +38,9 @@ #include "common/widgetcover.h" #include "dialogs/cssdebugdialog.h" #include "themetuner.h" +#include "style.h" #include "services/codeformatter.h" +#include "common/compatibility.h" #include #include #include @@ -48,6 +50,7 @@ #include #include #include +#include CFG_KEYS_DEFINE(MainWindow) MainWindow* MainWindow::instance = nullptr; @@ -126,13 +129,15 @@ void MainWindow::init() } #ifdef PORTABLE_CONFIG - connect(UPDATES, SIGNAL(updatesAvailable(QList)), this, SLOT(updatesAvailable(QList))); + connect(UPDATES, SIGNAL(updateAvailable(QString, QString)), this, SLOT(updateAvailable(QString, QString))); connect(UPDATES, SIGNAL(noUpdatesAvailable()), this, SLOT(noUpdatesAvailable())); #endif connect(statusField, SIGNAL(linkActivated(QString)), this, SLOT(statusFieldLinkClicked(QString))); connect(CFG_CORE.General.Language, SIGNAL(changed(QVariant)), this, SLOT(notifyAboutLanguageChange())); + connect(CFG_UI.General.AllowMultipleSessions, SIGNAL(changed(QVariant)), this, SLOT(updateMultipleSessionsSetting(QVariant))); + updateMultipleSessionsSetting(); fixFonts(); } @@ -257,14 +262,15 @@ void MainWindow::createActions() createAction(OPEN_DEBUG_CONSOLE, tr("Open Debug Console"), this, SLOT(openDebugConsole()), this); createAction(OPEN_CSS_CONSOLE, tr("Open CSS Console"), this, SLOT(openCssConsole()), this); createAction(REPORT_BUG, ICONS.BUG, tr("Report a &bug"), this, SLOT(reportBug()), this); + createAction(DONATE, ICONS.DONATE, tr("D&onate"), this, SLOT(donate()), this); createAction(FEATURE_REQUEST, ICONS.FEATURE_REQUEST, tr("Propose a new &feature"), this, SLOT(requestFeature()), this); createAction(ABOUT, ICONS.SQLITESTUDIO_APP16, tr("&About"), this, SLOT(aboutSqlitestudio()), this); createAction(LICENSES, ICONS.LICENSES, tr("&Licenses"), this, SLOT(licenses()), this); createAction(HOMEPAGE, ICONS.HOMEPAGE, tr("Open home &page"), this, SLOT(homepage()), this); - createAction(FORUM, ICONS.OPEN_FORUM, tr("Open fo&rum page"), this, SLOT(forum()), this); createAction(USER_MANUAL, ICONS.USER_MANUAL, tr("User &Manual"), this, SLOT(userManual()), this); createAction(SQLITE_DOCS, ICONS.SQLITE_DOCS, tr("SQLite &documentation"), this, SLOT(sqliteDocs()), this); createAction(BUG_REPORT_HISTORY, ICONS.BUG_LIST, tr("Bugs and feature &requests"), this, SLOT(reportHistory()), this); + createAction(QUIT, ICONS.QUIT, tr("Quit"), this, SLOT(quit()), this); #ifdef PORTABLE_CONFIG createAction(CHECK_FOR_UPDATES, ICONS.GET_UPDATE, tr("Check for &updates"), this, SLOT(checkForUpdates()), this); #endif @@ -276,26 +282,10 @@ void MainWindow::createActions() ui->dbToolbar->addAction(dbTree->getAction(DbTree::DISCONNECT_FROM_DB)); ui->dbToolbar->addSeparator(); ui->dbToolbar->addAction(dbTree->getAction(DbTree::ADD_DB)); - ui->dbToolbar->addAction(dbTree->getAction(DbTree::EDIT_DB)); - ui->dbToolbar->addAction(dbTree->getAction(DbTree::DELETE_DB)); - ui->dbToolbar->addSeparator(); ui->dbToolbar->addAction(dbTree->getAction(DbTree::REFRESH_SCHEMA)); ui->structureToolbar->addAction(dbTree->getAction(DbTree::ADD_TABLE)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::EDIT_TABLE)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::DEL_TABLE)); - ui->structureToolbar->addSeparator(); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::ADD_INDEX)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::EDIT_INDEX)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::DEL_INDEX)); - ui->structureToolbar->addSeparator(); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::ADD_TRIGGER)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::EDIT_TRIGGER)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::DEL_TRIGGER)); - ui->structureToolbar->addSeparator(); ui->structureToolbar->addAction(dbTree->getAction(DbTree::ADD_VIEW)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::EDIT_VIEW)); - ui->structureToolbar->addAction(dbTree->getAction(DbTree::DEL_VIEW)); ui->taskBar->initContextMenu(this); } @@ -315,12 +305,15 @@ void MainWindow::initMenuBar() dbMenu->addAction(dbTree->getAction(DbTree::DELETE_DB)); dbMenu->addSeparator(); dbMenu->addAction(dbTree->getAction(DbTree::EXPORT_DB)); - dbMenu->addAction(dbTree->getAction(DbTree::CONVERT_DB)); dbMenu->addAction(dbTree->getAction(DbTree::VACUUM_DB)); dbMenu->addAction(dbTree->getAction(DbTree::INTEGRITY_CHECK)); dbMenu->addSeparator(); dbMenu->addAction(dbTree->getAction(DbTree::REFRESH_SCHEMA)); dbMenu->addAction(dbTree->getAction(DbTree::REFRESH_SCHEMAS)); +#ifndef Q_OS_MACX + dbMenu->addSeparator(); + dbMenu->addAction(actionMap[QUIT]); +#endif // Structure menu structMenu = new QMenu(this); @@ -395,7 +388,6 @@ void MainWindow::initMenuBar() sqlitestudioMenu->addAction(actionMap[USER_MANUAL]); sqlitestudioMenu->addAction(actionMap[SQLITE_DOCS]); sqlitestudioMenu->addAction(actionMap[HOMEPAGE]); - sqlitestudioMenu->addAction(actionMap[FORUM]); sqlitestudioMenu->addSeparator(); #ifdef PORTABLE_CONFIG if (UPDATES->isPlatformEligibleForUpdate()) @@ -409,6 +401,7 @@ void MainWindow::initMenuBar() sqlitestudioMenu->addAction(actionMap[BUG_REPORT_HISTORY]); sqlitestudioMenu->addSeparator(); sqlitestudioMenu->addAction(actionMap[LICENSES]); + sqlitestudioMenu->addAction(actionMap[DONATE]); sqlitestudioMenu->addAction(actionMap[ABOUT]); } @@ -450,6 +443,7 @@ void MainWindow::restoreSession() if (sessionValue.size() == 0) { THEME_TUNER->tuneCurrentTheme(); + restoreState(saveState()); // workaround for probable Qt bug (?), reported in #3421 return; } @@ -463,6 +457,8 @@ void MainWindow::restoreSession() if (sessionValue.contains("state")) restoreState(sessionValue["state"].toByteArray()); + else + restoreState(saveState()); // workaround for probable Qt bug (?), reported in #3421 if (sessionValue.contains("dbTree")) dbTree->restoreSession(sessionValue["dbTree"]); @@ -550,13 +546,14 @@ void MainWindow::setStyle(const QString& styleName) notifyWarn(tr("Could not set style: %1", "main window").arg(styleName)); return; } - QApplication::setStyle(style); - THEME_TUNER->tuneTheme(styleName); + + STYLE->setStyle(style, styleName); + statusField->refreshColors(); } QString MainWindow::currentStyle() const { - return QApplication::style()->objectName(); + return STYLE->name(); } EditorWindow* MainWindow::openSqlEditor(Db* dbToSet, const QString& sql) @@ -606,7 +603,7 @@ void MainWindow::refreshMdiWindows() nameToAction[action->text()] = action; } - qSort(actionNames); + sSort(actionNames); for (const QString& name : actionNames) mdiMenu->addAction(nameToAction[name]); @@ -744,9 +741,9 @@ void MainWindow::homepage() QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getHomePage())); } -void MainWindow::forum() +void MainWindow::githubReleases() { - QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getForumPage())); + QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getGitHubReleases())); } void MainWindow::userManual() @@ -764,6 +761,11 @@ void MainWindow::reportHistory() QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getIssuesPage())); } +void MainWindow::donate() +{ + QDesktopServices::openUrl(QUrl(SQLITESTUDIO->getDonatePage())); +} + void MainWindow::statusFieldLinkClicked(const QString& link) { #ifdef PORTABLE_CONFIG @@ -775,12 +777,29 @@ void MainWindow::statusFieldLinkClicked(const QString& link) #endif } +void MainWindow::quit() +{ + close(); +} + +void MainWindow::updateMultipleSessionsSetting(const QVariant& newValue) +{ + QSettings sett; + sett.setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, newValue); +} + +void MainWindow::updateMultipleSessionsSetting() +{ + QSettings sett; + sett.setValue(ALLOW_MULTIPLE_SESSIONS_SETTING, CFG_UI.General.AllowMultipleSessions.get()); +} + #ifdef PORTABLE_CONFIG -void MainWindow::updatesAvailable(const QList& updates) +void MainWindow::updateAvailable(const QString& version, const QString& url) { manualUpdatesChecking = false; newVersionDialog = new NewVersionDialog(this); - newVersionDialog->setUpdates(updates); + newVersionDialog->setUpdate(version, url); notifyInfo(tr("New updates are available. Click here for details.").arg(openUpdatesUrl)); } @@ -820,7 +839,11 @@ void MainWindow::messageFromSecondaryInstance(quint32 instanceId, QByteArray mes { UNUSED(instanceId); QApplication::setActiveWindow(this); + if (isMinimized()) + showMaximized(); + raise(); + activateWindow(); QString dbToOpen = deserializeFromBytes(message).toString(); if (!dbToOpen.isNull()) openDb(dbToOpen); diff --git a/SQLiteStudio3/guiSQLiteStudio/mainwindow.h b/SQLiteStudio3/guiSQLiteStudio/mainwindow.h index fbc3317..cedbc20 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mainwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/mainwindow.h @@ -49,6 +49,7 @@ CFG_KEY_LIST(MainWindow, QObject::tr("Main window"), CFG_KEY_ENTRY(OPEN_CONFIG, Qt::Key_F2, QObject::tr("Open configuration dialog")) CFG_KEY_ENTRY(OPEN_DEBUG_CONSOLE, Qt::Key_F12, QObject::tr("Open Debug Console")) CFG_KEY_ENTRY(OPEN_CSS_CONSOLE, Qt::Key_F11, QObject::tr("Open CSS Console")) + CFG_KEY_ENTRY(QUIT, Qt::CTRL + Qt::Key_Q, QObject::tr("Quit the application")) ) class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer @@ -83,14 +84,15 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer OPEN_CSS_CONSOLE, LICENSES, HOMEPAGE, - FORUM, USER_MANUAL, SQLITE_DOCS, REPORT_BUG, FEATURE_REQUEST, ABOUT, + DONATE, BUG_REPORT_HISTORY, - CHECK_FOR_UPDATES + CHECK_FOR_UPDATES, + QUIT }; enum ToolBar @@ -124,6 +126,8 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer ThemeTuner* getThemeTuner() const; EditorWindow* openSqlEditor(Db* dbToSet, const QString& sql); + static_char* ALLOW_MULTIPLE_SESSIONS_SETTING = "AllowMultipleSessions"; + protected: void closeEvent(QCloseEvent *event); @@ -177,6 +181,13 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer void updateWindowActions(); void updateCornerDocking(); void messageFromSecondaryInstance(quint32 instanceId, QByteArray message); + void licenses(); + void homepage(); + void githubReleases(); + void userManual(); + void sqliteDocs(); + void reportHistory(); + void donate(); private slots: void notifyAboutLanguageChange(); @@ -201,18 +212,15 @@ class GUI_API_EXPORT MainWindow : public QMainWindow, public ExtActionContainer void reportBug(); void requestFeature(); void aboutSqlitestudio(); - void licenses(); - void homepage(); - void forum(); - void userManual(); - void sqliteDocs(); - void reportHistory(); #ifdef PORTABLE_CONFIG - void updatesAvailable(const QList& updates); + void updateAvailable(const QString& version, const QString& url); void noUpdatesAvailable(); void checkForUpdates(); #endif void statusFieldLinkClicked(const QString& link); + void quit(); + void updateMultipleSessionsSetting(); + void updateMultipleSessionsSetting(const QVariant& newValue); }; template diff --git a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h index 7003425..9a7be2b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/mdiwindow.h @@ -14,7 +14,7 @@ class GUI_API_EXPORT MdiWindow : public QMdiSubWindow Q_OBJECT public: - MdiWindow(MdiChild* mdiChild, MdiArea *mdiArea, Qt::WindowFlags flags = 0); + MdiWindow(MdiChild* mdiChild, MdiArea *mdiArea, Qt::WindowFlags flags = Qt::WindowFlags()); virtual ~MdiWindow(); virtual QVariant saveSession(); diff --git a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp index d766738..0f965cd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/multieditor/multieditor.cpp @@ -14,6 +14,8 @@ #include "uiconfig.h" #include "dialogs/configdialog.h" #include "formview.h" +#include "themetuner.h" +#include "common/compatibility.h" #include #include #include @@ -26,7 +28,6 @@ #include #include #include -#include static QHash missingEditorPluginsAlreadyWarned; @@ -55,7 +56,7 @@ void MultiEditor::init(TabsMode tabsMode) QFont font = cornerLabel->font(); font.setBold(true); cornerLabel->setFont(font); - cornerLabel->setFrameStyle(QFrame::StyledPanel|QFrame::Plain); + cornerLabel->setFrameStyle(QFrame::NoFrame); hbox->addWidget(cornerLabel); cornerLabel->setVisible(false); @@ -365,7 +366,7 @@ QList MultiEditor::getEditorTypes(const DataType& dataType) sortedEditors << editorWithPrio; } - qSort(sortedEditors.begin(), sortedEditors.end(), [=](const EditorWithPriority& ed1, const EditorWithPriority& ed2) -> bool + sSort(sortedEditors, [=](const EditorWithPriority& ed1, const EditorWithPriority& ed2) -> bool { return ed1.first < ed2.first; }); diff --git a/SQLiteStudio3/guiSQLiteStudio/qhexedit2/qhexedit_p.cpp b/SQLiteStudio3/guiSQLiteStudio/qhexedit2/qhexedit_p.cpp index 3919c19..18b85cf 100644 --- a/SQLiteStudio3/guiSQLiteStudio/qhexedit2/qhexedit_p.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/qhexedit2/qhexedit_p.cpp @@ -855,7 +855,7 @@ void QHexEditPrivate::updateCursor() void QHexEditPrivate::adjust() { - _charWidth = fontMetrics().width(QLatin1Char('9')); + _charWidth = fontMetrics().horizontalAdvance(QLatin1Char('9')); _charHeight = fontMetrics().height(); _xPosAdr = 0; diff --git a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp index 6ca5c59..58e4315 100644 --- a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.cpp @@ -1,6 +1,7 @@ #include "qtscriptsyntaxhighlighter.h" -#include "uiconfig.h" - +#include "style.h" +#include +#include #include JavaScriptSyntaxHighlighter::JavaScriptSyntaxHighlighter(QTextDocument *parent) @@ -183,6 +184,7 @@ JavaScriptSyntaxHighlighter::JavaScriptSyntaxHighlighter(QTextDocument *parent) m_knownIds << "userAgent"; keywordsFormat.setFontWeight(QFont::Bold); + commentFormat.setFontItalic(true); } void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) @@ -197,6 +199,12 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) Regex = 5 }; + commentFormat.setForeground(QApplication::style()->standardPalette().dark()); + keywordsFormat.setForeground(QApplication::style()->standardPalette().windowText()); + keywordsFormat.setFontWeight(QFont::Bold); + normalFormat.setForeground(QApplication::style()->standardPalette().text()); + stringFormat.setForeground(STYLE->extendedPalette().editorString()); + int state = previousBlockState(); int start = 0; int i = 0; @@ -225,13 +233,13 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) state = Comment; } else if (ch == '/' && next == '/') { i = text.length(); - setFormat(start, text.length(), CFG_UI.Colors.JavaScriptComment.get()); + setFormat(start, text.length(), commentFormat); } else if (ch == '/' && next != '*') { ++i; state = Regex; } else { if (!QString("(){}[]").contains(ch)) - setFormat(start, 1, CFG_UI.Colors.JavaScriptOperator.get()); + setFormat(start, 1, normalFormat); ++i; state = Start; } @@ -239,7 +247,7 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) case Number: if (ch.isSpace() || !ch.isDigit()) { - setFormat(start, i - start, CFG_UI.Colors.JavaScriptNumber.get()); + setFormat(start, i - start, normalFormat); state = Start; } else { ++i; @@ -250,12 +258,10 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == '_')) { QString token = text.mid(start, i - start).trimmed(); if (m_keywords.contains(token)) - { - keywordsFormat.setForeground(CFG_UI.Colors.JavaScriptKeyword.get()); setFormat(start, i - start, keywordsFormat); - } else if (m_knownIds.contains(token)) - setFormat(start, i - start, CFG_UI.Colors.JavaScriptBuiltIn.get()); + setFormat(start, i - start, normalFormat); + state = Start; } else { ++i; @@ -267,7 +273,7 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) QChar prev = (i > 0) ? text.at(i - 1) : QChar(); if (prev != '\\') { ++i; - setFormat(start, i - start, CFG_UI.Colors.JavaScriptString.get()); + setFormat(start, i - start, stringFormat); state = Start; } else { ++i; @@ -281,7 +287,7 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) if (ch == '*' && next == '/') { ++i; ++i; - setFormat(start, i - start, CFG_UI.Colors.JavaScriptComment.get()); + setFormat(start, i - start, commentFormat); state = Start; } else { ++i; @@ -293,7 +299,7 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) QChar prev = (i > 0) ? text.at(i - 1) : QChar(); if (prev != '\\') { ++i; - setFormat(start, i - start, CFG_UI.Colors.JavaScriptString.get()); + setFormat(start, i - start, normalFormat); state = Start; } else { ++i; @@ -310,7 +316,7 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) } if (state == Comment) - setFormat(start, text.length(), CFG_UI.Colors.JavaScriptComment.get()); + setFormat(start, text.length(), commentFormat); else state = Start; @@ -318,8 +324,8 @@ void JavaScriptSyntaxHighlighter::highlightBlock(const QString &text) int pos = 0; int len = m_markString.length(); QTextCharFormat markerFormat; - markerFormat.setBackground(CFG_UI.Colors.JavaScriptMarker.get()); - markerFormat.setForeground(CFG_UI.Colors.JavaScriptFg.get()); + markerFormat.setBackground(QApplication::style()->standardPalette().alternateBase()); + markerFormat.setForeground(QApplication::style()->standardPalette().text()); for (;;) { pos = text.indexOf(m_markString, pos, m_markCaseSensitivity); if (pos < 0) diff --git a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h index bf978d2..3d701de 100644 --- a/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h +++ b/SQLiteStudio3/guiSQLiteStudio/qtscriptsyntaxhighlighter.h @@ -56,7 +56,10 @@ class GUI_API_EXPORT JavaScriptSyntaxHighlighter : public QSyntaxHighlighter QSet m_knownIds; QString m_markString; Qt::CaseSensitivity m_markCaseSensitivity; + QTextCharFormat normalFormat; QTextCharFormat keywordsFormat; + QTextCharFormat commentFormat; + QTextCharFormat stringFormat; }; class GUI_API_EXPORT JavaScriptHighlighterPlugin : public BuiltInPlugin, public SyntaxHighlighterPlugin diff --git a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h index 02ab9fa..534da51 100644 --- a/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h +++ b/SQLiteStudio3/guiSQLiteStudio/searchtextlocator.h @@ -60,7 +60,7 @@ class GUI_API_EXPORT SearchTextLocator : public QObject int startPosition = 0; public slots: - bool find(QTextDocument::FindFlags flags = 0); + bool find(QTextDocument::FindFlags flags = QTextDocument::FindFlags()); void findNext(); void findPrev(); bool replaceAndFind(); diff --git a/SQLiteStudio3/guiSQLiteStudio/selectabledbobjmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/selectabledbobjmodel.cpp index 75579d5..d9d99ba 100644 --- a/SQLiteStudio3/guiSQLiteStudio/selectabledbobjmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/selectabledbobjmodel.cpp @@ -1,6 +1,7 @@ #include "selectabledbobjmodel.h" #include "dbtree/dbtreeitem.h" #include "dbtree/dbtreemodel.h" +#include "common/compatibility.h" #include #include @@ -29,10 +30,10 @@ bool SelectableDbObjModel::setData(const QModelIndex& index, const QVariant& val return true; } -Qt::ItemFlags SelectableDbObjModel::flags(const QModelIndex& index) const +Qt::ItemFlags SelectableDbObjModel::flags(const QModelIndex& idx) const { - Qt::ItemFlags flags = QSortFilterProxyModel::flags(index); - DbTreeItem* item = getItemForProxyIndex(index); + Qt::ItemFlags flags = QSortFilterProxyModel::flags(idx); + DbTreeItem* item = getItemForProxyIndex(idx); switch (item->getType()) { case DbTreeItem::Type::TABLE: @@ -47,7 +48,7 @@ Qt::ItemFlags SelectableDbObjModel::flags(const QModelIndex& index) const case DbTreeItem::Type::VIEWS: { flags |= Qt::ItemIsUserCheckable; - if (index.child(0, 0).isValid()) + if (index(0, 0, idx).isValid()) flags |= Qt::ItemIsTristate; break; @@ -76,12 +77,12 @@ void SelectableDbObjModel::setDbName(const QString& value) } QStringList SelectableDbObjModel::getCheckedObjects() const { - return checkedObjects.toList(); + return checkedObjects.values(); } void SelectableDbObjModel::setCheckedObjects(const QStringList& value) { - checkedObjects = value.toSet(); + checkedObjects = toSet(value); } void SelectableDbObjModel::setRootChecked(bool checked) @@ -147,13 +148,13 @@ DbTreeItem* SelectableDbObjModel::getItemForProxyIndex(const QModelIndex& index) return item; } -Qt::CheckState SelectableDbObjModel::getStateFromChilds(const QModelIndex& index) const +Qt::CheckState SelectableDbObjModel::getStateFromChilds(const QModelIndex& idx) const { - DbTreeItem* item = getItemForProxyIndex(index); + DbTreeItem* item = getItemForProxyIndex(idx); if (!item) return Qt::Unchecked; - if (!index.child(0, 0).isValid()) + if (!index(0, 0, idx).isValid()) { if (isObject(item) && checkedObjects.contains(item->text())) return Qt::Checked; @@ -166,7 +167,7 @@ Qt::CheckState SelectableDbObjModel::getStateFromChilds(const QModelIndex& index int partial = 0; Qt::CheckState state; QModelIndex child; - for (int i = 0; (child = index.child(i, 0)).isValid(); i++) + for (int i = 0; (child = index(i, 0, idx)).isValid(); i++) { if (!child.flags().testFlag(Qt::ItemIsUserCheckable)) continue; @@ -200,9 +201,9 @@ Qt::CheckState SelectableDbObjModel::getStateFromChilds(const QModelIndex& index return Qt::PartiallyChecked; } -void SelectableDbObjModel::setRecurrently(const QModelIndex& index, Qt::CheckState checked) +void SelectableDbObjModel::setRecurrently(const QModelIndex& idx, Qt::CheckState checked) { - DbTreeItem* item = getItemForProxyIndex(index); + DbTreeItem* item = getItemForProxyIndex(idx); if (!item) return; @@ -211,14 +212,14 @@ void SelectableDbObjModel::setRecurrently(const QModelIndex& index, Qt::CheckSta else checkedObjects.remove(item->text()); - if (!index.child(0, 0).isValid()) + if (!index(0, 0, idx).isValid()) return; // Limiting checked to 'checked/unchecked', cause recurrent marking cannot set partially checked, it makes no sense checked = (checked > 0 ? Qt::Checked : Qt::Unchecked); QModelIndex child; - for (int i = 0; (child = index.child(i, 0)).isValid(); i++) + for (int i = 0; (child = index(i, 0, idx)).isValid(); i++) setData(child, checked, Qt::CheckStateRole); } diff --git a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp index b3656a6..50189d4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/sqleditor.cpp @@ -17,6 +17,7 @@ #include "searchtextlocator.h" #include "services/codeformatter.h" #include "sqlitestudio.h" +#include "style.h" #include "dbtree/dbtreeitem.h" #include "dbtree/dbtree.h" #include "dbtree/dbtreemodel.h" @@ -31,6 +32,7 @@ #include #include #include +#include CFG_KEYS_DEFINE(SqlEditor) @@ -69,10 +71,9 @@ void SqlEditor::init() connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); connect(this, SIGNAL(textChanged()), this, SLOT(checkContentSize())); connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorMoved())); - connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine())); updateLineNumberAreaWidth(); - highlightCurrentLine(); + highlightCurrentCursorContext(); completer = new CompleterWindow(this); connect(completer, SIGNAL(accepted()), this, SLOT(completeSelected())); @@ -87,11 +88,11 @@ void SqlEditor::init() connect(autoCompleteTrigger, SIGNAL(triggered()), this, SLOT(checkForAutoCompletion())); queryParserTrigger = new LazyTrigger(queryParserDelay, this); - connect(autoCompleteTrigger, SIGNAL(triggered()), this, SLOT(parseContents())); + connect(queryParserTrigger, SIGNAL(triggered()), this, SLOT(parseContents())); connect(this, SIGNAL(textChanged()), this, SLOT(scheduleQueryParser())); - queryParser = new Parser(Dialect::Sqlite3); + queryParser = new Parser(); connect(this, &QWidget::customContextMenuRequested, this, &SqlEditor::customContextMenuRequested); connect(CFG_UI.Fonts.SqlEditor, SIGNAL(changed(QVariant)), this, SLOT(changeFont(QVariant))); @@ -215,8 +216,7 @@ bool SqlEditor::handleValidObjectContextMenu(const QPoint& pos) if (!obj) return false; - Dialect dialect = getDialect(); - QString objName = stripObjName(toPlainText().mid(obj->from, (obj->to - obj->from + 1)), dialect); + QString objName = stripObjName(toPlainText().mid(obj->from, (obj->to - obj->from + 1))); validObjContextMenu->clear(); @@ -492,12 +492,12 @@ void SqlEditor::completeSelected() ExpectedTokenPtr token = completer->getSelected(); QString value = token->value; if (token->needsWrapping()) - value = wrapObjIfNeeded(value, getDialect()); + value = wrapObjIfNeeded(value); if (!token->prefix.isNull()) { value.prepend("."); - value.prepend(wrapObjIfNeeded(token->prefix, getDialect())); + value.prepend(wrapObjIfNeeded(token->prefix)); } insertPlainText(value); @@ -508,7 +508,7 @@ void SqlEditor::checkForAutoCompletion() if (!db || !autoCompletion || deletionKeyPressed || !richFeaturesEnabled) return; - Lexer lexer(getDialect()); + Lexer lexer; QString sql = toPlainText(); int curPos = textCursor().position(); TokenList tokens = lexer.tokenize(sql.left(curPos)); @@ -546,11 +546,6 @@ void SqlEditor::refreshValidObjects() }); } -Dialect SqlEditor::getDialect() -{ - return !db ? Dialect::Sqlite3 : db->getDialect(); -} - void SqlEditor::setObjectLinks(bool enabled) { objectLinksEnabled = enabled; @@ -579,7 +574,7 @@ void SqlEditor::clearDbObjects() void SqlEditor::lineNumberAreaPaintEvent(QPaintEvent* event) { QPainter painter(lineNumberArea); - painter.fillRect(event->rect(), CFG_UI.Colors.SqlEditorLineNumAreaBg.get()); + painter.fillRect(event->rect(), STYLE->extendedPalette().editorLineBase()); QTextBlock block = firstVisibleBlock(); int blockNumber = block.blockNumber(); int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); @@ -589,7 +584,7 @@ void SqlEditor::lineNumberAreaPaintEvent(QPaintEvent* event) if (block.isVisible() && bottom >= event->rect().top()) { QString number = QString::number(blockNumber + 1); - painter.setPen(Qt::black); + painter.setPen(style()->standardPalette().text().color()); painter.drawText(0, top, lineNumberArea->width()-2, fontMetrics().height(), Qt::AlignRight, number); } @@ -610,27 +605,15 @@ int SqlEditor::lineNumberAreaWidth() digits++; } - int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; + int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; return space; } -void SqlEditor::highlightParenthesis() +void SqlEditor::highlightParenthesis(QList& selections) { if (!richFeaturesEnabled) return; - // Clear extra selections - QList selections = extraSelections(); - - // Just keep "current line" highlighting - QMutableListIterator it(selections); - while (it.hasNext()) - { - if (!it.next().format.property(QTextFormat::FullWidthSelection).toBool()) - it.remove(); - } - setExtraSelections(selections); - // Find out parenthesis under the cursor int curPos = textCursor().position(); TextBlockData* data = dynamic_cast(textCursor().block().userData()); @@ -664,6 +647,13 @@ void SqlEditor::highlightParenthesis() // Mark new match markMatchedParenthesis(thePar->position, matchedPar->position, selections); +} + +void SqlEditor::highlightCurrentCursorContext() +{ + QList selections; + highlightCurrentLine(selections); + highlightParenthesis(selections); setExtraSelections(selections); } @@ -671,7 +661,8 @@ void SqlEditor::markMatchedParenthesis(int pos1, int pos2, QListstandardPalette().windowText()); + selection.format.setForeground(style()->standardPalette().window()); QTextCursor cursor = textCursor(); @@ -841,11 +832,6 @@ void SqlEditor::parseContents() if (!richFeaturesEnabled) return; - // Updating dialect according to current database (if any) - Dialect dialect = Dialect::Sqlite3; - if (db && db->isValid()) - dialect = db->getDialect(); - QString sql = toPlainText(); if (!virtualSqlExpression.isNull()) { @@ -855,7 +841,6 @@ void SqlEditor::parseContents() sql = virtualSqlExpression.arg(sql); } - queryParser->setDialect(dialect); if (richFeaturesEnabled) { queryParser->parse(sql); @@ -863,7 +848,6 @@ void SqlEditor::parseContents() checkForSyntaxErrors(); highlighter->rehighlight(); } - } void SqlEditor::checkForSyntaxErrors() @@ -903,7 +887,6 @@ void SqlEditor::checkForValidObjects() return; QMutexLocker lock(&objectsInNamedDbMutex); - Dialect dialect = db->getDialect(); QList fullObjects; QString dbName; for (SqliteQueryPtr query : queryParser->getQueries()) @@ -911,18 +894,18 @@ void SqlEditor::checkForValidObjects() fullObjects = query->getContextFullObjects(); for (const SqliteStatement::FullObject& fullObj : fullObjects) { - dbName = fullObj.database ? stripObjName(fullObj.database->value, dialect) : "main"; + dbName = fullObj.database ? stripObjName(fullObj.database->value) : "main"; if (!objectsInNamedDb.contains(dbName)) continue; if (fullObj.type == SqliteStatement::FullObject::DATABASE) { // Valid db name - addDbObject(sqlIndex(fullObj.database->start), sqlIndex(fullObj.database->end), QString::null); + addDbObject(sqlIndex(fullObj.database->start), sqlIndex(fullObj.database->end), QString()); continue; } - if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value, dialect))) + if (!objectsInNamedDb[dbName].contains(stripObjName(fullObj.object->value))) continue; // Valid object name @@ -999,22 +982,18 @@ void SqlEditor::updateLineNumberAreaWidth() setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } -void SqlEditor::highlightCurrentLine() +void SqlEditor::highlightCurrentLine(QList& selections) { - QList selections; - if (!isReadOnly() && isEnabled()) { QTextEdit::ExtraSelection selection; - selection.format.setBackground(CFG_UI.Colors.SqlEditorCurrentLineBg.get()); + selection.format.setBackground(STYLE->extendedPalette().editorLineBase()); selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.cursor = textCursor(); selection.cursor.clearSelection(); selections.append(selection); } - - setExtraSelections(selections); } void SqlEditor::updateLineNumberArea(const QRect& rect, int dy) @@ -1036,8 +1015,7 @@ void SqlEditor::updateLineNumberArea(const QRect& rect, int dy) void SqlEditor::cursorMoved() { - highlightParenthesis(); - + highlightCurrentCursorContext(); if (!cursorMovingByLocator) { textLocator->setStartPosition(textCursor().position()); @@ -1321,6 +1299,7 @@ void SqlEditor::changeFont(const QVariant& font) void SqlEditor::configModified() { highlighter->rehighlight(); + highlightCurrentCursorContext(); } void SqlEditor::toggleComment() @@ -1503,7 +1482,7 @@ void SqlEditor::mousePressEvent(QMouseEvent* e) if (obj && e->button() == Qt::LeftButton) { QString objName = toPlainText().mid(obj->from, (obj->to - obj->from + 1)); - openObject(obj->dbName, stripObjName(objName, getDialect())); + openObject(obj->dbName, stripObjName(objName)); } } @@ -1597,7 +1576,7 @@ void SqlEditor::setVirtualSqlExpression(const QString& value) if (virtualSqlOffset == -1) { virtualSqlOffset = 0; - virtualSqlExpression = QString::null; + virtualSqlExpression = QString(); qWarning() << "Tried to set invalid virtualSqlExpression for SqlEditor. Ignored."; return; } @@ -1657,8 +1636,8 @@ void SqlEditor::LineNumberArea::paintEvent(QPaintEvent* event) void SqlEditor::changeEvent(QEvent* e) { - if (e->type() == QEvent::EnabledChange) - highlightCurrentLine(); +// if (e->type() == QEvent::EnabledChange) +// highlightCurrentLine(); QPlainTextEdit::changeEvent(e); } diff --git a/SQLiteStudio3/guiSQLiteStudio/sqleditor.h b/SQLiteStudio3/guiSQLiteStudio/sqleditor.h index c56492c..ca79a1c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqleditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/sqleditor.h @@ -165,13 +165,14 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void refreshValidObjects(); void checkForSyntaxErrors(); void checkForValidObjects(); - Dialect getDialect(); void setObjectLinks(bool enabled); void addDbObject(int from, int to, const QString& dbName); void clearDbObjects(); void lineNumberAreaPaintEvent(QPaintEvent* event); int lineNumberAreaWidth(); - void highlightParenthesis(); + void highlightParenthesis(QList& selections); + void highlightCurrentLine(QList& selections); + void highlightCurrentCursorContext(); const TextBlockData::Parenthesis* matchParenthesis(QList parList, const TextBlockData::Parenthesis* thePar); void markMatchedParenthesis(int pos1, int pos2, QList& selections); void doBackspace(int repeats = 1); @@ -269,7 +270,6 @@ class GUI_API_EXPORT SqlEditor : public QPlainTextEdit, public ExtActionContaine void parseContents(); void scheduleQueryParser(bool force = false); void updateLineNumberAreaWidth(); - void highlightCurrentLine(); void updateLineNumberArea(const QRect&rect, int dy); void cursorMoved(); void checkContentSize(); diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp index 55ccc08..92679e2 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.cpp @@ -1,10 +1,13 @@ #include "sqlitesyntaxhighlighter.h" #include "parser/lexer.h" -#include "uiconfig.h" #include "services/config.h" +#include "style.h" +#include "parser/keywords.h" #include #include #include +#include +#include SqliteSyntaxHighlighter::SqliteSyntaxHighlighter(QTextDocument *parent) : QSyntaxHighlighter(parent) @@ -15,12 +18,6 @@ SqliteSyntaxHighlighter::SqliteSyntaxHighlighter(QTextDocument *parent) : connect(CFG, SIGNAL(massSaveCommitted()), this, SLOT(setupFormats())); } -void SqliteSyntaxHighlighter::setSqliteVersion(int version) -{ - this->sqliteVersion = version; - rehighlight(); -} - void SqliteSyntaxHighlighter::setFormat(SqliteSyntaxHighlighter::State state, QTextCharFormat format) { formats[state] = format; @@ -36,46 +33,47 @@ void SqliteSyntaxHighlighter::setupFormats() QTextCharFormat format; // Standard - format.setForeground(CFG_UI.Colors.SqlEditorForeground.get()); + format.setForeground(QApplication::style()->standardPalette().text()); format.setFontWeight(QFont::Normal); format.setFontItalic(false); formats[State::STANDARD] = format; // Parenthesis + format.setForeground(QApplication::style()->standardPalette().text()); formats[State::PARENTHESIS] = format; // String - format.setForeground(CFG_UI.Colors.SqlEditorStringFg.get()); + format.setForeground(STYLE->extendedPalette().editorString()); format.setFontWeight(QFont::Normal); - format.setFontItalic(false); + format.setFontItalic(true); formats[State::STRING] = format; // Keyword - format.setForeground(CFG_UI.Colors.SqlEditorKeywordFg.get()); - format.setFontWeight(QFont::Bold); + format.setForeground(QApplication::style()->standardPalette().windowText()); + format.setFontWeight(QFont::ExtraBold); format.setFontItalic(false); formats[State::KEYWORD] = format; // BindParam - format.setForeground(CFG_UI.Colors.SqlEditorBindParamFg.get()); + format.setForeground(QApplication::style()->standardPalette().linkVisited()); format.setFontWeight(QFont::Normal); format.setFontItalic(false); formats[State::BIND_PARAM] = format; // Blob - format.setForeground(CFG_UI.Colors.SqlEditorBlobFg.get()); + format.setForeground(QApplication::style()->standardPalette().text()); format.setFontWeight(QFont::Normal); format.setFontItalic(false); formats[State::BLOB] = format; // Comment - format.setForeground(CFG_UI.Colors.SqlEditorCommentFg.get()); + format.setForeground(QApplication::style()->standardPalette().dark()); format.setFontWeight(QFont::Normal); format.setFontItalic(true); formats[State::COMMENT] = format; // Number - format.setForeground(CFG_UI.Colors.SqlEditorNumberFg.get()); + format.setForeground(QApplication::style()->standardPalette().text()); format.setFontWeight(QFont::Normal); format.setFontItalic(false); formats[State::NUMBER] = format; @@ -139,7 +137,7 @@ void SqliteSyntaxHighlighter::highlightBlock(const QString &text) idxModifier += statePrefix.size(); } - Lexer lexer(sqliteVersion == 2 ? Dialect::Sqlite2 : Dialect::Sqlite3); + Lexer lexer; lexer.setTolerantMode(true); lexer.prepare(statePrefix+text); @@ -157,22 +155,25 @@ void SqliteSyntaxHighlighter::highlightBlock(const QString &text) TextBlockData* data = new TextBlockData(); int errorStart = -1; TokenPtr token = lexer.getToken(); + TokenPtr aheadToken; while (token) { - if (handleToken(token, idxModifier, errorStart, data, prevData)) + aheadToken = lexer.getToken(); + + if (handleToken(token, aheadToken, idxModifier, errorStart, data, prevData)) errorStart = token->start + currentBlock().position(); if (data->getEndsWithQuerySeparator()) errorStart = -1; handleParenthesis(token, data); - token = lexer.getToken(); + token = aheadToken; } setCurrentBlockUserData(data); } -bool SqliteSyntaxHighlighter::handleToken(TokenPtr token, qint32 idxModifier, int errorStart, TextBlockData* currBlockData, +bool SqliteSyntaxHighlighter::handleToken(TokenPtr token, TokenPtr aheadToken, qint32 idxModifier, int errorStart, TextBlockData* currBlockData, TextBlockData* previousBlockData) { qint64 start = token->start - idxModifier; @@ -186,6 +187,9 @@ bool SqliteSyntaxHighlighter::handleToken(TokenPtr token, qint32 idxModifier, in if (createTriggerContext && token->type == Token::OTHER && (token->value.toLower() == "old" || token->value.toLower() == "new")) token->type = Token::KEYWORD; + if (aheadToken && aheadToken->type == Token::PAR_LEFT && token->type == Token::KEYWORD && isSoftKeyword(token->value)) + token->type = Token::OTHER; + bool limitedDamage = false; bool querySeparator = (token->type == Token::Type::OPERATOR && token->value == ";"); bool error = isError(start, lgt, &limitedDamage); @@ -203,7 +207,7 @@ bool SqliteSyntaxHighlighter::handleToken(TokenPtr token, qint32 idxModifier, in ); bool fatalError = (error && !limitedDamage) || wasError; - QTextCharFormat format; + QTextCharFormat format = formats[State::STANDARD]; // Applying valid object format. applyValidObjectFormat(format, valid, error, wasError); @@ -245,7 +249,7 @@ void SqliteSyntaxHighlighter::applyValidObjectFormat(QTextCharFormat& format, bo if (isError || wasError || !isValid) return; - format.setForeground(CFG_UI.Colors.SqlEditorValidObject.get()); + format.setForeground(QApplication::style()->standardPalette().link()); if (objectLinksEnabled) format.setUnderlineStyle(QTextCharFormat::SingleUnderline); } diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h index 48649d4..b17c45f 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h +++ b/SQLiteStudio3/guiSQLiteStudio/sqlitesyntaxhighlighter.h @@ -54,7 +54,6 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter explicit SqliteSyntaxHighlighter(QTextDocument *parent); - void setSqliteVersion(int version); void setFormat(State state, QTextCharFormat format); QTextCharFormat getFormat(State state) const; @@ -123,7 +122,7 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter * @param idxModifier Modifier for text highlighting in case of previous state defined by multi-character token. See getPreviousStatePrefix() for details. * @return true if the token is being marked as invalid (syntax error). */ - bool handleToken(TokenPtr token, qint32 idxModifier, int errorStart, TextBlockData* currBlockData, TextBlockData* previousBlockData); + bool handleToken(TokenPtr token, TokenPtr aheadToken, qint32 idxModifier, int errorStart, TextBlockData* currBlockData, TextBlockData* previousBlockData); bool isError(int start, int lgt, bool* limitedDamage); bool isValid(int start, int lgt); @@ -156,7 +155,6 @@ class GUI_API_EXPORT SqliteSyntaxHighlighter : public QSyntaxHighlighter void handleParenthesis(TokenPtr token, TextBlockData* data); static const int regulartTextBlockState = static_cast(TextBlockState::REGULAR); - int sqliteVersion = 3; QHash formats; QHash tokenTypeMapping; QList errors; diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlview.cpp b/SQLiteStudio3/guiSQLiteStudio/sqlview.cpp index e65a60f..6c38915 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlview.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/sqlview.cpp @@ -11,11 +11,6 @@ SqlView::SqlView(QWidget *parent) : setReadOnly(true); } -void SqlView::setSqliteVersion(int version) -{ - highlighter->setSqliteVersion(version); -} - void SqlView::setTextBackgroundColor(int from, int to, const QColor& color) { bool wasRo = false; diff --git a/SQLiteStudio3/guiSQLiteStudio/sqlview.h b/SQLiteStudio3/guiSQLiteStudio/sqlview.h index 7358a43..85e5b54 100644 --- a/SQLiteStudio3/guiSQLiteStudio/sqlview.h +++ b/SQLiteStudio3/guiSQLiteStudio/sqlview.h @@ -12,7 +12,6 @@ class GUI_API_EXPORT SqlView : public QTextEdit public: explicit SqlView(QWidget *parent = 0); - void setSqliteVersion(int version); void setTextBackgroundColor(int from, int to, const QColor& color); private: diff --git a/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp b/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp index 571fcba..11eed59 100644 --- a/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/statusfield.cpp @@ -13,6 +13,8 @@ #include #include +const QString StatusField::colorTpl = "QLabel {color: %1}"; + StatusField::StatusField(QWidget *parent) : QDockWidget(parent), ui(new Ui::StatusField) @@ -57,20 +59,20 @@ void StatusField::changeEvent(QEvent *e) void StatusField::info(const QString &text) { - addEntry(ICONS.STATUS_INFO, text, CFG_UI.Colors.StatusFieldInfoFg.get()); + addEntry(ICONS.STATUS_INFO, text, style()->standardPalette().text().color(), INFO); } void StatusField::warn(const QString &text) { - addEntry(ICONS.STATUS_WARNING, text, CFG_UI.Colors.StatusFieldWarnFg.get()); + addEntry(ICONS.STATUS_WARNING, text, style()->standardPalette().text().color(), WARN); } void StatusField::error(const QString &text) { - addEntry(ICONS.STATUS_ERROR, text, CFG_UI.Colors.StatusFieldErrorFg.get()); + addEntry(ICONS.STATUS_ERROR, text, QColor(Qt::red), ERROR); } -void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& color) +void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& color, EntryRole role) { int row = ui->tableWidget->rowCount(); ui->tableWidget->setRowCount(row+1); @@ -86,6 +88,7 @@ void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& item = new QTableWidgetItem(); item->setIcon(icon); + item->setData(Qt::UserRole, role); ui->tableWidget->setItem(row, 0, item); itemsCreated << item; @@ -95,16 +98,17 @@ void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& item = new QTableWidgetItem(timeStr); item->setForeground(QBrush(color)); item->setFont(font); + item->setData(Qt::UserRole, role); ui->tableWidget->setItem(row, 1, item); itemsCreated << item; item = new QTableWidgetItem(); item->setForeground(QBrush(color)); item->setFont(font); + item->setData(Qt::UserRole, role); ui->tableWidget->setItem(row, 2, item); itemsCreated << item; - static_qstring(colorTpl, "QLabel {color: %1}"); // While QLabel does detect if the text is rich automatically, we don't want to use qlabel for plain text, // because it's not wrapped correctly if the text is longer. if (text.contains("<")) @@ -117,6 +121,8 @@ void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& label->setStyleSheet(colorTpl.arg(color.name())); connect(label, SIGNAL(linkActivated(QString)), this, SIGNAL(linkActivated(QString))); ui->tableWidget->setCellWidget(row, 2, label); + ui->tableWidget->item(row, 2)->setData(Qt::UserRole, role); + ui->tableWidget->item(row, 2)->setData(Qt::UserRole+1, true); } else { @@ -127,34 +133,38 @@ void StatusField::addEntry(const QIcon &icon, const QString &text, const QColor& setVisible(true); ui->tableWidget->scrollToBottom(); - if (isVisible() && !noFlashing) - flashItems(itemsCreated, color); } -void StatusField::flashItems(const QList& items, const QColor& color) +void StatusField::refreshColors() { - QColor alphaColor = color; - alphaColor.setAlpha(0); - - QColor finalColor = color; - finalColor.setAlpha(150); - - QVariantAnimation* anim = new QVariantAnimation(); - anim->setDuration(500); - anim->setEasingCurve(QEasingCurve::OutQuad); - anim->setStartValue(finalColor); - anim->setEndValue(alphaColor); - - itemAnimations << anim; - connect(anim, &QObject::destroyed, [this, anim]() {itemAnimations.removeOne(anim);}); - - connect(anim, &QVariantAnimation::valueChanged, [items](const QVariant& value) - { - for (QTableWidgetItem* item : items) - item->setBackground(value.value()); - }); - - anim->start(QAbstractAnimation::DeleteWhenStopped); + const QColor stdColor = style()->standardPalette().text().color(); + const QColor errColor = QColor(Qt::red); + EntryRole role; + bool hasLabel; + QLabel* label = nullptr; + for (QTableWidgetItem* item : ui->tableWidget->findItems("", Qt::MatchContains)) { + role = (EntryRole)item->data(Qt::UserRole).toInt(); + + hasLabel = item->data(Qt::UserRole+1).toBool(); + label = hasLabel ? dynamic_cast(ui->tableWidget->cellWidget(item->row(), item->column())) : nullptr; + + switch (role) + { + case INFO: + case WARN: + item->setForeground(stdColor); + if (label != nullptr) + label->setStyleSheet(colorTpl.arg(stdColor.name())); + + break; + case ERROR: + item->setForeground(errColor); + if (label != nullptr) + label->setStyleSheet(colorTpl.arg(stdColor.name())); + + break; + } + } } void StatusField::setupMenu() @@ -177,7 +187,6 @@ void StatusField::setupMenu() void StatusField::readRecentMessages() { - noFlashing = true; for (const QString& msg : NotifyManager::getInstance()->getRecentInfos()) info(msg); @@ -186,8 +195,6 @@ void StatusField::readRecentMessages() for (const QString& msg : NotifyManager::getInstance()->getRecentErrors()) error(msg); - - noFlashing = false; } void StatusField::customContextMenuRequested(const QPoint &pos) diff --git a/SQLiteStudio3/guiSQLiteStudio/statusfield.h b/SQLiteStudio3/guiSQLiteStudio/statusfield.h index ac07f51..6f9ad22 100644 --- a/SQLiteStudio3/guiSQLiteStudio/statusfield.h +++ b/SQLiteStudio3/guiSQLiteStudio/statusfield.h @@ -27,7 +27,14 @@ class GUI_API_EXPORT StatusField : public QDockWidget void changeEvent(QEvent *e); private: - void addEntry(const QIcon& icon, const QString& text, const QColor &color); + enum EntryRole + { + INFO, + WARN, + ERROR + }; + + void addEntry(const QIcon& icon, const QString& text, const QColor& color, EntryRole role); void flashItems(const QList& items, const QColor& color); void setupMenu(); void readRecentMessages(); @@ -37,11 +44,12 @@ class GUI_API_EXPORT StatusField : public QDockWidget QAction* copyAction = nullptr; QAction* clearAction = nullptr; QList itemAnimations; - bool noFlashing = false; static const int timeStampColumnWidth = 70; static const int itemCountLimit = 30; + static const int itemRole = Qt::UserRole; static constexpr const char* timeStampFormat = "hh:mm:ss"; + static const QString colorTpl; private slots: void customContextMenuRequested(const QPoint& pos); @@ -51,6 +59,9 @@ class GUI_API_EXPORT StatusField : public QDockWidget void reset(); void fontChanged(const QVariant& variant); + public slots: + void refreshColors(); + signals: void linkActivated(const QString& link); }; diff --git a/SQLiteStudio3/guiSQLiteStudio/style.cpp b/SQLiteStudio3/guiSQLiteStudio/style.cpp new file mode 100644 index 0000000..525a832 --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/style.cpp @@ -0,0 +1,48 @@ +#include "style.h" +#include "themetuner.h" +#include "common/global.h" +#include "mainwindow.h" +#include +#include + +Style* Style::instance = nullptr; + +Style* Style::getInstance() +{ + if (instance == nullptr) + instance = new Style(QApplication::style()); + + return instance; +} + +const ExtendedPalette& Style::extendedPalette() const +{ + return extPalette; +} + +void Style::setStyle(QStyle *style, const QString &styleName) +{ + setBaseStyle(style); + + QApplication::setPalette(initialPalette); // reset palette, cause styles don't provide + // full palette when changed in runtime (i.e. windowsvista) + QApplication::setStyle(this); + QApplication::setPalette(standardPalette()); + THEME_TUNER->tuneTheme(styleName); + QToolTip::setPalette(standardPalette()); + + extPalette.styleChanged(this, styleName); + + MAINWINDOW->getMdiArea()->setBackground(extPalette.mdiAreaBase()); +} + +QString Style::name() const +{ + return baseStyle()->objectName(); +} + +Style::Style(QStyle *style) + : QProxyStyle(style) +{ + initialPalette = style->standardPalette(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/style.h b/SQLiteStudio3/guiSQLiteStudio/style.h new file mode 100644 index 0000000..ccb5e9e --- /dev/null +++ b/SQLiteStudio3/guiSQLiteStudio/style.h @@ -0,0 +1,29 @@ +#ifndef STYLE_H +#define STYLE_H + +#include "extendedpalette.h" +#include +#include + + +class Style : public QProxyStyle +{ + public: + static Style* getInstance(); + + const ExtendedPalette &extendedPalette() const; + void setStyle(QStyle* style, const QString& styleName); + QString name() const; + + private: + static Style* instance; + + Style(QStyle* style); + + ExtendedPalette extPalette; + QPalette initialPalette; +}; + +#define STYLE Style::getInstance() + +#endif // STYLE_H diff --git a/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp b/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp index fd0c338..b209bbc 100644 --- a/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/taskbar.cpp @@ -204,7 +204,7 @@ bool TaskBar::handleMouseMoveEvent(QMouseEvent* event) drag->setMimeData(generateMimeData()); dragStartIndex = tasks.indexOf(dragStartTask); - drag->start(Qt::MoveAction); + drag->exec(Qt::MoveAction); return true; } diff --git a/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp b/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp index e48865f..89092d6 100644 --- a/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/themetuner.cpp @@ -2,10 +2,12 @@ #include "uiconfig.h" #include "mainwindow.h" #include "uiconfig.h" +#include "style.h" #include #include #include #include +#include ThemeTuner* ThemeTuner::instance = nullptr; @@ -22,7 +24,7 @@ void ThemeTuner::tuneTheme(const QString& themeName) void ThemeTuner::tuneCurrentTheme() { - tuneTheme(QApplication::style()->objectName()); + tuneTheme(STYLE->name()); } void ThemeTuner::manageCompactLayout(QWidget* w) @@ -49,6 +51,23 @@ QString ThemeTuner::getDefaultCss(const QString& themeName) const return css; } +void ThemeTuner::darkThemeFix(QWizard* wizard) +{ + QString themeName = STYLE->name(); + if (qwizardThemeTuneRequired.contains(themeName)) + wizard->setWizardStyle(QWizard::ClassicStyle); +} + +void ThemeTuner::registerQWizardThemeTuneRequired(const QString& styleName) +{ + qwizardThemeTuneRequired << styleName; +} + +void ThemeTuner::deregisterQWizardThemeTuneRequired(const QString& styleName) +{ + qwizardThemeTuneRequired.removeOne(styleName); +} + ThemeTuner* ThemeTuner::getInstance() { if (!instance) diff --git a/SQLiteStudio3/guiSQLiteStudio/themetuner.h b/SQLiteStudio3/guiSQLiteStudio/themetuner.h index 9804464..805ca94 100644 --- a/SQLiteStudio3/guiSQLiteStudio/themetuner.h +++ b/SQLiteStudio3/guiSQLiteStudio/themetuner.h @@ -1,20 +1,29 @@ #ifndef THEMETUNER_H #define THEMETUNER_H +#include "guiSQLiteStudio_global.h" +#include #include #include #include -class ThemeTuner : public QObject +class QWizard; + +class GUI_API_EXPORT ThemeTuner : public QObject { Q_OBJECT public: + typedef std::function QWizardThemeTuner; + void tuneTheme(const QString& themeName); void tuneCurrentTheme(); void manageCompactLayout(QWidget* w); void manageCompactLayout(QList wList); QString getDefaultCss(const QString& themeName = QString()) const; + void darkThemeFix(QWizard* wizard); + void registerQWizardThemeTuneRequired(const QString& styleName); + void deregisterQWizardThemeTuneRequired(const QString& styleName); static ThemeTuner* getInstance(); static void cleanUp(); @@ -30,6 +39,7 @@ class ThemeTuner : public QObject QString defaultGeneralCss; QHash defaultPerStyleCss; QList widgetsForCompactLayout; + QStringList qwizardThemeTuneRequired; static ThemeTuner* instance; @@ -40,4 +50,6 @@ class ThemeTuner : public QObject #define THEME_TUNER ThemeTuner::getInstance() + + #endif // THEMETUNER_H diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts index ebd49cf..3962775 100644 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_ru.ts @@ -1,6 +1,6 @@ - + AboutDialog @@ -15,7 +15,7 @@ <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> - <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Бесплатный кроссплатформенный менеджер баз данных SQLite с открытым исходным кодом.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Автор и активный разработчик:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Бесплатный кроссплатформенный менеджер баз данных SQLite с открытым исходным кодом.<br/><a href="http://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">http://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Автор и активный разработчик:<br/>SalSoft (<a href="http://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">http://salsoft.com.pl</span></a>)<br/></p></body></html> @@ -60,7 +60,7 @@ <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio вер.%1</span></p><p align="center">Бесплатный кроссплатформенный менеджер баз данных SQLite с открытым исходным кодом.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Автор и активный разработчик:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> @@ -98,217 +98,217 @@ Query parameters - + Параметры запроса Please provide values for query parameters - + Пожалуйста укажите значения для параметров запроса BugDialog Bugs and ideas - Ошибки и предложения + Ошибки и предложения Reporter - Отправитель + Отправитель E-mail address - Адрес e-mail + Адрес e-mail Log in - Вход + Вход Short description - Краткое описание + Краткое описание Detailed description - Подробное описание + Подробное описание Show more details - Показать дополнительную информацию + Показать дополнительную информацию SQLiteStudio version - Версия SQLiteStudio + Версия SQLiteStudio Operating system - Операционная система + Операционная система Loaded plugins - Загруженные модули + Загруженные модули Send - Отправить + Отправить You can see all your reported bugs and ideas by selecting menu '%1' and then '%2'. - Вы можете просмотреть все отправленные вами отчёты об ошибках и предложения, выбрав в меню '%1' пункт '%2'. + Вы можете просмотреть все отправленные вами отчёты об ошибках и предложения, выбрав в меню '%1' пункт '%2'. A bug report sent successfully. - Отчёт об ошибке успешно отправлен. + Отчёт об ошибке успешно отправлен. An error occurred while sending a bug report: %1 %2 - При отправке отчёта об ошибке возникла проблема: %1 + При отправке отчёта об ошибке возникла проблема: %1 %2 You can retry sending. The contents will be restored when you open a report dialog after an error like this. - Вы можете повторить отправку. После такой ошибки содержимое полей окна отправки отчёта будет восстановлено при повторном открытии. + Вы можете повторить отправку. После такой ошибки содержимое полей окна отправки отчёта будет восстановлено при повторном открытии. An idea proposal sent successfully. - Предложение по улучшению было успешно отправлено. + Предложение по улучшению было успешно отправлено. An error occurred while sending an idea proposal: %1 %2 - При отправке предложения по улучшению возникла проблема: %1 + При отправке предложения по улучшению возникла проблема: %1 %2 A bug report - Отчёт об ошибке + Отчёт об ошибке Describe problem in few words - Опишите проблему в нескольких словах + Опишите проблему в нескольких словах Describe problem and how to reproduce it - Опишите проблему и шаги для её воспроизведения + Опишите проблему и шаги для её воспроизведения A new feature idea - Предложение по улучшению функционала + Предложение по улучшению функционала A title for your idea - Название для вашего предложения + Название для вашего предложения Describe your idea in more details - Опишите ваше предложение более подробно + Опишите ваше предложение более подробно Reporting as an unregistered user, using e-mail address. - Отправка от незарегистрированного пользователя, используя адрес e-mail + Отправка от незарегистрированного пользователя, используя адрес e-mail Reporting as a registered user. - Отправка от зарегистрированного пользователя + Отправка от зарегистрированного пользователя Log out - Выход + Выход Providing true email address will make it possible to contact you regarding your report. To learn more, press 'help' button on the right side. - Указание действительного адреса e-mail поможет связаться с вами касательно вашего отчёта. Для подробной информации нажмите кнопку Помощь справа. + Указание действительного адреса e-mail поможет связаться с вами касательно вашего отчёта. Для подробной информации нажмите кнопку Помощь справа. Enter vaild e-mail address, or log in. - Введите действительный адрес e-mail либо выполните вход. + Введите действительный адрес e-mail либо выполните вход. Short description requires at least 10 characters, but not more than 100. Longer description can be entered in the field below. - Краткое описание должно содержать от 10 до 100 символов. Более подробное описание можно ввести в поле ниже. + Краткое описание должно содержать от 10 до 100 символов. Более подробное описание можно ввести в поле ниже. Long description requires at least 30 characters. - Детальное описание должно содержать как минимум 30 символов. + Детальное описание должно содержать как минимум 30 символов. BugReportHistoryWindow Title - Заголовок + Заголовок Reported at - Дата отправки + Дата отправки URL - URL + URL Reports history - История отчётов + История отчётов Clear reports history - Очистить историю отчётов + Очистить историю отчётов Delete selected entry - Удалить выбранную запись + Удалить выбранную запись Invalid response from server. - Некорректный ответ сервера. + Некорректный ответ сервера. BugReportLoginDialog Log in - Вход + Вход Credentials - Данные для входа + Данные для входа Login: - Имя пользователя: + Имя пользователя: Password: - Пароль: + Пароль: Validation - Проверка + Проверка Validate - Проверить + Проверить Validation result message - Статус проверки + Статус проверки Abort - Прервать + Прервать A login must be at least 2 characters long. - Имя пользователя должно состоять как минимум из двух символов. + Имя пользователя должно состоять как минимум из двух символов. A password must be at least 5 characters long. - Пароль должен состоять как минимум из пяти символов. + Пароль должен состоять как минимум из пяти символов. Valid - Верно + Верно @@ -400,7 +400,7 @@ Collations editor window has uncommited modifications. - В редакторе сравнений имеются неподтверждённые изменения. + В редакторе сравнений имеются неподтверждённые изменения. @@ -454,16 +454,16 @@ Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - + Некорректное выражение для значения по умолчанию: %1. Если необходимо использовать простую строку как значение, не забудьте поместить её в кавычки. Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - + Некорректное выражение для значения по умолчанию. Если необходимо использовать простую строку как значение, не забудьте поместить её в кавычки. Invalid default value expression: %1 - Некорректное выражение для значения по умолчанию: %1 + Некорректное выражение для значения по умолчанию: %1 @@ -667,12 +667,12 @@ but it's okay to use it. Cannot use type other than INTEGER if AUTOINCREMENT is enabled in PRIMARY KEY. - + Невозможно использовать тип данных, отличный от INTEGER, если в первичном ключе установлен автоинкремент. INTEGER type was enforced due to enabled AUTOINCREMENT in PRIMARY KEY. - + В качестве типа данных был принудительно выбран INTEGER, так как в первичном ключе установлен автоинкремент. @@ -779,7 +779,7 @@ but it's okay to use it. Autoincrement (only for %1 type columns) column primary key - Автоинкремент (только для столбцов типа %1) + Автоинкремент (только для столбцов типа %1) @@ -998,7 +998,7 @@ but it's okay to use it. <html><head/><body><p>&lt;p&gt;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).&lt;/p&gt;</p></body></html> - <html><head/><body><p>&lt;p&gt;Если редактируется ячейка, содержащая NULL, и вводится пустая строка в качестве значения, то эта опция определяет, останется ли в качестве значения ячейки NULL (если опция активирована), или значение будет заменено на пустую строку (если эта опция деактивирована).&lt;/p&gt;</p></body></html> + <html><head/><body><p>&lt;p&gt;Если редактируется ячейка, содержащая NULL, и вводится пустая строка в качестве значения, то эта опция определяет, останется ли в качестве значения ячейки NULL (если опция активирована), или значение будет заменено на пустую строку (если эта опция деактивирована).&lt;/p&gt;</p></body></html> @@ -1007,7 +1007,7 @@ but it's okay to use it. General.KeepNullWhenEmptyValue - General.KeepNullWhenEmptyValue + General.KeepNullWhenEmptyValue @@ -1105,7 +1105,7 @@ but it's okay to use it. Don't show DDL preview dialog when commiting schema changes - Не показывать диалог предпросмотра DDL при подтверждении изменений схемы + Не показывать диалог предпросмотра DDL при подтверждении изменений схемы @@ -1288,12 +1288,12 @@ but it's okay to use it. <p>Maximum number of configurations of Populate Table dialog stored in configuration. Value of 100 should be sufficient.</p> - + <p>Макисмальное количество конфигураций окна Заполнения таблицы, сохраняемых в конфигурации программы. 100 конфигураций должно хватить.</p> Number of memorized table populating configurations - + Количество запоминаемых конфигураций заполнения таблицы @@ -1347,12 +1347,12 @@ but it's okay to use it. <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> - + <p>Максимальное количество параметров запроса (:param, @param, $param, ?), сохраняемых в истории. Когда вы повторно используете параметр с тем же именем/расположением, SQLiteStudio преварительно инициализирует его последним запомненным значением (которое затем можно изменить). 1000 параметров должно хватить.</p> Number of memorized query parameters - + Количество запоминаемых параметров запроса @@ -1526,15 +1526,15 @@ but it's okay to use it. <p>Any data changes will be outlined with this color, until they're commited to the database.</p> - <p>Все изменения данных будут обрамлены этим цветом, пока не будут записаны в базу данных.</p> + <p>Все изменения данных будут обрамлены этим цветом, пока не будут записаны в базу данных.</p> Uncommited data outline color - Цвет обрамления неподтверждённых изменений + Цвет обрамления неподтверждённых изменений <p>In case of error while commiting data changes, the problematic cell will be outlined with this color.</p> - <p>В случае ошибки при подтверждении изменений данных, этим цветом будут обрамлены проблемные ячейки.</p> + <p>В случае ошибки при подтверждении изменений данных, этим цветом будут обрамлены проблемные ячейки.</p> @@ -1870,18 +1870,18 @@ but it's okay to use it. Filter - + Фильтр Hit Enter key or press "Apply filter" button on toolbar to apply new value. - + Нажмите Enter или кнопку "Применить фильтр" на панели инструментов чтобы применить новое значение Show filter inputs per column data view - + Показывать поле ввода для фильтра в каждом столбце @@ -2091,7 +2091,7 @@ Browsing other pages will be possible after the row counting is done. Generate automatically - Сгенерировать автоматически + Сгенерировать автоматически @@ -2138,7 +2138,7 @@ Browsing other pages will be possible after the row counting is done. Generate name basing on file path - Генерировать имя на основе пути к файлу + Генерировать имя на основе пути к файлу Permanent @@ -2177,11 +2177,11 @@ Browsing other pages will be possible after the row counting is done. <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - + <p>Автоматическая генерация имени отключена, так как имя было задано вручную. Для автоматической генерации необходимо удалить содержимое из поля имени.</p> <p>Automatic name generation was disabled, becuase the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - <p>Автоматическая генерация имени отключена, так как имя было задано вручную. Для автоматической генерации необходимо удалить содержимое из поля имени.</p> + <p>Автоматическая генерация имени отключена, так как имя было задано вручную. Для автоматической генерации необходимо удалить содержимое из поля имени.</p> @@ -2200,7 +2200,7 @@ Browsing other pages will be possible after the row counting is done. Auto-generated - Автоматически сгенерировано + Автоматически сгенерировано The name will be auto-generated @@ -2208,7 +2208,7 @@ Browsing other pages will be possible after the row counting is done. Type the name - Введите имя + Введите имя @@ -2326,23 +2326,23 @@ Browsing other pages will be possible after the row counting is done. Add a database - Добавить базу данных + Добавить базу данных Edit the database - Редактировать базу данных + Редактировать базу данных Remove the database - Удалить базу данных + Удалить базу данных Connect to the database - Подключиться к базе данных + Подключиться к базе данных Disconnect from the database - Отключиться от базы данных + Отключиться от базы данных @@ -2351,31 +2351,31 @@ Browsing other pages will be possible after the row counting is done. Export the database - Экспортировать базу данных + Экспортировать базу данных Convert database type - Сконвертировать тип базы данных + Измениить тип базы данных Vacuum - Выполнить VACUUM + Выполнить VACUUM Integrity check - Проверить целостность + Проверить целостность Create a table - Создать таблицу + Создать таблицу Edit the table - Редактировать таблицу + Редактировать таблицу Delete the table - Удалить таблицу + Удалить таблицу @@ -2404,39 +2404,39 @@ Browsing other pages will be possible after the row counting is done. Create an index - Создать индекс + Создать индекс Edit the index - Редактировать индекс + Редактировать индекс Delete the index - Удалить индекс + Удалить индекс Create a trigger - Создать триггер + Создать триггер Edit the trigger - Редактировать триггер + Редактировать триггер Delete the trigger - Удалить триггер + Удалить триггер Create a view - Создать представление + Создать представление Edit the view - Редактировать представление + Редактировать представление Delete the view - Удалить представление + Удалить представление @@ -2465,131 +2465,131 @@ Browsing other pages will be possible after the row counting is done. Refresh all database schemas - Обновить структуры всех баз данных + Обновить структуры всех баз данных Refresh selected database schema - Обновить структуру выбранной базы данных + Обновить структуру выбранной базы данных Execution from file cancelled. Any queries executed so far have been rolled back. - + Выполнение запросов из файла отменено. Все выполненные ранее из него запросы откачены. &Add a database - + &Добавить базу данных &Edit the database - + &Редактировать базу данных &Remove the database - + &Удалить базу данных &Connect to the database - + &Подключиться к базе данных &Disconnect from the database - + &Отключиться от базы данных &Export the database - + &Экспортировать базу данных Con&vert database type - + И&зменить тип базы данных Vac&uum - + Оп&ерация VACUUM &Integrity check - + Проверить &целостность Create a &table - + Создать &таблицу Edit the t&able - + Редактировать т&аблицу Delete the ta&ble - + Удалить та&блицу Create an &index - + Создать &индекс Edit the i&ndex - + Редактировать и&ндекс Delete the in&dex - + Удалить инде&кс Create a trig&ger - + Создать три&ггер Edit the trigg&er - + Редактиро&вать триггер Delete the trigge&r - + Уда&лить триггер Create a &view - + &Создать представление Edit the v&iew - + Редактироват&ь представление Delete the vi&ew - + Удалить &представление &Refresh all database schemas - + Обновить структуры всех баз данн&ых Re&fresh selected database schema - + Обновить структуру выбранной базы данны&х @@ -2600,12 +2600,12 @@ Browsing other pages will be possible after the row counting is done. Open file's directory - + Открыть папку с этим файлом Execute SQL from file - + Выполнить SQL-запросы из файла @@ -2684,32 +2684,32 @@ All objects from this group will be moved to parent group. Could not execute SQL, because application has failed to start transaction: %1 - + Невозможно выполнить SQL-запрос, так как приложению не удалось начать транзакцию: %1 Could not open file '%1' for reading: %2 - Невозможно открыть файл '%1' для чтения: %2 + Невозможно открыть файл '%1' для чтения: %2 Could not execute SQL, because application has failed to commit the transaction: %1 - + Невозможно выполнить SQL-запрос, так как приложению не удалось завершить транзакцию: %1 Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - + Завершено выполнение %1 запросов за %2 секунд. %3 запросов не было выполнено из-за ошибок. Finished executing %1 queries in %2 seconds. - + Завершено выполнение %1 запросов за %2 секунд. Could not execute SQL due to error. - + Невозможно выполнить SQL-запрос из-за ошибки. Delete database @@ -2761,11 +2761,11 @@ All objects from this group will be moved to parent group. Autoincrement value for table '%1' has been reset successfly. - Сброс счётчика автоинкремента у таблицы '%1' успешно выполнен. + Сброс счётчика автоинкремента у таблицы '%1' успешно выполнен. Are you sure you want to delete all data from table '%1'? - Вы действительно хотите удалить все данные из таблицы '%1'? + Вы действительно хотите удалить все данные из таблицы '%1'? @@ -3108,7 +3108,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Delete selected SQL history entries sql editor - + Удалить выбранные записи из истории SQL-запросов @@ -3152,7 +3152,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Editor window "%1" has uncommited data. - В окне редактора "%1" имеются неподтверждённые данные. + В окне редактора "%1" имеются неподтверждённые данные. @@ -3178,57 +3178,57 @@ Please enter new, unique name, or press '%1' to abort the operation: Execute SQL from file - + Выполнение SQL-запросов из файла Input file - + Файл-источник Path to file - + Путь к файлу Browse for file - + Выбрать файл Options - Опции + Опции File encoding - + Кодировка файла Skip failing SQL statements - + Пропуск неудавшихся SQL-запросов SQL scripts (*.sql);;All files (*) - Скрипты SQL (*.sql);;Все файлы (*) + Скрипты SQL (*.sql);;Все файлы (*) Execute SQL file - + Выполнить SQL-запросы из файла Please provide file to be executed. - + Пожалуйста укажите файл с SQL-запросами Provided file does not exist or cannot be read. - + Указанный файл не существует или не может быть прочитан. @@ -3447,32 +3447,32 @@ Please enter new, unique name, or press '%1' to abort the operation: Execution errors - + Ошибки выполнения Following errors were encountered during execution of SQL statements from the file: - + При выполнении SQL-запросов из файла возникли следующие ошибки: SQL - + SQL-запрос Error - Ошибка + Ошибка Statements that were executed successfully were commited. - + Успешно выполненные запросы были записаны в базу. Statements that were executed successfully were rolled back. - + Успешно выполненные запросы были откачены. @@ -3704,7 +3704,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Functions editor window has uncommited modifications. - В окне редактора функций имеются неподтверждённые изменения. + В окне редактора функций имеются неподтверждённые изменения. @@ -4060,47 +4060,47 @@ Please enter new, unique name, or press '%1' to abort the operation: Open SQL editor - Открыть редактор SQL + Открыть редактор SQL Open DDL history - Открыть историю DDL + Открыть историю DDL Open SQL functions editor - Открыть редактор функций SQL + Открыть редактор функций SQL Open collations editor - Открыть редактор сравнений + Открыть редактор сравнений Import - Импорт + Импорт Export - Экспорт + Экспорт Open configuration dialog - Открыть диалог конфигурации + Открыть диалог конфигурации Tile windows - Расположить окна плиткой + Расположить окна плиткой Tile windows horizontally - Расположить окна по горизонтали + Расположить окна по горизонтали Tile windows vertically - Расположить окна по вертикали + Расположить окна по вертикали Cascade windows - Расположить окна каскадом + Расположить окна каскадом @@ -4119,23 +4119,23 @@ Please enter new, unique name, or press '%1' to abort the operation: Close selected window - Закрыть выбранное окно + Закрыть выбранное окно Close all windows but selected - Закрыть все окна, кроме выбранного + Закрыть все окна, кроме выбранного Close all windows - Закрыть все окна + Закрыть все окна Restore recently closed window - Восстановить недавно закрытые окна + Восстановить недавно закрытые окна Rename selected window - Переименовать выбранное окно + Переименовать выбранное окно @@ -4149,58 +4149,58 @@ Please enter new, unique name, or press '%1' to abort the operation: Report a bug - Сообщить об ошибке + Сообщить об ошибке Propose a new feature - Предложить новый функционал + Предложить новый функционал About - О программе + О программе Licenses - Лицензии + Лицензии Open home page - Открыть домашнюю страницу + Открыть домашнюю страницу Open forum page - Открыть страницу форума + Открыть страницу форума User Manual - Руководство пользователя + Руководство пользователя SQLite documentation - Документация по SQLite + Документация по SQLite Report history - История отчётов + История отчётов Check for updates - Проверить обновления + Проверить обновления Database menubar - База данных + База данных Structure menubar - Структура + Структура View menubar - Вид + Вид @@ -4211,175 +4211,175 @@ Please enter new, unique name, or press '%1' to abort the operation: Tools menubar - Инструменты + Инструменты Help - Справка + Справка Open SQL &editor - + &Открыть редактор SQL Open DDL &history - + О&ткрыть историю DDL Open SQL &functions editor - + От&крыть редактор функций SQL Open &collations editor - + Отк&рыть редактор сравнений Open ex&tension manager - + Откр&ыть менеджер расширений &Import - + &Импорт E&xport - + &Экспорт Open confi&guration dialog - + Открыт&ь диалог конфигурации &Tile windows - + Р&асположить окна плиткой Tile windows &horizontally - + Распо&ложить окна по горизонтали Tile windows &vertically - + Располо&жить окна по вертикали &Cascade windows - + Ра&сположить окна каскадом Close selected &window - + &Закрыть выбранное окно Close all windows &but selected - + Закрыть &все окна, кроме выбранного Close &all windows - + Закрыть вс&е окна Re&store recently closed window - + Восста&новить последнее закрытое окно &Rename selected window - + Переи&меновать выбранное окно Report a &bug - + Сообщить об о&шибке Propose a new &feature - + Предложить новую &функцию &About - + О про&грамме &Licenses - + Ли&цензии Open home &page - + Открыть домашн&юю страницу Open fo&rum page - + Открыть страниц&у форума User &Manual - + Руководство пользовател&я SQLite &documentation - + &Документация по SQLite Bugs and feature &requests - + Оши&бки и предложения Check for &updates - + &Проверить обновления &Database menubar - + &База данных &Structure menubar - + &Структура &View menubar - + &Вид &Tools menubar - + &Инструменты &Help - + С&правка @@ -4437,7 +4437,7 @@ Please enter new, unique name, or press '%1' to abort the operation:MdiWindow Uncommited changes - Неподтверждённые изменения + Неподтверждённые изменения @@ -4471,7 +4471,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Open another tab - + Открыть дополнительную вкладку @@ -4495,7 +4495,7 @@ Please enter new, unique name, or press '%1' to abort the operation:MultiEditorBool Boolean - Логическое + Логическое @@ -4503,14 +4503,14 @@ Please enter new, unique name, or press '%1' to abort the operation: Boolean - Логическое + Логическое MultiEditorDate Date - Дата + Дата @@ -4518,14 +4518,14 @@ Please enter new, unique name, or press '%1' to abort the operation: Date - Дата + Дата MultiEditorDateTime Date & time - Дата и время + Дата и время @@ -4533,14 +4533,14 @@ Please enter new, unique name, or press '%1' to abort the operation: Date & time - Дата и время + Дата и время MultiEditorHex Hex - Шестнадцатеричное + Шестнадцатеричное @@ -4548,7 +4548,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Hex - Шестнадцатеричное + Шестнадцатеричное @@ -4556,7 +4556,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Number numeric multi editor tab name - Число + Число @@ -4565,14 +4565,14 @@ Please enter new, unique name, or press '%1' to abort the operation: Number numeric multi editor tab name - Число + Число MultiEditorText Text - Текст + Текст @@ -4615,14 +4615,14 @@ Please enter new, unique name, or press '%1' to abort the operation: Text - Текст + Текст MultiEditorTime Time - Время + Время @@ -4630,7 +4630,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Time - Время + Время @@ -4707,11 +4707,11 @@ Please enter new, unique name, or press '%1' to abort the operation: This application will be closed and the update installer will start to download and install all the updates. - + Приложение будет закрыто, и установщик обновлений начнёт загрузку и установку обновлений. Current version - Текущая версия + Текущая версия @@ -4730,7 +4730,7 @@ Please enter new, unique name, or press '%1' to abort the operation: The update will be automatically downloaded and installed. This will also restart application at the end. - Обновление будет автоматически загружено и установлено. В конце процесса приложение будет перезапущено. + Обновление будет автоматически загружено и установлено. В конце процесса приложение будет перезапущено. @@ -4902,7 +4902,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Copy cell(s) contents together with header to clipboard - + Копировать имя столбца и содержимое ячеек в буфер обмена @@ -5264,11 +5264,11 @@ Please enter new, unique name, or press '%1' to abort the operation: Reports history window - Окно истории отчётов + Окно истории отчётов Delete selected entry - Удалить выбранную запись + Удалить выбранную запись @@ -5318,7 +5318,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Delete selected SQL history entries - + Удалить выбранные записи из истории SQL-запросов @@ -5440,7 +5440,7 @@ Please enter new, unique name, or press '%1' to abort the operation:QuitConfirmDialog Uncommited changes - Неподтверждённые изменения + Неподтверждённые изменения @@ -5749,7 +5749,7 @@ find next This cell is not editable, because: %1 - Эта ячейка нередактируема, причина: %1 + Эта ячейка нередактируема, причина: %1 @@ -5761,7 +5761,7 @@ find next SqlQueryItemDelegate Cannot edit this cell. Details: %2 - Невозможно редактировать данную ячейку. Подробности: %2 + Невозможно редактировать данную ячейку. Подробности: %2 @@ -5804,11 +5804,11 @@ find next Uncommited data - Неподтверждённые данные + Неподтверждённые данные There are uncommited data changes. Do you want to proceed anyway? All uncommited changes will be lost. - Имеются неподтверждённые изменения данных. Вы действительно хотите продолжить? Все неподтверждённые изменения будут утеряны. + Имеются неподтверждённые изменения данных. Вы действительно хотите продолжить? Все неподтверждённые изменения будут утеряны. @@ -5822,7 +5822,7 @@ find next An error occurred while commiting the transaction: %1 - При завершении транзакции возникла ошибка: %1 + При завершении транзакции возникла ошибка: %1 @@ -5836,7 +5836,7 @@ find next An error occurred while commiting the data: %1 - При подтверждении данных произошла ошибка: %1 + При подтверждении данных произошла ошибка: %1 @@ -5861,7 +5861,7 @@ find next Number of rows per page was decreased to %1 due to number of columns (%2) in the data view. - + Количество строк на странице было уменьшено до %1 из-за большого количества столбцов (%2) в окне данных. @@ -5935,7 +5935,7 @@ find next Copy with headers - + Копировать с именами столбцов @@ -5980,7 +5980,7 @@ find next Show value in a viewer - + Показать значение в просмотрщике @@ -6010,12 +6010,12 @@ find next Trim pasted text? - + Обрезать вставленный текст? The pasted text contains leading or trailing white space. Trim it automatically? - + В начале либо конце вставленного текста находятся непечатаемые символы. Обрезать их автоматически? @@ -6027,7 +6027,7 @@ find next SqlTableModel Error while commiting new row: %1 - Ошибка при подтверждении новой строки: %1 + Ошибка при подтверждении новой строки: %1 @@ -6045,112 +6045,112 @@ find next Filter extensions - + Фильтр расширений Leave empty to use default function - + Оставьте пустым для использования функции по умолчанию Extension file - + Файл расширения Initialization function - + Инициализирующая функция Databases - Базы данных + Базы данных Register in all databases - Зарегистрировать во всех базах данных + Зарегистрировать во всех базах данных Register in following databases: - Зарегистрировать в следующих базах данных: + Зарегистрировать в следующих базах данных: Extension manager window has uncommitted modifications. - + В менеджере расширений имеются неподтверждённые изменения. Extension manager - + Менеджер расширений Commit all extension changes - + Подтвердить все изменения расширений Rollback all extension changes - + Откатить все изменения расширений Add new extension - + Добавить новое расширение Remove selected extension - + Удалить выбранное расширение Editing extensions manual - + Руководство по редактированию расширений File with given path does not exist or is not readable. - + Файл по указанному пути не существует или не читается. Unable to load extension: %1 - + Невозможно загрузить расширение: %1 Invalid initialization function name. Function name can contain only alpha-numeric characters and underscore. - + Некорректное имя инициализирующей функции. Имя функции может состоять только из английских букв, цифр и подчёркивания. Dynamic link libraries (*.dll);;All files (*) - + Динамически подключаемые библиотеки (*.dll);;Все файлы (*) Shared objects (*.so);;All files (*) - + Общие объекты (*.so);;Все файлы (*) Dynamic libraries (*.dylib);;All files (*) - + Динамические библиотеки (*.dylib);;Все файлы (*) All files (*) - Все файлы (*) + Все файлы (*) Open file - Открыть файл + Открыть файл @@ -6728,11 +6728,11 @@ Do you want to commit the structure, or do you want to go back to the structure Commited changes for table '%1' successfly. - Изменения в таблицу '%1' внесены успешно. + Изменения в таблицу '%1' внесены успешно. Commited changes for table '%1' (named before '%2') successfly. - Изменения в таблицу '%1' (предыдущее название '%2') внесены успешно. + Изменения в таблицу '%1' (предыдущее название '%2') внесены успешно. @@ -6757,7 +6757,7 @@ Do you want to commit the structure, or do you want to go back to the structure Autoincrement value for table '%1' has been reset successfly. - Сброс счётчика автоинкремента у таблицы '%1' успешно выполнен. + Сброс счётчика автоинкремента у таблицы '%1' успешно выполнен. @@ -6810,12 +6810,12 @@ Are you sure you want to create a table with blank name? Uncommited changes - Неподтверждённые изменения + Неподтверждённые изменения There are uncommited structure modifications. You cannot browse or edit data until you have table structure settled. Do you want to commit the structure, or do you want to go back to the structure tab? - Имеются неподтверждённые изменения структуры. Невозможно просматривать или редактировать данные, пока структура таблицы не подтверждена. + Имеются неподтверждённые изменения структуры. Невозможно просматривать или редактировать данные, пока структура таблицы не подтверждена. Подтвердить структуру таблицы или вернуться на вкладку структуры? @@ -6878,15 +6878,15 @@ Do you want to commit the structure, or do you want to go back to the structure Table window "%1" has uncommited structure modifications and data. - В окне таблицы "%1" имеются неподтверждённые изменения структуры и данных. + В окне таблицы "%1" имеются неподтверждённые изменения структуры и данных. Table window "%1" has uncommited data. - В окне таблицы "%1" имеются неподтверждённые изменения данных. + В окне таблицы "%1" имеются неподтверждённые изменения данных. Table window "%1" has uncommited structure modifications. - В окне таблицы "%1" имеются неподтверждённые изменения структуры. + В окне таблицы "%1" имеются неподтверждённые изменения структуры. @@ -7231,15 +7231,15 @@ Do you want to commit the structure, or do you want to go back to the structure View window "%1" has uncommited structure modifications and data. - В окне представления "%1" имеются неподтверждённые изменения структуры и данных. + В окне представления "%1" имеются неподтверждённые изменения структуры и данных. View window "%1" has uncommited data. - В окне представления "%1" имеются неподтверждённые изменения данных. + В окне представления "%1" имеются неподтверждённые изменения данных. View window "%1" has uncommited structure modifications. - В окне представления "%1" имеются неподтверждённые изменения структуры. + В окне представления "%1" имеются неподтверждённые изменения структуры. @@ -7248,12 +7248,12 @@ Do you want to commit the structure, or do you want to go back to the structure Uncommited changes - Неподтверждённые изменения + Неподтверждённые изменения There are uncommited structure modifications. You cannot browse or edit data until you have the view structure settled. Do you want to commit the structure, or do you want to go back to the structure tab? - Имеются неподтверждённые изменения структуры. Невозможно просматривать или редактировать данные, пока структура представления не подтверждена. + Имеются неподтверждённые изменения структуры. Невозможно просматривать или редактировать данные, пока структура представления не подтверждена. Подтвердить структуру представления или вернуться на вкладку структуры? @@ -7268,11 +7268,11 @@ Do you want to commit the structure, or do you want to go back to the structure Commited changes for view '%1' successfly. - Изменения в представление '%1' внесены успешно. + Изменения в представление '%1' внесены успешно. Commited changes for view '%1' (named before '%2') successfly. - Изменения в представление '%1' (предыдущее название '%2') внесены успешно. + Изменения в представление '%1' (предыдущее название '%2') внесены успешно. diff --git a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts index cbfc48e..8a046cd 100644 --- a/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts +++ b/SQLiteStudio3/guiSQLiteStudio/translations/guiSQLiteStudio_zh_CN.ts @@ -60,12 +60,12 @@ <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">Free, open-source, cross-platform SQLite database manager.<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">Author and active maintainer:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> - + <html><head/><body><p align="center"><span style=" font-size:11pt; font-weight:600;">SQLiteStudio v%1</span></p><p align="center">自由,开源,跨平台的 SQLite 数据库管理工具。<br/><a href="https://sqlitestudio.pl"><span style=" text-decoration: underline; color:#0000ff;">https://sqlitestudio.pl</span></a><br/></p><p align="center">%2<br/></p><p align="center">作者和活跃维护人:<br/>SalSoft (<a href="https://salsoft.com.pl"><span style=" text-decoration: underline; color:#0000ff;">https://salsoft.com.pl</span></a>)<br/></p></body></html> Qt version: - Qt版本: + Qt 版本: @@ -98,12 +98,12 @@ Query parameters - + 查询参数 Please provide values for query parameters - + 请提供一个值作为查询参数 @@ -318,7 +318,7 @@ Filter collations - 过滤器排序规则 + 筛选排序规则 @@ -353,7 +353,7 @@ Collations editor - 排序编辑器 + 排序规则编辑器 @@ -363,7 +363,7 @@ Rollback all collation changes - 回滚所有排序更改 + 回滚所有排序规则更改 @@ -398,7 +398,7 @@ Collations editor window has uncommitted modifications. - + 排序规则编辑器存在未提交的改动。 Collations editor window has uncommited modifications. @@ -418,12 +418,12 @@ Collation name: - 排序名称: + 排序规则名称: Named constraint: - 约束名: + 已命名的约束: @@ -451,17 +451,17 @@ Enter a default value expression. - 默认值表达式。 + 输入默认值表达式。 Invalid default value expression: %1. If you want to use simple string as value, remember to surround it with quote characters. - + 无效的默认值表达式:%1。如果你想使用简单的字符串作为值,记得用引号将其框起来。 Invalid default value expression. If you want to use simple string as value, remember to surround it with quote characters. - + 无效的默认值表达式。如果你想使用简单的字符串作为值,记得用引号将其框起来。 Invalid default value expression: %1 @@ -654,12 +654,12 @@ This constraint is not officially supported by SQLite 2, but it's okay to use it. - SQLite2没有官方支持该约束,但是可以使用。 + SQLite 2 没有官方支持该约束,但是可以使用。 Scale is not allowed for INTEGER PRIMARY KEY columns. - + 小数长度在 INTEGER PRIMARY KEY 类型字段中不被允许。 @@ -708,17 +708,17 @@ but it's okay to use it. Foreign table: - + 外部表: Foreign column: - + 外部字段: Reactions - + 响应 @@ -728,7 +728,7 @@ but it's okay to use it. Named constraint - + 已命名的约束 @@ -738,12 +738,12 @@ but it's okay to use it. Pick the foreign table. - + 选择一个外部表。 Pick the foreign column. - + 选择一个外部字段。 @@ -756,7 +756,7 @@ but it's okay to use it. Autoincrement - Autoincrement + Autoincrement @@ -766,17 +766,17 @@ but it's okay to use it. Named constraint: - + 已命名的约束: On conflict: - 冲突: + 当冲突时: Enter a name of the constraint. - + 输入约束名称 Autoincrement (only for %1 type columns) @@ -789,12 +789,12 @@ but it's okay to use it. Named constraint: - + 已命名的约束: On conflict: - 冲突: + 当冲突时: @@ -856,7 +856,7 @@ but it's okay to use it. Operator: %1 completer statusbar - + 操作符:%1 @@ -880,7 +880,7 @@ but it's okay to use it. Collation: %1 completer statusbar - + 排序规则:%1 @@ -955,7 +955,7 @@ but it's okay to use it. Database dialog window - + 数据库对话窗口 @@ -980,7 +980,7 @@ but it's okay to use it. Data browsing and editing - 流量和编辑数据 + 浏览和编辑数据 @@ -996,7 +996,7 @@ but it's okay to use it. Limit initial data column width to (in pixels): - 限制宽度(单位:像素): + 限制初始数据列宽度(单位:像素): @@ -1006,7 +1006,7 @@ but it's okay to use it. Show column and row details tooltip in data view - + 在数据视图中展示字段与行的细节 @@ -1016,7 +1016,7 @@ but it's okay to use it. Inserting new row in data grid - + 在网格视图中插入新行 @@ -1036,32 +1036,32 @@ but it's okay to use it. <p>When enabled, Table Windows will show up with the data tab, instead of the structure tab.</p> - + <p>启用后,表窗口将显示数据选项卡,而不是结构选项卡。</p> <p>When enabled the "Data" tab will be placed as first tab in every Table Window, instead of being at second place.</p> - + <p>启用后,“数据”选项卡将作为第一个选项卡放置在每个表窗口中,而不是位于第二位。</p> Place data tab as first tab in a Table Window - + 将数据作为表窗口的第一项 <p>When enabled, View Windows will show up with the data tab, instead of the structure tab.</p> - + <p>启用后,视图窗口将显示数据选项卡,而不是结构选项卡。</p> <p>When enabled the "Data" tab will be placed as first tab in every View Window, instead of being at second place.</p> - + <p>启用后,“数据”选项卡将作为第一个选项卡放置在每个视图窗口中,而不是位于第二个位置。</p> Place data tab as first tab in a View Window - + 将数据选项卡作为视图窗口的第一选项卡 @@ -1081,7 +1081,7 @@ but it's okay to use it. Schema editing - 架构编辑 + 架构编辑 @@ -1100,7 +1100,7 @@ but it's okay to use it. SQL queries - SQL查询 + SQL 查询 @@ -1146,7 +1146,7 @@ but it's okay to use it. Status Field - + 状态栏 @@ -1156,17 +1156,17 @@ but it's okay to use it. Always open Status panel when new message is printed - + 当有新信息被输出时,总是打开状态面板 Filter shortcuts by name or key combination - + 以名称或按键组合筛选快捷键 Action - + 操作 @@ -1187,17 +1187,17 @@ but it's okay to use it. Compact layout - + 紧凑布局 <p>Compact layout reduces all margins and spacing on the UI to minimum, making space for displaying more data. It makes the interface a little bit less aesthetic, but allows to display more data at once.</p> - + <p>紧凑布局会将 UI 中的边框与空白降低到最小值,然后用这些空白展示更多数据。这会使界面看起来有一点不美观,但是允许一次展示更多数据。</p> Use compact layout - + 使用紧凑布局 @@ -1218,7 +1218,7 @@ but it's okay to use it. Expand tables node when connected to a database - 当连接到数据库时,展开数据库节点。 + 当连接到数据库时,展开数据库节点 @@ -1253,7 +1253,7 @@ but it's okay to use it. Expand views node when connected to a database - + 当连接到数据库时,展开视图节点 @@ -1263,7 +1263,7 @@ but it's okay to use it. Sort objects (tables, indexes, triggers and views) alphabetically - + 按字母顺序排序对象(表,索引,触发器与视图) @@ -1284,7 +1284,7 @@ but it's okay to use it. Keep NULL value when entering empty value - + 当输入空值时保持 NULL 值 @@ -1294,12 +1294,12 @@ but it's okay to use it. Use DEFAULT value (if defined), when committing NULL value - + 当提交 NULL 值时使用 DEFAULT 值(如果已被定义) Table windows - + 表窗口 @@ -1309,7 +1309,7 @@ but it's okay to use it. View windows - + 视图窗口 @@ -1319,7 +1319,7 @@ but it's okay to use it. Don't show DDL preview dialog when committing schema changes - + 当提交 schema 更改时不要展示 DDL 预览对话框 @@ -1335,7 +1335,7 @@ but it's okay to use it. Main window dock areas - + 主窗口停靠区域 @@ -1350,7 +1350,7 @@ but it's okay to use it. Hide built-in plugins - + 隐藏内建插件 @@ -1380,7 +1380,7 @@ but it's okay to use it. SQL editor font - SQL编辑器字体 + SQL 编辑器字体 @@ -1405,7 +1405,7 @@ but it's okay to use it. SQL editor colors - SQL编辑器颜色 + SQL 编辑器颜色 @@ -1415,7 +1415,7 @@ but it's okay to use it. <p>SQL strings are enclosed with single quote characters.</p> - <p>单引号内的SQL字符串</p> + <p>单引号内的 SQL 字符串</p> @@ -1445,7 +1445,7 @@ but it's okay to use it. BLOB value foreground - BLOB值的颜色 + BLOB 值的颜色 @@ -1480,12 +1480,12 @@ but it's okay to use it. Valid objects foreground - + 合法对象的颜色 Data view colors - + 数据视图颜色 @@ -1495,7 +1495,7 @@ but it's okay to use it. Uncommitted data outline color - + 未提交数据的轮廓颜色 @@ -1505,12 +1505,12 @@ but it's okay to use it. Commit error outline color - + 提交错误的轮廓颜色 NULL value foreground - NULL值的颜色 + NULL 值的颜色 @@ -1535,7 +1535,7 @@ but it's okay to use it. Status field colors - + 状态栏颜色 @@ -1592,7 +1592,7 @@ but it's okay to use it. Conflicts: plugin details - + 冲突: @@ -1646,27 +1646,27 @@ but it's okay to use it. The condition - + 条件 Named constraint: - + 已命名的约束: On conflict - + 当冲突时 Enter a valid condition. - + 输入一个合法的条件。 Enter a name of the constraint. - + 输一个约束的名称。 @@ -1675,49 +1675,49 @@ but it's okay to use it. New constraint constraint dialog - 新约束 + 新约束 Create constraint dialog - + 创建 Edit constraint dialog window - 编辑约束 + 编辑约束 Apply constraint dialog - + 应用 Primary key table constraints - + 主键 Foreign key table constraints - + 外键 Unique table constraints - 唯一 + 唯一 Not NULL table constraints - 非空 + 非 NULL @@ -1729,13 +1729,13 @@ but it's okay to use it. Collate table constraints - 排序规则 + 排序规则 Default table constraints - 默认 + 默认 @@ -1744,37 +1744,37 @@ but it's okay to use it. Table table constraints - + Column (%1) table constraints - + 字段(%1) Scope table constraints - + 作用域 Type table constraints - 类型 + 类型 Details table constraints - 详情 + 详情 Name table constraints - 名称 + 名称 @@ -1782,7 +1782,7 @@ but it's okay to use it. SQLiteStudio CSS console - + SQLiteStudio CSS 控制台 @@ -1791,29 +1791,29 @@ but it's okay to use it. Filter data data view - + 筛选数据 Grid view - + 网格视图 Form view - + 表格视图 Refresh table data data view - + 刷新表数据 First page data view - 首页 + 第一页 @@ -1831,71 +1831,71 @@ but it's okay to use it. Last page data view - 末页 + 最后一页 Filter - + 筛选 Hit Enter key or press "Apply filter" button on toolbar to apply new value. - + 按下回车或点击工具栏上的应用筛选按钮来应用新值。 Show filter inputs per column data view - + 在每一个字段上展示筛选器输入 Apply filter data view - + 应用筛选 Commit changes for selected cells data view - + 提交选中单元格的更改 Rollback changes for selected cells data view - + 回滚选中单元格的修改 Show grid view of results sql editor - + 展示结果的网格视图 Show form view of results sql editor - + 展示结果的表格视图 Filter by text data view - + 以文本筛选 Filter by the Regular Expression data view - + 以正则表达式筛选 Filter by SQL expression data view - + 以 SQL 表达式筛选 @@ -1913,19 +1913,19 @@ but it's okay to use it. Place new rows above selected row data view - + 放置新行于选中行之上 Place new rows below selected row data view - + 放置新行于选中行之下 Place new rows at the end of the data view data view - + 放置新行于数据视图末尾 @@ -1969,7 +1969,7 @@ Browsing other pages will be possible after the row counting is done. This is the file that will be created as a result of the conversion. - + 此文件将会被创建,并作为转换的结果。 @@ -1979,7 +1979,7 @@ Browsing other pages will be possible after the row counting is done. Name of the new database: - + 新数据库的名称: @@ -1994,17 +1994,17 @@ Browsing other pages will be possible after the row counting is done. Enter valid and writable file path. - + 输入一个合法的且可写的文件的路径。 Entered file exists and will be overwritten. - + 输入一个存在的且可覆写的文件。 Enter a not empty, unique name (as in the list of databases on the left). - + 输入一个非空,唯一的名称(用于左侧的数据库列表) @@ -2091,13 +2091,13 @@ Browsing other pages will be possible after the row counting is done. Name (on the list) - + 名称(显示在列表中) <p>Enable this if you want the database to be stored in configuration file and restored every time SQLiteStudio is started.</p> aasfd - + <p>如果您想让这个数据库被存储在配置文件中并且在 SQLiteStudio 每次启动中被恢复,请勾选此项。</p> @@ -2112,17 +2112,17 @@ Browsing other pages will be possible after the row counting is done. Enter an unique database name. - + 请输入一个唯一的数据库名称。 This name is already in use. Please enter unique name. - + 此名称已被使用,请输入一个唯一的名称。 <p>Automatic name generation was disabled, because the name was edited manually. To restore automatic generation please erase contents of the name field.</p> - + <p>自动命名已被禁用,因为名称已被手动编辑。为了恢复自动命名,请删除名称中的所有内容。</p> @@ -2199,23 +2199,24 @@ Browsing other pages will be possible after the row counting is done. Delete objects - 删除对象 + 删除对象 Are you sure you want to delete following objects: %1 - + 您确认要删除以下对象吗: +%1 Cannot start transaction. Details: %1 - + 无法开始事务。详情:%1 Cannot commit transaction. Details: %1 - + 无法提交事务。详情:%1 @@ -2228,7 +2229,7 @@ Browsing other pages will be possible after the row counting is done. Filter by name - 过滤名 + 按名称过滤 @@ -2326,7 +2327,7 @@ Browsing other pages will be possible after the row counting is done. Populate table - 填充表 + 填充表 @@ -2336,7 +2337,7 @@ Browsing other pages will be possible after the row counting is done. Reset autoincrement sequence - 重设 autoincrement + 重设 autoincrement Create an index @@ -2415,117 +2416,117 @@ Browsing other pages will be possible after the row counting is done. &Add a database - + 添加数据库(&A) &Edit the database - + 编辑数据库(&E) &Remove the database - + 移除数据库(&R) &Connect to the database - + 连接到数据库(&C) &Disconnect from the database - + 断开数据库连接(&D) &Export the database - + 导出该数据库(&E) Con&vert database type - + 转换数据库类型(&V) Vac&uum - + 清理(&U) &Integrity check - + 检查完整性(&I) Create a &table - + 新建表(&T) Edit the t&able - + 编辑该表(&A) Delete the ta&ble - + 删除该表(&B) Create an &index - + 创建索引(&I) Edit the i&ndex - + 编辑该索引(&N) Delete the in&dex - + 删除该索引(&D) Create a trig&ger - + 创建触发器(&G) Edit the trigg&er - + 编辑该触发器(&E) Delete the trigge&r - + 删除该触发器(&R) Create a &view - + 创建视图(&V) Edit the v&iew - + 编辑该视图(&I) Delete the vi&ew - + 删除该视图(&E) &Refresh all database schemas - + 刷新全部数据库的结构(&R) Re&fresh selected database schema - + 刷新已选数据库的结构(&F) @@ -2536,12 +2537,12 @@ Browsing other pages will be possible after the row counting is done. Open file's directory - + 打开文件目录 Execute SQL from file - + 从文件执行 SQL @@ -2557,7 +2558,7 @@ Browsing other pages will be possible after the row counting is done. Generate query for table - + 生成对表的查询 @@ -2590,23 +2591,24 @@ All objects from this group will be moved to parent group. Are you sure you want to remove database '%1' from the list? - + 您确认要移除数据库 %1 吗? Are you sure you want to remove following databases from the list: %1 - + 您确认要移除以下存在于列表中的数据库吗: +%1 Remove database - + 移除数据库 Vacuum (%1) - + 清理(%1) @@ -2616,37 +2618,37 @@ All objects from this group will be moved to parent group. Are you sure you want to delete all data from table(s): %1? - + 您想要删除表:%1的所有数据吗? Could not execute SQL, because application has failed to start transaction: %1 - + 应用程序无法开始事务,因此无法执行 SQL:%1 Could not open file '%1' for reading: %2 - + 无法以写模式打开文件 %1:%2 Could not execute SQL, because application has failed to commit the transaction: %1 - + 应用程序无法提交事务,因此无法执行 SQL:%1 Finished executing %1 queries in %2 seconds. %3 were not executed due to errors. - + 在 %2 秒内完成执行 %1 个查询。 %3 个由于错误而没有被执行。 Finished executing %1 queries in %2 seconds. - + 在 %2 秒内完成 %1 个查询。 Could not execute SQL due to error. - + 由于错误,无法执行 SQL。 Delete database @@ -2684,17 +2686,17 @@ All objects from this group will be moved to parent group. Reset autoincrement - 重置autoincrement + 重置autoincrement Are you sure you want to reset autoincrement value for table '%1'? - 您确定要重设“%1”的autoincrement吗? + 您确定要重设“%1”的autoincrement吗? An error occurred while trying to reset autoincrement value for table '%1': %2 - 在重设表“%1”的autoincrement时出现错误:%2 + 在重设表“%1”的autoincrement时出现错误:%2 Autoincrement value for table '%1' has been reset successfly. @@ -2861,7 +2863,7 @@ All objects from this group will be moved to parent group. Referenced tables - 参照表 + 参照表 @@ -2878,7 +2880,8 @@ All objects from this group will be moved to parent group. Following object already exists in the target database. Please enter new, unique name, or press '%1' to abort the operation: - + 以下的对象已经存在于目标数据库中。 +请输入一个新的,唯一的名称,或按下 '%1' 终止操作: @@ -2913,7 +2916,7 @@ Please enter new, unique name, or press '%1' to abort the operation: DDL history - DDL历史 + DDL 历史 @@ -2942,7 +2945,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Query - + 查询 @@ -2963,7 +2966,7 @@ Please enter new, unique name, or press '%1' to abort the operation: SQL editor %1 - SQL编辑器 %1 + SQL 编辑器 %1 @@ -3046,12 +3049,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Query finished in %1 second(s). Rows affected: %2 - + 查询在 %1 秒内完成。影响的行数:%2 Query finished in %1 second(s). - + 查询在 %1 秒内完成。 @@ -3061,7 +3064,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Are you sure you want to erase the entire SQL execution history? This cannot be undone. - 确定要删除全部的SQL执行历史吗?删除后不能恢复。 + 确定要删除全部的 SQL 执行历史吗?删除后不能恢复。 @@ -3071,12 +3074,12 @@ Please enter new, unique name, or press '%1' to abort the operation: No database selected in the SQL editor. Cannot create a view for unknown database. - + 没有在 SQL 编辑器中选中的数据库。无法为未知数据库创建视图。 Editor window "%1" has uncommitted data. - + 编辑器“%1”里有未提交的数据库。 Editor window "%1" has uncommited data. @@ -3106,57 +3109,57 @@ Please enter new, unique name, or press '%1' to abort the operation: Execute SQL from file - + 从文件执行 SQL Input file - + 输入文件 Path to file - + 文件路径 Browse for file - + 浏览文件 Options - 选项 + 选项 File encoding - + 文件编码 Skip failing SQL statements - + 跳过失败的 SQL statements SQL scripts (*.sql);;All files (*) - SQL文件 (*.sql);;所有文件 (*) + SQL 脚本 (*.sql);;所有文件 (*) Execute SQL file - + 执行 SQL 文件 Please provide file to be executed. - + 请提供一个文件以供执行。 Provided file does not exist or cannot be read. - + 提供的文件不存在或无法读取。 @@ -3229,17 +3232,17 @@ Please enter new, unique name, or press '%1' to abort the operation: Note, that exporting table indexes and triggers may be unsupported by some output formats. - + 注意,某些输出格式可能不支持导出表索引与触发器。 Select database objects to export - + 选择数据库对象进行导出 Export data from tables - + 从表中导出数据 @@ -3332,42 +3335,42 @@ Please enter new, unique name, or press '%1' to abort the operation: Select at least one object to export. - + 至少选择一个对象进行导出。 You must provide a file name to export to. - + 您必须选择一个导出文件。 Path you provided is an existing directory. You cannot overwrite it. - + 您提供的路径是一个存在的目录,您不能覆写它。 The directory '%1' does not exist. - + 目录 '%1' 不存在。 The file '%1' exists and will be overwritten. - + 文件“%1”存在且将被覆写。 All files (*) - 所有文件 (*) + 所有文件 (*) Pick file to export to - + 选择一个导出文件 Internal error during export. This is a bug. Please report it. - + 导出时发生了内部错误,这是一个 Bug,请反馈它。 @@ -3375,32 +3378,32 @@ Please enter new, unique name, or press '%1' to abort the operation: Execution errors - + 执行错误 Following errors were encountered during execution of SQL statements from the file: - + 从文件执行 SQL statements 期间遇到以下错误: SQL - + SQL Error - 错误 + 错误 Statements that were executed successfully were commited. - + 成功执行的 Statements 已被提交。 Statements that were executed successfully were rolled back. - + 成功执行的 Statements 已被回滚。 @@ -3417,7 +3420,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Active SQL formatter plugin - 激活SQL语句格式化插件 + 激活 SQL 语句格式化插件 @@ -3426,13 +3429,13 @@ Please enter new, unique name, or press '%1' to abort the operation: Commit row form view - 提交 + 提交 Rollback row form view - 回滚 + 回滚 @@ -3462,7 +3465,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Insert new row form view - 新插入行 + 插入新行 @@ -3527,12 +3530,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Function implementation code: - + 函数实现代码: Final step implementation code: - + 最终一步实现代码: @@ -3612,7 +3615,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Per step code: - + 每一步的代码: @@ -3628,7 +3631,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Functions editor window has uncommitted modifications. - + 函数编辑器窗口有未提交的更改。 @@ -3745,7 +3748,7 @@ Please enter new, unique name, or press '%1' to abort the operation: On table: - + 在表: @@ -3765,12 +3768,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Column - 字段 + 字段 Collation - + 排序规则 @@ -3785,12 +3788,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Moves selected index column up in the order, making it more significant in the index. - + 向上移动选中的索引,使它在索引中变得更重要。 Moves selected index column down in the order, making it less significant in the index. - + 向下移动选中的索引,使它在索引中变得不重要。 @@ -3825,7 +3828,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Pick the table for the index. - + 为索引选择一个表。 @@ -3835,7 +3838,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Enter a valid condition. - + 输入一个合法的条件。 @@ -3865,7 +3868,7 @@ Please enter new, unique name, or press '%1' to abort the operation: An error occurred while executing SQL statements: %1 - 在执行SQL语句时发生了错误:%1 + 在执行 SQL 语句时发生了错误:%1 @@ -3908,7 +3911,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Invalid expression. - + 无效的表达式。 @@ -3929,7 +3932,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Database toolbar - 数据工具栏 + 数据库工具栏 @@ -3949,7 +3952,8 @@ Please enter new, unique name, or press '%1' to abort the operation: View toolbar - 查看工具栏 + 查看这个词,在后面的翻译中翻译起来,有些地方的语句极其不通顺,故使用视图代替之。而且根据其菜单结构,主要是排布窗口,控件,比起查看,视图更佳 + 视图工具栏 @@ -3969,12 +3973,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Running in debug mode. Press %1 or use 'Help / Open debug console' menu entry to open the debug console. - + 正在以调试模式运行。按下 %1 或使用 帮助/打开调试控制台 菜单来打开调试控制台。 Running in debug mode. Debug messages are printed to the standard output. - + 正在以调试模式运行。调试信息将会被输出在标准输出中。 @@ -4064,7 +4068,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Open CSS Console - 打开CSS控制台 + 打开 CSS 控制台 Report a bug @@ -4139,166 +4143,166 @@ Please enter new, unique name, or press '%1' to abort the operation: Open SQL &editor - + 打开 SQL 编辑器(&E) Open DDL &history - + 打开数据库定义(DDL)历史(&H) Open SQL &functions editor - + 打开 SQL 函数编辑器(&F) Open &collations editor - + 打开排序规则编辑器(&C) Open ex&tension manager - + 打开扩展管理器(&T) &Import - + 导入(&I) E&xport - + 导出(&X) Open confi&guration dialog - + 打开配置对话框(&G) &Tile windows - + 平铺窗口(&T) Tile windows &horizontally - + 水平排列窗口(&H) Tile windows &vertically - + 垂直排列窗口(&V) &Cascade windows - + 层叠窗口(&C) Close selected &window - + 关闭当前窗口(&W) Close all windows &but selected - + 关闭其它窗口(&B) Close &all windows - + 关闭全部窗口(&A) Re&store recently closed window - + 恢复最近关闭的窗口(&S) &Rename selected window - + 重命名当前窗口(&R) Report a &bug - + 提交 Bug (&B) Propose a new &feature - + 提交新功能建议(&F) &About - + 关于(&A) &Licenses - + 许可(&L) Open home &page - + 访问主页(&P) Open fo&rum page - + 访问论坛(&R) User &Manual - + 用户手册(&M) SQLite &documentation - + SQLite 文档(&D) Bugs and feature &requests - + 提交 Bug 与请求新功能(&R) Check for &updates - + 检查更新(&U) &Database menubar - + 数据库(&D) &Structure menubar - + 结构(&S) &View menubar - + 视图(&V) &Tools menubar - + 工具(&T) &Help - + 帮助(&H) @@ -4349,7 +4353,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Could not add database %1 to list. - 未能将数据%1添加到列表 + 未能将数据 %1 添加到列表 @@ -4361,7 +4365,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Uncommitted changes - + 未提交的更改 @@ -4385,17 +4389,17 @@ Please enter new, unique name, or press '%1' to abort the operation: Configure editors for this data type - + 为数据类型设置编辑器 Open another tab - + 打开另一个选项卡 Data editor plugin '%1' not loaded, while it is defined for editing '%1' data type. - + 数据编辑器插件 '%1' 没有被加载,尽管它被定义为编辑 '%1' 数据类型。 @@ -4422,7 +4426,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Boolean - 布尔 + 布尔 @@ -4437,7 +4441,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Date - 日期 + 日期 @@ -4452,7 +4456,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Date & time - 日期和时间 + 日期和时间 @@ -4467,7 +4471,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Hex - 十六进制 + 十六进制 @@ -4626,7 +4630,7 @@ Please enter new, unique name, or press '%1' to abort the operation: This application will be closed and the update installer will start to download and install all the updates. - + 此应用将会被关闭,然后更新安装程序将会启动,下载并且安装所有更新。 Current version @@ -4667,12 +4671,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Populating configuration - 配置填充 + 配置填充 Configuring <b>%1</b> for column <b>%2</b> - 给字段 <b>%2</b> 配置 <b>%1</b> + 给字段 <b>%2</b> 配置 <b>%1</b> @@ -4700,7 +4704,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Number of rows to populate: - 填充的行数: + 填充的行数: @@ -4811,32 +4815,32 @@ Please enter new, unique name, or press '%1' to abort the operation: Data grid view - + 数据网格视图 Copy cell(s) contents to clipboard - + 复制单元格内容至剪贴板 Copy cell(s) contents together with header to clipboard - + 复制单元格内容与表头至剪贴板 Paste cell(s) contents from clipboard - + 从剪贴板粘贴单元格数据 Set empty value to selected cell(s) - + 将选中的单元格设置为空值 Set NULL value to selected cell(s) - + 将选中的单元格设置为 NULL @@ -4851,12 +4855,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Delete selected data row - + 删除选中的数据行 Insert new data row - + 插入新数据行 @@ -4866,47 +4870,47 @@ Please enter new, unique name, or press '%1' to abort the operation: Total pages available: %1 - + 可用页数:%1 Total rows loaded: %1 - + 已加载行数:%1 Data view (both grid and form) - + 数据视图(网格 + 表格) Refresh data - + 刷新数据 Switch to grid view of the data - + 切换至数据的网格视图 Switch to form view of the data - + 切换至数据的表格视图 Database list - 数据库列表 + 数据库列表 Delete selected item - + 删除选中项 Clear filter contents - + 清除筛选器内容 @@ -4921,134 +4925,134 @@ Please enter new, unique name, or press '%1' to abort the operation: Add database - + 添加数据库 Select all items - + 选中所有项 Copy selected item(s) - + 复制选中项 Paste from clipboard - + 从剪贴板粘贴 Tables - + Indexes - + 索引 Triggers - 触发器 + 触发器 Views - + 视图 Columns - 字段 + 字段 Data form view - + 数据表格视图 Commit changes for current row - + 提交当前行的更改 Rollback changes for current row - + 回滚当前行的更改 Go to first row on current page - + 前往当前页的第一行 Go to next row - + 前往下一行 Go to previous row - + 前往上一行 Go to last row on current page - + 前往当前页的最后一行 Insert new row - 新插入行 + 插入新行 Delete current row - 删除当前行 + 删除当前行 Main window - + 主窗口 Open SQL editor - 打开SQL编辑器 + 打开 SQL 编辑器 Previous window - 上一个窗口 + 上一个窗口 Next window - 下一个窗口 + 下一个窗口 Hide status area - + 隐藏状态栏 Open configuration dialog - 打开配置对话框 + 打开配置对话框 Open Debug Console - 打开调试终端 + 打开调试终端 Open CSS Console - 打开CSS控制台 + 打开 CSS 控制台 @@ -5059,31 +5063,31 @@ Please enter new, unique name, or press '%1' to abort the operation: Cut selected text - + 剪切选中文本 Copy selected text - + 复制选中文本 Delete selected text - + 删除选中文本 Undo - 撤销 + 撤销 Redo - 恢复 + 重做 @@ -5093,17 +5097,17 @@ Please enter new, unique name, or press '%1' to abort the operation: Select whole editor contents - + 选中整个编辑器的内容 Save contents into a file - + 将内容保存至文件 Load contents from a file - + 从文件加载内容 @@ -5128,17 +5132,17 @@ Please enter new, unique name, or press '%1' to abort the operation: Delete current line - + 删除当前行 Request code assistant - + 请求代码辅助 Format contents - + 格式化内容 @@ -5168,18 +5172,18 @@ Please enter new, unique name, or press '%1' to abort the operation: All SQLite databases - + 所有 SQLite 数据库 All files - + 所有文件 Database file - + 数据库文件 Delete selected entry @@ -5188,7 +5192,7 @@ Please enter new, unique name, or press '%1' to abort the operation: SQL editor window - + SQL 编辑器窗口 @@ -5213,12 +5217,12 @@ Please enter new, unique name, or press '%1' to abort the operation: Go to next editor tab - + 前往下一编辑器选项卡 Go to previous editor tab - + 前往上一编辑器选项卡 @@ -5238,27 +5242,27 @@ Please enter new, unique name, or press '%1' to abort the operation: Table window - + 表窗口 Refresh table structure - + 刷新表结构 Add new column - + 添加新字段 Edit selected column - + 编辑选中字段 Delete selected column - + 删除选中字段 @@ -5268,77 +5272,77 @@ Please enter new, unique name, or press '%1' to abort the operation: Import data to the table - + 导入数据至表中 Add new table constraint - + 添加新的表约束 Edit selected table constraint - + 编辑选中的表约束 Delete selected table constraint - + 删除选中的表约束 Refresh table index list - + 刷新表索引列表 Add new index - + 添加新索引 Edit selected index - + 编辑选中索引 Delete selected index - + 删除选中索引 Refresh table trigger list - + 刷新表触发器列表 Add new trigger - + 添加新触发器 Edit selected trigger - + 编辑选中触发器 Delete selected trigger - + 删除选中触发器 Go to next tab - + 前往下一选项卡 Go to previous tab - + 前往上一选项卡 @@ -5348,7 +5352,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Refresh view trigger list - + 刷新视图触发器列表 @@ -5360,7 +5364,7 @@ Please enter new, unique name, or press '%1' to abort the operation: Uncommitted changes - + 未提交的更改 @@ -5390,7 +5394,7 @@ Following items are pending: Search backwards - + 反向搜索 @@ -5424,7 +5428,7 @@ find next Sort by columns - + 按字段排序 @@ -5441,17 +5445,17 @@ find next Sort by: %1 - + 按:%1 排序 Move column up - + 上移字段 Move column down - + 下移字段 @@ -5520,7 +5524,7 @@ find next Select file to save SQL sql editor - + 选择 SQL 要保存到的文件 @@ -5591,7 +5595,7 @@ find next Saved SQL contents to file: %1 - + 保存 SQL 内容至文件:%1 @@ -5611,12 +5615,12 @@ find next Could not open file '%1' for writing: %2 - + 无法以写模式打开文件 %1:%2 SQL scripts (*.sql);;All files (*) - SQL文件 (*.sql);;所有文件 (*) + SQL文件 (*.sql);;所有文件 (*) @@ -5626,7 +5630,7 @@ find next Could not open file '%1' for reading: %2 - + 无法以读模式打开文件 %1:%2 @@ -5671,7 +5675,7 @@ find next The row is marked for deletion. - + 该行被标记为删除。 @@ -5680,7 +5684,7 @@ find next Cannot edit this cell. Details: %1 - + 无法编辑此单元格。详情:%1 @@ -5705,7 +5709,7 @@ find next Only one query can be executed simultaneously. - + 只允许同时执行一条查询。 @@ -5730,7 +5734,7 @@ find next Uncommitted data - + 未提交的数据 @@ -5740,12 +5744,12 @@ find next An error occurred while committing the transaction: %1 - + 在提交事务时发生错误:%1 An error occurred while committing the data: %1 - + 在提交数据时发生错误:%1 @@ -5756,12 +5760,12 @@ find next Error while executing SQL query on database '%1': %2 - + 在数据库“%1”执行 SQL 查询时发生错误:%2 Error while loading query results: %1 - + 在加载查询结果时出错:%1 @@ -5814,7 +5818,7 @@ find next Edit value in editor - + 在编辑器中编辑数值 @@ -5824,7 +5828,7 @@ find next Copy with headers - + 带表头复制 @@ -5834,12 +5838,12 @@ find next Commit selected cells - + 提交选中单元格 Rollback selected cells - + 回滚选中单元格 @@ -5889,7 +5893,7 @@ find next table '%1' - + 表“%1” @@ -5899,12 +5903,12 @@ find next Trim pasted text? - + 移除粘贴文本两端的空格? The pasted text contains leading or trailing white space. Trim it automatically? - + 粘贴的文本两端含有空格。自动移除? @@ -5921,7 +5925,7 @@ find next Error while committing new row: %1 - + 提交新行时发生了错误:%1 @@ -5934,72 +5938,72 @@ find next Filter extensions - + 筛选扩展 Leave empty to use default function - + 保持空白将使用默认函数 Extension file - + 扩展文件 Initialization function - + 初始化函数 Databases - 数据库 + 数据库 Register in all databases - 在所有数据库中注册 + 在所有数据库中注册 Register in following databases: - 在下列数据库中注册: + 在下列数据库中注册: Extension manager window has uncommitted modifications. - + 扩展管理窗口有未提交的更改。 Extension manager - + 扩展管理器 Commit all extension changes - + 提交所有扩展改变 Rollback all extension changes - + 回滚所有扩展改变 Add new extension - + 添加新的扩展 Remove selected extension - + 移除选中的扩展 Editing extensions manual - + 手动编辑扩展 @@ -6009,7 +6013,7 @@ find next Unable to load extension: %1 - + 无法加载扩展:%1 @@ -6019,7 +6023,7 @@ find next Dynamic link libraries (*.dll);;All files (*) - + 动态链接库 (*.dll);;所有文件 (*) @@ -6029,17 +6033,17 @@ find next Dynamic libraries (*.dylib);;All files (*) - + 动态库 (*.dylib);;所有文件 (*) All files (*) - 所有文件 (*) + 所有文件 (*) Open file - 打开文件 + 打开文件 @@ -6086,33 +6090,34 @@ find next Foreign table: - + 外部表: SQLite 2 does not support foreign keys officially, but it's okay to use them anyway. - + SQLite 2 不正式支持外键, +但总之,可以使用外键。 Columns - 字段 + 字段 Local column - + 本地字段 Foreign column - + 外部字段 Reactions - + 响应 @@ -6127,33 +6132,33 @@ but it's okay to use them anyway. Constraint name - 约束名称 + 约束名称 Pick the foreign column. - + 选择一个外部字段。 Pick the foreign table. - + 选择一个外部表。 Select at least one foreign column. - + 请至少选择一个外部字段。 Enter a name of the constraint. - + 输入一个约束的名称。 Foreign column table constraints - + 外部字段 @@ -6171,7 +6176,7 @@ but it's okay to use them anyway. Collation - + 排序规则 @@ -6186,7 +6191,7 @@ but it's okay to use them anyway. Autoincrement - Autoincrement + Autoincrement @@ -6196,18 +6201,18 @@ but it's okay to use them anyway. Constraint name - 约束名称 + 约束名称 On conflict - + 当冲突时 Collate table constraints - 排序规则 + 排序规则 @@ -6223,7 +6228,7 @@ but it's okay to use them anyway. Enter a name of the constraint. - + 输入一个约束的名称。 @@ -6232,33 +6237,33 @@ but it's okay to use them anyway. Name table structure columns - 名称 + 名称 Data type table structure columns - + 数据类型 Primary Key table structure columns - + 主键 Foreign Key table structure columns - + 外键 Unique table structure columns - 唯一 + 唯一 @@ -6271,19 +6276,19 @@ Key Not NULL table structure columns - + 非 NULL Collate table structure columns - 排序规则 + 排序规则 Default value table structure columns - + 默认值 @@ -6291,12 +6296,12 @@ NULL Structure - 结构 + 结构 Table name: - + 表名: @@ -6312,12 +6317,12 @@ NULL Indexes - + 触发器 Triggers - 触发器 + 触发器 @@ -6328,13 +6333,13 @@ NULL Export table table window - + 导出表 Import data to table table window - + 导入数据至表 @@ -6346,50 +6351,50 @@ NULL Refresh structure table window - + 刷新结构 Commit structure changes table window - + 提交结构修改 Rollback structure changes table window - + 回滚结构改变 Add column table window - + 添加字段 Edit column table window - + 编辑字段 Delete column table window - + 删除字段 Move column up table window - + 向上移动字段 Move column down table window - + 向下移动字段 @@ -6407,49 +6412,49 @@ NULL Add table constraint table window - + 添加表约束条件 Edit table constraint table window - + 编辑表约束 Delete table constraint table window - + 删除表约束 Move table constraint up table window - + 向上移动表约束 Move table constraint down table window - + 向下一移动表约束 Add table primary key table window - + 添加主键 Add table foreign key table window - + 添加外键 Add table unique constraint table window - + 添加表唯一约束 @@ -6461,19 +6466,19 @@ NULL Refresh index list table window - + 刷新索引列表 Create index table window - + 创建索引 Edit index table window - + 编辑索引 @@ -6485,19 +6490,19 @@ NULL Refresh trigger list table window - + 刷新触发器列表 Create trigger table window - + 创建触发器 Edit trigger table window - + 编辑触发器 @@ -6509,7 +6514,7 @@ NULL Are you sure you want to delete column '%1'? table window - + 您确定要删除字段“%1”吗? @@ -6527,12 +6532,12 @@ Would you like to proceed? Could not load data for table %1. Error details: %2 - + 无法加载表 %1 的数据。错误详情:%2 Could not process the %1 table correctly. Unable to open a table window. - + 无法正确处理表 %1。无法打开表窗口。 @@ -6558,12 +6563,12 @@ Would you like to proceed? New table %1 - + 新表 %1 Committed changes for table '%1' successfully. - + 成功提交表 '%1' 的修改。 @@ -6573,12 +6578,12 @@ Would you like to proceed? Autoincrement value for table '%1' has been reset successfully. - + 表“%1”的auincrement重设成功。 Uncommitted changes - + 未提交的更改 @@ -6589,23 +6594,23 @@ Do you want to commit the structure, or do you want to go back to the structure Table window "%1" has uncommitted structure modifications and data. - + 表窗口“%1”有未提交的结构更改与数据。 Table window "%1" has uncommitted data. - + 表窗口“%1”有未提交的数据。 Table window "%1" has uncommitted structure modifications. - + 表窗口“%1”有未提交的结构更改。 Could not commit table structure. Error message: %1 table window - + 无法提交表结构。错误信息:%1 @@ -6640,7 +6645,7 @@ Are you sure you want to create a table with blank name? Cannot create a table without at least one column. - + 无法创建没有任何字段的表。 @@ -6656,7 +6661,7 @@ Are you sure you want to create a table with blank name? Are you sure you want to delete table constraint '%1'? table window - + 您确定要删除表约束“%1”吗? @@ -6681,24 +6686,24 @@ Are you sure you want to create a table with blank name? Go back to structure tab - + 反汇结构选项卡 Commit modifications and browse data. - + 提交修改并浏览数据。 Name table window indexes - 名称 + 名称 Unique table window indexes - 唯一 + 唯一 @@ -6716,19 +6721,19 @@ Are you sure you want to create a table with blank name? Name table window triggers - 名称 + 名称 Event table window triggers - + 事件 Condition table window triggers - + 条件 @@ -6752,12 +6757,12 @@ Are you sure you want to create a table with blank name? Select all - 全选 + 全选 Deselect all - 全不选 + 全不选 @@ -6766,17 +6771,17 @@ Are you sure you want to create a table with blank name? Trigger - + 触发器 On table: - + 在表: Action: - + 操作: @@ -6787,22 +6792,22 @@ Are you sure you want to create a table with blank name? Pre-condition: - + 前提条件: The scope is still not fully supported by the SQLite database. - + 作用域仍没有被 SQLite 数据库完整支持。 Trigger name: - + 触发器名称: When: - + 当: @@ -6812,12 +6817,12 @@ Are you sure you want to create a table with blank name? Scope: - + 作用域: Code: - + 代码: @@ -6827,12 +6832,12 @@ Are you sure you want to create a table with blank name? DDL - DDL + DDL On view: - + 在视图: @@ -6842,24 +6847,24 @@ Are you sure you want to create a table with blank name? Enter a valid condition. - + 输入一个合法的条件。 Enter a valid trigger code. - + 输入合法的触发器代码。 Error trigger dialog - 错误 + 错误 An error occurred while executing SQL statements: %1 - 在执行SQL语句“%1”时发生了错误。 + 在执行SQL语句“%1”时发生了错误 @@ -6895,12 +6900,12 @@ Are you sure you want to create a table with blank name? View name: - + 视图名称: Output column names - + 输出字段名称 @@ -6916,7 +6921,7 @@ Are you sure you want to create a table with blank name? DDL - DDL + DDL @@ -6932,36 +6937,36 @@ Are you sure you want to create a table with blank name? Could not restore window '%1', because database %2 could not be open. - + 无法恢复窗口“%1”,因为数据库 %2 没有被打开。 Could not restore window '%1', because the view %2 doesn't exist in the database %3. - + 无法恢复窗口“%1”,因为视图 %2 不存在于数据库 %3 中。 New view %1 - + 新视图 %1 Refresh the view view window - + 刷新视图 Commit the view changes view window - + 提交视图更改 Rollback the view changes view window - + 回滚视图改变 @@ -6977,75 +6982,75 @@ Are you sure you want to create a table with blank name? Add column view window - + 添加字段 Edit column view window - + 编辑字段 Delete column view window - + 删除字段 Move column up view window - + 向上移动字段 Move column down view window - + 向下移动字段 Refresh trigger list view window - + 刷新触发器列表 Create new trigger view window - + 创建新触发器 Edit selected trigger view window - + 编辑选中的触发器 Delete selected trigger view window - + 删除选中的触发器 View window "%1" has uncommitted structure modifications and data. - + 视图“%1”有未提交的结构更改和数据。 View window "%1" has uncommitted data. - + 视图“%1”有未提交的数据。 View window "%1" has uncommitted structure modifications. - + 视图“%1”有未提交的结构更改。 Uncommitted changes - + 未提交的更改 @@ -7056,7 +7061,7 @@ Do you want to commit the structure, or do you want to go back to the structure Committed changes for view '%1' successfully. - + 成功提交视图“%1”的更改。 @@ -7066,7 +7071,7 @@ Do you want to commit the structure, or do you want to go back to the structure Could not load data for view %1. Error details: %2 - + 无法加载视图 %1 的数据。错误详情:%2 Uncommited changes @@ -7075,18 +7080,18 @@ Do you want to commit the structure, or do you want to go back to the structure Go back to structure tab - + 回到结构选项卡 Commit modifications and browse data. - + 提交更改并浏览数据。 Could not commit view changes. Error message: %1 view window - + 无法提交视图更改。错误信息:%1 @@ -7096,7 +7101,7 @@ Do you want to commit the structure, or do you want to go back to the structure Currently defined columns will be overriden. Do you want to continue? - + 当前定义的字段将会被覆写,您要继续吗? @@ -7119,18 +7124,18 @@ Do you want to commit the structure, or do you want to go back to the structure Condition view window triggers - + 条件 Details table window triggers - 详情 + 详情 Could not process the %1 view correctly. Unable to open a view window. - + 无法正确处理视图 %1。无法打开视图窗口。 @@ -7170,7 +7175,7 @@ Would you like to proceed? View modification view window - + 视图更改 @@ -7178,7 +7183,7 @@ Would you like to proceed? Interrupt - + 中断 diff --git a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h index 0f2d17c..8f16a1b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiconfig.h +++ b/SQLiteStudio3/guiSQLiteStudio/uiconfig.h @@ -32,37 +32,6 @@ CFG_CATEGORIES(Ui, CFG_ENTRY(QFont, StatusField, &Cfg::getDefaultItemViewFont) ) - CFG_CATEGORY(Colors, - CFG_ENTRY(QColor, SqlEditorParenthesisBg, Qt::green) - CFG_ENTRY(QColor, SqlEditorCurrentLineBg, QColor(Qt::cyan).lighter(190)) - CFG_ENTRY(QColor, SqlEditorLineNumAreaBg, QColor(Qt::lightGray).lighter(120)) - CFG_ENTRY(QColor, SqlEditorValidObject, Qt::blue) - CFG_ENTRY(QColor, SqlEditorForeground, Qt::black) - CFG_ENTRY(QColor, SqlEditorStringFg, Qt::darkGreen) - CFG_ENTRY(QColor, SqlEditorKeywordFg, Qt::black) - CFG_ENTRY(QColor, SqlEditorBindParamFg, Qt::darkMagenta) - CFG_ENTRY(QColor, SqlEditorBlobFg, Qt::darkCyan) - CFG_ENTRY(QColor, SqlEditorCommentFg, Qt::darkGray) - CFG_ENTRY(QColor, SqlEditorNumberFg, Qt::darkBlue) - CFG_ENTRY(QColor, DataUncommittedError, Qt::red) - CFG_ENTRY(QColor, DataUncommitted, Qt::blue) - CFG_ENTRY(QColor, DataNullFg, Qt::gray) - CFG_ENTRY(QColor, DataDeletedBg, Qt::gray) - CFG_ENTRY(QColor, DbTreeLabelsFg, Qt::blue) - CFG_ENTRY(QColor, StatusFieldInfoFg, Qt::darkBlue) - CFG_ENTRY(QColor, StatusFieldWarnFg, Qt::black) - CFG_ENTRY(QColor, StatusFieldErrorFg, Qt::red) - CFG_ENTRY(QColor, JavaScriptFg, "#000000") - CFG_ENTRY(QColor, JavaScriptComment, "#808080") - CFG_ENTRY(QColor, JavaScriptNumber, "#008000") - CFG_ENTRY(QColor, JavaScriptString, "#800000") - CFG_ENTRY(QColor, JavaScriptOperator, "#808000") - CFG_ENTRY(QColor, JavaScriptIdentifier, "#000020") - CFG_ENTRY(QColor, JavaScriptKeyword, "#000080") - CFG_ENTRY(QColor, JavaScriptBuiltIn, "#008080") - CFG_ENTRY(QColor, JavaScriptMarker, "#ffff00") - ) - CFG_CATEGORY(DbList, ) @@ -80,8 +49,10 @@ CFG_CATEGORIES(Ui, CFG_ENTRY(bool, ShowRegularTableLabels, false) CFG_ENTRY(bool, ShowVirtualTableLabels, true) CFG_ENTRY(int, NumberOfRowsPerPage, 1000) + CFG_ENTRY(bool, LimitRowsForManyColumns, true) CFG_ENTRY(QString, Style, &Cfg::getStyleDefaultValue) CFG_ENTRY(Cfg::Session, Session, Cfg::Session()) + CFG_ENTRY(bool, AllowMultipleSessions, false) CFG_ENTRY(bool, RestoreSession, true) CFG_ENTRY(bool, DontShowDdlPreview, false) CFG_ENTRY(bool, OpenTablesOnData, false) diff --git a/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp b/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp index 72dde94..fbdc4b3 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/uiutils.cpp @@ -2,6 +2,7 @@ #include "services/config.h" #include "common/widgetstateindicator.h" #include "common/utils.h" +#include "uiconfig.h" #include #include #include @@ -14,6 +15,8 @@ #include #include #include +#include +#include const QStringList pageSizes = { "A4", "B5", "Letter", "Legal", "Executive", "A0", "A1", "A2", "A3", "A5", "A6", "A7", "A8", "A9", "B0", "B1", @@ -29,9 +32,8 @@ QString getDbPath(bool newFileMode, const QString &startWith) dir = CFG->get("dialogCache", "lastDbDir").toString(); QStringList filters; - filters += QObject::tr("All SQLite databases")+" (*.db *.sdb *.sqlite *.db3 *.s3db *.sqlite3 *.sl3 *.db2 *.s2db *.sqlite2 *.sl2)"; + filters += QObject::tr("All SQLite databases")+" (*.db *.sdb *.sqlite *.db3 *.s3db *.sqlite3 *.sl3)"; filters += "SQLite3 (*.db3 *.s3db *.sqlite3 *.sl3)"; - filters += "SQLite2 (*.db2 *.s2db *.sqlite2 *.sl2)"; filters += QObject::tr("All files")+" (*)"; QString filter = filters.join(";;"); @@ -87,7 +89,7 @@ QString convertPageSize(QPagedPaintDevice::PageSize size) if (idx < 0 || idx >= pageSizesSize) { qDebug() << "Asked to convertPageSize() with page side enum value out of range:" << idx; - return QString::null; + return QString(); } return pageSizes[idx]; @@ -116,10 +118,31 @@ QPixmap addOpacity(const QPixmap& input, float opacity) void limitDialogWidth(QDialog* dialog) { - dialog->setMaximumWidth(QApplication::desktop()->availableGeometry().width()); + dialog->setMaximumWidth(QGuiApplication::primaryScreen()->availableGeometry().width()); } void fixTextCursorSelectedText(QString& text) { text.replace("\u2029", "\n"); } + +QColor styleSyntaxStringColor() +{ + static const QColor stdAltColor = QColor(Qt::green); + if (QApplication::style()->standardPalette().text().color().lightness() >= 128) + return stdAltColor.lighter(); + else + return stdAltColor.darker(); +} + +QBrush styleEditorLineColor() +{ + QPalette palette = QApplication::style()->standardPalette(); + if (CFG_UI.General.Style.get().toLower() != "macintosh") + return palette.alternateBase(); + + if (palette.base().color().lightness() < 128) + return QBrush(palette.alternateBase().color().darker(300)); + + return palette.alternateBase(); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/uiutils.h b/SQLiteStudio3/guiSQLiteStudio/uiutils.h index 455d97c..8163cd1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/uiutils.h +++ b/SQLiteStudio3/guiSQLiteStudio/uiutils.h @@ -7,7 +7,7 @@ class QWidget; -GUI_API_EXPORT QString getDbPath(bool newFileMode, const QString& startWith = QString::null); +GUI_API_EXPORT QString getDbPath(bool newFileMode, const QString& startWith = QString()); GUI_API_EXPORT void setValidState(QWidget* widget, bool valid, const QString& message = QString()); GUI_API_EXPORT void setValidStateWihtTooltip(QWidget* widget, const QString& tooltip, bool valid, const QString& message = QString()); GUI_API_EXPORT void setValidStateWarning(QWidget* widget, const QString& warning); @@ -19,6 +19,8 @@ GUI_API_EXPORT QPagedPaintDevice::PageSize convertPageSize(const QString& size); GUI_API_EXPORT QPixmap addOpacity(const QPixmap& input, float opacity); GUI_API_EXPORT void limitDialogWidth(QDialog* dialog); GUI_API_EXPORT void fixTextCursorSelectedText(QString& text); +GUI_API_EXPORT QColor styleSyntaxStringColor(); +GUI_API_EXPORT QBrush styleEditorLineColor(); #define UI_PROP_COLUMN "column_name" diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp index 5d36683..5cf5be4 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp @@ -1,6 +1,7 @@ #include "collationseditor.h" #include "ui_collationseditor.h" #include "common/unused.h" +#include "common/compatibility.h" #include "selectabledbmodel.h" #include "dbtree/dbtree.h" #include "dbtree/dbtreemodel.h" @@ -163,9 +164,9 @@ void CollationsEditor::collationSelected(int row) void CollationsEditor::clearEdits() { - ui->nameEdit->setText(QString::null); - ui->codeEdit->setPlainText(QString::null); - ui->langCombo->setCurrentText(QString::null); + ui->nameEdit->setText(QString()); + ui->codeEdit->setPlainText(QString()); + ui->langCombo->setCurrentText(QString()); ui->allDatabasesRadio->setChecked(true); ui->langCombo->setCurrentIndex(-1); } @@ -350,7 +351,7 @@ void CollationsEditor::updateModified() bool codeDiff = model->getCode(row) != ui->codeEdit->toPlainText(); bool langDiff = model->getLang(row) != ui->langCombo->currentText(); bool allDatabasesDiff = model->getAllDatabases(row) != ui->allDatabasesRadio->isChecked(); - bool dbDiff = getCurrentDatabases().toSet() != model->getDatabases(row).toSet(); // QSet to ignore order + bool dbDiff = toSet(getCurrentDatabases()) != toSet(model->getDatabases(row)); // QSet to ignore order currentModified = (nameDiff || codeDiff || langDiff || allDatabasesDiff || dbDiff); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.cpp index 1144fda..becb060 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.cpp @@ -82,7 +82,7 @@ QVariant ConstraintTabModel::data(SqliteCreateTable::Constraint* constr, int col case Columns::NAME: { if (role == Qt::DisplayRole) - return stripObjName(constr->name, createTable->dialect); + return stripObjName(constr->name); break; } @@ -124,7 +124,7 @@ QVariant ConstraintTabModel::data(SqliteCreateTable::Column::Constraint* constr, case Columns::NAME: { if (role == Qt::DisplayRole) - return stripObjName(constr->name, createTable->dialect); + return stripObjName(constr->name); break; } @@ -186,9 +186,9 @@ QString ConstraintTabModel::getTypeLabel(SqliteCreateTable::Constraint::Type typ case SqliteCreateTable::Constraint::FOREIGN_KEY: return "FOREIGN KEY"; case SqliteCreateTable::Constraint::NAME_ONLY: - return QString::null; + return QString(); } - return QString::null; + return QString(); } QString ConstraintTabModel::getTypeLabel(SqliteCreateTable::Column::Constraint::Type type) const @@ -207,6 +207,8 @@ QString ConstraintTabModel::getTypeLabel(SqliteCreateTable::Column::Constraint:: return "DEFAULT"; case SqliteCreateTable::Column::Constraint::COLLATE: return "COLLATE"; + case SqliteCreateTable::Column::Constraint::GENERATED: + return "GENERATED"; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return "FOREIGN KEY"; case SqliteCreateTable::Column::Constraint::NULL_: @@ -214,7 +216,7 @@ QString ConstraintTabModel::getTypeLabel(SqliteCreateTable::Column::Constraint:: case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: break; } - return QString::null; + return QString(); } QIcon ConstraintTabModel::getTypeIcon(SqliteCreateTable::Constraint::Type type) const @@ -251,6 +253,8 @@ QIcon ConstraintTabModel::getTypeIcon(SqliteCreateTable::Column::Constraint::Typ return ICONS.CONSTRAINT_DEFAULT; case SqliteCreateTable::Column::Constraint::COLLATE: return ICONS.CONSTRAINT_COLLATION; + case SqliteCreateTable::Column::Constraint::GENERATED: + return ICONS.CONSTRAINT_GENERATED; case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return ICONS.CONSTRAINT_FOREIGN_KEY; case SqliteCreateTable::Column::Constraint::NULL_: @@ -274,9 +278,9 @@ QString ConstraintTabModel::getDetails(SqliteCreateTable::Constraint* constr) co case SqliteCreateTable::Constraint::FOREIGN_KEY: return getFkDetails(constr); case SqliteCreateTable::Constraint::NAME_ONLY: - return QString::null; + return QString(); } - return QString::null; + return QString(); } QString ConstraintTabModel::getDetails(SqliteCreateTable::Column::Constraint* constr) const @@ -295,6 +299,8 @@ QString ConstraintTabModel::getDetails(SqliteCreateTable::Column::Constraint* co return getDefaultDetails(constr); case SqliteCreateTable::Column::Constraint::COLLATE: return getCollateDetails(constr); + case SqliteCreateTable::Column::Constraint::GENERATED: + return getGeneratedDetails(constr); case SqliteCreateTable::Column::Constraint::FOREIGN_KEY: return getFkDetails(constr); case SqliteCreateTable::Column::Constraint::NULL_: @@ -302,7 +308,7 @@ QString ConstraintTabModel::getDetails(SqliteCreateTable::Column::Constraint* co case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY: break; } - return QString::null; + return QString(); } QString ConstraintTabModel::getPkDetails(SqliteCreateTable::Constraint* constr) const @@ -365,6 +371,12 @@ QString ConstraintTabModel::getCollateDetails(SqliteCreateTable::Column::Constra return getConstrDetails(constr, idx + 1); } +QString ConstraintTabModel::getGeneratedDetails(SqliteCreateTable::Column::Constraint* constr) const +{ + int idx = constr->tokens.indexOf(Token::KEYWORD, "GENERATED", Qt::CaseInsensitive); + return getConstrDetails(constr, idx + 1); +} + QString ConstraintTabModel::getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const { int idx = constr->tokens.indexOf(Token::KEYWORD, "DEFAULT", Qt::CaseInsensitive); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.h index f93415b..3292117 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/constrainttabmodel.h @@ -52,6 +52,7 @@ class GUI_API_EXPORT ConstraintTabModel : public QAbstractTableModel QString getFkDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getNotNullDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getCollateDetails(SqliteCreateTable::Column::Constraint* constr) const; + QString getGeneratedDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getDefaultDetails(SqliteCreateTable::Column::Constraint* constr) const; QString getConstrDetails(SqliteCreateTable::Constraint* constr, int tokenOffset) const; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp index cd3e135..f0f980d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp @@ -22,6 +22,7 @@ #include "themetuner.h" #include "dialogs/bindparamsdialog.h" #include "common/bindparam.h" +#include "common/dbcombobox.h" #include #include #include @@ -106,7 +107,7 @@ void EditorWindow::init() Db* treeSelectedDb = DBTREE->getSelectedOpenDb(); if (treeSelectedDb) - dbCombo->setCurrentIndex(dbComboModel->getIndexForDb(treeSelectedDb)); + dbCombo->setCurrentDb(treeSelectedDb); Db* currentDb = getCurrentDb(); resultsModel->setDb(currentDb); @@ -230,11 +231,8 @@ QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery) bool EditorWindow::setCurrentDb(Db *db) { - if (dbCombo->findText(db->getName()) == -1) - return false; - - dbCombo->setCurrentText(db->getName()); - return true; + dbCombo->setCurrentDb(db); + return dbCombo->currentIndex() > -1; } void EditorWindow::setContents(const QString &sql) @@ -319,7 +317,7 @@ void EditorWindow::changeEvent(QEvent *e) Db* EditorWindow::getCurrentDb() { - return dbComboModel->getDb(dbCombo->currentIndex()); + return dbCombo->currentDb(); } void EditorWindow::updateResultsDisplayMode() @@ -366,6 +364,8 @@ void EditorWindow::updateResultsDisplayMode() void EditorWindow::createActions() { // SQL editor toolbar + actionMap[CURRENT_DB] = ui->toolBar->addWidget(dbCombo); + ui->toolBar->addSeparator(); createAction(EXEC_QUERY, ICONS.EXEC_QUERY, tr("Execute query"), this, SLOT(execQuery()), ui->toolBar, ui->sqlEdit); createAction(EXPLAIN_QUERY, ICONS.EXPLAIN_QUERY, tr("Explain query"), this, SLOT(explainQuery()), ui->toolBar, ui->sqlEdit); ui->toolBar->addSeparator(); @@ -383,8 +383,6 @@ void EditorWindow::createActions() ui->toolBar->addAction(ui->sqlEdit->getAction(SqlEditor::FIND)); ui->toolBar->addAction(ui->sqlEdit->getAction(SqlEditor::REPLACE)); ui->toolBar->addSeparator(); - actionMap[CURRENT_DB] = ui->toolBar->addWidget(dbCombo); - ui->toolBar->addSeparator(); ui->toolBar->addAction(staticActions[RESULTS_IN_TAB]); ui->toolBar->addAction(staticActions[RESULTS_BELOW]); createAction(PREV_DB, tr("Previous database"), this, SLOT(prevDb()), this); @@ -404,10 +402,7 @@ void EditorWindow::createActions() void EditorWindow::createDbCombo() { - dbCombo = new QComboBox(this); - dbComboModel = new DbListModel(this); - dbComboModel->setCombo(dbCombo); - dbCombo->setModel(dbComboModel); + dbCombo = new DbComboBox(this); dbCombo->setEditable(false); dbCombo->setFixedWidth(100); connect(dbCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(dbChanged())); @@ -424,17 +419,12 @@ void EditorWindow::setupDefShortcuts() void EditorWindow::selectCurrentQuery(bool fallBackToPreviousIfNecessary) { - Dialect dialect = Dialect::Sqlite3; - Db* db = getCurrentDb(); - if (db && db->isValid()) - dialect = db->getDialect(); - QTextCursor cursor = ui->sqlEdit->textCursor(); int pos = cursor.position(); int queryStartPos; QString contents = ui->sqlEdit->toPlainText(); QString query = getQueryWithPosition(contents, pos, &queryStartPos); - TokenList tokens = Lexer::tokenize(query, dialect); + TokenList tokens = Lexer::tokenize(query); tokens.trim(); tokens.trimRight(Token::OPERATOR, ";"); @@ -445,7 +435,7 @@ void EditorWindow::selectCurrentQuery(bool fallBackToPreviousIfNecessary) if (pos > -1) { query = getQueryWithPosition(contents, pos, &queryStartPos); - tokens = Lexer::tokenize(query, dialect); + tokens = Lexer::tokenize(query); tokens.trim(); tokens.trimRight(Token::OPERATOR, ";"); } @@ -504,14 +494,8 @@ void EditorWindow::explainQuery() bool EditorWindow::processBindParams(QString& sql, QHash& queryParams) { - // Determin dialect - Dialect dialect = Dialect::Sqlite3; - Db* db = getCurrentDb(); - if (db && db->isValid()) - dialect = db->getDialect(); - // Get all bind parameters from the query - TokenList tokens = Lexer::tokenize(sql, dialect); + TokenList tokens = Lexer::tokenize(sql); TokenList bindTokens = tokens.filter(Token::BIND_PARAM); // No bind tokens? Return fast. @@ -522,18 +506,29 @@ bool EditorWindow::processBindParams(QString& sql, QHash& que static_qstring(paramTpl, ":arg%1"); QString arg; QVector bindParams; + QHash namedBindParams; BindParam* bindParam = nullptr; + bool isNamed = false; + bool nameAlreadyInList = false; int i = 0; for (const TokenPtr& token : bindTokens) { + isNamed = (token->value != "?"); + nameAlreadyInList = isNamed && namedBindParams.contains(token->value); + bindParam = new BindParam(); bindParam->position = i; bindParam->originalName = token->value; - bindParam->newName = paramTpl.arg(i); - bindParams << bindParam; - i++; - + bindParam->newName = (isNamed && nameAlreadyInList) ? namedBindParams[token->value] : paramTpl.arg(i); token->value = bindParam->newName; + + if (!isNamed || !nameAlreadyInList) + bindParams << bindParam; + + if (isNamed && !nameAlreadyInList) + namedBindParams[bindParam->originalName] = bindParam->newName; + + i++; } // Show dialog to query user for values @@ -719,7 +714,7 @@ void EditorWindow::exportResults() } QString query = lastSuccessfulQuery.isEmpty() ? getQueryToExecute() : lastSuccessfulQuery; - QStringList queries = splitQueries(query, getCurrentDb()->getDialect(), false, true); + QStringList queries = splitQueries(query, false, true); if (queries.size() == 0) { qWarning() << "No queries after split in EditorWindow::exportResults()"; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h index 296a9e2..35a3b9b 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h @@ -22,6 +22,7 @@ class IntValidator; class FormView; class SqlQueryItem; class SqlEditor; +class DbComboBox; CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"), CFG_KEY_ENTRY(EXEC_QUERY, Qt::Key_F9, QObject::tr("Execute query")) @@ -128,8 +129,7 @@ class GUI_API_EXPORT EditorWindow : public MdiChild Ui::EditorWindow *ui = nullptr; SqlQueryModel* resultsModel = nullptr; QHash actionGroups; - QComboBox* dbCombo = nullptr; - DbListModel* dbComboModel = nullptr; + DbComboBox* dbCombo = nullptr; int sqlEditorNum = 1; qint64 lastQueryHistoryId = 0; QString lastSuccessfulQuery; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp index 9894098..3ac32ac 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp @@ -2,6 +2,7 @@ #include "ui_functionseditor.h" #include "common/unused.h" #include "common/utils.h" +#include "common/compatibility.h" #include "uiutils.h" #include "functionseditormodel.h" #include "services/pluginmanager.h" @@ -178,8 +179,8 @@ void FunctionsEditor::functionDeselected(int row) } else { - model->setInitCode(row, QString::null); - model->setFinalCode(row, QString::null); + model->setInitCode(row, QString()); + model->setFinalCode(row, QString()); } if (!ui->undefArgsCheck->isChecked()) @@ -239,9 +240,9 @@ void FunctionsEditor::functionSelected(int row) void FunctionsEditor::clearEdits() { - ui->nameEdit->setText(QString::null); - ui->mainCodeEdit->setPlainText(QString::null); - ui->langCombo->setCurrentText(QString::null); + ui->nameEdit->setText(QString()); + ui->mainCodeEdit->setPlainText(QString()); + ui->langCombo->setCurrentText(QString()); ui->undefArgsCheck->setChecked(true); ui->argsList->clear(); ui->allDatabasesRadio->setChecked(true); @@ -371,7 +372,7 @@ void FunctionsEditor::updateModified() bool undefArgsDiff = model->getUndefinedArgs(row) != ui->undefArgsCheck->isChecked(); bool allDatabasesDiff = model->getAllDatabases(row) != ui->allDatabasesRadio->isChecked(); bool argDiff = getCurrentArgList() != model->getArguments(row); - bool dbDiff = getCurrentDatabases().toSet() != model->getDatabases(row).toSet(); // QSet to ignore order + bool dbDiff = toSet(getCurrentDatabases()) != toSet(model->getDatabases(row)); // QSet to ignore order bool typeDiff = model->getType(row) != getCurrentFunctionType(); currentModified = (nameDiff || codeDiff || typeDiff || langDiff || undefArgsDiff || allDatabasesDiff || argDiff || dbDiff || diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp index 623ebd8..8d6d87c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp @@ -87,7 +87,7 @@ void FunctionsEditorModel::setCode(int row, const QString& code) QString FunctionsEditorModel::getCode(int row) const { - GETTER(functionList[row]->data.code, QString::null); + GETTER(functionList[row]->data.code, QString()); } void FunctionsEditorModel::setFinalCode(int row, const QString& code) @@ -97,7 +97,7 @@ void FunctionsEditorModel::setFinalCode(int row, const QString& code) QString FunctionsEditorModel::getFinalCode(int row) const { - GETTER(functionList[row]->data.finalCode, QString::null); + GETTER(functionList[row]->data.finalCode, QString()); } void FunctionsEditorModel::setInitCode(int row, const QString& code) @@ -107,7 +107,7 @@ void FunctionsEditorModel::setInitCode(int row, const QString& code) QString FunctionsEditorModel::getInitCode(int row) const { - GETTER(functionList[row]->data.initCode, QString::null); + GETTER(functionList[row]->data.initCode, QString()); } void FunctionsEditorModel::setName(int row, const QString& newName) @@ -117,7 +117,7 @@ void FunctionsEditorModel::setName(int row, const QString& newName) QString FunctionsEditorModel::getName(int row) const { - GETTER(functionList[row]->data.name, QString::null); + GETTER(functionList[row]->data.name, QString()); } void FunctionsEditorModel::setLang(int row, const QString& lang) @@ -127,7 +127,7 @@ void FunctionsEditorModel::setLang(int row, const QString& lang) QString FunctionsEditorModel::getLang(int row) const { - GETTER(functionList[row]->data.lang, QString::null); + GETTER(functionList[row]->data.lang, QString()); } bool FunctionsEditorModel::getUndefinedArgs(int row) const diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp index ca45eff..4351312 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp @@ -11,6 +11,7 @@ #include "services/dbmanager.h" #include "services/notifymanager.h" #include "common/lazytrigger.h" +#include "common/compatibility.h" #include #include #include @@ -173,8 +174,8 @@ void SqliteExtensionEditor::extensionSelected(int row) void SqliteExtensionEditor::clearEdits() { - ui->fileEdit->setText(QString::null); - ui->initEdit->setText(QString::null); + ui->fileEdit->setText(QString()); + ui->initEdit->setText(QString()); ui->allDatabasesRadio->setChecked(true); } @@ -220,7 +221,7 @@ bool SqliteExtensionEditor::validateExtension(int row) { QString filePath = model->getFilePath(row); QString initFunc = model->getInitFunction(row); - return validateExtension(filePath, initFunc); + return validateExtension(filePath, initFunc, nullptr, nullptr, new QString); } bool SqliteExtensionEditor::validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk, bool* initOk, QString* fileError) @@ -388,7 +389,7 @@ void SqliteExtensionEditor::updateModified() 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 + bool dbDiff = toSet(getCurrentDatabases()) != toSet(model->getDatabases(row)); // QSet to ignore order currentModified = (fileDiff || initDiff || allDatabasesDiff || dbDiff); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h index c8ea3d0..5d81085 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h @@ -61,9 +61,15 @@ class SqliteExtensionEditor : public MdiChild void selectExtension(int row); QStringList getCurrentDatabases() const; bool tryToLoad(const QString& filePath, const QString& initFunc, QString* resultError); - bool validateExtension(bool* fileOk = nullptr, bool* initOk = nullptr, QString* fileError = nullptr); + bool validateExtension(bool* fileOk = nullptr, + bool* initOk = nullptr, + QString* fileError = nullptr); bool validateExtension(int row); - bool validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk = nullptr, bool* initOk = nullptr, QString* fileError = nullptr); + bool validateExtension(const QString& filePath, + const QString& initFunc, + bool* fileOk = nullptr, + bool* initOk = nullptr, + QString* fileError = nullptr); void initStateForAll(); Ui::SqliteExtensionEditor *ui; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tableconstraintsmodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tableconstraintsmodel.cpp index 0f95a98..74e7dd8 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tableconstraintsmodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tableconstraintsmodel.cpp @@ -46,7 +46,7 @@ QVariant TableConstraintsModel::data(const QModelIndex& index, int role) const case Columns::NAME: { if (role == Qt::DisplayRole) - return stripObjName(constr->name, createTable->dialect); + return stripObjName(constr->name); break; } @@ -321,9 +321,9 @@ QString TableConstraintsModel::getTypeLabel(SqliteCreateTable::Constraint::Type case SqliteCreateTable::Constraint::FOREIGN_KEY: return "FOREIGN KEY"; case SqliteCreateTable::Constraint::NAME_ONLY: - return QString::null; + return QString(); } - return QString::null; + return QString(); } QIcon TableConstraintsModel::getTypeIcon(SqliteCreateTable::Constraint::Type type) const @@ -357,9 +357,9 @@ QString TableConstraintsModel::getDetails(SqliteCreateTable::Constraint* constr) case SqliteCreateTable::Constraint::FOREIGN_KEY: return getFkDetails(constr); case SqliteCreateTable::Constraint::NAME_ONLY: - return QString::null; + return QString(); } - return QString::null; + return QString(); } QString TableConstraintsModel::getPkDetails(SqliteCreateTable::Constraint* constr) const diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp index 62b6613..41d6ed9 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp @@ -1,10 +1,11 @@ #include "tablestructuremodel.h" #include "iconmanager.h" #include "common/unused.h" -#include "uiconfig.h" #include #include #include +#include +#include TableStructureModel::TableStructureModel(QObject *parent) : QAbstractTableModel(parent) @@ -26,14 +27,7 @@ int TableStructureModel::columnCount(const QModelIndex& parent) const if (createTable.isNull()) return 0; - switch (createTable->dialect) - { - case Dialect::Sqlite3: - return 9; - case Dialect::Sqlite2: - return 7; - } - return 0; + return 10; } QVariant TableStructureModel::data(const QModelIndex& index, int role) const @@ -109,6 +103,13 @@ QVariant TableStructureModel::data(const QModelIndex& index, int role) const return getColumnCollate(row); } + case TableStructureModel::Columns::GENERATED: + { + if (role != Qt::DecorationRole) + break; + + return getColumnGenerate(row); + } case TableStructureModel::Columns::DEFAULT: { if (role == Qt::FontRole) @@ -142,23 +143,12 @@ QVariant TableStructureModel::headerData(int section, Qt::Orientation orientatio TableStructureModel::Columns TableStructureModel::getHeaderColumn(int colIdx) const { - if (!createTable.isNull() && createTable->dialect == Dialect::Sqlite2) - { - if (colIdx >= 3) - colIdx++; // skip FK - - if (colIdx >= 7) - colIdx++; // skip COLLATE - } return static_cast(colIdx); } bool TableStructureModel::isValidColumnIdx(int colIdx) const { - if (!createTable.isNull() && createTable->dialect == Dialect::Sqlite2) - return colIdx >= 0 && colIdx < 7; - - return colIdx >= 0 && colIdx < 9; + return colIdx >= 0 && colIdx < 10; } SqliteCreateTable::Column* TableStructureModel::getColumn(int colIdx) const @@ -313,10 +303,12 @@ QString TableStructureModel::columnLabel(int column) const return tr("Not\nNULL", "table structure columns"); case Columns::COLLATE: return tr("Collate", "table structure columns"); + case Columns::GENERATED: + return tr("Generated", "table structure columns"); case Columns::DEFAULT: return tr("Default value", "table structure columns"); } - return QString::null; + return QString(); } QVariant TableStructureModel::getColumnName(int row) const @@ -378,6 +370,23 @@ QVariant TableStructureModel::getColumnCollate(int row) const return QVariant(); } +QVariant TableStructureModel::getColumnGenerate(int row) const +{ + SqliteCreateTable::Column* column = getColumn(row); + SqliteCreateTable::Column::Constraint* constr = column->getConstraint(SqliteCreateTable::Column::Constraint::GENERATED); + if (!constr) + return QVariant(); + + switch (constr->generatedType) { + case SqliteCreateTable::Column::Constraint::GeneratedType::STORED: + return ICONS.CONSTRAINT_GENERATED_STORED; + case SqliteCreateTable::Column::Constraint::GeneratedType::VIRTUAL: + case SqliteCreateTable::Column::Constraint::GeneratedType::null: + break; + } + return ICONS.CONSTRAINT_GENERATED_VIRTUAL; +} + QVariant TableStructureModel::getColumnDefaultValue(int row) const { QVariant value = getColumnDefault(row); @@ -403,7 +412,7 @@ QVariant TableStructureModel::getColumnDefaultColor(int row) const { QVariant value = getColumnDefault(row); if (value.isNull()) - return QColor(CFG_UI.Colors.DataNullFg); + return QApplication::style()->standardPalette().dark().color(); return QVariant(); } @@ -494,6 +503,14 @@ bool TableStructureModel::isColumnCollate(SqliteCreateTable::Column* column) con return false; } +bool TableStructureModel::isColumnGenerate(SqliteCreateTable::Column* column) const +{ + if (column->hasConstraint(SqliteCreateTable::Column::Constraint::GENERATED)) + return true; + + return false; +} + void TableStructureModel::setCreateTable(SqliteCreateTable* value) { beginResetModel(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h index e1cfa4e..088a964 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h @@ -47,6 +47,7 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel CHECK, NOTNULL, COLLATE, + GENERATED, DEFAULT }; @@ -54,7 +55,6 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel bool isValidColumnIdx(int colIdx) const; bool doesColumnHasConstraint(SqliteCreateTable::Column* column, SqliteCreateTable::Column::Constraint::Type type); QString columnLabel(int column) const; - QString columnLabelForSqlite2(int column) const; QVariant getColumnName(int row) const; QVariant getColumnType(int row) const; QVariant getColumnPk(int row) const; @@ -63,6 +63,7 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel QVariant getColumnCheck(int row) const; QVariant getColumnNotNull(int row) const; QVariant getColumnCollate(int row) const; + QVariant getColumnGenerate(int row) const; QVariant getColumnDefaultValue(int row) const; QVariant getColumnDefaultFont(int row) const; QVariant getColumnDefaultColor(int row) const; @@ -73,6 +74,7 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel bool isColumnCheck(SqliteCreateTable::Column* column) const; bool isColumnNotNull(SqliteCreateTable::Column* column) const; bool isColumnCollate(SqliteCreateTable::Column* column) const; + bool isColumnGenerate(SqliteCreateTable::Column* column) const; static const constexpr char* mimeType = "application/x-sqlitestudio-tablestructuremodel-row-index"; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp index 526ae1b..c9356d8 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp @@ -38,6 +38,7 @@ #include "dialogs/importdialog.h" #include "dialogs/populatedialog.h" #include "datagrid/sqlqueryitem.h" +#include "common/dbcombobox.h" #include #include #include @@ -164,6 +165,7 @@ void TableWindow::init() initActions(); updateTabsOrder(); + createDbCombo(); connect(dataModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful())); connect(dataModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString))); @@ -225,6 +227,9 @@ void TableWindow::createStructureActions() createAction(MOVE_COLUMN_UP, ICONS.MOVE_UP, tr("Move column up", "table window"), this, SLOT(moveColumnUp()), ui->structureToolBar, ui->structureView); createAction(MOVE_COLUMN_DOWN, ICONS.MOVE_DOWN, tr("Move column down", "table window"), this, SLOT(moveColumnDown()), ui->structureToolBar, ui->structureView); ui->structureToolBar->addSeparator(); + createAction(ADD_INDEX_STRUCT, ICONS.INDEX_ADD, tr("Create index", "table window"), this, SLOT(addIndex()), ui->structureToolBar, ui->structureView); + createAction(ADD_TRIGGER_STRUCT, ICONS.TRIGGER_ADD, tr("Create trigger", "table window"), this, SLOT(addTrigger()), ui->structureToolBar, ui->structureView); + ui->structureToolBar->addSeparator(); ui->structureToolBar->addAction(actionMap[IMPORT]); ui->structureToolBar->addAction(actionMap[EXPORT]); ui->structureToolBar->addAction(actionMap[POPULATE]); @@ -289,12 +294,16 @@ void TableWindow::editColumn(const QModelIndex& idx) SqliteCreateTable::Column* column = structureModel->getColumn(idx.row()); ColumnDialog columnDialog(db, this); columnDialog.setColumn(column); + if (hasAnyPkDefined() && !column->hasConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY)) + columnDialog.disableConstraint(ConstraintDialog::Constraint::PK); + if (columnDialog.exec() != QDialog::Accepted) return; SqliteCreateTable::Column* modifiedColumn = columnDialog.getModifiedColumn(); structureModel->replaceColumn(idx.row(), modifiedColumn); resizeStructureViewColumns(); + updateTableConstraintsToolbarState(); } void TableWindow::delColumn(const QModelIndex& idx) @@ -314,6 +323,7 @@ void TableWindow::delColumn(const QModelIndex& idx) structureModel->delColumn(idx.row()); resizeStructureViewColumns(); + updateTableConstraintsToolbarState(); } void TableWindow::executeStructureChanges() @@ -441,6 +451,7 @@ void TableWindow::updateTableConstraintsToolbarState() actionMap[DEL_TABLE_CONSTRAINT]->setEnabled(anyColumn && validIdx); actionMap[MOVE_CONSTRAINT_UP]->setEnabled(anyColumn && validIdx && !isFirst); actionMap[MOVE_CONSTRAINT_DOWN]->setEnabled(anyColumn && validIdx && !isLast); + actionMap[ADD_TABLE_PK]->setEnabled(!hasAnyPkDefined()); } void TableWindow::setupDefShortcuts() @@ -480,21 +491,15 @@ void TableWindow::executionFailed(const QString& errorText) void TableWindow::initDbAndTable() { - int totalConstrCols = 6; - if (db->getVersion() == 2) - { - ui->withoutRowIdCheck->setVisible(false); - totalConstrCols -= 2; - } - - totalConstrCols += 2; // we start at 3rd column - for (int colIdx = 2; colIdx < totalConstrCols; colIdx++) + for (int colIdx = 2; colIdx < 9; colIdx++) ui->structureView->setItemDelegateForColumn(colIdx, constraintColumnsDelegate); + ui->dbCombo->setCurrentDb(db); if (existingTable) { dataModel->setDb(db); dataModel->setDatabaseAndTable(database, table); + ui->dbCombo->setDisabled(true); } ui->tableNameEdit->setText(table); // TODO no attached/temp db name support here @@ -563,7 +568,6 @@ void TableWindow::initDbAndTable() connect(ui->withoutRowIdCheck, SIGNAL(clicked()), this, SLOT(withOutRowIdChanged())); - ui->ddlEdit->setSqliteVersion(db->getVersion()); parseDdl(); updateIndexes(); updateTriggers(); @@ -605,7 +609,6 @@ void TableWindow::parseDdl() { createTable = SqliteCreateTablePtr::create(); createTable->table = table; - createTable->dialect = db->getDialect(); } originalCreateTable = SqliteCreateTablePtr::create(*createTable); structureModel->setCreateTable(createTable.data()); @@ -621,6 +624,13 @@ void TableWindow::parseDdl() updateDdlTab(); } +void TableWindow::createDbCombo() +{ + ui->dbCombo->setFixedWidth(100); + ui->dbCombo->setToolTip(tr("Database")); + connect(ui->dbCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(dbChanged())); +} + void TableWindow::changeEvent(QEvent *e) { QWidget::changeEvent(e); @@ -904,6 +914,9 @@ void TableWindow::addColumn() ColumnDialog columnDialog(db, this); columnDialog.setColumn(&column); + if (hasAnyPkDefined()) + columnDialog.disableConstraint(ConstraintDialog::Constraint::PK); + if (columnDialog.exec() != QDialog::Accepted) return; @@ -913,6 +926,7 @@ void TableWindow::addColumn() ui->structureView->setCurrentIndex(structureModel->index(structureModel->rowCount()-1, 0)); resizeStructureViewColumns(); + updateTableConstraintsToolbarState(); } void TableWindow::editColumn() @@ -954,6 +968,9 @@ void TableWindow::moveColumnDown() void TableWindow::addConstraint(ConstraintDialog::Constraint mode) { NewConstraintDialog dialog(mode, createTable.data(), db, this); + if (hasAnyPkDefined()) + dialog.disableMode(ConstraintDialog::PK); + if (dialog.exec() != QDialog::Accepted) return; @@ -968,6 +985,7 @@ void TableWindow::addConstraint(ConstraintDialog::Constraint mode) structureConstraintsModel->appendConstraint(tableConstr); ui->tableConstraintsView->resizeColumnToContents(0); ui->tableConstraintsView->resizeColumnToContents(1); + updateTableConstraintsToolbarState(); } bool TableWindow::validate(bool skipWarning) @@ -1060,7 +1078,7 @@ QString TableWindow::getCurrentIndex() const int row = ui->indexList->currentRow(); QTableWidgetItem* item = ui->indexList->item(row, 0); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -1070,7 +1088,7 @@ QString TableWindow::getCurrentTrigger() const int row = ui->triggerList->currentRow(); QTableWidgetItem* item = ui->triggerList->item(row, 0); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -1100,6 +1118,31 @@ int TableWindow::getStructureTabIdx() const return ui->tabWidget->indexOf(ui->structureTab); } +bool TableWindow::hasAnyPkDefined() const +{ + if (structureConstraintsModel) + { + for (int i = 0, total = structureConstraintsModel->rowCount(); i < total; ++i) + { + SqliteCreateTable::Constraint* constraint = structureConstraintsModel->getConstraint(i); + if (constraint->type == SqliteCreateTable::Constraint::PRIMARY_KEY) + return true; + } + } + + if (structureModel) + { + for (int i = 0, total = structureModel->rowCount(); i < total; ++i) + { + SqliteCreateTable::Column* column = structureModel->getColumn(i); + if (column->hasConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY)) + return true; + } + } + + return false; +} + void TableWindow::updateDdlTab() { createTable->rebuildTokens(); @@ -1121,6 +1164,8 @@ void TableWindow::updateNewTableState() actionMap[CREATE_SIMILAR]->setEnabled(existingTable); actionMap[RESET_AUTOINCREMENT]->setEnabled(existingTable); actionMap[REFRESH_STRUCTURE]->setEnabled(existingTable); + actionMap[ADD_INDEX_STRUCT]->setEnabled(existingTable); + actionMap[ADD_TRIGGER_STRUCT]->setEnabled(existingTable); } void TableWindow::addConstraint() @@ -1173,6 +1218,7 @@ void TableWindow::delConstraint(const QModelIndex& idx) structureConstraintsModel->delConstraint(idx.row()); ui->structureView->resizeColumnToContents(0); + updateTableConstraintsToolbarState(); } void TableWindow::moveConstraintUp() @@ -1309,7 +1355,7 @@ void TableWindow::withOutRowIdChanged() if (!createTable) return; - createTable->withOutRowId = ui->withoutRowIdCheck->isChecked() ? QStringLiteral("ROWID") : QString::null; + createTable->withOutRowId = ui->withoutRowIdCheck->isChecked() ? QStringLiteral("ROWID") : QString(); updateDdlTab(); emit modifyStatusChanged(); } @@ -1446,7 +1492,7 @@ void TableWindow::updateIndexes() return; SchemaResolver resolver(db); - resolver.setIgnoreSystemObjects(true); + resolver.setIgnoreSystemObjects(false); QList indexes = resolver.getParsedIndexesForTable(database, table); ui->indexList->setColumnCount(4); @@ -1458,10 +1504,6 @@ void TableWindow::updateIndexes() tr("Partial index condition", "table window indexes"), }); - Dialect dialect= db->getDialect(); - if (dialect == Dialect::Sqlite2) - ui->indexList->setColumnCount(3); - ui->indexList->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); QTableWidgetItem* item = nullptr; @@ -1482,12 +1524,9 @@ void TableWindow::updateIndexes() item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); ui->indexList->setItem(row, 2, item); - if (dialect == Dialect::Sqlite3) - { - item = new QTableWidgetItem(index->where ? index->where->detokenize() : ""); - item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); - ui->indexList->setItem(row, 3, item); - } + item = new QTableWidgetItem(index->where ? index->where->detokenize() : ""); + item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable); + ui->indexList->setItem(row, 3, item); row++; } @@ -1653,3 +1692,13 @@ void TableWindow::updateFont() view->verticalHeader()->setDefaultSectionSize(fm.height() + 4); } } + +void TableWindow::dbChanged() +{ + disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfTableDeleted(QString,QString,DbObjectType))); + + db = ui->dbCombo->currentDb(); + dataModel->setDb(db); + + connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfTableDeleted(QString,QString,DbObjectType))); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h index f44af4f..fb4d67d 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h @@ -23,6 +23,7 @@ class WidgetCover; class SqliteSyntaxHighlighter; class CenteredIconItemDelegate; class ConstraintTabModel; +class DbComboBox; namespace Ui { class TableWindow; @@ -76,6 +77,8 @@ class GUI_API_EXPORT TableWindow : public MdiChild ADD_TABLE_CHECK, MOVE_CONSTRAINT_UP, MOVE_CONSTRAINT_DOWN, + ADD_INDEX_STRUCT, + ADD_TRIGGER_STRUCT, EXPORT, IMPORT, POPULATE, @@ -138,6 +141,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild void init(); void newTable(); void parseDdl(); + void createDbCombo(); void initDbAndTable(); void setupCoverWidget(); void createStructureActions(); @@ -162,6 +166,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild void resizeStructureViewColumns(); int getDataTabIdx() const; int getStructureTabIdx() const; + bool hasAnyPkDefined() const; int newTableWindowNum = 1; @@ -183,6 +188,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild bool modifyingThisTable = false; CenteredIconItemDelegate* constraintColumnsDelegate = nullptr; bool tabsMoving = false; + DbComboBox* dbCombo = nullptr; private slots: void executionSuccessful(); @@ -238,6 +244,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild void prevTab(); void updateTabsOrder(); void updateFont(); + void dbChanged(); public slots: void updateIndexes(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui index 14e278a..4ae8bdf 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui @@ -54,6 +54,16 @@ 2 + + + + + 200 + 16777215 + + + + @@ -317,6 +327,11 @@ QTableWidget
common/exttablewidget.h
+ + DbComboBox + QComboBox +
common/dbcombobox.h
+
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp index c7ec7d8..2181934 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp @@ -45,8 +45,8 @@ ViewWindow::ViewWindow(Db* db, QWidget* parent) : { newView(); init(); + ui->dbCombo->setCurrentDb(db); applyInitialTab(); - updateDbRelatedUiElements(); } ViewWindow::ViewWindow(const ViewWindow& win) : @@ -59,7 +59,6 @@ ViewWindow::ViewWindow(const ViewWindow& win) : init(); initView(); applyInitialTab(); - updateDbRelatedUiElements(); } ViewWindow::ViewWindow(QWidget* parent, Db* db, const QString& database, const QString& view) : @@ -72,7 +71,6 @@ ViewWindow::ViewWindow(QWidget* parent, Db* db, const QString& database, const Q init(); initView(); applyInitialTab(); - updateDbRelatedUiElements(); } ViewWindow::~ViewWindow() @@ -94,6 +92,9 @@ void ViewWindow::changeEvent(QEvent *e) QVariant ViewWindow::saveSession() { + if (!db || DBLIST->isTemporary(db)) + return QVariant(); + QHash sessionValue; sessionValue["view"] = view; sessionValue["db"] = db->getName(); @@ -139,7 +140,6 @@ bool ViewWindow::restoreSession(const QVariant& sessionValue) initView(); applyInitialTab(); - updateDbRelatedUiElements(); return true; } @@ -192,7 +192,7 @@ void ViewWindow::setupDefShortcuts() bool ViewWindow::restoreSessionNextTime() { - return existingView; + return existingView && db && !DBLIST->isTemporary(db); } QToolBar* ViewWindow::getToolBar(int toolbar) const @@ -253,6 +253,7 @@ void ViewWindow::init() updateOutputColumnsVisibility(); updateTabsOrder(); + createDbCombo(); updateFont(); refreshTriggers(); @@ -268,6 +269,13 @@ void ViewWindow::updateAfterInit() ui->tabWidget->setTabEnabled(ui->tabWidget->indexOf(tab), existingView); } +void ViewWindow::createDbCombo() +{ + ui->dbCombo->setFixedWidth(100); + ui->dbCombo->setToolTip(tr("Database")); + connect(ui->dbCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(dbChanged())); +} + void ViewWindow::newView() { existingView = false; @@ -283,14 +291,16 @@ void ViewWindow::initView() if (!createView) return; // error occured while parsing ddl, window will be closed + ui->dbCombo->setCurrentDb(db); if (existingView) { dataModel->setDb(db); dataModel->setQuery(originalCreateView->select->detokenize()); dataModel->setView(view); + ui->dbCombo->setDisabled(true); } - ui->queryEdit->setDb(db); + ui->queryEdit->setPlainText(createView->select->detokenize()); if (createView->columns.size() > 0) @@ -301,8 +311,6 @@ void ViewWindow::initView() updateDdlTab(); - ui->ddlEdit->setSqliteVersion(db->getVersion()); - refreshTriggers(); connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); @@ -454,7 +462,7 @@ QString ViewWindow::getCurrentTrigger() const int row = ui->triggersList->currentRow(); QTableWidgetItem* item = ui->triggersList->item(row, 0); if (!item) - return QString::null; + return QString(); return item->text(); } @@ -475,7 +483,7 @@ QString ViewWindow::getCurrentDdl() const columnsStr = "(" + collectColumnNames().join(", ") + ")"; return ddlTpl.arg( - wrapObjIfNeeded(ui->nameEdit->text(), db->getDialect()), + wrapObjIfNeeded(ui->nameEdit->text()), columnsStr, ui->queryEdit->toPlainText() ); @@ -492,10 +500,9 @@ QStringList ViewWindow::indexedColumnsToNamesOnly(const QListgetDialect() : Dialect::Sqlite3; QStringList cols; for (int row = 0; row < ui->outputColumnsTable->count(); row++) - cols << wrapObjIfNeeded(ui->outputColumnsTable->item(row)->text(), dialect); + cols << wrapObjIfNeeded(ui->outputColumnsTable->item(row)->text()); return cols; } @@ -627,17 +634,20 @@ void ViewWindow::changesSuccessfullyCommitted() database = createView->database; QString oldView = view; view = createView->view; + + if (!existingView) + notifyInfo(tr("View '%1' was committed successfully.").arg(view)); + else if (oldView.compare(view, Qt::CaseInsensitive) == 0) + notifyInfo(tr("Committed changes for view '%1' successfully.").arg(view)); + else + notifyInfo(tr("Committed changes for view '%1' (named before '%2') successfully.").arg(view, oldView)); + existingView = true; initView(); updateQueryToolbarStatus(); updateWindowTitle(); updateAfterInit(); - if (oldView.compare(view, Qt::CaseInsensitive) == 0) - notifyInfo(tr("Committed changes for view '%1' successfully.").arg(view)); - else - notifyInfo(tr("Committed changes for view '%1' (named before '%2') successfully.").arg(view, oldView)); - DBTREE->refreshSchema(db); } @@ -675,6 +685,7 @@ void ViewWindow::prevTab() void ViewWindow::dbClosedFinalCleanup() { + db = nullptr; dataModel->setDb(nullptr); ui->queryEdit->setDb(nullptr); structureExecutor->setDb(nullptr); @@ -827,13 +838,6 @@ void ViewWindow::generateOutputColumns() } } -void ViewWindow::updateDbRelatedUiElements() -{ - bool enabled = db->getDialect() == Dialect::Sqlite3; - outputColumnsCheck->setVisible(enabled); - outputColumnsSeparator->setVisible(enabled); -} - void ViewWindow::updateTabsOrder() { tabsMoving = true; @@ -927,7 +931,6 @@ void ViewWindow::parseDdl() { createView = SqliteCreateViewPtr::create(); createView->view = view; - createView->dialect = db->getDialect(); } originalCreateView = SqliteCreateViewPtr::create(*createView); @@ -972,7 +975,7 @@ bool ViewWindow::validate(bool skipWarnings) // Rebuilding createView statement and validating it on the fly. QString ddl = getCurrentDdl(); - Parser parser(db->getDialect()); + Parser parser; if (!parser.parse(ddl) || parser.getQueries().size() < 1) { notifyError(tr("The SELECT statement could not be parsed. Please correct the query and retry.\nDetails: %1").arg(parser.getErrorString())); @@ -1005,7 +1008,7 @@ void ViewWindow::executeStructureChanges() } else { - Parser parser(db->getDialect()); + Parser parser; if (!parser.parse(theDdl)) { qCritical() << "Could not re-parse the view for executing it:" << parser.getErrorString(); @@ -1069,3 +1072,14 @@ void ViewWindow::updateFont() view->verticalHeader()->setDefaultSectionSize(fm.height() + 4); } } + +void ViewWindow::dbChanged() +{ + disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); + + db = ui->dbCombo->currentDb(); + dataModel->setDb(db); + ui->queryEdit->setDb(db); + + connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType))); +} diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h index 1a8b5b3..a2ef4f7 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h @@ -97,6 +97,7 @@ class GUI_API_EXPORT ViewWindow : public MdiChild private: void init(); void updateAfterInit(); + void createDbCombo(); void newView(); void initView(); void setupCoverWidget(); @@ -163,10 +164,10 @@ class GUI_API_EXPORT ViewWindow : public MdiChild void moveColumnDown(); void updateColumnButtons(); void generateOutputColumns(); - void updateDbRelatedUiElements(); void updateTabsOrder(); void triggerViewDoubleClicked(const QModelIndex& idx); void updateFont(); + void dbChanged(); public slots: void refreshTriggers(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui index 8c17205..5f2acef 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui @@ -54,6 +54,16 @@ 2 + + + + + 200 + 16777215 + + + + @@ -227,16 +237,21 @@
dataview.h
1 - - SqlEditor - QPlainTextEdit -
sqleditor.h
-
ExtTableWidget QTableWidget
common/exttablewidget.h
+ + DbComboBox + QComboBox +
common/dbcombobox.h
+
+ + SqlEditor + QPlainTextEdit +
sqleditor.h
+
-- cgit v1.2.3