summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/constraints
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/constraints')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.cpp56
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.h27
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.cpp107
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.h40
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.ui69
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp204
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.h41
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.ui56
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp266
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.h48
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.ui147
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.cpp13
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.h17
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp127
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.h36
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.ui105
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.cpp99
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.h37
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.ui72
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.cpp13
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.h22
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp175
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.h49
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.ui72
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp96
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.h79
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.cpp56
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.h27
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp418
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.h57
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui215
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp299
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.h63
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.ui228
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.cpp83
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.h23
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.ui102
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.cpp21
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.h16
39 files changed, 3681 insertions, 0 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.cpp
new file mode 100644
index 0000000..8402c21
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.cpp
@@ -0,0 +1,56 @@
+#include "columncheckpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/parser.h"
+#include <QDebug>
+
+ColumnCheckPanel::ColumnCheckPanel(QWidget *parent) :
+ ConstraintCheckPanel(parent)
+{
+}
+
+SqliteExpr* ColumnCheckPanel::readExpr()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ return constr->expr;
+}
+
+QString ColumnCheckPanel::readName()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ return constr->name;
+}
+
+void ColumnCheckPanel::storeType()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::CHECK;
+}
+
+SqliteConflictAlgo ColumnCheckPanel::readConflictAlgo()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ return constr->onConflict;
+}
+
+void ColumnCheckPanel::storeExpr(SqliteExpr* expr)
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->expr = expr;
+}
+
+void ColumnCheckPanel::storeName(const QString& name)
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->name = name;
+}
+
+void ColumnCheckPanel::storeConflictAlgo(SqliteConflictAlgo algo)
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->onConflict = algo;
+}
+
+SqliteCreateTable* ColumnCheckPanel::getCreateTable()
+{
+ return dynamic_cast<SqliteCreateTable*>(constraint->parentStatement()->parentStatement());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.h
new file mode 100644
index 0000000..8c23770
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columncheckpanel.h
@@ -0,0 +1,27 @@
+#ifndef COLUMNCHECKPANEL_H
+#define COLUMNCHECKPANEL_H
+
+#include "constraintcheckpanel.h"
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+class GUI_API_EXPORT ColumnCheckPanel : public ConstraintCheckPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnCheckPanel(QWidget *parent = 0);
+
+ protected:
+ SqliteExpr* readExpr();
+ QString readName();
+ void storeType();
+ SqliteConflictAlgo readConflictAlgo();
+ void storeExpr(SqliteExpr* expr);
+ void storeName(const QString& name);
+ void storeConflictAlgo(SqliteConflictAlgo algo);
+ SqliteCreateTable* getCreateTable();
+};
+
+#endif // COLUMNCHECKPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.cpp
new file mode 100644
index 0000000..de78b2b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.cpp
@@ -0,0 +1,107 @@
+#include "columncollatepanel.h"
+#include "ui_columncollatepanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "schemaresolver.h"
+#include "uiutils.h"
+#include <QStringListModel>
+
+ColumnCollatePanel::ColumnCollatePanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ColumnCollatePanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ColumnCollatePanel::~ColumnCollatePanel()
+{
+ delete ui;
+}
+
+void ColumnCollatePanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void ColumnCollatePanel::init()
+{
+ collationModel = new QStringListModel(this);
+ ui->collationCombo->setModel(collationModel);
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->collationCombo->lineEdit(), SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void ColumnCollatePanel::readConstraint()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ ui->collationCombo->setCurrentText(constr->collationName);
+
+ if (!constr->name.isNull())
+ {
+ ui->namedCheck->setChecked(true);
+ ui->namedEdit->setText(constr->name);
+ }
+}
+
+void ColumnCollatePanel::readCollations()
+{
+ SchemaResolver resolver(db);
+ QStringList collList = resolver.getCollations();
+
+ if (collList.size() > 0)
+ collList.prepend("");
+
+ collationModel->setStringList(collList);
+}
+
+void ColumnCollatePanel::updateState()
+{
+ ui->namedEdit->setEnabled(ui->namedCheck->isChecked());
+}
+
+bool ColumnCollatePanel::validate()
+{
+ bool nameOk = true;
+ if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty())
+ nameOk = false;
+
+ bool collationOk = !ui->collationCombo->currentText().isEmpty();
+
+ setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint."));
+ setValidState(ui->collationCombo, collationOk, tr("Enter a collation name."));
+
+ return nameOk && collationOk;
+}
+
+void ColumnCollatePanel::constraintAvailable()
+{
+ if (constraint.isNull())
+ return;
+
+ readCollations();
+ readConstraint();
+}
+
+void ColumnCollatePanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::COLLATE;
+
+ if (ui->namedCheck->isChecked())
+ constr->name = ui->namedEdit->text();
+
+ constr->collationName = ui->collationCombo->currentText();
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.h
new file mode 100644
index 0000000..a7afb54
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.h
@@ -0,0 +1,40 @@
+#ifndef COLUMNCOLLATEPANEL_H
+#define COLUMNCOLLATEPANEL_H
+
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+
+namespace Ui {
+ class ColumnCollatePanel;
+}
+
+class QStringListModel;
+
+class GUI_API_EXPORT ColumnCollatePanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnCollatePanel(QWidget *parent = 0);
+ ~ColumnCollatePanel();
+
+ bool validate();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+
+ private:
+ void init();
+ void readConstraint();
+ void readCollations();
+
+ QStringListModel* collationModel = nullptr;
+ Ui::ColumnCollatePanel *ui = nullptr;
+
+ private slots:
+ void updateState();
+};
+
+#endif // COLUMNCOLLATEPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.ui
new file mode 100644
index 0000000..a481875
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columncollatepanel.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnCollatePanel</class>
+ <widget class="QWidget" name="ColumnCollatePanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>79</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QWidget" name="collationWidget" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="collationLabel">
+ <property name="text">
+ <string>Collation name:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="collationCombo">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="namedCheck">
+ <property name="text">
+ <string>Named constraint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp
new file mode 100644
index 0000000..3d7090a
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.cpp
@@ -0,0 +1,204 @@
+#include "columndefaultpanel.h"
+#include "ui_columndefaultpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/parser.h"
+#include "parser/keywords.h"
+#include "uiutils.h"
+#include <QDebug>
+
+ColumnDefaultPanel::ColumnDefaultPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ColumnDefaultPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ColumnDefaultPanel::~ColumnDefaultPanel()
+{
+ delete ui;
+}
+
+void ColumnDefaultPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+
+bool ColumnDefaultPanel::validate()
+{
+ bool nameOk = true;
+ if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty())
+ nameOk = false;
+
+ bool exprOk = !ui->exprEdit->toPlainText().trimmed().isEmpty() &&
+ !ui->exprEdit->haveErrors();
+
+ bool exprCheckedOk = exprOk && ui->exprEdit->isSyntaxChecked();
+
+ setValidState(ui->exprEdit, exprOk, tr("Enter a default value expression."));
+ setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return exprCheckedOk && nameOk;
+}
+
+bool ColumnDefaultPanel::validateOnly()
+{
+ ui->exprEdit->checkSyntaxNow();
+ return validate();
+}
+
+void ColumnDefaultPanel::constraintAvailable()
+{
+ if (constraint.isNull())
+ return;
+
+ readConstraint();
+ updateVirtualSql();
+}
+
+void ColumnDefaultPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::DEFAULT;
+
+ SqliteExprPtr expr = parseExpression(ui->exprEdit->toPlainText());
+ SqliteExpr* newExpr = new SqliteExpr(*expr.data());
+ newExpr->setParent(constraint.data());
+ constr->expr = newExpr;
+
+ if (ui->namedCheck->isChecked())
+ constr->name = ui->namedEdit->text();
+}
+
+void ColumnDefaultPanel::init()
+{
+ setFocusProxy(ui->exprEdit);
+ ui->exprEdit->setShowLineNumbers(false);
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->exprEdit, SIGNAL(textChanged()), this, SIGNAL(updateValidation()));
+ connect(ui->exprEdit, SIGNAL(errorsChecked(bool)), this, SIGNAL(updateValidation()));
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+
+ updateState();
+}
+
+void ColumnDefaultPanel::readConstraint()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+
+ if (constr->expr)
+ ui->exprEdit->setPlainText(constr->expr->detokenize());
+ else if (!constr->literalValue.isNull())
+ ui->exprEdit->setPlainText(constr->literalValue.toString());
+
+ if (!constr->name.isNull())
+ {
+ ui->namedCheck->setChecked(true);
+ ui->namedEdit->setText(constr->name);
+ }
+}
+
+void ColumnDefaultPanel::updateVirtualSql()
+{
+ ui->exprEdit->setDb(db);
+
+ SqliteCreateTable::Column* column = dynamic_cast<SqliteCreateTable::Column*>(constraint->parentStatement());
+ SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(column->parentStatement());
+
+ createTable->rebuildTokens();
+ TokenList tokens = createTable->tokens;
+ TokenList colTokens = column->tokens;
+ if (createTable->columns.indexOf(column) == -1)
+ {
+ if (createTable->columns.size() == 0)
+ {
+ // No columns. Cannot get any context info.
+ return;
+ }
+
+ colTokens = createTable->columns.last()->tokens;
+ }
+
+ if (colTokens.size() == 0)
+ {
+ qWarning() << "CREATE TABLE tokens are invalid (0) while call to ColumnDefaultPanel::updateVirtualSql().";
+ return;
+ }
+
+ int idx = tokens.lastIndexOf(colTokens.last());
+ if (idx == -1)
+ {
+ qWarning() << "CREATE TABLE tokens are invalid while call to ColumnDefaultPanel::updateVirtualSql().";
+ return;
+ }
+ idx++;
+
+ TokenList newTokens;
+ newTokens << TokenPtr::create(Token::SPACE, " ")
+ << TokenPtr::create(Token::KEYWORD, "DEFAULT")
+ << TokenPtr::create(Token::SPACE, " ");
+
+ if (constraint->dialect == Dialect::Sqlite3)
+ {
+ newTokens << TokenPtr::create(Token::PAR_LEFT, "(")
+ << TokenPtr::create(Token::OTHER, "%1")
+ << TokenPtr::create(Token::PAR_RIGHT, ")");
+ }
+ else
+ {
+ newTokens << TokenPtr::create(Token::OTHER, "%1");
+ }
+
+ tokens.insert(idx, newTokens);
+ QString sql = tokens.detokenize();
+
+ ui->exprEdit->setVirtualSqlExpression(sql);
+}
+
+SqliteExprPtr ColumnDefaultPanel::parseExpression(const QString& sql)
+{
+ Parser parser(db->getDialect());
+ if (!parser.parse("SELECT "+sql))
+ return SqliteExprPtr();
+
+ QList<SqliteQueryPtr> queries = parser.getQueries();
+ if (queries.size() == 0)
+ return SqliteExprPtr();
+
+ SqliteQueryPtr first = queries.first();
+ if (first->queryType != SqliteQueryType::Select)
+ return SqliteExprPtr();
+
+ SqliteSelectPtr select = first.dynamicCast<SqliteSelect>();
+ if (select->coreSelects.size() < 1)
+ return SqliteExprPtr();
+
+ SqliteSelect::Core* core = select->coreSelects.first();
+ if (core->resultColumns.size() < 1)
+ return SqliteExprPtr();
+
+ SqliteSelect::Core::ResultColumn* resCol = core->resultColumns.first();
+ if (!resCol->expr)
+ return SqliteExprPtr();
+
+ return resCol->expr->detach().dynamicCast<SqliteExpr>();
+}
+
+void ColumnDefaultPanel::updateState()
+{
+ ui->namedEdit->setEnabled(ui->namedCheck->isChecked());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.h
new file mode 100644
index 0000000..933c2dd
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.h
@@ -0,0 +1,41 @@
+#ifndef COLUMNDEFAULTPANEL_H
+#define COLUMNDEFAULTPANEL_H
+
+#include "constraintpanel.h"
+#include "parser/ast/sqliteconflictalgo.h"
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+namespace Ui {
+ class ColumnDefaultPanel;
+}
+
+class GUI_API_EXPORT ColumnDefaultPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnDefaultPanel(QWidget *parent = 0);
+ ~ColumnDefaultPanel();
+
+ bool validate();
+ bool validateOnly();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+
+ private:
+ void init();
+ void readConstraint();
+ void updateVirtualSql();
+ SqliteExprPtr parseExpression(const QString& sql);
+
+ Ui::ColumnDefaultPanel *ui = nullptr;
+
+ private slots:
+ void updateState();
+};
+
+#endif // COLUMNDEFAULTPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.ui
new file mode 100644
index 0000000..fdf55b0
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columndefaultpanel.ui
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnDefaultPanel</class>
+ <widget class="QWidget" name="ColumnDefaultPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>169</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="exprGroup">
+ <property name="title">
+ <string>Default value:</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="SqlEditor" name="exprEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="namedCheck">
+ <property name="text">
+ <string>Named constraint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SqlEditor</class>
+ <extends>QPlainTextEdit</extends>
+ <header>sqleditor.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp
new file mode 100644
index 0000000..cf234f7
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.cpp
@@ -0,0 +1,266 @@
+#include "columnforeignkeypanel.h"
+#include "ui_columnforeignkeypanel.h"
+#include "schemaresolver.h"
+#include "uiutils.h"
+#include <QDebug>
+#include <QSignalMapper>
+
+ColumnForeignKeyPanel::ColumnForeignKeyPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ColumnForeignKeyPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ColumnForeignKeyPanel::~ColumnForeignKeyPanel()
+{
+ delete ui;
+}
+
+void ColumnForeignKeyPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+
+bool ColumnForeignKeyPanel::validate()
+{
+ bool tableOk = (ui->fkTableCombo->currentIndex() > -1);
+ bool columnOk = (ui->fkColumnCombo->currentIndex() > -1);
+ bool nameOk = !ui->namedCheckBox->isChecked() || !ui->nameEdit->text().isEmpty();
+
+ setValidState(ui->fkTableCombo, tableOk, tr("Pick the foreign table."));
+ setValidState(ui->fkColumnCombo, columnOk, tr("Pick the foreign column."));
+ setValidState(ui->nameEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return tableOk && columnOk && nameOk;
+}
+
+void ColumnForeignKeyPanel::constraintAvailable()
+{
+ readTables();
+ readConstraint();
+}
+
+void ColumnForeignKeyPanel::init()
+{
+ setFocusProxy(ui->fkTableCombo);
+
+ ui->fkColumnCombo->setModel(&fkColumnsModel);
+ connect(ui->fkColumnCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFkColumns()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState()));
+ connect(ui->onDeleteCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->onUpdateCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->matchCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+
+ ui->deferrableCombo->addItems({
+ "",
+ sqliteDeferrable(SqliteDeferrable::DEFERRABLE),
+ sqliteDeferrable(SqliteDeferrable::NOT_DEFERRABLE)
+ });
+ ui->initiallyCombo->addItems({
+ "",
+ sqliteInitially(SqliteInitially::DEFERRED),
+ sqliteInitially(SqliteInitially::IMMEDIATE),
+ });
+
+ QStringList reactions = {
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::NO_ACTION),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_NULL),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_DEFAULT),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::CASCADE),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::RESTRICT)
+ };
+ ui->onUpdateCombo->addItems(reactions);
+ ui->onDeleteCombo->addItems(reactions);
+ ui->matchCombo->addItems({"SIMPLE", "FULL", "PARTIAL"});
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void ColumnForeignKeyPanel::updateState()
+{
+ bool tableSelected = (ui->fkTableCombo->currentIndex() > -1);
+ ui->fkColumnCombo->setEnabled(tableSelected);
+ ui->deferrableCombo->setEnabled(tableSelected);
+ ui->initiallyCombo->setEnabled(tableSelected);
+ ui->namedCheckBox->setEnabled(tableSelected);
+ ui->nameEdit->setEnabled(tableSelected && ui->namedCheckBox->isChecked());
+ ui->onDeleteCheckBox->setEnabled(tableSelected);
+ ui->onUpdateCheckBox->setEnabled(tableSelected);
+ ui->matchCheckBox->setEnabled(tableSelected);
+ ui->onDeleteCombo->setEnabled(tableSelected && ui->onDeleteCheckBox->isChecked());
+ ui->onUpdateCombo->setEnabled(tableSelected && ui->onUpdateCheckBox->isChecked());
+ ui->matchCombo->setEnabled(tableSelected && ui->matchCheckBox->isChecked());
+}
+
+void ColumnForeignKeyPanel::updateFkColumns()
+{
+ QStringList columns;
+ if (ui->fkTableCombo->currentIndex() == -1)
+ {
+ fkColumnsModel.setStringList(columns);
+ updateState();
+ return;
+ }
+
+ SchemaResolver resolver(db);
+ columns = resolver.getTableColumns(ui->fkTableCombo->currentText()); // TODO named db attach not supported
+ fkColumnsModel.setStringList(columns);
+}
+
+void ColumnForeignKeyPanel::readConstraint()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Column* column = dynamic_cast<SqliteCreateTable::Column*>(constraint->parent());
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ if (!constr->foreignKey)
+ return;
+
+ // Table
+ if (!constr->foreignKey->foreignTable.isNull())
+ ui->fkTableCombo->setCurrentText(constr->foreignKey->foreignTable);
+
+ // Conditions
+ foreach (SqliteForeignKey::Condition* condition, constr->foreignKey->conditions)
+ readCondition(condition);
+
+ // Initially, Deferrable
+ ui->deferrableCombo->setCurrentText(sqliteDeferrable(constr->foreignKey->deferrable));
+ ui->initiallyCombo->setCurrentText(sqliteInitially(constr->foreignKey->initially));
+
+ // Name
+ if (!constr->name.isNull())
+ {
+ ui->namedCheckBox->setChecked(true);
+ ui->nameEdit->setText(constr->name);
+ }
+
+ // Column
+ if (constr->foreignKey->indexedColumns.size() > 1)
+ {
+ qWarning() << "More than one referenced column in the column foreign key:" << constr->detokenize();
+ return;
+ }
+
+ QString fkColumn = column->name;
+ if (constr->foreignKey->indexedColumns.size() == 1)
+ fkColumn = constr->foreignKey->indexedColumns.first()->name;
+
+ ui->fkColumnCombo->setCurrentText(fkColumn);
+}
+
+void ColumnForeignKeyPanel::readCondition(SqliteForeignKey::Condition* condition)
+{
+ switch (condition->action)
+ {
+ case SqliteForeignKey::Condition::UPDATE:
+ ui->onUpdateCheckBox->setChecked(true);
+ ui->onUpdateCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ case SqliteForeignKey::Condition::INSERT:
+ // INSERT is not officially supported.
+ break;
+ case SqliteForeignKey::Condition::DELETE:
+ ui->onDeleteCheckBox->setChecked(true);
+ ui->onDeleteCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ case SqliteForeignKey::Condition::MATCH:
+ ui->matchCheckBox->setChecked(true);
+ ui->matchCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ }
+}
+
+void ColumnForeignKeyPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ // Type
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::FOREIGN_KEY;
+
+ // Cleanup & initial setup
+ if (constr->foreignKey)
+ delete constr->foreignKey;
+
+ constr->foreignKey = new SqliteForeignKey();
+ constr->foreignKey->setParent(constr);
+
+ // Foreign table
+ constr->foreignKey->foreignTable = ui->fkTableCombo->currentText();
+
+ // Column
+ SqliteIndexedColumn* idxCol = new SqliteIndexedColumn(ui->fkColumnCombo->currentText());
+ idxCol->setParent(constr->foreignKey);
+ constr->foreignKey->indexedColumns << idxCol;
+
+ // Actions/reactions
+ if (ui->onDeleteCheckBox->isChecked())
+ storeCondition(SqliteForeignKey::Condition::DELETE, ui->onDeleteCombo->currentText());
+
+ if (ui->onUpdateCheckBox->isChecked())
+ storeCondition(SqliteForeignKey::Condition::UPDATE, ui->onDeleteCombo->currentText());
+
+ if (ui->matchCheckBox->isChecked())
+ storeMatchCondition(ui->matchCombo->currentText());
+
+ // Deferred/initially
+ constr->foreignKey->deferrable = sqliteDeferrable(ui->deferrableCombo->currentText());
+ constr->foreignKey->initially = sqliteInitially(ui->initiallyCombo->currentText());
+
+ // Name
+ constr->name = QString::null;
+ if (ui->namedCheckBox->isChecked())
+ constr->name = ui->nameEdit->text();
+}
+
+void ColumnForeignKeyPanel::storeCondition(SqliteForeignKey::Condition::Action action, const QString& reaction)
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+
+ SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(
+ action,
+ SqliteForeignKey::Condition::toEnum(reaction)
+ );
+ condition->setParent(constr->foreignKey);
+ constr->foreignKey->conditions << condition;
+}
+
+void ColumnForeignKeyPanel::storeMatchCondition(const QString& reaction)
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+
+ SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(reaction);
+ condition->setParent(constr->foreignKey);
+ constr->foreignKey->conditions << condition;
+}
+
+void ColumnForeignKeyPanel::readTables()
+{
+ SchemaResolver resolver(db);
+ resolver.setIgnoreSystemObjects(true);
+ QStringList tables = resolver.getTables(); // TODO named db attach not supported
+
+ tables.sort(Qt::CaseInsensitive);
+
+ ui->fkTableCombo->addItems(tables);
+ ui->fkTableCombo->setCurrentIndex(-1);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.h
new file mode 100644
index 0000000..3fe3077
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.h
@@ -0,0 +1,48 @@
+#ifndef COLUMNFOREIGNKEYPANEL_H
+#define COLUMNFOREIGNKEYPANEL_H
+
+#include "constraintpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "guiSQLiteStudio_global.h"
+#include <QStringListModel>
+#include <QWidget>
+
+namespace Ui {
+ class ColumnForeignKeyPanel;
+}
+
+class QGridLayout;
+class QSignalMapper;
+
+class GUI_API_EXPORT ColumnForeignKeyPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnForeignKeyPanel(QWidget *parent = 0);
+ ~ColumnForeignKeyPanel();
+
+ bool validate();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+
+ private:
+ void init();
+ void readConstraint();
+ void readTables();
+ void readCondition(SqliteForeignKey::Condition* condition);
+ void storeCondition(SqliteForeignKey::Condition::Action action, const QString& reaction);
+ void storeMatchCondition(const QString& reaction);
+
+ Ui::ColumnForeignKeyPanel *ui = nullptr;
+ QStringListModel fkColumnsModel;
+
+ private slots:
+ void updateState();
+ void updateFkColumns();
+};
+
+#endif // COLUMNFOREIGNKEYPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.ui
new file mode 100644
index 0000000..c442967
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnforeignkeypanel.ui
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnForeignKeyPanel</class>
+ <widget class="QWidget" name="ColumnForeignKeyPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>320</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>320</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QWidget" name="fkTableWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="fkTableLabel">
+ <property name="text">
+ <string>Foreign table:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="fkTableCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="fkColumnWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="fkColumnLabel">
+ <property name="text">
+ <string>Foreign column:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="fkColumnCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="reactionsGroup">
+ <property name="title">
+ <string>Reactions</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="onUpdateCheckBox">
+ <property name="text">
+ <string>ON UPDATE</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="onUpdateCombo"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="onDeleteCheckBox">
+ <property name="text">
+ <string>ON DELETE</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="matchCheckBox">
+ <property name="text">
+ <string>MATCH</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="onDeleteCombo"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="matchCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="deferredGroup">
+ <property name="title">
+ <string>Deferred foreign key</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QComboBox" name="deferrableCombo"/>
+ </item>
+ <item>
+ <widget class="QComboBox" name="initiallyCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="namedCheckBox">
+ <property name="text">
+ <string>Named constraint</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="nameEdit">
+ <property name="placeholderText">
+ <string>Constraint name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>fkTableCombo</tabstop>
+ <tabstop>fkColumnCombo</tabstop>
+ <tabstop>onUpdateCheckBox</tabstop>
+ <tabstop>onUpdateCombo</tabstop>
+ <tabstop>onDeleteCheckBox</tabstop>
+ <tabstop>onDeleteCombo</tabstop>
+ <tabstop>matchCheckBox</tabstop>
+ <tabstop>matchCombo</tabstop>
+ <tabstop>deferrableCombo</tabstop>
+ <tabstop>initiallyCombo</tabstop>
+ <tabstop>namedCheckBox</tabstop>
+ <tabstop>nameEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.cpp
new file mode 100644
index 0000000..9da20db
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.cpp
@@ -0,0 +1,13 @@
+#include "columnnotnullpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+
+ColumnNotNullPanel::ColumnNotNullPanel(QWidget *parent) :
+ ColumnUniqueAndNotNullPanel(parent)
+{
+}
+
+void ColumnNotNullPanel::storeType()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::UNIQUE;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.h
new file mode 100644
index 0000000..1cde833
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnnotnullpanel.h
@@ -0,0 +1,17 @@
+#ifndef COLUMNNOTNULLPANEL_H
+#define COLUMNNOTNULLPANEL_H
+
+#include "guiSQLiteStudio_global.h"
+#include "columnuniqueandnotnullpanel.h"
+
+class GUI_API_EXPORT ColumnNotNullPanel : public ColumnUniqueAndNotNullPanel
+{
+ Q_OBJECT
+ public:
+ explicit ColumnNotNullPanel(QWidget *parent = 0);
+
+ protected:
+ void storeType();
+};
+
+#endif // COLUMNNOTNULLPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp
new file mode 100644
index 0000000..d10b223
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.cpp
@@ -0,0 +1,127 @@
+#include "columnprimarykeypanel.h"
+#include "ui_columnprimarykeypanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/keywords.h"
+#include "datatype.h"
+#include "uiutils.h"
+
+ColumnPrimaryKeyPanel::ColumnPrimaryKeyPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ColumnPrimaryKeyPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ColumnPrimaryKeyPanel::~ColumnPrimaryKeyPanel()
+{
+ delete ui;
+}
+
+void ColumnPrimaryKeyPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void ColumnPrimaryKeyPanel::init()
+{
+ QStringList sortOrders = {sqliteSortOrder(SqliteSortOrder::ASC), sqliteSortOrder(SqliteSortOrder::DESC)};
+ ui->sortOrderCombo->addItems(sortOrders);
+
+ ui->conflictCombo->addItems(getConflictAlgorithms());
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->sortOrderCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->conflictCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void ColumnPrimaryKeyPanel::readConstraint()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ if (constraint->dialect == Dialect::Sqlite3)
+ ui->autoIncrCheck->setChecked(constr->autoincrKw);
+
+ if (constr->sortOrder != SqliteSortOrder::null)
+ {
+ ui->sortOrderCheck->setChecked(true);
+ ui->sortOrderCombo->setCurrentText(sqliteSortOrder(constr->sortOrder));
+ }
+
+ if (!constr->name.isNull())
+ {
+ ui->namedCheck->setEnabled(true);
+ ui->namedEdit->setText(constr->name);
+ }
+
+ if (constr->onConflict != SqliteConflictAlgo::null)
+ {
+ ui->conflictCheck->setChecked(true);
+ ui->conflictCombo->setCurrentText(sqliteConflictAlgo(constr->onConflict));
+ }
+}
+
+void ColumnPrimaryKeyPanel::updateState()
+{
+ ui->sortOrderCombo->setEnabled(ui->sortOrderCheck->isChecked());
+ ui->namedEdit->setEnabled(ui->namedCheck->isChecked());
+ ui->conflictCombo->setEnabled(ui->conflictCheck->isChecked());
+}
+
+
+bool ColumnPrimaryKeyPanel::validate()
+{
+ bool nameOk = true;
+ if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty())
+ nameOk = false;
+
+ setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return nameOk;
+}
+
+void ColumnPrimaryKeyPanel::constraintAvailable()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Column* column = dynamic_cast<SqliteCreateTable::Column*>(constraint->parent());
+ ui->autoIncrCheck->setVisible(constraint->dialect == Dialect::Sqlite3);
+ ui->autoIncrCheck->setEnabled(column->type &&
+ DataType::fromString(column->type->detokenize().trimmed(), Qt::CaseInsensitive) == DataType::INTEGER);
+
+ if (!ui->autoIncrCheck->isEnabled())
+ ui->autoIncrCheck->setText(tr("Autoincrement (only for %1 type columns)", "column primary key").arg("INTEGER"));
+
+ readConstraint();
+}
+
+void ColumnPrimaryKeyPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::PRIMARY_KEY;
+
+ if (constraint->dialect == Dialect::Sqlite3)
+ constr->autoincrKw = ui->autoIncrCheck->isChecked();
+
+ if (ui->sortOrderCheck->isChecked() && ui->sortOrderCombo->currentIndex() > -1)
+ constr->sortOrder = sqliteSortOrder(ui->sortOrderCombo->currentText());
+
+ if (ui->namedCheck->isChecked())
+ constr->name = ui->namedEdit->text();
+
+ if (ui->conflictCheck->isChecked() && ui->conflictCombo->currentIndex() > -1)
+ constr->onConflict = sqliteConflictAlgo(ui->conflictCombo->currentText());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.h
new file mode 100644
index 0000000..ac97363
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.h
@@ -0,0 +1,36 @@
+#ifndef COLUMNPRIMARYKEYPANEL_H
+#define COLUMNPRIMARYKEYPANEL_H
+
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+
+namespace Ui {
+ class ColumnPrimaryKeyPanel;
+}
+
+class GUI_API_EXPORT ColumnPrimaryKeyPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnPrimaryKeyPanel(QWidget *parent = 0);
+ ~ColumnPrimaryKeyPanel();
+
+ bool validate();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+
+ private:
+ void init();
+ void readConstraint();
+
+ Ui::ColumnPrimaryKeyPanel *ui = nullptr;
+
+ private slots:
+ void updateState();
+};
+
+#endif // COLUMNPRIMARYKEYPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.ui
new file mode 100644
index 0000000..bedabca
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnprimarykeypanel.ui
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnPrimaryKeyPanel</class>
+ <widget class="QWidget" name="ColumnPrimaryKeyPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>379</width>
+ <height>110</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>110</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="autoIncrCheck">
+ <property name="text">
+ <string>Autoincrement</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="sortOrderWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="sortOrderCheck">
+ <property name="text">
+ <string>Sort order:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="sortOrderCombo">
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="namedCheck">
+ <property name="text">
+ <string>Named constraint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="conflictWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="conflictCheck">
+ <property name="text">
+ <string>On conflict:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="conflictCombo">
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.cpp
new file mode 100644
index 0000000..7c0f5a8
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.cpp
@@ -0,0 +1,99 @@
+#include "columnuniqueandnotnullpanel.h"
+#include "ui_columnuniqueandnotnullpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/keywords.h"
+#include "uiutils.h"
+
+ColumnUniqueAndNotNullPanel::ColumnUniqueAndNotNullPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ColumnUniqueAndNotNullPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ColumnUniqueAndNotNullPanel::~ColumnUniqueAndNotNullPanel()
+{
+ delete ui;
+}
+
+void ColumnUniqueAndNotNullPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void ColumnUniqueAndNotNullPanel::init()
+{
+ ui->conflictCombo->addItems(getConflictAlgorithms());
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->conflictCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void ColumnUniqueAndNotNullPanel::readConstraint()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+
+ if (!constr->name.isNull())
+ {
+ ui->namedCheck->setChecked(true);
+ ui->namedEdit->setText(constr->name);
+ }
+
+ if (constr->onConflict != SqliteConflictAlgo::null)
+ {
+ ui->conflictCheck->setChecked(true);
+ ui->conflictCombo->setCurrentText(sqliteConflictAlgo(constr->onConflict));
+ }
+}
+
+void ColumnUniqueAndNotNullPanel::updateState()
+{
+ ui->namedEdit->setEnabled(ui->namedCheck->isChecked());
+ ui->conflictCombo->setEnabled(ui->conflictCheck->isChecked());
+}
+
+
+bool ColumnUniqueAndNotNullPanel::validate()
+{
+ bool nameOk = true;
+ if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty())
+ nameOk = false;
+
+ setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return nameOk;
+}
+
+void ColumnUniqueAndNotNullPanel::constraintAvailable()
+{
+ if (constraint.isNull())
+ return;
+
+ readConstraint();
+}
+
+void ColumnUniqueAndNotNullPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ storeType();
+
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ if (ui->namedCheck->isChecked())
+ constr->name = ui->namedEdit->text();
+
+ if (ui->conflictCheck->isChecked() && ui->conflictCombo->currentIndex() > -1)
+ constr->onConflict = sqliteConflictAlgo(ui->conflictCombo->currentText());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.h
new file mode 100644
index 0000000..95cd5fb
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.h
@@ -0,0 +1,37 @@
+#ifndef COLUMNUNIQUEANDNOTNULLPANEL_H
+#define COLUMNUNIQUEANDNOTNULLPANEL_H
+
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+
+namespace Ui {
+ class ColumnUniqueAndNotNullPanel;
+}
+
+class GUI_API_EXPORT ColumnUniqueAndNotNullPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnUniqueAndNotNullPanel(QWidget *parent = 0);
+ ~ColumnUniqueAndNotNullPanel();
+
+ bool validate();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+ virtual void storeType() = 0;
+
+ private:
+ void init();
+ void readConstraint();
+
+ Ui::ColumnUniqueAndNotNullPanel *ui = nullptr;
+
+ private slots:
+ void updateState();
+};
+
+#endif // COLUMNUNIQUEANDNOTNULLPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.ui
new file mode 100644
index 0000000..16efc89
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniqueandnotnullpanel.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ColumnUniqueAndNotNullPanel</class>
+ <widget class="QWidget" name="ColumnUniqueAndNotNullPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>82</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="namedCheck">
+ <property name="text">
+ <string>Named constraint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="conflictWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="conflictCheck">
+ <property name="text">
+ <string>On conflict:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="conflictCombo">
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.cpp
new file mode 100644
index 0000000..da5e00b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.cpp
@@ -0,0 +1,13 @@
+#include "columnuniquepanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+
+ColumnUniquePanel::ColumnUniquePanel(QWidget *parent) :
+ ColumnUniqueAndNotNullPanel(parent)
+{
+}
+
+void ColumnUniquePanel::storeType()
+{
+ SqliteCreateTable::Column::Constraint* constr = dynamic_cast<SqliteCreateTable::Column::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Column::Constraint::UNIQUE;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.h
new file mode 100644
index 0000000..3abf779
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/columnuniquepanel.h
@@ -0,0 +1,22 @@
+#ifndef COLUMNUNIQUEPANEL_H
+#define COLUMNUNIQUEPANEL_H
+
+#include "columnuniqueandnotnullpanel.h"
+#include "guiSQLiteStudio_global.h"
+
+namespace Ui {
+ class ColumnUniquePanel;
+}
+
+class GUI_API_EXPORT ColumnUniquePanel : public ColumnUniqueAndNotNullPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ColumnUniquePanel(QWidget *parent = 0);
+
+ protected:
+ void storeType();
+};
+
+#endif // COLUMNUNIQUEPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp
new file mode 100644
index 0000000..adb5e2b
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.cpp
@@ -0,0 +1,175 @@
+#include "constraintcheckpanel.h"
+#include "ui_constraintcheckpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/parser.h"
+#include "parser/keywords.h"
+#include "uiutils.h"
+#include <QDebug>
+
+ConstraintCheckPanel::ConstraintCheckPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::ConstraintCheckPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+ConstraintCheckPanel::~ConstraintCheckPanel()
+{
+ delete ui;
+}
+
+void ConstraintCheckPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+
+bool ConstraintCheckPanel::validate()
+{
+ bool nameOk = true;
+ if (ui->namedCheck->isChecked() && ui->namedEdit->text().isEmpty())
+ nameOk = false;
+
+ bool exprOk = !ui->exprEdit->toPlainText().trimmed().isEmpty() &&
+ !ui->exprEdit->haveErrors();
+
+ bool exprCheckedOk = exprOk && ui->exprEdit->isSyntaxChecked();
+
+ setValidState(ui->exprEdit, exprOk, tr("Enter a valid condition."));
+ setValidState(ui->namedEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return exprCheckedOk && nameOk;
+}
+
+void ConstraintCheckPanel::constraintAvailable()
+{
+ if (constraint.isNull())
+ return;
+
+ if (constraint->dialect == Dialect::Sqlite3)
+ {
+ ui->onConflictCheck->setVisible(false);
+ ui->onConflictCombo->setVisible(false);
+ }
+
+ readConstraint();
+ updateVirtualSql();
+}
+
+void ConstraintCheckPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ storeType();
+
+ SqliteExprPtr expr = parseExpression(ui->exprEdit->toPlainText());
+ SqliteExpr* newExpr = new SqliteExpr(*expr.data());
+ newExpr->setParent(constraint.data());
+ storeExpr(newExpr);
+
+ QString name = QString::null;
+ if (ui->namedCheck->isChecked())
+ name = ui->namedEdit->text();
+
+ storeName(name);
+
+ if (constraint->dialect == Dialect::Sqlite2 && ui->onConflictCheck->isChecked())
+ storeConflictAlgo(sqliteConflictAlgo(ui->onConflictCombo->currentText()));
+}
+
+void ConstraintCheckPanel::init()
+{
+ setFocusProxy(ui->exprEdit);
+ ui->exprEdit->setShowLineNumbers(false);
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->exprEdit, SIGNAL(textChanged()), this, SIGNAL(updateValidation()));
+ connect(ui->exprEdit, SIGNAL(errorsChecked(bool)), this, SIGNAL(updateValidation()));
+
+ connect(ui->namedCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->onConflictCheck, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+
+ ui->onConflictCombo->addItems(getConflictAlgorithms());
+
+ updateState();
+}
+
+void ConstraintCheckPanel::readConstraint()
+{
+ SqliteExpr* expr = readExpr();
+ if (expr)
+ ui->exprEdit->setPlainText(expr->detokenize());
+
+ QString name = readName();
+ if (!name.isNull())
+ {
+ ui->namedCheck->setChecked(true);
+ ui->namedEdit->setText(name);
+ }
+
+ SqliteConflictAlgo onConflict = readConflictAlgo();
+ if (constraint->dialect == Dialect::Sqlite2 && onConflict != SqliteConflictAlgo::null)
+ {
+ ui->onConflictCheck->setChecked(true);
+ ui->onConflictCombo->setCurrentText(sqliteConflictAlgo(onConflict));
+ }
+}
+
+void ConstraintCheckPanel::updateVirtualSql()
+{
+ ui->exprEdit->setDb(db);
+
+ SqliteCreateTable* createTable = getCreateTable();
+
+ createTable->rebuildTokens();
+ TokenList tokens = createTable->tokens;
+ int idx = tokens.lastIndexOf(Token::PAR_RIGHT);
+ if (idx == -1)
+ {
+ qWarning() << "CREATE TABLE tokens are invalid while call to ConstraintCheckPanel::updateVirtualSql().";
+ return;
+ }
+
+ TokenList newTokens;
+ newTokens << TokenPtr::create(Token::OPERATOR, ",")
+ << TokenPtr::create(Token::SPACE, " ")
+ << TokenPtr::create(Token::KEYWORD, "CHECK")
+ << TokenPtr::create(Token::SPACE, " ")
+ << TokenPtr::create(Token::PAR_LEFT, "(")
+ << TokenPtr::create(Token::OTHER, "%1")
+ << TokenPtr::create(Token::PAR_RIGHT, ")");
+
+ tokens.insert(idx, newTokens);
+ QString sql = tokens.detokenize();
+
+ ui->exprEdit->setVirtualSqlExpression(sql);
+}
+
+SqliteExprPtr ConstraintCheckPanel::parseExpression(const QString& sql)
+{
+ Parser parser(db->getDialect());
+ SqliteExpr *expr = parser.parseExpr(sql);
+ return SqliteExprPtr(expr);
+}
+
+void ConstraintCheckPanel::updateState()
+{
+ ui->namedEdit->setEnabled(ui->namedCheck->isChecked());
+ ui->onConflictCombo->setEnabled(ui->onConflictCheck->isChecked());
+}
+
+bool ConstraintCheckPanel::validateOnly()
+{
+ ui->exprEdit->checkSyntaxNow();
+ return validate();
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.h
new file mode 100644
index 0000000..5098880
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.h
@@ -0,0 +1,49 @@
+#ifndef CONSTRAINTCHECKPANEL_H
+#define CONSTRAINTCHECKPANEL_H
+
+#include "constraintpanel.h"
+#include "parser/ast/sqliteconflictalgo.h"
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+namespace Ui {
+ class ConstraintCheckPanel;
+}
+
+class GUI_API_EXPORT ConstraintCheckPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit ConstraintCheckPanel(QWidget *parent = 0);
+ ~ConstraintCheckPanel();
+
+ bool validate();
+ bool validateOnly();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+ virtual SqliteExpr* readExpr() = 0;
+ virtual QString readName() = 0;
+ virtual void storeType() = 0;
+ virtual SqliteConflictAlgo readConflictAlgo() = 0;
+ virtual void storeExpr(SqliteExpr* expr) = 0;
+ virtual void storeName(const QString& name) = 0;
+ virtual void storeConflictAlgo(SqliteConflictAlgo algo) = 0;
+ virtual SqliteCreateTable* getCreateTable() = 0;
+
+ private:
+ void init();
+ void readConstraint();
+ void updateVirtualSql();
+ SqliteExprPtr parseExpression(const QString& sql);
+
+ Ui::ConstraintCheckPanel *ui = nullptr;
+
+ private slots:
+ void updateState();
+};
+
+#endif // CONSTRAINTCHECKPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.ui
new file mode 100644
index 0000000..58f58e6
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintcheckpanel.ui
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConstraintCheckPanel</class>
+ <widget class="QWidget" name="ConstraintCheckPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>197</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="exprGroup">
+ <property name="title">
+ <string>The expression</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="SqlEditor" name="exprEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="namedCheck">
+ <property name="text">
+ <string>Named constraint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="onConflictWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="onConflictCheck">
+ <property name="text">
+ <string>On conflict</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="onConflictCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SqlEditor</class>
+ <extends>QPlainTextEdit</extends>
+ <header>sqleditor.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp
new file mode 100644
index 0000000..031fbfe
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.cpp
@@ -0,0 +1,96 @@
+#include "constraintpanel.h"
+#include "common/unused.h"
+#include "constraints/tableprimarykeypanel.h"
+#include "constraints/tableforeignkeypanel.h"
+#include "constraints/tableuniquepanel.h"
+#include "constraints/tablecheckpanel.h"
+#include "constraints/columncheckpanel.h"
+#include "constraints/columncollatepanel.h"
+#include "constraints/columndefaultpanel.h"
+#include "constraints/columnforeignkeypanel.h"
+#include "constraints/columnnotnullpanel.h"
+#include "constraints/columnprimarykeypanel.h"
+#include "constraints/columnuniquepanel.h"
+#include <QDebug>
+
+ConstraintPanel::ConstraintPanel(QWidget *parent) :
+ QWidget(parent)
+{
+}
+
+ConstraintPanel::~ConstraintPanel()
+{
+}
+
+void ConstraintPanel::setConstraint(SqliteStatement* stmt)
+{
+ constraint = stmt;
+ constraintAvailable();
+}
+
+void ConstraintPanel::storeDefinition()
+{
+ storeConfiguration();
+ constraint->rebuildTokens();
+}
+
+void ConstraintPanel::setDb(Db* value)
+{
+ db = value;
+}
+
+bool ConstraintPanel::validateOnly()
+{
+ return validate();
+}
+
+ConstraintPanel* ConstraintPanel::produce(SqliteCreateTable::Constraint* constr)
+{
+ switch (constr->type)
+ {
+ case SqliteCreateTable::Constraint::PRIMARY_KEY:
+ return new TablePrimaryKeyPanel();
+ case SqliteCreateTable::Constraint::UNIQUE:
+ return new TableUniquePanel();
+ case SqliteCreateTable::Constraint::CHECK:
+ return new TableCheckPanel();
+ case SqliteCreateTable::Constraint::FOREIGN_KEY:
+ return new TableForeignKeyPanel();
+ case SqliteCreateTable::Constraint::NAME_ONLY:
+ break;
+ }
+
+ qCritical() << "No panel defined in ConstraintPanel::createConstraintPanel()!";
+ Q_ASSERT_X(true, "ConstraintPanel::produce()", "No panel defined!");
+ return nullptr;
+}
+
+ConstraintPanel* ConstraintPanel::produce(SqliteCreateTable::Column::Constraint* constr)
+{
+ switch (constr->type)
+ {
+ case SqliteCreateTable::Column::Constraint::PRIMARY_KEY:
+ return new ColumnPrimaryKeyPanel();
+ case SqliteCreateTable::Column::Constraint::NOT_NULL:
+ return new ColumnNotNullPanel();
+ case SqliteCreateTable::Column::Constraint::UNIQUE:
+ return new ColumnUniquePanel();
+ case SqliteCreateTable::Column::Constraint::CHECK:
+ return new ColumnCheckPanel();
+ case SqliteCreateTable::Column::Constraint::DEFAULT:
+ return new ColumnDefaultPanel();
+ case SqliteCreateTable::Column::Constraint::COLLATE:
+ return new ColumnCollatePanel();
+ case SqliteCreateTable::Column::Constraint::FOREIGN_KEY:
+ return new ColumnForeignKeyPanel();
+ case SqliteCreateTable::Column::Constraint::NULL_:
+ case SqliteCreateTable::Column::Constraint::NAME_ONLY:
+ case SqliteCreateTable::Column::Constraint::DEFERRABLE_ONLY:
+ break;
+ }
+
+ qCritical() << "No panel defined in ConstraintPanel::createConstraintPanel()!";
+ Q_ASSERT_X(true, "ConstraintPanel::produce()", "No panel defined");
+ return nullptr;
+}
+
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.h
new file mode 100644
index 0000000..9f875a9
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/constraintpanel.h
@@ -0,0 +1,79 @@
+#ifndef CONSTRAINTPANEL_H
+#define CONSTRAINTPANEL_H
+
+#include "db/db.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+#include <QPointer>
+
+class GUI_API_EXPORT ConstraintPanel : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ explicit ConstraintPanel(QWidget *parent = 0);
+ virtual ~ConstraintPanel();
+
+ void setConstraint(SqliteStatement* stmt);
+ void storeDefinition();
+ virtual void setDb(Db* value);
+
+ /**
+ * @brief validate Validates panel for correct data filled in.
+ * @return true if the data is valid, or false otherwise.
+ * Apart from returning boolean result it also marks
+ * invalid fields with red color. See validateOnly() description
+ * for details on differences between those two methods.
+ */
+ virtual bool validate() = 0;
+
+ /**
+ * @brief validateOnly Validates panel for correct data filled in.
+ * @return true if the data is valid, or false otherwise.
+ * The difference between validateOnly() and validate() is that validateOnly()
+ * will run all validations immediately (ie. SQL syntax checking
+ * in DEFAULT constraint, etc), while the validate() will wait for
+ * SqlEditor to do the validation in its scheduled time and return
+ * false until the validation isn't done yet.
+ * The validate() should be used when user actually edits the panel,
+ * while validateOnly() is to be used when using ConstraintPanel for validation
+ * of a Constraint object, but not displayed - in that case the validation
+ * result is needed immediately and that's where validateOnly() does its job.
+ * Not every constraint panel has to reimplement this. Most of the constraints
+ * don't work asynchronously and return proper result just from a validate() call.
+ * In that case the default implementation of validateOnly() will do the job.
+ */
+ virtual bool validateOnly();
+
+ static ConstraintPanel* produce(SqliteCreateTable::Constraint* constr);
+ static ConstraintPanel* produce(SqliteCreateTable::Column::Constraint* constr);
+
+ protected:
+ /**
+ * @brief constraintAvailable
+ * This method is called once the constraint object (the member variable)
+ * is available to the panel as well, as the database (the db member).
+ * The implementation should read values from constraint object and put them
+ * to panel's fields, but also initialise any database related data,
+ * like existing collations, etc.
+ */
+ virtual void constraintAvailable() = 0;
+
+ /**
+ * @brief storeConfiguration
+ * The implementation should store all field valies into the constraint object.
+ */
+ virtual void storeConfiguration() = 0;
+
+ Db* db = nullptr;
+ QPointer<SqliteStatement> constraint;
+
+ public slots:
+
+ signals:
+ void updateValidation();
+
+};
+
+#endif // CONSTRAINTPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.cpp
new file mode 100644
index 0000000..72c9d35
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.cpp
@@ -0,0 +1,56 @@
+#include "tablecheckpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "parser/parser.h"
+#include <QDebug>
+
+TableCheckPanel::TableCheckPanel(QWidget *parent) :
+ ConstraintCheckPanel(parent)
+{
+}
+
+SqliteExpr* TableCheckPanel::readExpr()
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ return constr->expr;
+}
+
+QString TableCheckPanel::readName()
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ return constr->name;
+}
+
+void TableCheckPanel::storeType()
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Constraint::CHECK;
+}
+
+SqliteConflictAlgo TableCheckPanel::readConflictAlgo()
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ return constr->onConflict;
+}
+
+void TableCheckPanel::storeExpr(SqliteExpr* expr)
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->expr = expr;
+}
+
+void TableCheckPanel::storeName(const QString& name)
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->name = name;
+}
+
+void TableCheckPanel::storeConflictAlgo(SqliteConflictAlgo algo)
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->onConflict = algo;
+}
+
+SqliteCreateTable* TableCheckPanel::getCreateTable()
+{
+ return dynamic_cast<SqliteCreateTable*>(constraint->parentStatement());
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.h
new file mode 100644
index 0000000..af9039e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablecheckpanel.h
@@ -0,0 +1,27 @@
+#ifndef TABLECHECKPANEL_H
+#define TABLECHECKPANEL_H
+
+#include "constraintcheckpanel.h"
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+#include <QWidget>
+
+class GUI_API_EXPORT TableCheckPanel : public ConstraintCheckPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit TableCheckPanel(QWidget *parent = 0);
+
+ protected:
+ SqliteExpr* readExpr();
+ QString readName();
+ void storeType();
+ SqliteConflictAlgo readConflictAlgo();
+ void storeExpr(SqliteExpr* expr);
+ void storeName(const QString& name);
+ void storeConflictAlgo(SqliteConflictAlgo algo);
+ SqliteCreateTable* getCreateTable();
+};
+
+#endif // TABLECHECKPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp
new file mode 100644
index 0000000..60da220
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp
@@ -0,0 +1,418 @@
+#include "tableforeignkeypanel.h"
+#include "ui_tableforeignkeypanel.h"
+#include "schemaresolver.h"
+#include "uiutils.h"
+#include "common/widgetstateindicator.h"
+#include <QDebug>
+#include <QSignalMapper>
+
+TableForeignKeyPanel::TableForeignKeyPanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::TableForeignKeyPanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+TableForeignKeyPanel::~TableForeignKeyPanel()
+{
+ delete ui;
+}
+
+void TableForeignKeyPanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+
+bool TableForeignKeyPanel::validate()
+{
+ bool tableOk = (ui->fkTableCombo->currentIndex() > -1);
+
+ bool columnsOk = false;
+ bool columnsSelected = true;
+ bool idxOk = true;
+ QCheckBox* check = nullptr;
+ QComboBox* combo = nullptr;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ check = qobject_cast<QCheckBox*>(columnsLayout->itemAtPosition(i, 0)->widget());
+ combo = qobject_cast<QComboBox*>(columnsLayout->itemAtPosition(i, 1)->widget());
+ if (check->isChecked())
+ {
+ columnsOk = true;
+
+ idxOk = (combo->currentIndex() > -1);
+ setValidState(combo, idxOk, tr("Pick the foreign column."));
+ if (!idxOk)
+ columnsSelected = false;
+
+ break;
+ }
+ }
+
+ bool nameOk = true;
+ if (ui->namedCheckBox->isChecked() && ui->nameEdit->text().isEmpty())
+ nameOk = false;
+
+ setValidState(ui->fkTableCombo, tableOk, tr("Pick the foreign table."));
+ setValidState(ui->columnsGroup, columnsOk, tr("Select at least one foreign column."));
+ setValidState(ui->nameEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return tableOk && columnsOk && nameOk && columnsSelected;
+}
+
+void TableForeignKeyPanel::setDb(Db* value)
+{
+ ConstraintPanel::setDb(value);
+ ui->sqlite2Warn->setVisible(value->getDialect() == Dialect::Sqlite2);
+}
+
+void TableForeignKeyPanel::constraintAvailable()
+{
+ readTables();
+ buildColumns();
+ readConstraint();
+}
+
+void TableForeignKeyPanel::init()
+{
+ setFocusProxy(ui->fkTableCombo);
+ columnsLayout = new QGridLayout();
+ ui->columnsScrollContents->setLayout(columnsLayout);
+
+ columnSignalMapping = new QSignalMapper(this);
+ connect(columnSignalMapping, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int)));
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->nameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateFkColumns()));
+ connect(ui->fkTableCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateState()));
+ connect(ui->onDeleteCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->onUpdateCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->matchCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+
+ ui->deferrableCombo->addItems({
+ "",
+ sqliteDeferrable(SqliteDeferrable::DEFERRABLE),
+ sqliteDeferrable(SqliteDeferrable::NOT_DEFERRABLE)
+ });
+ ui->initiallyCombo->addItems({
+ "",
+ sqliteInitially(SqliteInitially::DEFERRED),
+ sqliteInitially(SqliteInitially::IMMEDIATE),
+ });
+
+ QStringList reactions = {
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::NO_ACTION),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_NULL),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::SET_DEFAULT),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::CASCADE),
+ SqliteForeignKey::Condition::toString(SqliteForeignKey::Condition::RESTRICT)
+ };
+ ui->onUpdateCombo->addItems(reactions);
+ ui->onDeleteCombo->addItems(reactions);
+ ui->matchCombo->addItems({"SIMPLE", "FULL", "PARTIAL"});
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void TableForeignKeyPanel::updateState()
+{
+ bool tableSelected = (ui->fkTableCombo->currentIndex() > -1);
+ for (int i = 0; i < totalColumns; i++)
+ updateColumnState(i, tableSelected);
+
+ ui->deferrableCombo->setEnabled(tableSelected);
+ ui->initiallyCombo->setEnabled(tableSelected);
+ ui->namedCheckBox->setEnabled(tableSelected);
+ ui->nameEdit->setEnabled(tableSelected && ui->namedCheckBox->isChecked());
+ ui->onDeleteCheckBox->setEnabled(tableSelected);
+ ui->onUpdateCheckBox->setEnabled(tableSelected);
+ ui->matchCheckBox->setEnabled(tableSelected);
+ ui->onDeleteCombo->setEnabled(tableSelected && ui->onDeleteCheckBox->isChecked());
+ ui->onUpdateCombo->setEnabled(tableSelected && ui->onUpdateCheckBox->isChecked());
+ ui->matchCombo->setEnabled(tableSelected && ui->matchCheckBox->isChecked());
+}
+
+void TableForeignKeyPanel::updateColumnState(int rowIdx, bool tableSelected)
+{
+ QCheckBox* check = qobject_cast<QCheckBox*>(columnsLayout->itemAtPosition(rowIdx, 0)->widget());
+ bool wasEnabled = check->isEnabled();
+ check->setEnabled(tableSelected);
+
+ QComboBox* combo = qobject_cast<QComboBox*>(columnsLayout->itemAtPosition(rowIdx, 1)->widget());
+ combo->setEnabled(tableSelected && check->isChecked());
+
+ if (!wasEnabled && check->isEnabled())
+ {
+ // Automatically set matching column
+ int idx = fkColumnsModel.stringList().indexOf(check->text());
+ if (idx > -1)
+ combo->setCurrentIndex(idx);
+ }
+}
+
+void TableForeignKeyPanel::updateColumnState(int rowIdx)
+{
+ bool tableSelected = (ui->fkTableCombo->currentIndex() > -1);
+ updateColumnState(rowIdx, tableSelected);
+}
+
+void TableForeignKeyPanel::updateFkColumns()
+{
+ QStringList columns;
+ if (ui->fkTableCombo->currentIndex() == -1)
+ {
+ fkColumnsModel.setStringList(columns);
+ updateState();
+ return;
+ }
+
+ SchemaResolver resolver(db);
+ columns = resolver.getTableColumns(ui->fkTableCombo->currentText()); // TODO named db attach not supported
+ fkColumnsModel.setStringList(columns);
+}
+
+void TableForeignKeyPanel::buildColumns()
+{
+ totalColumns = 0;
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(constraint->parentStatement());
+ int row = 0;
+ foreach (SqliteCreateTable::Column* column, createTable->columns)
+ buildColumn(column, row++);
+}
+
+void TableForeignKeyPanel::buildColumn(SqliteCreateTable::Column* column, int row)
+{
+ int col = 0;
+
+ QCheckBox* check = new QCheckBox(column->name);
+ columnsLayout->addWidget(check, row, col++);
+ columnSignalMapping->setMapping(check, row);
+ connect(check, SIGNAL(toggled(bool)), columnSignalMapping, SLOT(map()));
+ connect(check, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+
+ QComboBox* fkCol = new QComboBox();
+ fkCol->setToolTip(tr("Foreign column", "table constraints"));
+ fkCol->setModel(&fkColumnsModel);
+ columnsLayout->addWidget(fkCol, row, col++);
+ connect(fkCol, SIGNAL(currentIndexChanged(int)), this, SIGNAL(updateValidation()));
+
+ totalColumns++;
+
+ updateColumnState(row);
+}
+
+void TableForeignKeyPanel::readConstraint()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ if (!constr->foreignKey)
+ return;
+
+ if (!constr->foreignKey->foreignTable.isNull())
+ ui->fkTableCombo->setCurrentText(constr->foreignKey->foreignTable);
+
+ foreach (SqliteForeignKey::Condition* condition, constr->foreignKey->conditions)
+ readCondition(condition);
+
+ ui->deferrableCombo->setCurrentText(sqliteDeferrable(constr->foreignKey->deferrable));
+ ui->initiallyCombo->setCurrentText(sqliteInitially(constr->foreignKey->initially));
+
+ // Name
+ if (!constr->name.isNull())
+ {
+ ui->namedCheckBox->setChecked(true);
+ ui->nameEdit->setText(constr->name);
+ }
+
+ // Columns
+ int idx;
+ QCheckBox* check = nullptr;
+ QComboBox* combo = nullptr;
+ SqliteIndexedColumn* localCol = nullptr;
+ SqliteIndexedColumn* foreignCol = nullptr;
+ int i = 0;
+ foreach (localCol, constr->indexedColumns)
+ {
+ // Foreign col
+ if (i < constr->foreignKey->indexedColumns.size())
+ foreignCol = constr->foreignKey->indexedColumns[i];
+ else
+ foreignCol = nullptr;
+
+ i++;
+
+ // Column index
+ idx = getColumnIndex(localCol->name);
+ if (idx < 0)
+ continue;
+
+ // Column states
+ check = dynamic_cast<QCheckBox*>(columnsLayout->itemAtPosition(idx, 0)->widget());
+ check->setChecked(true);
+
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(idx, 1)->widget());
+ if (foreignCol)
+ combo->setCurrentText(foreignCol->name);
+ else if (fkColumnsModel.stringList().contains(localCol->name))
+ combo->setCurrentText(localCol->name);
+ else
+ combo->setCurrentIndex(-1);
+ }
+}
+
+void TableForeignKeyPanel::readCondition(SqliteForeignKey::Condition* condition)
+{
+ switch (condition->action)
+ {
+ case SqliteForeignKey::Condition::UPDATE:
+ ui->onUpdateCheckBox->setChecked(true);
+ ui->onUpdateCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ case SqliteForeignKey::Condition::INSERT:
+ // INSERT is not officially supported.
+ break;
+ case SqliteForeignKey::Condition::DELETE:
+ ui->onDeleteCheckBox->setChecked(true);
+ ui->onDeleteCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ case SqliteForeignKey::Condition::MATCH:
+ ui->matchCheckBox->setChecked(true);
+ ui->matchCombo->setCurrentText(SqliteForeignKey::Condition::toString(condition->reaction));
+ break;
+ }
+}
+
+void TableForeignKeyPanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ // Type
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Constraint::FOREIGN_KEY;
+
+ // Cleanup & initial setup
+ if (constr->foreignKey)
+ delete constr->foreignKey;
+
+ foreach (SqliteIndexedColumn* idxCol, constr->indexedColumns)
+ delete idxCol;
+
+ constr->indexedColumns.clear();
+
+ constr->foreignKey = new SqliteForeignKey();
+ constr->foreignKey->setParent(constr);
+
+ // Foreign table
+ constr->foreignKey->foreignTable = ui->fkTableCombo->currentText();
+
+ // Columns
+ QCheckBox* check = nullptr;
+ QComboBox* combo = nullptr;
+ SqliteIndexedColumn* idxCol = nullptr;
+ QString name;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ // Local column
+ check = dynamic_cast<QCheckBox*>(columnsLayout->itemAtPosition(i, 0)->widget());
+ if (!check->isChecked())
+ continue;
+
+ idxCol = new SqliteIndexedColumn(check->text());
+ idxCol->setParent(constr);
+ constr->indexedColumns << idxCol;
+
+ // Foreign column
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(i, 1)->widget());
+
+ idxCol = new SqliteIndexedColumn(combo->currentText());
+ idxCol->setParent(constr->foreignKey);
+ constr->foreignKey->indexedColumns << idxCol;
+ }
+
+ // Actions/reactions
+ if (ui->onDeleteCheckBox->isChecked())
+ storeCondition(SqliteForeignKey::Condition::DELETE, ui->onDeleteCombo->currentText());
+
+ if (ui->onUpdateCheckBox->isChecked())
+ storeCondition(SqliteForeignKey::Condition::UPDATE, ui->onDeleteCombo->currentText());
+
+ if (ui->matchCheckBox->isChecked())
+ storeMatchCondition(ui->matchCombo->currentText());
+
+ // Deferred/initially
+ constr->foreignKey->deferrable = sqliteDeferrable(ui->deferrableCombo->currentText());
+ constr->foreignKey->initially = sqliteInitially(ui->initiallyCombo->currentText());
+
+ // Name
+ constr->name = QString::null;
+ if (ui->namedCheckBox->isChecked())
+ constr->name = ui->nameEdit->text();
+}
+
+void TableForeignKeyPanel::storeCondition(SqliteForeignKey::Condition::Action action, const QString& reaction)
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+
+ SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(
+ action,
+ SqliteForeignKey::Condition::toEnum(reaction)
+ );
+ condition->setParent(constr->foreignKey);
+ constr->foreignKey->conditions << condition;
+}
+
+void TableForeignKeyPanel::storeMatchCondition(const QString& reaction)
+{
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+
+ SqliteForeignKey::Condition* condition = new SqliteForeignKey::Condition(reaction);
+ condition->setParent(constr->foreignKey);
+ constr->foreignKey->conditions << condition;
+}
+
+void TableForeignKeyPanel::readTables()
+{
+ SchemaResolver resolver(db);
+ resolver.setIgnoreSystemObjects(true);
+ QStringList tables = resolver.getTables(); // TODO named db attach not supported
+
+ SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(constraint->parentStatement());
+ tables.removeOne(createTable->table);
+
+ tables.sort(Qt::CaseInsensitive);
+
+ ui->fkTableCombo->addItems(tables);
+ ui->fkTableCombo->setCurrentIndex(-1);
+}
+
+int TableForeignKeyPanel::getColumnIndex(const QString& colName)
+{
+ QWidget* item = nullptr;
+ QCheckBox* cb = nullptr;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ item = columnsLayout->itemAtPosition(i, 0)->widget();
+ cb = qobject_cast<QCheckBox*>(item);
+ if (cb->text().compare(colName, Qt::CaseInsensitive) == 0)
+ return i;
+ }
+ return -1;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.h
new file mode 100644
index 0000000..cecc04a
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.h
@@ -0,0 +1,57 @@
+#ifndef TABLEFOREIGNKEYPANEL_H
+#define TABLEFOREIGNKEYPANEL_H
+
+#include "constraintpanel.h"
+#include "parser/ast/sqlitecreatetable.h"
+#include "guiSQLiteStudio_global.h"
+#include <QStringListModel>
+#include <QWidget>
+
+namespace Ui {
+ class TableForeignKeyPanel;
+}
+
+class QGridLayout;
+class QSignalMapper;
+
+class GUI_API_EXPORT TableForeignKeyPanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit TableForeignKeyPanel(QWidget *parent = 0);
+ ~TableForeignKeyPanel();
+
+ bool validate();
+ void setDb(Db* value);
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ void storeConfiguration();
+
+ private:
+ void init();
+ void buildColumns();
+ void buildColumn(SqliteCreateTable::Column* column, int row);
+ void readConstraint();
+ void readTables();
+ void readCondition(SqliteForeignKey::Condition* condition);
+ int getColumnIndex(const QString& colName);
+ void storeCondition(SqliteForeignKey::Condition::Action action, const QString& reaction);
+ void storeMatchCondition(const QString& reaction);
+
+ Ui::TableForeignKeyPanel *ui = nullptr;
+ QGridLayout* columnsLayout = nullptr;
+ int totalColumns = 0;
+ QStringListModel fkColumnsModel;
+ QSignalMapper* columnSignalMapping = nullptr;
+
+ private slots:
+ void updateState();
+ void updateColumnState(int rowIdx, bool tableSelected);
+ void updateColumnState(int rowIdx);
+ void updateFkColumns();
+};
+
+#endif // TABLEFOREIGNKEYPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui
new file mode 100644
index 0000000..8917381
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.ui
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TableForeignKeyPanel</class>
+ <widget class="QWidget" name="TableForeignKeyPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>400</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QWidget" name="fkTableWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="fkTableLabel">
+ <property name="text">
+ <string>Foreign table:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="fkTableCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="sqlite2Warn">
+ <property name="font">
+ <font>
+ <pointsize>8</pointsize>
+ <italic>true</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>SQLite 2 does not support foreign keys officially,
+but it's okay to use them anyway.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="columnsGroup">
+ <property name="title">
+ <string>Columns</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QWidget" name="columnsHeader" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="colHdrLocal">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Local column</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="colHdrForeign">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Foreign column</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="columnsArea">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="columnsScrollContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>362</width>
+ <height>25</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="reactionsGroup">
+ <property name="title">
+ <string>Reactions</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="onUpdateCheckBox">
+ <property name="text">
+ <string>ON UPDATE</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="onUpdateCombo"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="onDeleteCheckBox">
+ <property name="text">
+ <string>ON DELETE</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="matchCheckBox">
+ <property name="text">
+ <string>MATCH</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="onDeleteCombo"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="matchCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="deferredGroup">
+ <property name="title">
+ <string>Deferred foreign key</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QComboBox" name="deferrableCombo"/>
+ </item>
+ <item>
+ <widget class="QComboBox" name="initiallyCombo"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QCheckBox" name="namedCheckBox">
+ <property name="text">
+ <string>Named constraint</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="nameEdit">
+ <property name="placeholderText">
+ <string>Constraint name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>fkTableCombo</tabstop>
+ <tabstop>columnsArea</tabstop>
+ <tabstop>onUpdateCheckBox</tabstop>
+ <tabstop>onUpdateCombo</tabstop>
+ <tabstop>onDeleteCheckBox</tabstop>
+ <tabstop>onDeleteCombo</tabstop>
+ <tabstop>matchCheckBox</tabstop>
+ <tabstop>matchCombo</tabstop>
+ <tabstop>deferrableCombo</tabstop>
+ <tabstop>initiallyCombo</tabstop>
+ <tabstop>namedCheckBox</tabstop>
+ <tabstop>nameEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp
new file mode 100644
index 0000000..f2a0ada
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.cpp
@@ -0,0 +1,299 @@
+#include "tablepkanduniquepanel.h"
+#include "ui_tablepkanduniquepanel.h"
+#include "parser/keywords.h"
+#include "schemaresolver.h"
+#include "uiutils.h"
+#include <QGridLayout>
+#include <QHBoxLayout>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QLabel>
+#include <QSignalMapper>
+#include <QDebug>
+#include <QScrollBar>
+
+TablePrimaryKeyAndUniquePanel::TablePrimaryKeyAndUniquePanel(QWidget *parent) :
+ ConstraintPanel(parent),
+ ui(new Ui::TablePrimaryKeyAndUniquePanel)
+{
+ ui->setupUi(this);
+ init();
+}
+
+TablePrimaryKeyAndUniquePanel::~TablePrimaryKeyAndUniquePanel()
+{
+ delete ui;
+}
+
+void TablePrimaryKeyAndUniquePanel::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void TablePrimaryKeyAndUniquePanel::init()
+{
+ columnsLayout = new QGridLayout();
+ ui->scrollAreaWidgetContents->setLayout(columnsLayout);
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+ connect(ui->namedLineEdit, SIGNAL(textChanged(QString)), this, SIGNAL(updateValidation()));
+
+ ui->conflictComboBox->addItems(getConflictAlgorithms());
+
+ columnSignalMapping = new QSignalMapper(this);
+ connect(columnSignalMapping, SIGNAL(mapped(int)), this, SLOT(updateColumnState(int)));
+
+ connect(ui->namedCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ connect(ui->conflictCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateState()));
+ updateState();
+}
+
+void TablePrimaryKeyAndUniquePanel::readCollations()
+{
+ SchemaResolver resolver(db);
+ QStringList collList = resolver.getCollations();
+
+ if (collList.size() > 0)
+ collList.prepend("");
+
+ collations.setStringList(collList);
+}
+
+void TablePrimaryKeyAndUniquePanel::buildColumn(SqliteCreateTable::Column* column, int row)
+{
+ int col = 0;
+
+ QCheckBox* check = new QCheckBox(column->name);
+ columnsLayout->addWidget(check, row, col++);
+ columnSignalMapping->setMapping(check, row);
+ connect(check, SIGNAL(toggled(bool)), columnSignalMapping, SLOT(map()));
+ connect(check, SIGNAL(toggled(bool)), this, SIGNAL(updateValidation()));
+
+ QComboBox* collation = nullptr;
+ if (!constraint.isNull() && constraint->dialect == Dialect::Sqlite3)
+ {
+ collation = new QComboBox();
+ collation->setMaximumWidth(ui->colHdrCollation->width());
+ collation->setMinimumWidth(ui->colHdrCollation->width() - ui->scrollArea->verticalScrollBar()->width());
+ collation->setEditable(true);
+ collation->lineEdit()->setPlaceholderText(tr("Collate", "table constraints"));
+ collation->setModel(&collations);
+ columnsLayout->addWidget(collation, row, col++);
+ }
+
+ QComboBox* sortOrder = new QComboBox();
+ sortOrder->setFixedWidth(ui->colHdrSort->width());
+ sortOrder->setToolTip(tr("Sort order", "table constraints"));
+ columnsLayout->addWidget(sortOrder, row, col++);
+
+ QStringList sortList = {"", sqliteSortOrder(SqliteSortOrder::ASC), sqliteSortOrder(SqliteSortOrder::DESC)};
+ sortOrder->addItems(sortList);
+
+ totalColumns++;
+
+ updateColumnState(row);
+}
+
+int TablePrimaryKeyAndUniquePanel::getColumnIndex(const QString& colName)
+{
+ QWidget* item = nullptr;
+ QCheckBox* cb = nullptr;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ item = columnsLayout->itemAtPosition(i, 0)->widget();
+ cb = qobject_cast<QCheckBox*>(item);
+ if (cb->text().compare(colName, Qt::CaseInsensitive) == 0)
+ return i;
+ }
+ return -1;
+}
+
+void TablePrimaryKeyAndUniquePanel::updateColumnState(int colIdx)
+{
+ QWidget* item = columnsLayout->itemAtPosition(colIdx, 0)->widget();
+ QCheckBox* cb = qobject_cast<QCheckBox*>(item);
+ bool enable = cb->isChecked();
+
+ item = columnsLayout->itemAtPosition(colIdx, 1)->widget();
+ qobject_cast<QComboBox*>(item)->setEnabled(enable);
+
+ if (!constraint.isNull() && constraint->dialect == Dialect::Sqlite3)
+ {
+ item = columnsLayout->itemAtPosition(colIdx, 2)->widget();
+ qobject_cast<QComboBox*>(item)->setEnabled(enable);
+ }
+
+ updateState();
+}
+
+void TablePrimaryKeyAndUniquePanel::updateState()
+{
+ ui->namedLineEdit->setEnabled(ui->namedCheckBox->isChecked());
+ ui->conflictComboBox->setEnabled(ui->conflictCheckBox->isChecked());
+}
+
+void TablePrimaryKeyAndUniquePanel::constraintAvailable()
+{
+ readCollations();
+ buildColumns();
+ readConstraint();
+}
+
+bool TablePrimaryKeyAndUniquePanel::validate()
+{
+ bool countOk = false;
+ QWidget* item = nullptr;
+ QCheckBox* cb = nullptr;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ item = columnsLayout->itemAtPosition(i, 0)->widget();
+ cb = qobject_cast<QCheckBox*>(item);
+ if (cb->isChecked())
+ {
+ countOk = true;
+ break;
+ }
+ }
+
+ bool nameOk = true;
+ if (ui->namedCheckBox->isChecked() && ui->namedLineEdit->text().isEmpty())
+ nameOk = false;
+
+ setValidState(ui->groupBox, countOk, tr("Select at least one column."));
+ setValidState(ui->namedLineEdit, nameOk, tr("Enter a name of the constraint."));
+
+ return countOk && nameOk;
+}
+
+
+void TablePrimaryKeyAndUniquePanel::storeConfiguration()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+
+ // Name
+ constr->name = QString::null;
+ if (ui->namedCheckBox->isChecked())
+ constr->name = ui->namedLineEdit->text();
+
+ // On conflict
+ if (ui->conflictCheckBox->isChecked())
+ constr->onConflict = sqliteConflictAlgo(ui->conflictComboBox->currentText());
+
+ // Columns
+ foreach (SqliteIndexedColumn* idxCol, constr->indexedColumns)
+ delete idxCol;
+
+ constr->indexedColumns.clear();
+
+ QCheckBox* check = nullptr;
+ QComboBox* combo = nullptr;
+ SqliteIndexedColumn* idxCol = nullptr;
+ QString name;
+ QString collate;
+ SqliteSortOrder sortOrder;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ check = dynamic_cast<QCheckBox*>(columnsLayout->itemAtPosition(i, 0)->widget());
+ if (!check->isChecked())
+ continue;
+
+ name = check->text();
+
+ if (constr->dialect == Dialect::Sqlite3)
+ {
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(i, 1)->widget());
+ collate = combo->currentText();
+ if (collate.isEmpty())
+ collate = QString::null;
+
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(i, 2)->widget());
+ sortOrder = sqliteSortOrder(combo->currentText());
+ }
+ else
+ {
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(i, 1)->widget());
+ sortOrder = sqliteSortOrder(combo->currentText());
+ }
+
+ idxCol = new SqliteIndexedColumn(name, collate, sortOrder);
+ idxCol->setParent(constr);
+ constr->indexedColumns << idxCol;
+ }
+}
+
+void TablePrimaryKeyAndUniquePanel::readConstraint()
+{
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+
+ // Name
+ if (!constr->name.isNull())
+ {
+ ui->namedCheckBox->setChecked(true);
+ ui->namedLineEdit->setText(constr->name);
+ }
+
+ // On conflict
+ if (constr->onConflict != SqliteConflictAlgo::null)
+ {
+ ui->conflictCheckBox->setChecked(true);
+ ui->conflictComboBox->setCurrentText(sqliteConflictAlgo(constr->onConflict));
+ }
+
+ // Columns
+ int idx;
+ QCheckBox* check = nullptr;
+ QComboBox* combo = nullptr;
+ foreach (SqliteIndexedColumn* idxCol, constr->indexedColumns)
+ {
+ idx = getColumnIndex(idxCol->name);
+ if (idx < 0)
+ continue;
+
+ check = dynamic_cast<QCheckBox*>(columnsLayout->itemAtPosition(idx, 0)->widget());
+ check->setChecked(true);
+
+ if (constr->dialect == Dialect::Sqlite3)
+ {
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(idx, 1)->widget());
+ combo->setCurrentText(idxCol->collate);
+
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(idx, 2)->widget());
+ combo->setCurrentText(sqliteSortOrder(idxCol->sortOrder));
+ }
+ else
+ {
+ combo = dynamic_cast<QComboBox*>(columnsLayout->itemAtPosition(idx, 1)->widget());
+ combo->setCurrentText(sqliteSortOrder(idxCol->sortOrder));
+ }
+ }
+
+ if (constr->dialect == Dialect::Sqlite2)
+ {
+ ui->colHdrCollation->setVisible(false);
+ }
+}
+
+void TablePrimaryKeyAndUniquePanel::buildColumns()
+{
+ totalColumns = 0;
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(constraint->parentStatement());
+ int row = 0;
+ foreach (SqliteCreateTable::Column* column, createTable->columns)
+ buildColumn(column, row++);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.h
new file mode 100644
index 0000000..1092115
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.h
@@ -0,0 +1,63 @@
+#ifndef PRIMARYKEYANDUNIQUEPANEL_H
+#define PRIMARYKEYANDUNIQUEPANEL_H
+
+#include "parser/ast/sqlitecreatetable.h"
+#include "constraintpanel.h"
+#include "guiSQLiteStudio_global.h"
+#include <QStringListModel>
+
+namespace Ui {
+ class TablePrimaryKeyAndUniquePanel;
+}
+
+class QGridLayout;
+class QSignalMapper;
+
+class GUI_API_EXPORT TablePrimaryKeyAndUniquePanel : public ConstraintPanel
+{
+ Q_OBJECT
+
+ public:
+ explicit TablePrimaryKeyAndUniquePanel(QWidget *parent = 0);
+ ~TablePrimaryKeyAndUniquePanel();
+
+ bool validate();
+ void storeConfiguration();
+
+ protected:
+ void changeEvent(QEvent *e);
+ void constraintAvailable();
+ virtual void readConstraint();
+
+ Ui::TablePrimaryKeyAndUniquePanel *ui = nullptr;
+ QGridLayout* columnsLayout = nullptr;
+
+ /**
+ * @brief totalColumns
+ * Used to count columns created by buildColumns().
+ * It's a workaround for QGridLayout::rowCount(), which doesn't return
+ * number of visible rows, but instead a number of allocated rows,
+ * which isn't useful in any way.
+ * This variable lets us to avoid asking for widgets from rows
+ * that does not exist.
+ */
+ int totalColumns = 0;
+
+ private:
+ void init();
+ void readCollations();
+ void buildColumns();
+ void buildColumn(SqliteCreateTable::Column* column, int row);
+ int getColumnIndex(const QString& colName);
+
+ QStringListModel collations;
+ QSignalMapper* columnSignalMapping = nullptr;
+
+ private slots:
+ void updateColumnState(int colIdx);
+
+ protected slots:
+ virtual void updateState();
+};
+
+#endif // PRIMARYKEYANDUNIQUEPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.ui
new file mode 100644
index 0000000..9453d3c
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tablepkanduniquepanel.ui
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TablePrimaryKeyAndUniquePanel</class>
+ <widget class="QWidget" name="TablePrimaryKeyAndUniquePanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>386</width>
+ <height>269</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>PrimaryKeyOrUniquePanel</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="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Columns</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QWidget" name="columnsHeader" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <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="QLabel" name="colHdrColumn">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Column</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="colHdrCollation">
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Collation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="colHdrSort">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sort</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>132</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoIncrCheckBox">
+ <property name="toolTip">
+ <string>Valid only for a single column with INTEGER data type</string>
+ </property>
+ <property name="text">
+ <string>Autoincrement</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <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="QCheckBox" name="namedCheckBox">
+ <property name="text">
+ <string>Named constraint</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedLineEdit">
+ <property name="placeholderText">
+ <string>Constraint name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="conflictWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_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="QCheckBox" name="conflictCheckBox">
+ <property name="text">
+ <string>On conflict</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="conflictComboBox">
+ <property name="maximumSize">
+ <size>
+ <width>140</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>scrollArea</tabstop>
+ <tabstop>autoIncrCheckBox</tabstop>
+ <tabstop>namedCheckBox</tabstop>
+ <tabstop>namedLineEdit</tabstop>
+ <tabstop>conflictCheckBox</tabstop>
+ <tabstop>conflictComboBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.cpp
new file mode 100644
index 0000000..c538990
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.cpp
@@ -0,0 +1,83 @@
+#include "tableprimarykeypanel.h"
+#include "ui_tablepkanduniquepanel.h"
+#include <QDebug>
+
+TablePrimaryKeyPanel::TablePrimaryKeyPanel(QWidget *parent) :
+ TablePrimaryKeyAndUniquePanel(parent)
+{
+}
+
+void TablePrimaryKeyPanel::storeConfiguration()
+{
+ TablePrimaryKeyAndUniquePanel::storeConfiguration();
+
+ if (constraint.isNull())
+ return;
+
+ // Type
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Constraint::PRIMARY_KEY;
+
+ // Autoincr
+ constr->autoincrKw = ui->autoIncrCheckBox->isChecked();
+}
+
+
+void TablePrimaryKeyPanel::readConstraint()
+{
+ TablePrimaryKeyAndUniquePanel::readConstraint();
+
+ if (constraint.isNull())
+ return;
+
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+
+ // Autoincr
+ if (constr->autoincrKw)
+ ui->autoIncrCheckBox->setChecked(true);
+}
+
+void TablePrimaryKeyPanel::updateState()
+{
+ TablePrimaryKeyAndUniquePanel::updateState();
+
+ // Autoincr
+ QStringList columns;
+ QWidget* item = nullptr;
+ QCheckBox* cb = nullptr;
+ for (int i = 0; i < totalColumns; i++)
+ {
+ item = columnsLayout->itemAtPosition(i, 0)->widget();
+ cb = qobject_cast<QCheckBox*>(item);
+ if (cb->isChecked())
+ columns << cb->text();
+ }
+
+ if (columns.size() != 1)
+ {
+ ui->autoIncrCheckBox->setChecked(false);
+ ui->autoIncrCheckBox->setEnabled(false);
+ return;
+ }
+
+ SqliteCreateTable* createTable = dynamic_cast<SqliteCreateTable*>(constraint->parentStatement());
+ QString colName = columns.first();
+ SqliteCreateTable::Column* column = createTable->getColumn(colName);
+ if (!column)
+ {
+ qCritical() << "Could not find column when checking for AUTOINCREMENT checkbox state:" << colName
+ << "\nCreateTable statement:" << createTable->detokenize();
+ ui->autoIncrCheckBox->setChecked(false);
+ ui->autoIncrCheckBox->setEnabled(false);
+ return;
+ }
+
+ if (!column->type || column->type->detokenize().trimmed().compare("INTEGER", Qt::CaseInsensitive) != 0)
+ {
+ ui->autoIncrCheckBox->setChecked(false);
+ ui->autoIncrCheckBox->setEnabled(false);
+ return;
+ }
+
+ ui->autoIncrCheckBox->setEnabled(true);
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.h
new file mode 100644
index 0000000..d5e2e59
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.h
@@ -0,0 +1,23 @@
+#ifndef PRIMARYKEYPANEL_H
+#define PRIMARYKEYPANEL_H
+
+#include "tablepkanduniquepanel.h"
+#include "guiSQLiteStudio_global.h"
+
+class GUI_API_EXPORT TablePrimaryKeyPanel : public TablePrimaryKeyAndUniquePanel
+{
+ Q_OBJECT
+
+ public:
+ explicit TablePrimaryKeyPanel(QWidget *parent = 0);
+
+ void storeConfiguration();
+
+ private:
+ void readConstraint();
+
+ private slots:
+ void updateState();
+};
+
+#endif // PRIMARYKEYPANEL_H
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.ui b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.ui
new file mode 100644
index 0000000..76febc9
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableprimarykeypanel.ui
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PrimaryKeyPanel</class>
+ <widget class="QWidget" name="PrimaryKeyPanel">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>386</width>
+ <height>269</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Column</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>147</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="autoIncrCheckBox">
+ <property name="text">
+ <string>Autoincrement</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="namedWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="namedCheckBox">
+ <property name="text">
+ <string>Named constraint</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="namedLineEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="conflictWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="conflictCheckBox">
+ <property name="text">
+ <string>On conflict</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboBox">
+ <property name="maximumSize">
+ <size>
+ <width>140</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.cpp b/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.cpp
new file mode 100644
index 0000000..14b298e
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.cpp
@@ -0,0 +1,21 @@
+#include "tableuniquepanel.h"
+#include "ui_tablepkanduniquepanel.h"
+
+TableUniquePanel::TableUniquePanel(QWidget *parent) :
+ TablePrimaryKeyAndUniquePanel(parent)
+{
+ ui->autoIncrCheckBox->setVisible(false);
+}
+
+
+void TableUniquePanel::storeConfiguration()
+{
+ TablePrimaryKeyAndUniquePanel::storeConfiguration();
+
+ if (constraint.isNull())
+ return;
+
+ // Type
+ SqliteCreateTable::Constraint* constr = dynamic_cast<SqliteCreateTable::Constraint*>(constraint.data());
+ constr->type = SqliteCreateTable::Constraint::UNIQUE;
+}
diff --git a/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.h b/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.h
new file mode 100644
index 0000000..bd77910
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/constraints/tableuniquepanel.h
@@ -0,0 +1,16 @@
+#ifndef TABLEUNIQUEPANEL_H
+#define TABLEUNIQUEPANEL_H
+
+#include "tablepkanduniquepanel.h"
+#include "guiSQLiteStudio_global.h"
+
+class GUI_API_EXPORT TableUniquePanel : public TablePrimaryKeyAndUniquePanel
+{
+ Q_OBJECT
+ public:
+ explicit TableUniquePanel(QWidget *parent = 0);
+
+ void storeConfiguration();
+};
+
+#endif // TABLEUNIQUEPANEL_H