diff options
| author | 2014-12-06 17:33:25 -0500 | |
|---|---|---|
| committer | 2014-12-06 17:33:25 -0500 | |
| commit | 7167ce41b61d2ba2cdb526777a4233eb84a3b66a (patch) | |
| tree | a35c14143716e1f2c98f808c81f89426045a946f /SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp | |
Imported Upstream version 2.99.6upstream/2.99.6
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp')
| -rw-r--r-- | SQLiteStudio3/guiSQLiteStudio/constraints/tableforeignkeypanel.cpp | 418 |
1 files changed, 418 insertions, 0 deletions
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; +} |
