summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/windows
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp350
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h91
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui250
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp287
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h80
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp19
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h6
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp24
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h1
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui18
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp83
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h29
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui4
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp46
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h7
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui25
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp18
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h3
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp27
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h7
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui12
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp2
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp97
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h1
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp80
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h45
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui13
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp83
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h28
29 files changed, 1575 insertions, 161 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp
new file mode 100644
index 0000000..82cade7
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.cpp
@@ -0,0 +1,350 @@
+#include "codesnippeteditor.h"
+#include "common/unused.h"
+#include "ui_codesnippeteditor.h"
+#include "uiconfig.h"
+#include "windows/codesnippeteditormodel.h"
+#include "sqlitestudio.h"
+#include "iconmanager.h"
+#include "uiutils.h"
+#include "codesnippeteditormodel.h"
+#include <QSortFilterProxyModel>
+#include <QItemSelection>
+#include <QDesktopServices>
+#include <QStyleFactory>
+
+CFG_KEYS_DEFINE(CodeSnippetEditor)
+
+CodeSnippetEditor::CodeSnippetEditor(QWidget *parent) :
+ MdiChild(parent),
+ ui(new Ui::CodeSnippetEditor)
+{
+ init();
+}
+
+CodeSnippetEditor::~CodeSnippetEditor()
+{
+ delete ui;
+}
+
+bool CodeSnippetEditor::restoreSessionNextTime()
+{
+ return false;
+}
+
+bool CodeSnippetEditor::isUncommitted() const
+{
+ return model->isModified() || currentModified;
+}
+
+QString CodeSnippetEditor::getQuitUncommittedConfirmMessage() const
+{
+ return tr("Code Snippets editor window has uncommitted modifications.");
+}
+
+QVariant CodeSnippetEditor::saveSession()
+{
+ return QVariant();
+}
+
+bool CodeSnippetEditor::restoreSession(const QVariant& sessionValue)
+{
+ UNUSED(sessionValue);
+ return true;
+}
+
+Icon* CodeSnippetEditor::getIconNameForMdiWindow()
+{
+ return ICONS.CODE_SNIPPET;
+}
+
+QString CodeSnippetEditor::getTitleForMdiWindow()
+{
+ return tr("Code Snippets editor");
+}
+
+void CodeSnippetEditor::createActions()
+{
+ createAction(COMMIT, ICONS.COMMIT, tr("Commit all snippet changes"), this, SLOT(commit()), ui->toolBar, this);
+ createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all snippet changes"), this, SLOT(rollback()), ui->toolBar, this);
+ ui->toolBar->addSeparator();
+ createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new snippet"), this, SLOT(newSnippet()), ui->toolBar, this);
+ createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected snippet"), this, SLOT(deleteSnippet()), ui->toolBar, this);
+ ui->toolBar->addSeparator();
+ createAction(MOVE_UP, ICONS.MOVE_UP, tr("Move the snippet up"), this, SLOT(moveSnippetUp()), ui->toolBar, this);
+ createAction(MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move the snippet down"), this, SLOT(moveSnippetDown()), ui->toolBar, this);
+ ui->toolBar->addSeparator();
+ createAction(HELP, ICONS.HELP, tr("Code snippets manual"), this, SLOT(help()), ui->toolBar, this);
+
+#ifdef Q_OS_MACX
+ QStyle *fusion = QStyleFactory::create("Fusion");
+ ui->toolBar->setStyle(fusion);
+#endif
+}
+
+void CodeSnippetEditor::setupDefShortcuts()
+{
+ // Widget context
+ setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut);
+ BIND_SHORTCUTS(CodeSnippetEditor, Action);
+}
+
+QToolBar* CodeSnippetEditor::getToolBar(int toolbar) const
+{
+ UNUSED(toolbar);
+ return ui->toolBar;
+}
+
+void CodeSnippetEditor::init()
+{
+ ui->setupUi(this);
+ clearEdits();
+
+ ui->mainCodeEdit->setFont(CFG_UI.Fonts.SqlEditor.get());
+
+ model = new CodeSnippetEditorModel(this);
+ snippetFilterModel = new QSortFilterProxyModel(this);
+ snippetFilterModel->setSourceModel(model);
+ ui->list->setModel(snippetFilterModel);
+
+ initActions();
+
+ connect(ui->list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(snippetSelected(QItemSelection,QItemSelection)));
+ connect(ui->list->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateState()));
+ connect(ui->assistantShortcutEdit, SIGNAL(keySequenceChanged(QKeySequence)), this, SLOT(updateModified()));
+ connect(ui->mainCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified()));
+ connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified()));
+ connect(CFG_UI.Fonts.SqlEditor, SIGNAL(changed(QVariant)), this, SLOT(changeFont(QVariant)));
+ connect(ui->clearAssistantShortcutButton, SIGNAL(clicked(bool)), this, SLOT(clearAssistantShortcutPressed()));
+
+ model->setData(CODESNIPPETS->getSnippets());
+
+ updateCurrentSnippetState();
+}
+
+int CodeSnippetEditor::getCurrentSnippetRow() const
+{
+ QModelIndexList idxList = ui->list->selectionModel()->selectedIndexes();
+ if (idxList.size() == 0)
+ return -1;
+
+ return idxList.first().row();
+}
+
+void CodeSnippetEditor::snippetDeselected(int row)
+{
+ model->setName(row, ui->nameEdit->text());
+ model->setCode(row, ui->mainCodeEdit->toPlainText());
+ model->setHotkey(row, ui->assistantShortcutEdit->keySequence());
+ model->setModified(row, currentModified);
+ model->validateNames();
+}
+
+void CodeSnippetEditor::snippetSelected(int row)
+{
+ updatesForSelection = true;
+ ui->nameEdit->setText(model->getName(row));
+ ui->mainCodeEdit->setPlainText(model->getCode(row));
+ ui->assistantShortcutEdit->setKeySequence(model->getHotkey(row));
+
+ updatesForSelection = false;
+ currentModified = model->isModified(row);
+
+ updateCurrentSnippetState();
+}
+
+void CodeSnippetEditor::selectSnippet(int row, bool skipModelUpdates)
+{
+ if (!model->isValidRowIndex(row))
+ return;
+
+ if (skipModelUpdates)
+ skipModelUpdatesDuringSelection = true;
+
+ ui->list->selectionModel()->setCurrentIndex(model->index(row), QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent);
+
+ if (skipModelUpdates)
+ skipModelUpdatesDuringSelection = false;
+}
+
+void CodeSnippetEditor::clearEdits()
+{
+ ui->nameEdit->clear();
+ ui->assistantShortcutEdit->clear();
+ ui->mainCodeEdit->setPlainText(QString());
+}
+
+void CodeSnippetEditor::commit()
+{
+ int row = getCurrentSnippetRow();
+ if (model->isValidRowIndex(row))
+ snippetDeselected(row);
+
+ QList<CodeSnippetManager::CodeSnippet*> snippets = model->generateSnippets();
+
+ CODESNIPPETS->setSnippets(snippets);
+ model->clearModified();
+ currentModified = false;
+
+ if (model->isValidRowIndex(row))
+ selectSnippet(row);
+
+ updateState();
+}
+
+void CodeSnippetEditor::rollback()
+{
+ int selectedBefore = getCurrentSnippetRow();
+
+ model->setData(CODESNIPPETS->getSnippets());
+ currentModified = false;
+ clearEdits();
+
+ if (model->isValidRowIndex(selectedBefore))
+ selectSnippet(selectedBefore);
+
+ updateState();
+}
+
+void CodeSnippetEditor::newSnippet()
+{
+ CodeSnippetManager::CodeSnippet* snip = new CodeSnippetManager::CodeSnippet();
+ snip->name = generateUniqueName("snippet", model->getSnippetNames());
+
+ model->addSnippet(snip);
+
+ selectSnippet(model->rowCount() - 1);
+}
+
+void CodeSnippetEditor::deleteSnippet()
+{
+ int row = getCurrentSnippetRow();
+ model->deleteSnippet(row);
+ clearEdits();
+
+ row = getCurrentSnippetRow();
+ if (model->isValidRowIndex(row))
+ snippetSelected(row);
+
+ updateState();
+}
+
+void CodeSnippetEditor::moveSnippetUp()
+{
+ int row = getCurrentSnippetRow();
+ int newRow = model->moveUp(row);
+ if (row != newRow)
+ selectSnippet(newRow, true);
+}
+
+void CodeSnippetEditor::moveSnippetDown()
+{
+ int row = getCurrentSnippetRow();
+ int newRow = model->moveDown(row);
+ if (row != newRow)
+ selectSnippet(newRow, true);
+}
+
+void CodeSnippetEditor::updateModified()
+{
+ if (updatesForSelection)
+ return;
+
+ int row = getCurrentSnippetRow();
+ if (model->isValidRowIndex(row))
+ {
+ bool nameDiff = model->getName(row) != ui->nameEdit->text();
+ bool codeDiff = model->getCode(row) != ui->mainCodeEdit->toPlainText();
+ bool hotkeyDiff = model->getHotkey(row) != ui->assistantShortcutEdit->keySequence();
+ currentModified = (nameDiff || codeDiff || hotkeyDiff);
+ }
+
+ updateCurrentSnippetState();
+}
+
+void CodeSnippetEditor::updateCurrentSnippetState()
+{
+ int row = getCurrentSnippetRow();
+ bool validRow = model->isValidRowIndex(row);
+ ui->rightWidget->setEnabled(validRow);
+ if (!validRow)
+ {
+ setValidState(ui->nameEdit, true);
+ setValidState(ui->mainCodeEdit, true);
+ setValidState(ui->assistantShortcutEdit, true);
+ updateState();
+ return;
+ }
+
+ QString name = ui->nameEdit->text();
+ bool nameOk = !name.trimmed().isEmpty() && model->isAllowedName(row, name);
+ setValidState(ui->nameEdit, nameOk, tr("Enter a non-empty, unique name of the snippet."));
+
+ bool codeOk = !ui->mainCodeEdit->toPlainText().trimmed().isEmpty();
+ setValidState(ui->mainCodeEdit, codeOk, tr("Enter a non-empty snippet content."));
+
+ QKeySequence assistantHotkey = ui->assistantShortcutEdit->keySequence();
+ bool hotkeyOk = assistantHotkey.isEmpty() || model->isAllowedHotkey(row, assistantHotkey);
+ setValidState(ui->assistantShortcutWidget, hotkeyOk, tr("This hotkey is not unique in context of a code assistant."));
+
+ model->setValid(row, codeOk && nameOk && hotkeyOk);
+ updateState();
+}
+
+void CodeSnippetEditor::updateState()
+{
+ bool modified = model->isModified() || currentModified;
+ bool valid = model->isValid();
+
+ actionMap[COMMIT]->setEnabled(modified && valid);
+ actionMap[ROLLBACK]->setEnabled(modified);
+ actionMap[DELETE]->setEnabled(ui->list->selectionModel()->selectedIndexes().size() > 0);
+}
+
+void CodeSnippetEditor::snippetSelected(const QItemSelection& selected, const QItemSelection& deselected)
+{
+ if (skipModelUpdatesDuringSelection)
+ return;
+
+ int deselCnt = deselected.indexes().size();
+ int selCnt = selected.indexes().size();
+
+ if (deselCnt > 0)
+ snippetDeselected(deselected.indexes().first().row());
+
+ if (selCnt > 0)
+ snippetSelected(selected.indexes().first().row());
+
+ if (deselCnt > 0 && selCnt == 0)
+ {
+ currentModified = false;
+ clearEdits();
+ }
+}
+
+void CodeSnippetEditor::applyFilter(const QString& value)
+{
+ // The selection hack (clear & set) below is described in more details in FunctionsEditor::applyFilter().
+ int row = getCurrentSnippetRow();
+ ui->list->selectionModel()->clearSelection();
+
+ snippetFilterModel->setFilterFixedString(value);
+
+ selectSnippet(row);
+}
+
+void CodeSnippetEditor::changeFont(const QVariant& font)
+{
+ ui->mainCodeEdit->setFont(font.value<QFont>());
+}
+
+void CodeSnippetEditor::clearAssistantShortcutPressed()
+{
+ ui->assistantShortcutEdit->clear();
+ ui->assistantShortcutEdit->setFocus();
+}
+
+void CodeSnippetEditor::help()
+{
+ static const QString url = QStringLiteral("https://github.com/pawelsalawa/sqlitestudio/wiki/User_Manual#code-snippets");
+ QDesktopServices::openUrl(QUrl(url, QUrl::StrictMode));
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h
new file mode 100644
index 0000000..2247960
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.h
@@ -0,0 +1,91 @@
+#ifndef CODESNIPPETEDITOR_H
+#define CODESNIPPETEDITOR_H
+
+#include "mdichild.h"
+#include <QWidget>
+
+namespace Ui {
+class CodeSnippetEditor;
+}
+
+class CodeSnippetEditorModel;
+class QSortFilterProxyModel;
+class QSyntaxHighlighter;
+class QItemSelection;
+
+CFG_KEY_LIST(CodeSnippetEditor, QObject::tr("A code snippets editor window"),
+ CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes"))
+ CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes"))
+)
+
+class CodeSnippetEditor : public MdiChild
+{
+ Q_OBJECT
+
+ public:
+ enum Action
+ {
+ COMMIT,
+ ROLLBACK,
+ ADD,
+ DELETE,
+ MOVE_UP,
+ MOVE_DOWN,
+ HELP
+ };
+ Q_ENUM(Action)
+
+ enum ToolBar
+ {
+ TOOLBAR
+ };
+
+ explicit CodeSnippetEditor(QWidget *parent = nullptr);
+ ~CodeSnippetEditor();
+
+ bool restoreSessionNextTime();
+ bool isUncommitted() const;
+ QString getQuitUncommittedConfirmMessage() const;
+
+ protected:
+ QVariant saveSession();
+ bool restoreSession(const QVariant &sessionValue);
+ Icon* getIconNameForMdiWindow();
+ QString getTitleForMdiWindow();
+ void createActions();
+ void setupDefShortcuts();
+ QToolBar* getToolBar(int toolbar) const;
+
+ private:
+ void init();
+ int getCurrentSnippetRow() const;
+ void snippetDeselected(int row);
+ void snippetSelected(int row);
+ void selectSnippet(int row, bool forRowMovement = false);
+ void clearEdits();
+
+ Ui::CodeSnippetEditor *ui;
+ CodeSnippetEditorModel* model = nullptr;
+ QSortFilterProxyModel* snippetFilterModel = nullptr;
+ bool currentModified = false;
+ bool updatesForSelection = false;
+ bool skipModelUpdatesDuringSelection = false;
+
+ private slots:
+ void commit();
+ void rollback();
+ void newSnippet();
+ void deleteSnippet();
+ void moveSnippetUp();
+ void moveSnippetDown();
+ void updateModified();
+ void updateCurrentSnippetState();
+ void updateState();
+ void snippetSelected(const QItemSelection& selected, const QItemSelection& deselected);
+ void applyFilter(const QString& value);
+ void changeFont(const QVariant& font);
+ void clearAssistantShortcutPressed();
+ void help();
+};
+
+#endif // CODESNIPPETEDITOR_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui
new file mode 100644
index 0000000..4a18b12
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditor.ui
@@ -0,0 +1,250 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CodeSnippetEditor</class>
+ <widget class="QWidget" name="CodeSnippetEditor">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>913</width>
+ <height>595</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string notr="true">Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QToolBar" name="toolBar"/>
+ </item>
+ <item>
+ <widget class="QWidget" name="mainWidget" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QWidget" name="leftWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLineEdit" name="snippetFilterEdit">
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="placeholderText">
+ <string>Filter snippets</string>
+ </property>
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListView" name="list">
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="verticalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="rightWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,100">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topWidget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="3,0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item row="1" column="1">
+ <widget class="QWidget" name="assistantShortcutWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QKeySequenceEdit" name="assistantShortcutEdit">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="clearAssistantShortcutButton">
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="icon">
+ <iconset resource="../icons.qrc">
+ <normaloff>:/icons/img/clear_lineedit.png</normaloff>:/icons/img/clear_lineedit.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="nameLabel">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Snippet name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLineEdit" name="nameEdit">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The name of the snippet will be displayed in the code assistant. To access the list of snippets user has to hit the code assistant shortcut twice.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="assistantShortcutLabel">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Optional shortcut, that will work only in context of the active code assistant window. It allows user to use key combinations, that would otherwise be conflicting with other shortcuts. Having the code assistant window as required context makes the choice of keys more versatile.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Code assistant shortcut</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="mainCodeGroup">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>2</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Snippet code</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QPlainTextEdit" name="mainCodeEdit">
+ <property name="lineWrapMode">
+ <enum>QPlainTextEdit::NoWrap</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>snippetFilterEdit</tabstop>
+ <tabstop>list</tabstop>
+ <tabstop>mainCodeEdit</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../icons.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp
new file mode 100644
index 0000000..585db7b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.cpp
@@ -0,0 +1,287 @@
+#include "codesnippeteditormodel.h"
+#include "common/strhash.h"
+#include "common/unused.h"
+#include "iconmanager.h"
+
+#include <QKeySequence>
+
+#define SETTER(X, Y) \
+if (!isValidRowIndex(row) || X == Y) \
+ return; \
+ \
+ X = Y; \
+ emitDataChanged(row);
+
+#define GETTER(X, Y) \
+if (!isValidRowIndex(row)) \
+ return Y; \
+ \
+ return X;
+
+CodeSnippetEditorModel::CodeSnippetEditorModel(QObject* parent) :
+ QAbstractListModel(parent)
+{
+}
+
+void CodeSnippetEditorModel::clearModified()
+{
+ beginResetModel();
+ for (Snippet*& snip : snippetList)
+ snip->modified = false;
+
+ listModified = false;
+ originalSnippetList = snippetList;
+
+ endResetModel();
+}
+
+bool CodeSnippetEditorModel::isModified() const
+{
+ if (snippetList != originalSnippetList)
+ return true;
+
+ for (Snippet* snip : snippetList)
+ {
+ if (snip->modified)
+ return true;
+ }
+ return false;
+}
+
+bool CodeSnippetEditorModel::isModified(int row) const
+{
+ GETTER(snippetList[row]->modified, false);
+}
+
+void CodeSnippetEditorModel::setModified(int row, bool modified)
+{
+ SETTER(snippetList[row]->modified, modified);
+}
+
+bool CodeSnippetEditorModel::isValid() const
+{
+ for (Snippet* snip : snippetList)
+ {
+ if (!snip->valid)
+ return false;
+ }
+ return true;
+}
+
+bool CodeSnippetEditorModel::isValid(int row) const
+{
+ GETTER(snippetList[row]->valid, false);
+}
+
+void CodeSnippetEditorModel::setValid(int row, bool valid)
+{
+ SETTER(snippetList[row]->valid, valid);
+}
+
+void CodeSnippetEditorModel::setCode(int row, const QString& code)
+{
+ SETTER(snippetList[row]->data.code, code);
+}
+
+QString CodeSnippetEditorModel::getCode(int row) const
+{
+ GETTER(snippetList[row]->data.code, QString());
+}
+
+void CodeSnippetEditorModel::setName(int row, const QString& newName)
+{
+ SETTER(snippetList[row]->data.name, newName);
+}
+
+QString CodeSnippetEditorModel::getName(int row) const
+{
+ GETTER(snippetList[row]->data.name, QString());
+}
+
+void CodeSnippetEditorModel::setHotkey(int row, const QKeySequence& value)
+{
+ SETTER(snippetList[row]->data.hotkey, value.toString());
+}
+
+QKeySequence CodeSnippetEditorModel::getHotkey(int row) const
+{
+ GETTER(QKeySequence(snippetList[row]->data.hotkey), QKeySequence());
+}
+
+void CodeSnippetEditorModel::setData(const QList<CodeSnippetManager::CodeSnippet*>& snippets)
+{
+ beginResetModel();
+
+ for (Snippet*& snippetPtr : snippetList)
+ delete snippetPtr;
+
+ snippetList.clear();
+
+ for (CodeSnippetManager::CodeSnippet* snip : snippets)
+ snippetList << new Snippet(snip);
+
+ listModified = false;
+ originalSnippetList = snippetList;
+
+ endResetModel();
+}
+
+void CodeSnippetEditorModel::addSnippet(CodeSnippetManager::CodeSnippet* snippet)
+{
+ int row = snippetList.size();
+
+ beginInsertRows(QModelIndex(), row, row);
+
+ snippetList << new Snippet(snippet);
+ listModified = true;
+
+ endInsertRows();
+}
+
+void CodeSnippetEditorModel::deleteSnippet(int row)
+{
+ if (!isValidRowIndex(row))
+ return;
+
+ beginRemoveRows(QModelIndex(), row, row);
+
+ delete snippetList[row];
+ snippetList.removeAt(row);
+
+ listModified = true;
+
+ endRemoveRows();
+}
+
+QList<CodeSnippetManager::CodeSnippet*> CodeSnippetEditorModel::generateSnippets() const
+{
+ QList<CodeSnippetManager::CodeSnippet*> results;
+ for (Snippet* snip: snippetList)
+ results << new CodeSnippetManager::CodeSnippet(snip->data);
+
+ return results;
+}
+
+QStringList CodeSnippetEditorModel::getSnippetNames() const
+{
+ QStringList names;
+ for (Snippet* snip : snippetList)
+ names << snip->data.name;
+
+ return names;
+}
+
+void CodeSnippetEditorModel::validateNames()
+{
+ StrHash<QList<int>> counter;
+
+ int row = 0;
+ for (Snippet*& snip : snippetList)
+ {
+ snip->valid &= true;
+ counter[snip->data.name] << row++;
+ }
+
+ QHashIterator<QString,QList<int>> cntIt = counter.iterator();
+ while (cntIt.hasNext())
+ {
+ cntIt.next();
+ if (cntIt.value().size() > 1)
+ {
+ for (int cntRow : cntIt.value())
+ setValid(cntRow, false);
+ }
+ }
+
+ QModelIndex idx;
+ for (int i = 0; i < snippetList.size(); i++)
+ {
+ idx = index(i);
+ emit dataChanged(idx, idx);
+ }
+}
+
+bool CodeSnippetEditorModel::isAllowedName(int rowToSkip, const QString& nameToValidate)
+{
+ QStringList names = getSnippetNames();
+ names.removeAt(rowToSkip);
+ return !names.contains(nameToValidate, Qt::CaseInsensitive);
+}
+
+bool CodeSnippetEditorModel::isAllowedHotkey(int rowToSkip, const QKeySequence& hotkeyToValidate)
+{
+ QList<QKeySequence> keys;
+ for (Snippet*& snip : snippetList)
+ keys << snip->data.hotkey;
+
+ keys.removeAt(rowToSkip);
+ return !keys.contains(hotkeyToValidate);
+}
+
+bool CodeSnippetEditorModel::isValidRowIndex(int row) const
+{
+ return (row >= 0 && row < snippetList.size());
+}
+
+int CodeSnippetEditorModel::moveUp(int row)
+{
+ if (row <= 0 || row >= snippetList.size())
+ return row;
+
+ snippetList.move(row, row - 1);
+ return row - 1;
+}
+
+int CodeSnippetEditorModel::moveDown(int row)
+{
+ if (row < 0 || row + 1 >= snippetList.size())
+ return row;
+
+ snippetList.move(row, row + 1);
+ return row + 1;
+}
+
+int CodeSnippetEditorModel::rowCount(const QModelIndex& parent) const
+{
+ UNUSED(parent);
+ return snippetList.size();
+}
+
+QVariant CodeSnippetEditorModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid() || !isValidRowIndex(index.row()))
+ return QVariant();
+
+ if (role == Qt::DisplayRole)
+ {
+ Snippet* sn = snippetList[index.row()];
+ return sn->data.name;
+ }
+
+ if (role == Qt::DecorationRole)
+ {
+ QIcon icon = ICONS.CODE_SNIPPET;
+ if (!snippetList[index.row()]->valid)
+ icon = Icon::merge(icon, Icon::ERROR);
+
+ return icon;
+ }
+
+ return QVariant();
+}
+
+void CodeSnippetEditorModel::emitDataChanged(int row)
+{
+ QModelIndex idx = index(row);
+ emit dataChanged(idx, idx);
+}
+
+CodeSnippetEditorModel::Snippet::Snippet()
+{
+}
+
+CodeSnippetEditorModel::Snippet::Snippet(CodeSnippetManager::CodeSnippet* other)
+{
+ data = CodeSnippetManager::CodeSnippet(*other);
+ originalName = data.name;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h
new file mode 100644
index 0000000..ae5fa86
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/codesnippeteditormodel.h
@@ -0,0 +1,80 @@
+#ifndef CODESNIPPETEDITORMODEL_H
+#define CODESNIPPETEDITORMODEL_H
+
+#include "guiSQLiteStudio_global.h"
+#include "services/codesnippetmanager.h"
+#include <QAbstractListModel>
+
+class GUI_API_EXPORT CodeSnippetEditorModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ public:
+ using QAbstractItemModel::setData;
+
+ enum Role
+ {
+ CODE = 1000,
+ MODIFIED = 1001,
+ VALID = 1002
+ };
+
+ explicit CodeSnippetEditorModel(QObject *parent = 0);
+
+ void clearModified();
+ bool isModified() const;
+ bool isModified(int row) const;
+ void setModified(int row, bool modified);
+ bool isValid() const;
+ bool isValid(int row) const;
+ void setValid(int row, bool valid);
+ void setCode(int row, const QString& code);
+ QString getCode(int row) const;
+ void setName(int row, const QString& newName);
+ QString getName(int row) const;
+ void setHotkey(int row, const QKeySequence& value);
+ QKeySequence getHotkey(int row) const;
+ void setData(const QList<CodeSnippetManager::CodeSnippet*>& snippets);
+ void addSnippet(CodeSnippetManager::CodeSnippet* snippet);
+ void deleteSnippet(int row);
+ QList<CodeSnippetManager::CodeSnippet*> generateSnippets() const;
+ QStringList getSnippetNames() const;
+ void validateNames();
+ bool isAllowedName(int rowToSkip, const QString& nameToValidate);
+ bool isAllowedHotkey(int rowToSkip, const QKeySequence& hotkeyToValidate);
+ bool isValidRowIndex(int row) const;
+
+ int moveUp(int row);
+ int moveDown(int row);
+ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+ QVariant data(const QModelIndex& index, int role) const;
+
+ private:
+ struct Snippet
+ {
+ Snippet();
+ Snippet(CodeSnippetManager::CodeSnippet* other);
+
+ CodeSnippetManager::CodeSnippet data;
+ bool modified = false;
+ bool valid = true;
+ QString originalName;
+ };
+
+ void emitDataChanged(int row);
+
+ QList<Snippet*> snippetList;
+
+ /**
+ * @brief List of snippets pointers before modifications.
+ *
+ * This list is kept to check for modifications in the overall list of snippets.
+ * Pointers on this list may be already deleted, so don't use them!
+ * It's only used to compare list of pointers to snippetList, so it can tell you
+ * if the list was modified in regards of adding or deleting functions.
+ */
+ QList<Snippet*> originalSnippetList;
+ bool listModified = false;
+};
+
+#endif // CODESNIPPETEDITORMODEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp
index 5cf5be4..df72db2 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.cpp
@@ -15,6 +15,8 @@
#include <QDesktopServices>
#include <QSyntaxHighlighter>
+CFG_KEYS_DEFINE(CollationsEditor)
+
CollationsEditor::CollationsEditor(QWidget *parent) :
MdiChild(parent),
ui(new Ui::CollationsEditor)
@@ -55,18 +57,20 @@ QString CollationsEditor::getTitleForMdiWindow()
void CollationsEditor::createActions()
{
- createAction(COMMIT, ICONS.COMMIT, tr("Commit all collation changes"), this, SLOT(commit()), ui->toolbar);
- createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all collation changes"), this, SLOT(rollback()), ui->toolbar);
+ createAction(COMMIT, ICONS.COMMIT, tr("Commit all collation changes"), this, SLOT(commit()), ui->toolbar, this);
+ createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all collation changes"), this, SLOT(rollback()), ui->toolbar, this);
ui->toolbar->addSeparator();
- createAction(ADD, ICONS.NEW_COLLATION, tr("Create new collation"), this, SLOT(newCollation()), ui->toolbar);
- createAction(DELETE, ICONS.DELETE_COLLATION, tr("Delete selected collation"), this, SLOT(deleteCollation()), ui->toolbar);
+ createAction(ADD, ICONS.NEW_COLLATION, tr("Create new collation"), this, SLOT(newCollation()), ui->toolbar, this);
+ createAction(DELETE, ICONS.DELETE_COLLATION, tr("Delete selected collation"), this, SLOT(deleteCollation()), ui->toolbar, this);
ui->toolbar->addSeparator();
- createAction(HELP, ICONS.HELP, tr("Editing collations manual"), this, SLOT(help()), ui->toolbar);
+ createAction(HELP, ICONS.HELP, tr("Editing collations manual"), this, SLOT(help()), ui->toolbar, this);
}
void CollationsEditor::setupDefShortcuts()
{
-
+ // Widget context
+ setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut);
+ BIND_SHORTCUTS(CollationsEditor, Action);
}
QToolBar* CollationsEditor::getToolBar(int toolbar) const
@@ -378,10 +382,9 @@ void CollationsEditor::changeFont(const QVariant& font)
setFont(font.value<QFont>());
}
-
bool CollationsEditor::isUncommitted() const
{
- return model->isModified();
+ return model->isModified() || currentModified;
}
QString CollationsEditor::getQuitUncommittedConfirmMessage() const
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h
index 7b2e469..61c9165 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/collationseditor.h
@@ -17,6 +17,11 @@ class CollationsEditorModel;
class QSortFilterProxyModel;
class QSyntaxHighlighter;
+CFG_KEY_LIST(CollationsEditor, QObject::tr("A collation editor window"),
+ CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes"))
+ CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes"))
+)
+
class GUI_API_EXPORT CollationsEditor : public MdiChild
{
Q_OBJECT
@@ -30,6 +35,7 @@ class GUI_API_EXPORT CollationsEditor : public MdiChild
DELETE,
HELP
};
+ Q_ENUM(Action)
enum ToolBar
{
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp
index 795158a..c083cbd 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.cpp
@@ -1,14 +1,12 @@
#include "ddlhistorywindow.h"
#include "ui_ddlhistorywindow.h"
#include "services/config.h"
-#include "common/userinputfilter.h"
-#include "common/extlineedit.h"
-#include "dblistmodel.h"
#include "ddlhistorymodel.h"
#include "common/unused.h"
#include "iconmanager.h"
#include <QDate>
#include <QLineEdit>
+#include <QMessageBox>
#include <QStringListModel>
DdlHistoryWindow::DdlHistoryWindow(QWidget *parent) :
@@ -58,6 +56,7 @@ void DdlHistoryWindow::init()
connect(ui->tableView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this, SLOT(activated(QModelIndex,QModelIndex)));
+ connect(ui->clearButton, SIGNAL(clicked(bool)), this, SLOT(clearHistory()));
}
void DdlHistoryWindow::activated(const QModelIndex& current, const QModelIndex& previous)
@@ -76,12 +75,8 @@ void DdlHistoryWindow::activated(const QModelIndex& current, const QModelIndex&
QStringList contentEntries;
QList<Config::DdlHistoryEntryPtr> entries = CFG->getDdlHistoryFor(dbName, dbFile, date);
- for (Config::DdlHistoryEntryPtr entry : entries)
- {
- contentEntries << templ.arg(entry->dbName).arg(entry->dbFile)
- .arg(entry->timestamp.toString("yyyy-MM-dd HH:mm:ss"))
- .arg(entry->queries);
- }
+ for (Config::DdlHistoryEntryPtr& entry : entries)
+ contentEntries << templ.arg(entry->dbName, entry->dbFile, entry->timestamp.toString("yyyy-MM-dd HH:mm:ss"), entry->queries);
ui->ddlEdit->setPlainText(contentEntries.join("\n\n"));
}
@@ -98,6 +93,17 @@ void DdlHistoryWindow::refreshDbList()
dbListModel->setStringList(dbList);
}
+void DdlHistoryWindow::clearHistory()
+{
+ QMessageBox::StandardButton result = QMessageBox::question(this, tr("Clear history"), tr("Are you sure you want to erase entire DDL history?"));
+ if (result != QMessageBox::Yes)
+ return;
+
+ CFG->clearDdlHistory();
+ dataModel->refresh();
+ ui->ddlEdit->setPlainText("");
+}
+
bool DdlHistoryWindow::restoreSessionNextTime()
{
return false;
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h
index 1a04831..c8de66a 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.h
@@ -49,6 +49,7 @@ class GUI_API_EXPORT DdlHistoryWindow : public MdiChild
void activated(const QModelIndex& current, const QModelIndex& previous);
void applyFilter(const QString& filterValue);
void refreshDbList();
+ void clearHistory();
};
#endif // DDLHISTORYWINDOW_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui
index 33cdb66..77fa5ca 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/ddlhistorywindow.ui
@@ -50,6 +50,20 @@
</widget>
</item>
<item>
+ <widget class="QToolButton" name="clearButton">
+ <property name="toolTip">
+ <string>Clear entire history</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset resource="../icons.qrc">
+ <normaloff>:/icons/img/act_clear.png</normaloff>:/icons/img/act_clear.png</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -121,6 +135,8 @@
<header>sqlview.h</header>
</customwidget>
</customwidgets>
- <resources/>
+ <resources>
+ <include location="../icons.qrc"/>
+ </resources>
<connections/>
</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp
index f0f980d..23cb651 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.cpp
@@ -113,6 +113,12 @@ void EditorWindow::init()
resultsModel->setDb(currentDb);
ui->sqlEdit->setDb(currentDb);
+ connect(CFG_UI.General.SqlEditorCurrQueryHighlight, SIGNAL(changed(QVariant)), this, SLOT(queryHighlightingConfigChanged(QVariant)));
+ if (CFG_UI.General.SqlEditorCurrQueryHighlight.get())
+ ui->sqlEdit->setCurrentQueryHighlighting(true);
+
+ connect(ui->sqlEdit, SIGNAL(textChanged()), this, SLOT(checkTextChangedForSession()));
+
connect(resultsModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful()));
connect(resultsModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString)));
connect(resultsModel, SIGNAL(storeExecutionInHistory()), this, SLOT(storeExecutionInHistory()));
@@ -205,7 +211,7 @@ QAction* EditorWindow::getAction(EditorWindow::Action action)
return ExtActionContainer::getAction(action);
}
-QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery)
+QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery, QueryExecMode querySelectionMode)
{
QString sql;
if (ui->sqlEdit->textCursor().hasSelection())
@@ -213,7 +219,11 @@ QString EditorWindow::getQueryToExecute(bool doSelectCurrentQuery)
sql = ui->sqlEdit->textCursor().selectedText();
fixTextCursorSelectedText(sql);
}
- else if (CFG_UI.General.ExecuteCurrentQueryOnly.get())
+ else if (querySelectionMode == ALL)
+ {
+ sql = ui->sqlEdit->toPlainText();
+ }
+ else if (CFG_UI.General.ExecuteCurrentQueryOnly.get() || querySelectionMode == SINGLE)
{
ui->sqlEdit->saveSelection();
selectCurrentQuery(true);
@@ -237,7 +247,9 @@ bool EditorWindow::setCurrentDb(Db *db)
void EditorWindow::setContents(const QString &sql)
{
+ settingSqlContents = true;
ui->sqlEdit->setPlainText(sql);
+ settingSqlContents = false;
}
QString EditorWindow::getContents() const
@@ -394,6 +406,8 @@ void EditorWindow::createActions()
createAction(FOCUS_RESULTS_BELOW, tr("Focus results below", "sql editor"), this, SLOT(focusResultsBelow()), this);
createAction(FOCUS_EDITOR_ABOVE, tr("Focus SQL editor above", "sql editor"), this, SLOT(focusEditorAbove()), this);
createAction(DELETE_SINGLE_HISTORY_SQL, tr("Delete selected SQL history entries", "sql editor"), this, SLOT(deleteSelectedSqlHistory()), ui->historyList);
+ createAction(EXEC_ONE_QUERY, ICONS.EXEC_QUERY, tr("Execute single query under cursor"), this, SLOT(execOneQuery()), this);
+ createAction(EXEC_ALL_QUERIES, ICONS.EXEC_QUERY, tr("Execute all queries in editor"), this, SLOT(execAllQueries()), this);
// Static action triggers
connect(staticActions[RESULTS_IN_TAB], SIGNAL(triggered()), this, SLOT(updateResultsDisplayMode()));
@@ -421,35 +435,19 @@ void EditorWindow::selectCurrentQuery(bool fallBackToPreviousIfNecessary)
{
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);
- tokens.trim();
- tokens.trimRight(Token::OPERATOR, ";");
- if (tokens.size() == 0 && fallBackToPreviousIfNecessary)
- {
- // Fallback
- pos = contents.lastIndexOf(";", pos - 1);
- if (pos > -1)
- {
- query = getQueryWithPosition(contents, pos, &queryStartPos);
- tokens = Lexer::tokenize(query);
- tokens.trim();
- tokens.trimRight(Token::OPERATOR, ";");
- }
- }
+ QString contents = ui->sqlEdit->toPlainText();
+ QPair boundries = getQueryBoundriesForPosition(contents, pos, fallBackToPreviousIfNecessary);
- if (tokens.size() == 0)
+ if (boundries.second < 0)
{
qWarning() << "No tokens to select in EditorWindow::selectCurrentQuery().";
return;
}
cursor.clearSelection();
- cursor.setPosition(tokens.first()->start + queryStartPos);
- cursor.setPosition(tokens.last()->end + 1 + queryStartPos, QTextCursor::KeepAnchor);
+ cursor.setPosition(boundries.first);
+ cursor.setPosition(boundries.second, QTextCursor::KeepAnchor);
ui->sqlEdit->setTextCursor(cursor);
}
@@ -459,13 +457,13 @@ void EditorWindow::updateShortcutTips()
{
QString prevDbKey = actionMap[PREV_DB]->shortcut().toString(QKeySequence::NativeText);
QString nextDbKey = actionMap[NEXT_DB]->shortcut().toString(QKeySequence::NativeText);
- dbCombo->setToolTip(tr("Active database (%1/%2)").arg(prevDbKey).arg(nextDbKey));
+ dbCombo->setToolTip(tr("Active database (%1/%2)").arg(prevDbKey, nextDbKey));
}
}
-void EditorWindow::execQuery(bool explain)
+void EditorWindow::execQuery(bool explain, QueryExecMode querySelectionMode)
{
- QString sql = getQueryToExecute(true);
+ QString sql = getQueryToExecute(true, querySelectionMode);
QHash<QString, QVariant> bindParams;
bool proceed = processBindParams(sql, bindParams);
if (!proceed)
@@ -487,6 +485,16 @@ void EditorWindow::execQuery(bool explain)
}
}
+void EditorWindow::execOneQuery()
+{
+ execQuery(false, SINGLE);
+}
+
+void EditorWindow::execAllQueries()
+{
+ execQuery(false, ALL);
+}
+
void EditorWindow::explainQuery()
{
execQuery(true);
@@ -504,7 +512,6 @@ bool EditorWindow::processBindParams(QString& sql, QHash<QString, QVariant>& que
// Process bind tokens, prepare list for a dialog.
static_qstring(paramTpl, ":arg%1");
- QString arg;
QVector<BindParam*> bindParams;
QHash<QString, QString> namedBindParams;
BindParam* bindParam = nullptr;
@@ -539,14 +546,14 @@ bool EditorWindow::processBindParams(QString& sql, QHash<QString, QVariant>& que
// Transfer values from dialog to arguments for query
if (accepted)
{
- for (BindParam* bindParam : bindParams)
+ for (BindParam*& bindParam : bindParams)
queryParams[bindParam->newName] = bindParam->value;
sql = tokens.detokenize();
}
// Cleanup
- for (BindParam* bindParam : bindParams)
+ for (BindParam*& bindParam : bindParams)
delete bindParam;
return accepted;
@@ -676,7 +683,7 @@ void EditorWindow::deleteSelectedSqlHistory()
return;
QList<qint64> ids;
- for (const QModelIndex& idx : ui->historyList->selectionModel()->selectedRows(0))
+ for (QModelIndex& idx : ui->historyList->selectionModel()->selectedRows(0))
ids += idx.data().toLongLong();
CFG->deleteSqlHistory(ids);
@@ -747,6 +754,22 @@ void EditorWindow::updateState()
actionMap[EXPLAIN_QUERY]->setEnabled(!executionInProgress);
}
+void EditorWindow::checkTextChangedForSession()
+{
+ if (!ui->sqlEdit->getHighlightingSyntax() && !settingSqlContents)
+ emit sessionValueChanged();
+}
+
+void EditorWindow::queryHighlightingConfigChanged(const QVariant& enabled)
+{
+ ui->sqlEdit->setCurrentQueryHighlighting(enabled.toBool());
+}
+
+void EditorWindow::refreshValidDbObjects()
+{
+ ui->sqlEdit->refreshValidObjects();
+}
+
int qHash(EditorWindow::ActionGroup actionGroup)
{
return static_cast<int>(actionGroup);
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h
index 35a3b9b..977784e 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.h
@@ -26,6 +26,8 @@ class DbComboBox;
CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"),
CFG_KEY_ENTRY(EXEC_QUERY, Qt::Key_F9, QObject::tr("Execute query"))
+ CFG_KEY_ENTRY(EXEC_ONE_QUERY, Qt::CTRL + Qt::Key_F9, QObject::tr("Execute single query under cursor"))
+ CFG_KEY_ENTRY(EXEC_ALL_QUERIES, Qt::SHIFT + Qt::Key_F9, QObject::tr("Execute all queries in editor"))
CFG_KEY_ENTRY(EXPLAIN_QUERY, Qt::Key_F8, QObject::tr("Execute \"%1\" query").arg("EXPLAIN"))
CFG_KEY_ENTRY(PREV_DB, Qt::CTRL + Qt::Key_Up, QObject::tr("Switch current working database to previous on the list"))
CFG_KEY_ENTRY(NEXT_DB, Qt::CTRL + Qt::Key_Down, QObject::tr("Switch current working database to next on the list"))
@@ -38,8 +40,7 @@ CFG_KEY_LIST(EditorWindow, QObject::tr("SQL editor window"),
class GUI_API_EXPORT EditorWindow : public MdiChild
{
- Q_OBJECT
- Q_ENUMS(Action)
+ Q_OBJECT
public:
enum class ResultsDisplayMode
@@ -51,6 +52,8 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
enum Action
{
EXEC_QUERY,
+ EXEC_ONE_QUERY,
+ EXEC_ALL_QUERIES,
EXPLAIN_QUERY,
RESULTS_IN_TAB,
RESULTS_BELOW,
@@ -66,6 +69,14 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
CREATE_VIEW_FROM_QUERY,
DELETE_SINGLE_HISTORY_SQL
};
+ Q_ENUM(Action)
+
+ enum QueryExecMode
+ {
+ DEFAULT,
+ SINGLE,
+ ALL
+ };
enum ToolBar
{
@@ -89,7 +100,7 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
QSize sizeHint() const;
QAction* getAction(Action action);
- QString getQueryToExecute(bool doSelectCurrentQuery = false);
+ QString getQueryToExecute(bool doSelectCurrentQuery = false, QueryExecMode querySelectionMode = DEFAULT);
bool setCurrentDb(Db* db);
void setContents(const QString& sql);
QString getContents() const;
@@ -98,6 +109,7 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
SqlEditor* getEditor() const;
bool isUncommitted() const;
QString getQuitUncommittedConfirmMessage() const;
+ Db* getCurrentDb();
protected:
void changeEvent(QEvent *e);
@@ -105,7 +117,6 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
bool restoreSession(const QVariant& sessionValue);
Icon* getIconNameForMdiWindow();
QString getTitleForMdiWindow();
- Db* getCurrentDb();
private:
static void createStaticActions();
@@ -134,9 +145,12 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
qint64 lastQueryHistoryId = 0;
QString lastSuccessfulQuery;
QMenu* sqlHistoryMenu = nullptr;
+ bool settingSqlContents = false;
private slots:
- void execQuery(bool explain = false);
+ void execQuery(bool explain = false, QueryExecMode querySelectionMode = DEFAULT);
+ void execOneQuery();
+ void execAllQueries();
void explainQuery();
void dbChanged();
void executionSuccessful();
@@ -157,6 +171,11 @@ class GUI_API_EXPORT EditorWindow : public MdiChild
void exportResults();
void createViewFromQuery();
void updateState();
+ void checkTextChangedForSession();
+ void queryHighlightingConfigChanged(const QVariant& enabled);
+
+ public slots:
+ void refreshValidDbObjects();
};
GUI_API_EXPORT int qHash(EditorWindow::ActionGroup action);
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui
index f3f44e3..94e3dba 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/editorwindow.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string notr="true">SQL editor</string>
+ <string>SQL editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
@@ -77,7 +77,7 @@
</widget>
<widget class="QWidget" name="results">
<attribute name="title">
- <string notr="true">Results</string>
+ <string>Results</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp
index 3ac32ac..4d964c8 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.cpp
@@ -8,10 +8,8 @@
#include "services/pluginmanager.h"
#include "dbtree/dbtree.h"
#include "dbtree/dbtreemodel.h"
-#include "dbtree/dbtreeitem.h"
#include "iconmanager.h"
#include "syntaxhighlighterplugin.h"
-#include "sqlitesyntaxhighlighter.h"
#include "plugins/scriptingplugin.h"
#include "common/userinputfilter.h"
#include "selectabledbmodel.h"
@@ -19,9 +17,12 @@
#include <QDebug>
#include <QDesktopServices>
#include <QStyleFactory>
+#include <QSyntaxHighlighter>
// TODO handle plugin loading/unloading to update editor state
+CFG_KEYS_DEFINE(FunctionsEditor)
+
FunctionsEditor::FunctionsEditor(QWidget *parent) :
MdiChild(parent),
ui(new Ui::FunctionsEditor)
@@ -52,26 +53,26 @@ Icon* FunctionsEditor::getIconNameForMdiWindow()
QString FunctionsEditor::getTitleForMdiWindow()
{
- return tr("SQL function editor");
+ return tr("SQL functions editor");
}
void FunctionsEditor::createActions()
{
- createAction(COMMIT, ICONS.COMMIT, tr("Commit all function changes"), this, SLOT(commit()), ui->toolBar);
- createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all function changes"), this, SLOT(rollback()), ui->toolBar);
+ createAction(COMMIT, ICONS.COMMIT, tr("Commit all function changes"), this, SLOT(commit()), ui->toolBar, this);
+ createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all function changes"), this, SLOT(rollback()), ui->toolBar, this);
ui->toolBar->addSeparator();
- createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new function"), this, SLOT(newFunction()), ui->toolBar);
- createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected function"), this, SLOT(deleteFunction()), ui->toolBar);
+ createAction(ADD, ICONS.NEW_FUNCTION, tr("Create new function"), this, SLOT(newFunction()), ui->toolBar, this);
+ createAction(DELETE, ICONS.DELETE_FUNCTION, tr("Delete selected function"), this, SLOT(deleteFunction()), ui->toolBar, this);
ui->toolBar->addSeparator();
- createAction(HELP, ICONS.HELP, tr("Custom SQL functions manual"), this, SLOT(help()), ui->toolBar);
+ createAction(HELP, ICONS.HELP, tr("Custom SQL functions manual"), this, SLOT(help()), ui->toolBar, this);
// Args toolbar
- createAction(ARG_ADD, ICONS.INSERT_FN_ARG, tr("Add function argument"), this, SLOT(addFunctionArg()), ui->argsToolBar);
- createAction(ARG_EDIT, ICONS.RENAME_FN_ARG, tr("Rename function argument"), this, SLOT(editFunctionArg()), ui->argsToolBar);
- createAction(ARG_DEL, ICONS.DELETE_FN_ARG, tr("Delete function argument"), this, SLOT(delFunctionArg()), ui->argsToolBar);
+ createAction(ARG_ADD, ICONS.INSERT_FN_ARG, tr("Add function argument"), this, SLOT(addFunctionArg()), ui->argsToolBar, this);
+ createAction(ARG_EDIT, ICONS.RENAME_FN_ARG, tr("Rename function argument"), this, SLOT(editFunctionArg()), ui->argsToolBar, this);
+ createAction(ARG_DEL, ICONS.DELETE_FN_ARG, tr("Delete function argument"), this, SLOT(delFunctionArg()), ui->argsToolBar, this);
ui->argsToolBar->addSeparator();
- createAction(ARG_MOVE_UP, ICONS.MOVE_UP, tr("Move function argument up"), this, SLOT(moveFunctionArgUp()), ui->argsToolBar);
- createAction(ARG_MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move function argument down"), this, SLOT(moveFunctionArgDown()), ui->argsToolBar);
+ createAction(ARG_MOVE_UP, ICONS.MOVE_UP, tr("Move function argument up"), this, SLOT(moveFunctionArgUp()), ui->argsToolBar, this);
+ createAction(ARG_MOVE_DOWN, ICONS.MOVE_DOWN, tr("Move function argument down"), this, SLOT(moveFunctionArgDown()), ui->argsToolBar, this);
#ifdef Q_OS_MACX
QStyle *fusion = QStyleFactory::create("Fusion");
@@ -82,6 +83,9 @@ void FunctionsEditor::createActions()
void FunctionsEditor::setupDefShortcuts()
{
+ // Widget context
+ setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut);
+ BIND_SHORTCUTS(FunctionsEditor, Action);
}
QToolBar* FunctionsEditor::getToolBar(int toolbar) const
@@ -123,11 +127,12 @@ void FunctionsEditor::init()
connect(ui->mainCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified()));
connect(ui->finalCodeEdit, SIGNAL(textChanged()), this, SLOT(updateModified()));
connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SLOT(updateModified()));
- connect(ui->undefArgsCheck, SIGNAL(clicked()), this, SLOT(updateModified()));
+ connect(ui->undefArgsCheck, SIGNAL(toggled(bool)), this, SLOT(updateModified()));
connect(ui->allDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified()));
connect(ui->selDatabasesRadio, SIGNAL(clicked()), this, SLOT(updateModified()));
connect(ui->langCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateModified()));
connect(ui->typeCombo, SIGNAL(currentTextChanged(QString)), this, SLOT(updateModified()));
+ connect(ui->deterministicCheck, SIGNAL(toggled(bool)), this, SLOT(updateModified()));
connect(ui->argsList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(updateArgsState()));
connect(ui->argsList->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateModified()));
@@ -141,13 +146,13 @@ void FunctionsEditor::init()
model->setData(FUNCTIONS->getAllScriptFunctions());
// Language plugins
- for (ScriptingPlugin* plugin : PLUGINS->getLoadedPlugins<ScriptingPlugin>())
+ for (ScriptingPlugin*& plugin : PLUGINS->getLoadedPlugins<ScriptingPlugin>())
scriptingPlugins[plugin->getLanguage()] = plugin;
ui->langCombo->addItems(scriptingPlugins.keys());
// Syntax highlighting plugins
- for (SyntaxHighlighterPlugin* plugin : PLUGINS->getLoadedPlugins<SyntaxHighlighterPlugin>())
+ for (SyntaxHighlighterPlugin*& plugin : PLUGINS->getLoadedPlugins<SyntaxHighlighterPlugin>())
highlighterPlugins[plugin->getLanguageName()] = plugin;
updateState();
@@ -170,6 +175,7 @@ void FunctionsEditor::functionDeselected(int row)
model->setUndefinedArgs(row, ui->undefArgsCheck->isChecked());
model->setAllDatabases(row, ui->allDatabasesRadio->isChecked());
model->setCode(row, ui->mainCodeEdit->toPlainText());
+ model->setDeterministic(row, ui->deterministicCheck->isChecked());
model->setModified(row, currentModified);
if (model->isAggregate(row))
@@ -201,6 +207,7 @@ void FunctionsEditor::functionSelected(int row)
ui->finalCodeEdit->setPlainText(model->getFinalCode(row));
ui->undefArgsCheck->setChecked(model->getUndefinedArgs(row));
ui->langCombo->setCurrentText(model->getLang(row));
+ ui->deterministicCheck->setChecked(model->isDeterministic(row));
// Arguments
ui->argsList->clear();
@@ -248,6 +255,7 @@ void FunctionsEditor::clearEdits()
ui->allDatabasesRadio->setChecked(true);
ui->typeCombo->setCurrentIndex(0);
ui->langCombo->setCurrentIndex(-1);
+ ui->deterministicCheck->setChecked(false);
}
void FunctionsEditor::selectFunction(int row)
@@ -374,9 +382,10 @@ void FunctionsEditor::updateModified()
bool argDiff = getCurrentArgList() != model->getArguments(row);
bool dbDiff = toSet(getCurrentDatabases()) != toSet(model->getDatabases(row)); // QSet to ignore order
bool typeDiff = model->getType(row) != getCurrentFunctionType();
+ bool deterministicDiff = model->isDeterministic(row) != ui->deterministicCheck->isChecked();
currentModified = (nameDiff || codeDiff || typeDiff || langDiff || undefArgsDiff || allDatabasesDiff || argDiff || dbDiff ||
- initCodeDiff || finalCodeDiff);
+ initCodeDiff || finalCodeDiff || deterministicDiff);
}
updateCurrentFunctionState();
@@ -415,6 +424,7 @@ void FunctionsEditor::updateCurrentFunctionState()
ui->mainCodeGroup->setEnabled(langOk);
ui->finalCodeGroup->setEnabled(langOk);
ui->argsGroup->setEnabled(langOk);
+ ui->deterministicCheck->setEnabled(langOk);
ui->databasesGroup->setEnabled(langOk);
ui->nameEdit->setEnabled(langOk);
ui->nameLabel->setEnabled(langOk);
@@ -624,7 +634,7 @@ QVariant FunctionsEditor::saveSession()
bool FunctionsEditor::isUncommitted() const
{
- return model->isModified();
+ return model->isModified() || currentModified;
}
QString FunctionsEditor::getQuitUncommittedConfirmMessage() const
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h
index 72455d3..d3fcf12 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.h
@@ -3,7 +3,6 @@
#include "mdichild.h"
#include "common/extactioncontainer.h"
-#include "services/config.h"
#include "services/functionmanager.h"
#include <QItemSelection>
#include <QSortFilterProxyModel>
@@ -20,6 +19,11 @@ class QTreeWidgetItem;
class QSyntaxHighlighter;
class SelectableDbModel;
+CFG_KEY_LIST(FunctionsEditor, QObject::tr("A function editor window"),
+ CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes"))
+ CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes"))
+)
+
class GUI_API_EXPORT FunctionsEditor : public MdiChild
{
Q_OBJECT
@@ -38,6 +42,7 @@ class GUI_API_EXPORT FunctionsEditor : public MdiChild
ARG_MOVE_DOWN,
HELP
};
+ Q_ENUM(Action)
enum ToolBar
{
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui
index 17c3859..2c78fbe 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditor.ui
@@ -78,7 +78,7 @@
</size>
</property>
<property name="placeholderText">
- <string>Filter funtions</string>
+ <string>Filter functions</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
@@ -228,7 +228,7 @@
<bool>false</bool>
</attribute>
<attribute name="headerDefaultSectionSize">
- <number>0</number>
+ <number>35</number>
</attribute>
</widget>
</item>
@@ -247,13 +247,19 @@
<property name="bottomMargin">
<number>0</number>
</property>
- <item row="0" column="1">
+ <item row="0" column="2">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
+ <item row="1" column="3">
+ <widget class="QComboBox" name="langCombo"/>
+ </item>
+ <item row="1" column="2">
+ <widget class="QComboBox" name="typeCombo"/>
+ </item>
<item row="0" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
@@ -264,18 +270,19 @@
<item row="1" column="0">
<widget class="QLineEdit" name="nameEdit"/>
</item>
- <item row="1" column="1">
- <widget class="QComboBox" name="typeCombo"/>
- </item>
- <item row="0" column="2">
+ <item row="0" column="3">
<widget class="QLabel" name="langLabel">
<property name="text">
<string>Implementation language:</string>
</property>
</widget>
</item>
- <item row="1" column="2">
- <widget class="QComboBox" name="langCombo"/>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="deterministicCheck">
+ <property name="text">
+ <string>Deterministic</string>
+ </property>
+ </widget>
</item>
</layout>
</widget>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp
index 8d6d87c..46ae8d9 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.cpp
@@ -28,7 +28,7 @@ FunctionsEditorModel::FunctionsEditorModel(QObject *parent) :
void FunctionsEditorModel::clearModified()
{
beginResetModel();
- for (Function* func : functionList)
+ for (Function*& func : functionList)
func->modified = false;
listModified = false;
@@ -170,6 +170,16 @@ bool FunctionsEditorModel::isScalar(int row) const
GETTER(functionList[row]->data.type == FunctionManager::ScriptFunction::SCALAR, false);
}
+void FunctionsEditorModel::setDeterministic(int row, bool value)
+{
+ SETTER(functionList[row]->data.deterministic, value);
+}
+
+bool FunctionsEditorModel::isDeterministic(int row) const
+{
+ GETTER(functionList[row]->data.deterministic, false);
+}
+
QStringList FunctionsEditorModel::getArguments(int row) const
{
GETTER(functionList[row]->data.arguments, QStringList());
@@ -194,7 +204,7 @@ void FunctionsEditorModel::setData(const QList<FunctionManager::ScriptFunction*>
{
beginResetModel();
- for (Function* functionPtr : functionList)
+ for (Function*& functionPtr : functionList)
delete functionPtr;
functionList.clear();
@@ -259,7 +269,7 @@ void FunctionsEditorModel::validateNames()
StrHash<QList<int>> counter;
int row = 0;
- for (Function* func : functionList)
+ for (Function*& func : functionList)
{
func->valid &= true;
counter[func->data.name] << row++;
@@ -322,7 +332,7 @@ QVariant FunctionsEditorModel::data(const QModelIndex& index, int role) const
void FunctionsEditorModel::init()
{
- for (ScriptingPlugin* plugin : PLUGINS->getLoadedPlugins<ScriptingPlugin>())
+ for (ScriptingPlugin*& plugin : PLUGINS->getLoadedPlugins<ScriptingPlugin>())
langToIcon[plugin->getLanguage()] = QIcon(plugin->getIconPath());
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h
index 79f073f..7caf06c 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/functionseditormodel.h
@@ -1,7 +1,6 @@
#ifndef FUNCTIONSEDITORMODEL_H
#define FUNCTIONSEDITORMODEL_H
-#include "services/config.h"
#include "services/functionmanager.h"
#include "guiSQLiteStudio_global.h"
#include <QIcon>
@@ -49,6 +48,8 @@ class GUI_API_EXPORT FunctionsEditorModel : public QAbstractListModel
void setType(int row, FunctionManager::ScriptFunction::Type type);
bool isAggregate(int row) const;
bool isScalar(int row) const;
+ void setDeterministic(int row, bool value);
+ bool isDeterministic(int row) const;
bool getUndefinedArgs(int row) const;
void setUndefinedArgs(int row, bool value);
bool getAllDatabases(int row) const;
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp
index 4351312..f8dd621 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.cpp
@@ -9,13 +9,14 @@
#include "uiconfig.h"
#include "db/db.h"
#include "services/dbmanager.h"
-#include "services/notifymanager.h"
#include "common/lazytrigger.h"
#include "common/compatibility.h"
#include <QDesktopServices>
#include <QFileDialog>
#include <QSortFilterProxyModel>
+CFG_KEYS_DEFINE(SqliteExtensionEditor)
+
SqliteExtensionEditor::SqliteExtensionEditor(QWidget *parent) :
MdiChild(parent),
ui(new Ui::SqliteExtensionEditor)
@@ -36,7 +37,7 @@ bool SqliteExtensionEditor::restoreSessionNextTime()
bool SqliteExtensionEditor::isUncommitted() const
{
- return model->isModified();
+ return model->isModified() || currentModified;
}
QString SqliteExtensionEditor::getQuitUncommittedConfirmMessage() const
@@ -67,17 +68,20 @@ QString SqliteExtensionEditor::getTitleForMdiWindow()
void SqliteExtensionEditor::createActions()
{
- createAction(COMMIT, ICONS.COMMIT, tr("Commit all extension changes"), this, SLOT(commit()), ui->toolbar);
- createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all extension changes"), this, SLOT(rollback()), ui->toolbar);
+ createAction(COMMIT, ICONS.COMMIT, tr("Commit all extension changes"), this, SLOT(commit()), ui->toolbar, this);
+ createAction(ROLLBACK, ICONS.ROLLBACK, tr("Rollback all extension changes"), this, SLOT(rollback()), ui->toolbar, this);
ui->toolbar->addSeparator();
- createAction(ADD, ICONS.EXTENSION_ADD, tr("Add new extension"), this, SLOT(newExtension()), ui->toolbar);
- createAction(DELETE, ICONS.EXTENSION_DELETE, tr("Remove selected extension"), this, SLOT(deleteExtension()), ui->toolbar);
+ createAction(ADD, ICONS.EXTENSION_ADD, tr("Add new extension"), this, SLOT(newExtension()), ui->toolbar, this);
+ createAction(DELETE, ICONS.EXTENSION_DELETE, tr("Remove selected extension"), this, SLOT(deleteExtension()), ui->toolbar, this);
ui->toolbar->addSeparator();
- createAction(HELP, ICONS.HELP, tr("Editing extensions manual"), this, SLOT(help()), ui->toolbar);
+ createAction(HELP, ICONS.HELP, tr("Editing extensions manual"), this, SLOT(help()), ui->toolbar, this);
}
void SqliteExtensionEditor::setupDefShortcuts()
{
+ // Widget context
+ setShortcutContext({COMMIT, ROLLBACK}, Qt::WidgetWithChildrenShortcut);
+ BIND_SHORTCUTS(SqliteExtensionEditor, Action);
}
QToolBar* SqliteExtensionEditor::getToolBar(int toolbar) const
@@ -224,6 +228,13 @@ bool SqliteExtensionEditor::validateExtension(int row)
return validateExtension(filePath, initFunc, nullptr, nullptr, new QString);
}
+bool SqliteExtensionEditor::validateCurrentExtension()
+{
+ QString filePath = ui->fileEdit->text();
+ QString initFunc = ui->initEdit->text();
+ return validateExtension(filePath, initFunc, nullptr, nullptr, new QString);
+}
+
bool SqliteExtensionEditor::validateExtension(const QString& filePath, const QString& initFunc, bool* fileOk, bool* initOk, QString* fileError)
{
bool localFileOk = true;
@@ -326,7 +337,7 @@ void SqliteExtensionEditor::deleteExtension()
void SqliteExtensionEditor::updateState()
{
bool modified = model->isModified() || currentModified;
- bool valid = model->isValid();
+ bool valid = model->isValid() && validateCurrentExtension();
actionMap[COMMIT]->setEnabled(modified && valid);
actionMap[ROLLBACK]->setEnabled(modified);
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h
index 5d81085..8f67415 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.h
@@ -17,6 +17,11 @@ class SelectableDbModel;
class Db;
class LazyTrigger;
+CFG_KEY_LIST(SqliteExtensionEditor, QObject::tr("A SQLite extension editor window"),
+ CFG_KEY_ENTRY(COMMIT, QKeySequence::Save, QObject::tr("Commit the pending changes"))
+ CFG_KEY_ENTRY(ROLLBACK, QKeySequence::Cancel, QObject::tr("Rollback the pending changes"))
+)
+
class SqliteExtensionEditor : public MdiChild
{
Q_OBJECT
@@ -30,6 +35,7 @@ class SqliteExtensionEditor : public MdiChild
DELETE,
HELP
};
+ Q_ENUM(Action)
enum ToolBar
{
@@ -65,6 +71,7 @@ class SqliteExtensionEditor : public MdiChild
bool* initOk = nullptr,
QString* fileError = nullptr);
bool validateExtension(int row);
+ bool validateCurrentExtension();
bool validateExtension(const QString& filePath,
const QString& initFunc,
bool* fileOk = nullptr,
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui
index 747aa7f..93e938d 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditor.ui
@@ -14,6 +14,18 @@
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
<widget class="QToolBar" name="toolbar"/>
</item>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp
index 6e94a64..832875d 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/sqliteextensioneditormodel.cpp
@@ -126,7 +126,7 @@ bool SqliteExtensionEditorModel::isValid() const
{
for (Extension* ext : extensionList)
{
- if (!ext->valid)
+ if (ext->modified && !ext->valid)
return false;
}
return true;
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
index 41d6ed9..bf8c583 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp
@@ -1,6 +1,8 @@
#include "tablestructuremodel.h"
+#include "datagrid/sqlquerymodelcolumn.h"
#include "iconmanager.h"
#include "common/unused.h"
+#include "parser/ast/sqlitecreatetable.h"
#include <QFont>
#include <QDebug>
#include <QMimeData>
@@ -45,6 +47,9 @@ QVariant TableStructureModel::data(const QModelIndex& index, int role) const
if (createTable->columns.size() <= row)
return QVariant();
+ if (role == Qt::ToolTipRole)
+ return getToolTip(row, getHeaderColumn(index.column()));
+
switch (getHeaderColumn(index.column()))
{
case TableStructureModel::Columns::NAME:
@@ -511,6 +516,98 @@ bool TableStructureModel::isColumnGenerate(SqliteCreateTable::Column* column) co
return false;
}
+QString TableStructureModel::getToolTip(int row, Columns modelColumn) const
+{
+ static const QString tooltipTpl = "<table><tr><td width=16><img src=\"%1\"/></td><td style=\"white-space: pre\"><b>%2</b></td><td>%3</td></tr></table>";
+
+ if (row >= createTable->columns.size())
+ return QString();
+
+ SqliteCreateTable::Column* col = createTable->columns[row];
+ if (col->constraints.isEmpty() && createTable->constraints.isEmpty())
+ return QString();
+
+ SqliteCreateTable::Column::Constraint::Type lookFor;
+ SqliteCreateTable::Constraint::Type lookForTableType;
+ bool tableConstrDefined = false;
+ switch (modelColumn)
+ {
+ case Columns::PK:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::PRIMARY_KEY;
+ lookForTableType = SqliteCreateTable::Constraint::Type::PRIMARY_KEY;
+ tableConstrDefined = true;
+ break;
+ case Columns::FK:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::FOREIGN_KEY;
+ lookForTableType = SqliteCreateTable::Constraint::Type::FOREIGN_KEY;
+ tableConstrDefined = true;
+ break;
+ case Columns::UNIQUE:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::UNIQUE;
+ lookForTableType = SqliteCreateTable::Constraint::Type::UNIQUE;
+ tableConstrDefined = true;
+ break;
+ case Columns::CHECK:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::CHECK;
+ // Not defined for Table-level constraint, because it cannot (easily) determin if it's affected by table CHECK.
+ break;
+ case Columns::NOTNULL:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::NOT_NULL;
+ break;
+ case Columns::COLLATE:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::COLLATE;
+ break;
+ case Columns::GENERATED:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::GENERATED;
+ break;
+ case Columns::DEFAULT:
+ lookFor = SqliteCreateTable::Column::Constraint::Type::DEFAULT;
+ break;
+ case Columns::NAME:
+ case Columns::TYPE:
+ return QString();
+ }
+
+ SqliteCreateTable::Column::Constraint* constraint = findFirst<SqliteCreateTable::Column::Constraint>(
+ col->constraints,
+ [lookFor](SqliteCreateTable::Column::Constraint* constr) -> bool
+ {
+ return constr->type == lookFor;
+ }
+ );
+
+ SqliteCreateTable::Constraint* tableConstraint = nullptr;
+ if (tableConstrDefined)
+ {
+ tableConstraint = findFirst<SqliteCreateTable::Constraint>(
+ createTable->constraints,
+ [lookForTableType](SqliteCreateTable::Constraint* constr) -> bool
+ {
+ return constr->type == lookForTableType;
+ }
+ );
+ }
+
+ if (!constraint && !tableConstraint)
+ return QString();
+
+ if (constraint)
+ {
+ SqlQueryModelColumn::Constraint* constr = SqlQueryModelColumn::Constraint::create(constraint);
+ QString tooltip = tooltipTpl.arg(constr->getIcon()->toUrl(), constr->getTypeString(), constr->getDetails());
+ delete constr;
+ return tooltip;
+ }
+
+ SqlQueryModelColumn::Constraint* constr = SqlQueryModelColumn::Constraint::create(createTable->columns[row]->name, tableConstraint);
+ if (!constr)
+ return QString();
+
+ QString tooltip = tooltipTpl.arg(constr->getIcon()->toUrl(), constr->getTypeString(), constr->getDetails());
+ delete constr;
+ return tooltip;
+}
+
void TableStructureModel::setCreateTable(SqliteCreateTable* value)
{
beginResetModel();
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h
index 088a964..d003715 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.h
@@ -75,6 +75,7 @@ class GUI_API_EXPORT TableStructureModel : public QAbstractTableModel
bool isColumnNotNull(SqliteCreateTable::Column* column) const;
bool isColumnCollate(SqliteCreateTable::Column* column) const;
bool isColumnGenerate(SqliteCreateTable::Column* column) const;
+ QString getToolTip(int row, TableStructureModel::Columns modelColumn) 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 c9356d8..86f48bf 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
@@ -2,21 +2,16 @@
#include "ui_tablewindow.h"
#include "services/dbmanager.h"
#include "services/notifymanager.h"
-#include "sqlitestudio.h"
#include "common/unused.h"
#include "schemaresolver.h"
#include "iconmanager.h"
-#include "common/intvalidator.h"
-#include "common/extlineedit.h"
#include "datagrid/sqltablemodel.h"
-#include "common/extaction.h"
#include "mainwindow.h"
#include "tablestructuremodel.h"
#include "tableconstraintsmodel.h"
#include "dialogs/columndialog.h"
#include "dialogs/constraintdialog.h"
#include "mdiarea.h"
-#include "sqlitesyntaxhighlighter.h"
#include "dialogs/newconstraintdialog.h"
#include "db/chainexecutor.h"
#include "common/widgetcover.h"
@@ -37,7 +32,6 @@
#include "themetuner.h"
#include "dialogs/importdialog.h"
#include "dialogs/populatedialog.h"
-#include "datagrid/sqlqueryitem.h"
#include "common/dbcombobox.h"
#include <QMenu>
#include <QToolButton>
@@ -217,10 +211,10 @@ void TableWindow::createActions()
void TableWindow::createStructureActions()
{
- createAction(REFRESH_STRUCTURE, ICONS.RELOAD, tr("Refresh structure", "table window"), this, SLOT(refreshStructure()), ui->structureToolBar);
+ createAction(REFRESH_STRUCTURE, ICONS.RELOAD, tr("Refresh structure", "table window"), this, SLOT(refreshStructure()), ui->structureToolBar, ui->structureView);
ui->structureToolBar->addSeparator();
- createAction(COMMIT_STRUCTURE, ICONS.COMMIT, tr("Commit structure changes", "table window"), this, SLOT(commitStructure()), ui->structureToolBar);
- createAction(ROLLBACK_STRUCTURE, ICONS.ROLLBACK, tr("Rollback structure changes", "table window"), this, SLOT(rollbackStructure()), ui->structureToolBar);
+ createAction(COMMIT_STRUCTURE, ICONS.COMMIT, tr("Commit structure changes", "table window"), this, SLOT(commitStructure()), ui->structureToolBar, ui->structureView);
+ createAction(ROLLBACK_STRUCTURE, ICONS.ROLLBACK, tr("Rollback structure changes", "table window"), this, SLOT(rollbackStructure()), ui->structureToolBar, ui->structureView);
createAction(ADD_COLUMN, ICONS.TABLE_COLUMN_ADD, tr("Add column", "table window"), this, SLOT(addColumn()), ui->structureToolBar, ui->structureView);
createAction(EDIT_COLUMN, ICONS.TABLE_COLUMN_EDIT, tr("Edit column", "table window"), this, SLOT(editColumn()), ui->structureToolBar, ui->structureView);
createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "table window"), this, SLOT(delColumn()), ui->structureToolBar, ui->structureView);
@@ -348,10 +342,10 @@ void TableWindow::executeStructureChanges()
MessageListDialog dialog(tr("Following problems will take place while modifying the table.\n"
"Would you like to proceed?", "table window"));
dialog.setWindowTitle(tr("Table modification", "table window"));
- for (const QString& error : tableModifier->getErrors())
+ for (QString& error : tableModifier->getErrors())
dialog.addError(error);
- for (const QString& warn : tableModifier->getWarnings())
+ for (QString& warn : tableModifier->getWarnings())
dialog.addWarning(warn);
if (dialog.exec() != QDialog::Accepted)
@@ -458,8 +452,9 @@ void TableWindow::setupDefShortcuts()
{
// Widget context
setShortcutContext({
+ COMMIT_STRUCTURE,
+ ROLLBACK_STRUCTURE,
REFRESH_STRUCTURE,
- REFRESH_INDEXES,
REFRESH_TRIGGERS,
ADD_COLUMN,
EDIT_COLUMN,
@@ -567,6 +562,7 @@ void TableWindow::initDbAndTable()
ui->constraintsView->setModel(constraintTabModel);
connect(ui->withoutRowIdCheck, SIGNAL(clicked()), this, SLOT(withOutRowIdChanged()));
+ connect(ui->strictTableCheck, SIGNAL(clicked()), this, SLOT(strictChanged()));
parseDdl();
updateIndexes();
@@ -614,7 +610,8 @@ void TableWindow::parseDdl()
structureModel->setCreateTable(createTable.data());
structureConstraintsModel->setCreateTable(createTable.data());
constraintTabModel->setCreateTable(createTable.data());
- ui->withoutRowIdCheck->setChecked(!createTable->withOutRowId.isNull());
+ ui->withoutRowIdCheck->setChecked(createTable->withOutRowId);
+ ui->strictTableCheck->setChecked(createTable->strict);
ui->tableConstraintsView->resizeColumnsToContents();
ui->structureView->resizeColumnsToContents();
ui->constraintsView->resizeColumnsToContents();
@@ -645,7 +642,7 @@ void TableWindow::changeEvent(QEvent *e)
QVariant TableWindow::saveSession()
{
- if (!db || DBLIST->isTemporary(db))
+ if (!db || DBLIST->isTemporary(db) || !existingTable)
return QVariant();
QHash<QString,QVariant> sessionValue;
@@ -681,7 +678,7 @@ bool TableWindow::restoreSession(const QVariant& sessionValue)
SchemaResolver resolver(db);
if (!resolver.getTables(database).contains(table, Qt::CaseInsensitive))
{
- notifyWarn(tr("Could not restore window '%1'', because the table %2 doesn't exist in the database %3.").arg(value["title"].toString(), table, db->getName()));
+ notifyWarn(tr("Could not restore window '%1', because the table %2 doesn't exist in the database %3.").arg(value["title"].toString(), table, db->getName()));
return false;
}
@@ -759,6 +756,7 @@ void TableWindow::checkIfTableDeleted(const QString& database, const QString& ob
if (object.compare(table, Qt::CaseInsensitive) == 0)
{
dbClosedFinalCleanup();
+ MDIAREA->enforceCurrentTaskSelectionAfterWindowClose();
getMdiWindow()->close();
}
}
@@ -832,6 +830,8 @@ void TableWindow::changesSuccessfullyCommitted()
updateNewTableState();
updateWindowTitle();
+ emit sessionValueChanged();
+
NotifyManager* notifyManager = NotifyManager::getInstance();
if (oldTable.compare(table, Qt::CaseInsensitive) == 0 || oldTable.isEmpty())
{
@@ -883,6 +883,8 @@ void TableWindow::rollbackStructure()
structureConstraintsModel->setCreateTable(createTable.data());
constraintTabModel->setCreateTable(createTable.data());
ui->tableNameEdit->setText(createTable->table);
+ ui->withoutRowIdCheck->setChecked(createTable->withOutRowId);
+ ui->strictTableCheck->setChecked(createTable->strict);
updateStructureCommitState();
updateStructureToolbarState();
@@ -1040,6 +1042,26 @@ bool TableWindow::validate(bool skipWarning)
}
}
+ if (ui->strictTableCheck->isChecked())
+ {
+ QStringList nonStrictColumns;
+ for (SqliteCreateTable::Column* column : createTable->columns)
+ {
+ if (DataType::isStrict(column->type->name))
+ continue;
+
+ nonStrictColumns << column->name;
+ }
+
+ if (!nonStrictColumns.isEmpty())
+ {
+ notifyError(tr("Following columns have non-strict data type: %1."
+ " Either disable strict mode of the table, or fix column data types. Valid strict data types are: %2")
+ .arg(nonStrictColumns.join(", "), DataType::getStrictValueNames().join(", ")));
+ return false;
+ }
+ }
+
return true;
}
@@ -1049,7 +1071,8 @@ bool TableWindow::isModified() const
(structureConstraintsModel && structureConstraintsModel->isModified()) ||
(originalCreateTable &&
(originalCreateTable->table != ui->tableNameEdit->text() ||
- originalCreateTable->withOutRowId != createTable->withOutRowId)
+ originalCreateTable->withOutRowId != createTable->withOutRowId ||
+ originalCreateTable->strict != createTable->strict)
) ||
!existingTable;
}
@@ -1355,7 +1378,26 @@ void TableWindow::withOutRowIdChanged()
if (!createTable)
return;
- createTable->withOutRowId = ui->withoutRowIdCheck->isChecked() ? QStringLiteral("ROWID") : QString();
+ createTable->withOutRowId = ui->withoutRowIdCheck->isChecked();
+ updateDdlTab();
+ emit modifyStatusChanged();
+}
+
+void TableWindow::strictChanged()
+{
+ if (!createTable)
+ return;
+
+ createTable->strict = ui->strictTableCheck->isChecked();
+ if (createTable->strict)
+ {
+ for (SqliteCreateTable::Column* column : createTable->columns)
+ {
+ column->type->precision = QVariant();
+ column->type->scale = QVariant();
+ }
+ }
+
updateDdlTab();
emit modifyStatusChanged();
}
@@ -1401,7 +1443,7 @@ void TableWindow::delIndex()
return;
DbObjectDialogs dialogs(db, this);
- dialogs.dropObject(index);
+ dialogs.dropObject(DbObjectDialogs::Type::INDEX, index);
updateIndexes();
}
@@ -1448,7 +1490,7 @@ void TableWindow::delTrigger()
return;
DbObjectDialogs dialogs(db, this);
- dialogs.dropObject(trigger);
+ dialogs.dropObject(DbObjectDialogs::Type::TRIGGER, trigger);
updateTriggers();
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h
index fb4d67d..e71162d 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h
@@ -30,31 +30,32 @@ namespace Ui {
}
CFG_KEY_LIST(TableWindow, QObject::tr("Table window"),
- CFG_KEY_ENTRY(REFRESH_STRUCTURE, Qt::Key_F5, QObject::tr("Refresh table structure"))
- CFG_KEY_ENTRY(ADD_COLUMN, Qt::Key_Insert, QObject::tr("Add new column"))
- CFG_KEY_ENTRY(EDIT_COLUMN, Qt::Key_Return, QObject::tr("Edit selected column"))
- CFG_KEY_ENTRY(DEL_COLUMN, Qt::Key_Delete, QObject::tr("Delete selected column"))
- CFG_KEY_ENTRY(EXPORT, Qt::CTRL + Qt::Key_E, QObject::tr("Export table data"))
- CFG_KEY_ENTRY(IMPORT, Qt::CTRL + Qt::Key_I, QObject::tr("Import data to the table"))
- CFG_KEY_ENTRY(ADD_TABLE_CONSTRAINT, Qt::Key_Insert, QObject::tr("Add new table constraint"))
- CFG_KEY_ENTRY(EDIT_TABLE_CONSTRAINT, Qt::Key_Return, QObject::tr("Edit selected table constraint"))
- CFG_KEY_ENTRY(DEL_TABLE_CONSTRAINT, Qt::Key_Delete, QObject::tr("Delete selected table constraint"))
- CFG_KEY_ENTRY(REFRESH_INDEXES, Qt::Key_F5, QObject::tr("Refresh table index list"))
- CFG_KEY_ENTRY(ADD_INDEX, Qt::Key_Insert, QObject::tr("Add new index"))
- CFG_KEY_ENTRY(EDIT_INDEX, Qt::Key_Return, QObject::tr("Edit selected index"))
- CFG_KEY_ENTRY(DEL_INDEX, Qt::Key_Delete, QObject::tr("Delete selected index"))
- CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh table trigger list"))
- CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger"))
- CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger"))
- CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger"))
- CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab"))
- CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab"))
+ CFG_KEY_ENTRY(COMMIT_STRUCTURE, QKeySequence::Save, QObject::tr("Commit the table structure"))
+ CFG_KEY_ENTRY(ROLLBACK_STRUCTURE, QKeySequence::Cancel, QObject::tr("Rollback pending changes in the table structure"))
+ CFG_KEY_ENTRY(REFRESH_STRUCTURE, Qt::Key_F5, QObject::tr("Refresh table structure"))
+ CFG_KEY_ENTRY(ADD_COLUMN, Qt::Key_Insert, QObject::tr("Add new column"))
+ CFG_KEY_ENTRY(EDIT_COLUMN, Qt::Key_Return, QObject::tr("Edit selected column"))
+ CFG_KEY_ENTRY(DEL_COLUMN, Qt::Key_Delete, QObject::tr("Delete selected column"))
+ CFG_KEY_ENTRY(EXPORT, Qt::CTRL + Qt::Key_E, QObject::tr("Export table data"))
+ CFG_KEY_ENTRY(IMPORT, Qt::CTRL + Qt::Key_I, QObject::tr("Import data to the table"))
+ CFG_KEY_ENTRY(ADD_TABLE_CONSTRAINT, Qt::Key_Insert, QObject::tr("Add new table constraint"))
+ CFG_KEY_ENTRY(EDIT_TABLE_CONSTRAINT, Qt::Key_Return, QObject::tr("Edit selected table constraint"))
+ CFG_KEY_ENTRY(DEL_TABLE_CONSTRAINT, Qt::Key_Delete, QObject::tr("Delete selected table constraint"))
+ CFG_KEY_ENTRY(REFRESH_INDEXES, Qt::Key_F5, QObject::tr("Refresh table index list"))
+ CFG_KEY_ENTRY(ADD_INDEX, Qt::Key_Insert, QObject::tr("Add new index"))
+ CFG_KEY_ENTRY(EDIT_INDEX, Qt::Key_Return, QObject::tr("Edit selected index"))
+ CFG_KEY_ENTRY(DEL_INDEX, Qt::Key_Delete, QObject::tr("Delete selected index"))
+ CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh table trigger list"))
+ CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger"))
+ CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger"))
+ CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger"))
+ CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab"))
+ CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab"))
)
class GUI_API_EXPORT TableWindow : public MdiChild
{
- Q_OBJECT
- Q_ENUMS(Action)
+ Q_OBJECT
public:
enum Action
@@ -98,6 +99,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild
NEXT_TAB,
PREV_TAB
};
+ Q_ENUM(Action)
enum ToolBar
{
@@ -230,6 +232,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild
void constraintsViewDoubleClicked(const QModelIndex &index);
void nameChanged();
void withOutRowIdChanged();
+ void strictChanged();
void addIndex();
void editCurrentIndex();
void indexViewDoubleClicked(const QModelIndex& idx);
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui
index 4ae8bdf..851daf1 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui
@@ -83,12 +83,25 @@
</item>
<item>
<widget class="QCheckBox" name="withoutRowIdCheck">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables/disabled the WITHOUT ROWID clause on the table. Such table will no longer have the &amp;quot;rowid&amp;quot; hidden column. For such table an explicit PRIMARY KEY column is mandatory. You can read more details on this in SQLite official documentation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
<property name="text">
<string notr="true">WITHOUT ROWID</string>
</property>
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="strictTableCheck">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enables/disables the STRICT clause for the table. Such table makes strict verification of data stored in columns against declared data types for these columns. This is similar to how data types are usually enforced in most other database engines. Keep it disabled to use the classical SQLite bahavior (i.e. no data type enforcement). You can find more details in the official SQLite documentation.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string notr="true">STRICT</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
index 2181934..44025e5 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
@@ -6,7 +6,6 @@
#include "services/dbmanager.h"
#include "mainwindow.h"
#include "mdiarea.h"
-#include "sqlitesyntaxhighlighter.h"
#include "datagrid/sqlquerymodel.h"
#include "common/utils_sql.h"
#include "viewmodifier.h"
@@ -180,10 +179,13 @@ void ViewWindow::setupDefShortcuts()
{
// Widget context
setShortcutContext({
+ COMMIT_QUERY,
+ ROLLBACK_QUERY,
REFRESH_TRIGGERS,
ADD_TRIGGER,
EDIT_TRIGGER,
DEL_TRIGGER,
+ EXECUTE_QUERY
},
Qt::WidgetWithChildrenShortcut);
@@ -223,6 +225,7 @@ void ViewWindow::init()
ui->queryEdit->setVirtualSqlExpression("CREATE VIEW name AS %1");
ui->queryEdit->setDb(db);
+ ui->queryEdit->setOpenSaveActionsEnabled(false);
connect(dataModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful()));
connect(dataModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString)));
@@ -233,11 +236,12 @@ void ViewWindow::init()
connect(ui->triggersList, SIGNAL(itemSelectionChanged()), this, SLOT(updateTriggersState()));
connect(ui->triggersList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(triggerViewDoubleClicked(QModelIndex)));
connect(ui->outputColumnsTable, SIGNAL(currentRowChanged(int)), this, SLOT(updateColumnButtons()));
- connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateColumnButtons()));
- connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateQueryToolbarStatus()));
+ connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateColumnButtons()));
+ connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateQueryToolbarStatus()));
connect(ui->outputColumnsTable, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(updateQueryToolbarStatus()));
- connect(CFG_UI.General.DataTabAsFirstInViews, SIGNAL(changed(const QVariant&)), this, SLOT(updateTabsOrder()));
+ connect(CFG_UI.General.DataTabAsFirstInViews, SIGNAL(changed(QVariant)), this, SLOT(updateTabsOrder()));
connect(CFG_UI.Fonts.DataView, SIGNAL(changed(QVariant)), this, SLOT(updateFont()));
+ connect(NotifyManager::getInstance(), SIGNAL(objectModified(Db*,QString,QString)), this, SLOT(handleObjectModified(Db*,QString,QString)));
structureExecutor = new ChainExecutor(this);
connect(structureExecutor, SIGNAL(success(SqlQueryPtr)), this, SLOT(changesSuccessfullyCommitted()));
@@ -296,7 +300,7 @@ void ViewWindow::initView()
{
dataModel->setDb(db);
dataModel->setQuery(originalCreateView->select->detokenize());
- dataModel->setView(view);
+ dataModel->setDatabaseAndView(database, view);
ui->dbCombo->setDisabled(true);
}
ui->queryEdit->setDb(db);
@@ -313,6 +317,8 @@ void ViewWindow::initView()
refreshTriggers();
+ // Disconnect first in case this method is (re)executed upon dependant table changes.
+ disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType)));
connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType)));
}
@@ -325,10 +331,10 @@ void ViewWindow::setupCoverWidget()
void ViewWindow::createQueryTabActions()
{
- createAction(REFRESH_QUERY, ICONS.RELOAD, tr("Refresh the view", "view window"), this, SLOT(refreshView()), ui->queryToolbar);
+ createAction(REFRESH_QUERY, ICONS.RELOAD, tr("Refresh the view", "view window"), this, SLOT(refreshView()), ui->queryToolbar, ui->queryEdit);
ui->queryToolbar->addSeparator();
- createAction(COMMIT_QUERY, ICONS.COMMIT, tr("Commit the view changes", "view window"), this, SLOT(commitView()), ui->queryToolbar);
- createAction(ROLLBACK_QUERY, ICONS.ROLLBACK, tr("Rollback the view changes", "view window"), this, SLOT(rollbackView()), ui->queryToolbar);
+ createAction(COMMIT_QUERY, ICONS.COMMIT, tr("Commit the view changes", "view window"), this, SLOT(commitView()), ui->queryToolbar, ui->queryEdit);
+ createAction(ROLLBACK_QUERY, ICONS.ROLLBACK, tr("Rollback the view changes", "view window"), this, SLOT(rollbackView()), ui->queryToolbar, ui->queryEdit);
ui->queryToolbar->addSeparator();
ui->queryToolbar->addAction(ui->queryEdit->getAction(SqlEditor::FORMAT_SQL));
@@ -344,6 +350,7 @@ void ViewWindow::createQueryTabActions()
createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "view window"), this, SLOT(delColumn()), ui->queryToolbar);
createAction(MOVE_COLUMN_UP, ICONS.MOVE_UP, tr("Move column up", "view window"), this, SLOT(moveColumnUp()), ui->queryToolbar);
createAction(MOVE_COLUMN_DOWN, ICONS.MOVE_DOWN, tr("Move column down", "view window"), this, SLOT(moveColumnDown()), ui->queryToolbar);
+ createAction(EXECUTE_QUERY, QString(), this, SLOT(executeQuery()), this);
}
void ViewWindow::createTriggersTabActions()
@@ -431,8 +438,23 @@ void ViewWindow::refreshView()
updateTriggersState();
}
-void ViewWindow::commitView(bool skipWarnings)
+void ViewWindow::executeQuery()
+{
+ if (isModified())
+ {
+ if (!actionMap[COMMIT_QUERY]->isEnabled())
+ return;
+
+ commitView(false, true);
+ return;
+ }
+
+ switchToDataAndReload();
+}
+
+void ViewWindow::commitView(bool skipWarnings, bool loadDataAfterNextCommit)
{
+ this->loadDataAfterNextCommit = loadDataAfterNextCommit;
if (!isModified())
{
qWarning() << "Called ViewWindow::commitView(), but isModified() returned false.";
@@ -535,6 +557,12 @@ int ViewWindow::getDdlTabIdx() const
return ui->tabWidget->indexOf(ui->ddlTab);
}
+void ViewWindow::switchToDataAndReload()
+{
+ ui->tabWidget->setCurrentWidget(ui->dataTab);
+ // Query execution will happen automatically upon changing tab to data tab.
+}
+
void ViewWindow::addTrigger()
{
DbObjectDialogs dialogs(db, this);
@@ -560,7 +588,7 @@ void ViewWindow::deleteTrigger()
return;
DbObjectDialogs dialogs(db, this);
- dialogs.dropObject(trigger);
+ dialogs.dropObject(DbObjectDialogs::Type::TRIGGER, trigger);
refreshTriggers();
}
@@ -573,7 +601,7 @@ void ViewWindow::executionSuccessful()
void ViewWindow::executionFailed(const QString& errorMessage)
{
modifyingThisView = false;
- notifyError(tr("Could not load data for view %1. Error details: %2").arg(view).arg(errorMessage));
+ notifyError(tr("Could not load data for view %1. Error details: %2").arg(view, errorMessage));
}
void ViewWindow::tabChanged(int tabIdx)
@@ -615,7 +643,8 @@ void ViewWindow::updateQueryToolbarStatus()
{
bool modified = isModified();
bool queryOk = ui->queryEdit->isSyntaxChecked() && !ui->queryEdit->haveErrors();
- actionMap[COMMIT_QUERY]->setEnabled(modified && queryOk);
+ bool dbOk = ui->dbCombo->currentIndex() > -1;
+ actionMap[COMMIT_QUERY]->setEnabled(modified && queryOk && dbOk);
actionMap[ROLLBACK_QUERY]->setEnabled(modified && existingView);
actionMap[REFRESH_QUERY]->setEnabled(existingView);
}
@@ -635,6 +664,8 @@ void ViewWindow::changesSuccessfullyCommitted()
QString oldView = view;
view = createView->view;
+ emit sessionValueChanged();
+
if (!existingView)
notifyInfo(tr("View '%1' was committed successfully.").arg(view));
else if (oldView.compare(view, Qt::CaseInsensitive) == 0)
@@ -649,6 +680,12 @@ void ViewWindow::changesSuccessfullyCommitted()
updateAfterInit();
DBTREE->refreshSchema(db);
+
+ if (loadDataAfterNextCommit)
+ {
+ loadDataAfterNextCommit = false;
+ switchToDataAndReload();
+ }
}
void ViewWindow::changesFailedToCommit(int errorCode, const QString& errorText)
@@ -720,6 +757,7 @@ void ViewWindow::checkIfViewDeleted(const QString& database, const QString& obje
if (object.compare(view, Qt::CaseInsensitive) == 0)
{
dbClosedFinalCleanup();
+ MDIAREA->enforceCurrentTaskSelectionAfterWindowClose();
getMdiWindow()->close();
}
}
@@ -1075,11 +1113,28 @@ void ViewWindow::updateFont()
void ViewWindow::dbChanged()
{
- disconnect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType)));
+ if (db)
+ 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)));
+ if (db)
+ connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfViewDeleted(QString,QString,DbObjectType)));
+}
+
+void ViewWindow::handleObjectModified(Db* db, const QString& database, const QString& object)
+{
+ UNUSED(db);
+ UNUSED(database);
+ if (object.compare(view, Qt::CaseInsensitive) != 0)
+ return;
+
+// TODO uncomment below when dbnames are supported
+// if (this->database != database)
+// return;
+
+ view = object;
+ refreshView();
}
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h
index a2ef4f7..642c11a 100644
--- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h
@@ -21,18 +21,20 @@ class ViewModifier;
class SqlViewModel;
CFG_KEY_LIST(ViewWindow, QObject::tr("A view window"),
- CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh view trigger list"))
- CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger"))
- CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger"))
- CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger"))
- CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab"))
- CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab"))
+ CFG_KEY_ENTRY(COMMIT_QUERY, QKeySequence::Save, QObject::tr("Commit the view's query"))
+ CFG_KEY_ENTRY(ROLLBACK_QUERY, QKeySequence::Cancel, QObject::tr("Rollback pending changes in the view's query"))
+ CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh view trigger list"))
+ CFG_KEY_ENTRY(EXECUTE_QUERY, Qt::Key_F9, QObject::tr("Execute the view's query"))
+ CFG_KEY_ENTRY(ADD_TRIGGER, Qt::Key_Insert, QObject::tr("Add new trigger"))
+ CFG_KEY_ENTRY(EDIT_TRIGGER, Qt::Key_Return, QObject::tr("Edit selected trigger"))
+ CFG_KEY_ENTRY(DEL_TRIGGER, Qt::Key_Delete, QObject::tr("Delete selected trigger"))
+ CFG_KEY_ENTRY(NEXT_TAB, Qt::ALT + Qt::Key_Right, QObject::tr("Go to next tab"))
+ CFG_KEY_ENTRY(PREV_TAB, Qt::ALT + Qt::Key_Left, QObject::tr("Go to previous tab"))
)
class GUI_API_EXPORT ViewWindow : public MdiChild
{
- Q_OBJECT
- Q_ENUMS(Action)
+ Q_OBJECT
public:
enum Action
@@ -54,8 +56,10 @@ class GUI_API_EXPORT ViewWindow : public MdiChild
DEL_TRIGGER,
// All tabs
NEXT_TAB,
- PREV_TAB
+ PREV_TAB,
+ EXECUTE_QUERY
};
+ Q_ENUM(Action)
enum ToolBar
{
@@ -117,6 +121,7 @@ class GUI_API_EXPORT ViewWindow : public MdiChild
int getDataTabIdx() const;
int getQueryTabIdx() const;
int getDdlTabIdx() const;
+ void switchToDataAndReload();
Db* db = nullptr;
QString database;
@@ -137,10 +142,12 @@ class GUI_API_EXPORT ViewWindow : public MdiChild
QAction* outputColumnsCheck = nullptr;
QAction* outputColumnsSeparator = nullptr;
bool tabsMoving = false;
+ bool loadDataAfterNextCommit = false;
private slots:
void refreshView();
- void commitView(bool skipWarnings = false);
+ void executeQuery();
+ void commitView(bool skipWarnings = false, bool loadDataAfterNextCommit = false);
void rollbackView();
void addTrigger();
void editTrigger();
@@ -168,6 +175,7 @@ class GUI_API_EXPORT ViewWindow : public MdiChild
void triggerViewDoubleClicked(const QModelIndex& idx);
void updateFont();
void dbChanged();
+ void handleObjectModified(Db* db, const QString& database, const QString& object);
public slots:
void refreshTriggers();