summaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp1508
1 files changed, 1508 insertions, 0 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
new file mode 100644
index 0000000..56accd0
--- /dev/null
+++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp
@@ -0,0 +1,1508 @@
+#include "tablewindow.h"
+#include "ui_tablewindow.h"
+#include "services/dbmanager.h"
+#include "services/notifymanager.h"
+#include "sqlitestudio.h"
+#include "common/unused.h"
+#include "schemaresolver.h"
+#include "iconmanager.h"
+#include "common/intvalidator.h"
+#include "common/extlineedit.h"
+#include "datagrid/sqltablemodel.h"
+#include "common/extaction.h"
+#include "mainwindow.h"
+#include "tablestructuremodel.h"
+#include "tableconstraintsmodel.h"
+#include "dialogs/columndialog.h"
+#include "dialogs/constraintdialog.h"
+#include "mdiarea.h"
+#include "sqlitesyntaxhighlighter.h"
+#include "dialogs/newconstraintdialog.h"
+#include "db/chainexecutor.h"
+#include "common/widgetcover.h"
+#include "mdiwindow.h"
+#include "dbtree/dbtree.h"
+#include "constrainttabmodel.h"
+#include "parser/ast/sqlitecreateindex.h"
+#include "parser/ast/sqlitecreatetrigger.h"
+#include "dialogs/messagelistdialog.h"
+#include "services/codeformatter.h"
+#include "uiconfig.h"
+#include "dialogs/ddlpreviewdialog.h"
+#include "services/config.h"
+#include "services/importmanager.h"
+#include "dbobjectdialogs.h"
+#include "dialogs/exportdialog.h"
+#include <QMenu>
+#include <QToolButton>
+#include <QLabel>
+#include <QDebug>
+#include <QMessageBox>
+#include <tablemodifier.h>
+#include <QProgressBar>
+#include <QPushButton>
+#include <QDebug>
+#include <QStyleFactory>
+#include <dialogs/importdialog.h>
+#include <dialogs/populatedialog.h>
+
+// TODO extend QTableView for columns and constraints, so they show full-row-width drop indicator,
+// instead of single column drop indicator.
+
+CFG_KEYS_DEFINE(TableWindow)
+
+TableWindow::TableWindow(QWidget* parent) :
+ MdiChild(parent),
+ ui(new Ui::TableWindow)
+{
+ init();
+ applyInitialTab();
+}
+
+TableWindow::TableWindow(Db* db, QWidget* parent) :
+ MdiChild(parent),
+ db(db),
+ ui(new Ui::TableWindow)
+{
+ newTable();
+ init();
+ initDbAndTable();
+ applyInitialTab();
+}
+
+TableWindow::TableWindow(const TableWindow& win) :
+ MdiChild(win.parentWidget()),
+ db(win.db),
+ database(win.database),
+ table(win.table),
+ ui(new Ui::TableWindow)
+{
+ init();
+ initDbAndTable();
+ applyInitialTab();
+}
+
+TableWindow::TableWindow(QWidget *parent, Db* db, const QString& database, const QString& table) :
+ MdiChild(parent),
+ db(db),
+ database(database),
+ table(table),
+ ui(new Ui::TableWindow)
+{
+ init();
+ initDbAndTable();
+ applyInitialTab();
+}
+
+TableWindow::~TableWindow()
+{
+ delete ui;
+
+ if (tableModifier)
+ {
+ delete tableModifier;
+ tableModifier = nullptr;
+ }
+}
+
+void TableWindow::staticInit()
+{
+ qRegisterMetaType<TableWindow>("TableWindow");
+}
+
+void TableWindow::insertAction(ExtActionPrototype* action, TableWindow::ToolBar toolbar)
+{
+ return ExtActionContainer::insertAction<TableWindow>(action, toolbar);
+}
+
+void TableWindow::insertActionBefore(ExtActionPrototype* action, TableWindow::Action beforeAction, TableWindow::ToolBar toolbar)
+{
+ return ExtActionContainer::insertActionBefore<TableWindow>(action, beforeAction, toolbar);
+}
+
+void TableWindow::insertActionAfter(ExtActionPrototype* action, TableWindow::Action afterAction, TableWindow::ToolBar toolbar)
+{
+ return ExtActionContainer::insertActionAfter<TableWindow>(action, afterAction, toolbar);
+}
+
+void TableWindow::removeAction(ExtActionPrototype* action, TableWindow::ToolBar toolbar)
+{
+ ExtActionContainer::removeAction<TableWindow>(action, toolbar);
+}
+
+void TableWindow::newTable()
+{
+ existingTable = false;
+ table = "";
+}
+
+void TableWindow::init()
+{
+ ui->setupUi(this);
+ ui->structureSplitter->setStretchFactor(0, 2);
+
+#ifdef Q_OS_MACX
+ QStyle *fusion = QStyleFactory::create("Fusion");
+ ui->structureToolBar->setStyle(fusion);
+ ui->structureTab->layout()->setSpacing(0);
+ ui->tableConstraintsToolbar->setStyle(fusion);
+ ui->constraintsWidget->layout()->setSpacing(0);
+ ui->indexToolBar->setStyle(fusion);
+ ui->indexesTab->layout()->setSpacing(0);
+ ui->triggerToolBar->setStyle(fusion);
+ ui->triggersTab->layout()->setSpacing(0);
+#endif
+
+ dataModel = new SqlTableModel(this);
+ ui->dataView->init(dataModel);
+
+ initActions();
+
+ connect(dataModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful()));
+ connect(dataModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString)));
+ connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
+ connect(this, SIGNAL(modifyStatusChanged()), this, SLOT(updateStructureCommitState()));
+ connect(ui->tableNameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(modifyStatusChanged()));
+ connect(ui->tableNameEdit, SIGNAL(textChanged(QString)), this, SLOT(nameChanged()));
+ connect(ui->indexList, SIGNAL(itemSelectionChanged()), this, SLOT(updateIndexesState()));
+ connect(ui->triggerList, SIGNAL(itemSelectionChanged()), this, SLOT(updateTriggersState()));
+
+ structureExecutor = new ChainExecutor(this);
+ connect(structureExecutor, SIGNAL(success()), this, SLOT(changesSuccessfullyCommited()));
+ connect(structureExecutor, SIGNAL(failure(int,QString)), this, SLOT(changesFailedToCommit(int,QString)));
+
+ setupCoverWidget();
+ updateAfterInit();
+}
+
+void TableWindow::createActions()
+{
+ createAction(EXPORT, ICONS.TABLE_EXPORT, tr("Export table", "table window"), this, SLOT(exportTable()), this);
+ createAction(IMPORT, ICONS.TABLE_IMPORT, tr("Import data to table", "table window"), this, SLOT(importTable()), this);
+ createAction(POPULATE, ICONS.TABLE_POPULATE, tr("Populate table", "table window"), this, SLOT(populateTable()), this);
+
+ createStructureActions();
+ createDataGridActions();
+ createDataFormActions();
+ createIndexActions();
+ createTriggerActions();
+
+ createAction(NEXT_TAB, "next tab", this, SLOT(nextTab()), this);
+ createAction(PREV_TAB, "prev tab", this, SLOT(prevTab()), this);
+}
+
+void TableWindow::createStructureActions()
+{
+ createAction(REFRESH_STRUCTURE, ICONS.RELOAD, tr("Refresh structure", "table window"), this, SLOT(refreshStructure()), ui->structureToolBar);
+ ui->structureToolBar->addSeparator();
+ createAction(COMMIT_STRUCTURE, ICONS.COMMIT, tr("Commit structure changes", "table window"), this, SLOT(commitStructure()), ui->structureToolBar);
+ createAction(ROLLBACK_STRUCTURE, ICONS.ROLLBACK, tr("Rollback structure changes", "table window"), this, SLOT(rollbackStructure()), ui->structureToolBar);
+ createAction(ADD_COLUMN, ICONS.TABLE_COLUMN_ADD, tr("Add column", "table window"), this, SLOT(addColumn()), ui->structureToolBar, ui->structureView);
+ createAction(EDIT_COLUMN, ICONS.TABLE_COLUMN_EDIT, tr("Edit column", "table window"), this, SLOT(editColumn()), ui->structureToolBar, ui->structureView);
+ createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "table window"), this, SLOT(delColumn()), ui->structureToolBar, ui->structureView);
+ createAction(MOVE_COLUMN_UP, ICONS.MOVE_UP, tr("Move column up", "table window"), this, SLOT(moveColumnUp()), ui->structureToolBar, ui->structureView);
+ createAction(MOVE_COLUMN_DOWN, ICONS.MOVE_DOWN, tr("Move column down", "table window"), this, SLOT(moveColumnDown()), ui->structureToolBar, ui->structureView);
+ ui->structureToolBar->addSeparator();
+ ui->structureToolBar->addAction(actionMap[IMPORT]);
+ ui->structureToolBar->addAction(actionMap[EXPORT]);
+ ui->structureToolBar->addAction(actionMap[POPULATE]);
+ ui->structureToolBar->addSeparator();
+ createAction(CREATE_SIMILAR, ICONS.TABLE_CREATE_SIMILAR, tr("Create similar table", "table window"), this, SLOT(createSimilarTable()), ui->structureToolBar);
+
+ // Table constraints
+ createAction(ADD_TABLE_CONSTRAINT, ICONS.TABLE_CONSTRAINT_ADD, tr("Add table constraint", "table window"), this, SLOT(addConstraint()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(EDIT_TABLE_CONSTRAINT, ICONS.TABLE_CONSTRAINT_EDIT, tr("Edit table constraint", "table window"), this, SLOT(editConstraint()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(DEL_TABLE_CONSTRAINT, ICONS.TABLE_COLUMN_DELETE, tr("Delete table constraint", "table window"), this, SLOT(delConstraint()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(MOVE_CONSTRAINT_UP, ICONS.MOVE_UP, tr("Move table constraint up", "table window"), this, SLOT(moveConstraintUp()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(MOVE_CONSTRAINT_DOWN, ICONS.MOVE_DOWN, tr("Move table constraint down", "table window"), this, SLOT(moveConstraintDown()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ ui->tableConstraintsToolbar->addSeparator();
+ createAction(ADD_TABLE_PK, ICONS.CONSTRAINT_PRIMARY_KEY_ADD, tr("Add table primary key", "table window"), this, SLOT(addPk()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(ADD_TABLE_FK, ICONS.CONSTRAINT_FOREIGN_KEY_ADD, tr("Add table foreign key", "table window"), this, SLOT(addFk()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(ADD_TABLE_UNIQUE, ICONS.CONSTRAINT_UNIQUE_ADD, tr("Add table unique constraint", "table window"), this, SLOT(addUnique()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+ createAction(ADD_TABLE_CHECK, ICONS.CONSTRAINT_CHECK_ADD, tr("Add table check constraint", "table window"), this, SLOT(addCheck()), ui->tableConstraintsToolbar, ui->tableConstraintsView);
+}
+
+void TableWindow::createDataGridActions()
+{
+ QAction* before = ui->dataView->getAction(DataView::FILTER_VALUE);
+ ui->dataView->getToolBar(DataView::TOOLBAR_GRID)->insertAction(before, actionMap[IMPORT]);
+ ui->dataView->getToolBar(DataView::TOOLBAR_GRID)->insertAction(before, actionMap[EXPORT]);
+ ui->dataView->getToolBar(DataView::TOOLBAR_GRID)->insertAction(before, actionMap[POPULATE]);
+ ui->dataView->getToolBar(DataView::TOOLBAR_GRID)->insertSeparator(before);
+}
+
+void TableWindow::createDataFormActions()
+{
+}
+
+void TableWindow::createIndexActions()
+{
+ createAction(REFRESH_INDEXES, ICONS.RELOAD, tr("Refresh index list", "table window"), this, SLOT(updateIndexes()), ui->indexToolBar, ui->indexList);
+ ui->indexToolBar->addSeparator();
+ createAction(ADD_INDEX, ICONS.INDEX_ADD, tr("Create index", "table window"), this, SLOT(addIndex()), ui->indexToolBar, ui->indexList);
+ createAction(EDIT_INDEX, ICONS.INDEX_EDIT, tr("Edit index", "table window"), this, SLOT(editIndex()), ui->indexToolBar, ui->indexList);
+ createAction(DEL_INDEX, ICONS.INDEX_DEL, tr("Delete index", "table window"), this, SLOT(delIndex()), ui->indexToolBar, ui->indexList);
+ connect(ui->indexList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editIndex()));
+}
+
+void TableWindow::createTriggerActions()
+{
+ createAction(REFRESH_TRIGGERS, ICONS.RELOAD, tr("Refresh trigger list", "table window"), this, SLOT(updateTriggers()), ui->triggerToolBar, ui->triggerList);
+ ui->triggerToolBar->addSeparator();
+ createAction(ADD_TRIGGER, ICONS.TRIGGER_ADD, tr("Create trigger", "table window"), this, SLOT(addTrigger()), ui->triggerToolBar, ui->triggerList);
+ createAction(EDIT_TRIGGER, ICONS.TRIGGER_EDIT, tr("Edit trigger", "table window"), this, SLOT(editTrigger()), ui->triggerToolBar, ui->triggerList);
+ createAction(DEL_TRIGGER, ICONS.TRIGGER_DEL, tr("Delete trigger", "table window"), this, SLOT(delTrigger()), ui->triggerToolBar, ui->triggerList);
+ connect(ui->triggerList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(editTrigger()));
+}
+
+void TableWindow::editColumn(const QModelIndex& idx)
+{
+ if (!idx.isValid())
+ {
+ qWarning() << "Called TableWindow::editColumn() with invalid index.";
+ return;
+ }
+
+ SqliteCreateTable::Column* column = structureModel->getColumn(idx.row());
+ ColumnDialog columnDialog(db, this);
+ columnDialog.setColumn(column);
+ if (columnDialog.exec() != QDialog::Accepted)
+ return;
+
+ SqliteCreateTable::Column* modifiedColumn = columnDialog.getModifiedColumn();
+ structureModel->replaceColumn(idx.row(), modifiedColumn);
+ ui->structureView->resizeColumnToContents(0);
+}
+
+void TableWindow::delColumn(const QModelIndex& idx)
+{
+ if (!idx.isValid())
+ {
+ qWarning() << "Called TableWindow::delColumn() with invalid index.";
+ return;
+ }
+
+ SqliteCreateTable::Column* column = structureModel->getColumn(idx.row());
+
+ QString msg = tr("Are you sure you want to delete column '%1'?", "table window").arg(column->name);
+ int btn = QMessageBox::question(this, tr("Delete column", "table window"), msg);
+ if (btn != QMessageBox::Yes)
+ return;
+
+ structureModel->delColumn(idx.row());
+ ui->structureView->resizeColumnToContents(0);
+}
+
+void TableWindow::executeStructureChanges()
+{
+ QStringList sqls;
+
+ createTable->rebuildTokens();
+ if (!existingTable)
+ {
+ sqls << createTable->detokenize();
+ }
+ else
+ {
+ if (tableModifier)
+ delete tableModifier;
+
+ tableModifier = new TableModifier(db, database, table);
+ tableModifier->alterTable(createTable);
+
+ if (tableModifier->hasMessages())
+ {
+ MessageListDialog dialog(tr("Following problems will take place while modifying the table.\n"
+ "Would you like to proceed?", "table window"));
+ dialog.setWindowTitle(tr("Table modification", "table window"));
+ foreach (const QString& error, tableModifier->getErrors())
+ dialog.addError(error);
+
+ foreach (const QString& warn, tableModifier->getWarnings())
+ dialog.addWarning(warn);
+
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+ }
+
+ sqls = tableModifier->generateSqls();
+ }
+
+ if (!CFG_UI.General.DontShowDdlPreview.get())
+ {
+ DdlPreviewDialog dialog(db, this);
+ dialog.setDdl(sqls);
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+ }
+
+ modifyingThisTable = true;
+ structureExecutor->setDb(db);
+ structureExecutor->setQueries(sqls);
+ widgetCover->show();
+ structureExecutor->exec();
+}
+
+void TableWindow::updateAfterInit()
+{
+ updateStructureCommitState();
+ updateStructureToolbarState();
+ updateTableConstraintsToolbarState();
+ updateNewTableState();
+ updateIndexesState();
+ updateTriggersState();
+}
+
+QModelIndex TableWindow::structureCurrentIndex() const
+{
+ return ui->structureView->selectionModel()->currentIndex();
+}
+
+void TableWindow::updateStructureToolbarState()
+{
+ QItemSelectionModel *selModel = ui->structureView->selectionModel();
+ bool validIdx = false;
+ bool isFirst = false;
+ bool isLast = false;
+ if (selModel)
+ {
+ QModelIndex currIdx = selModel->currentIndex();
+ if (currIdx.isValid())
+ {
+ validIdx = true;
+ if (currIdx.row() == 0)
+ isFirst = true;
+
+ if (currIdx.row() == (structureModel->rowCount() - 1))
+ isLast = true;
+ }
+ }
+
+ actionMap[EDIT_COLUMN]->setEnabled(validIdx);
+ actionMap[DEL_COLUMN]->setEnabled(validIdx);
+ actionMap[MOVE_COLUMN_UP]->setEnabled(validIdx && !isFirst);
+ actionMap[MOVE_COLUMN_DOWN]->setEnabled(validIdx && !isLast);
+}
+
+void TableWindow::updateStructureCommitState()
+{
+ bool modified = isModified();
+ actionMap[COMMIT_STRUCTURE]->setEnabled(modified);
+ actionMap[ROLLBACK_STRUCTURE]->setEnabled(modified && existingTable);
+}
+
+void TableWindow::updateTableConstraintsToolbarState()
+{
+ QItemSelectionModel *selModel = ui->tableConstraintsView->selectionModel();
+ bool anyColumn = structureModel && structureModel->rowCount() > 0;
+ bool validIdx = false;
+ bool isFirst = false;
+ bool isLast = false;
+ if (selModel)
+ {
+ QModelIndex currIdx = selModel->currentIndex();
+ if (currIdx.isValid())
+ {
+ validIdx = true;
+ if (currIdx.row() == 0)
+ isFirst = true;
+
+ if (currIdx.row() == (structureConstraintsModel->rowCount() - 1))
+ isLast = true;
+ }
+ }
+
+ actionMap[EDIT_TABLE_CONSTRAINT]->setEnabled(anyColumn && validIdx);
+ actionMap[DEL_TABLE_CONSTRAINT]->setEnabled(anyColumn && validIdx);
+ actionMap[MOVE_CONSTRAINT_UP]->setEnabled(anyColumn && validIdx && !isFirst);
+ actionMap[MOVE_CONSTRAINT_DOWN]->setEnabled(anyColumn && validIdx && !isLast);
+}
+
+void TableWindow::setupDefShortcuts()
+{
+ // Widget context
+ setShortcutContext({
+ REFRESH_STRUCTURE,
+ REFRESH_INDEXES,
+ REFRESH_TRIGGERS,
+ ADD_COLUMN,
+ EDIT_COLUMN,
+ DEL_COLUMN,
+ ADD_TABLE_CONSTRAINT,
+ EDIT_TABLE_CONSTRAINT,
+ DEL_TABLE_CONSTRAINT,
+ ADD_INDEX,
+ EDIT_INDEX,
+ DEL_INDEX,
+ ADD_TRIGGER,
+ EDIT_TRIGGER,
+ DEL_TRIGGER,
+ },
+ Qt::WidgetWithChildrenShortcut);
+
+ BIND_SHORTCUTS(TableWindow, Action);
+}
+
+void TableWindow::executionSuccessful()
+{
+ modifyingThisTable = false;
+ dataLoaded = true;
+}
+
+void TableWindow::executionFailed(const QString& errorText)
+{
+ modifyingThisTable = false;
+ notifyError(tr("Could not load data for table %1. Error details: %2").arg(table).arg(errorText));
+}
+
+void TableWindow::initDbAndTable()
+{
+ if (db->getVersion() == 2)
+ {
+ ui->withoutRowIdCheck->setVisible(false);
+ }
+
+ if (existingTable)
+ {
+ dataModel->setDb(db);
+ dataModel->setDatabaseAndTable(database, table);
+ }
+
+ ui->tableNameEdit->setText(table); // TODO no attached/temp db name support here
+
+ if (structureModel)
+ {
+ delete structureModel;
+ structureModel = nullptr;
+ }
+
+ if (structureConstraintsModel)
+ {
+ delete structureConstraintsModel;
+ structureConstraintsModel = nullptr;
+ }
+
+ if (constraintTabModel)
+ {
+ delete constraintTabModel;
+ constraintTabModel = nullptr;
+ }
+
+ structureModel = new TableStructureModel(this);
+ structureConstraintsModel = new TableConstraintsModel(this);
+ constraintTabModel = new ConstraintTabModel(this);
+
+ // Columns model signals
+ connect(structureModel, SIGNAL(columnModified(QString,SqliteCreateTable::Column*)),
+ structureConstraintsModel, SLOT(columnModified(QString,SqliteCreateTable::Column*)));
+ connect(structureModel, SIGNAL(columnDeleted(QString)),
+ structureConstraintsModel, SLOT(columnDeleted(QString)));
+ connect(structureModel, SIGNAL(columnsOrderChanged()), this, SLOT(updateStructureToolbarState()));
+
+ connect(structureModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateDdlTab()));
+ connect(structureModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateDdlTab()));
+ connect(structureModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateDdlTab()));
+ connect(structureModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateDdlTab()));
+ connect(structureModel, SIGNAL(modifiyStateChanged()), this, SIGNAL(modifyStatusChanged()));
+
+ ui->structureView->setModel(structureModel);
+ ui->structureView->verticalHeader()->setDefaultSectionSize(ui->structureView->fontMetrics().height() + 8);
+
+ // Constraints model signals
+ connect(structureConstraintsModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateDdlTab()));
+ connect(structureConstraintsModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateDdlTab()));
+ connect(structureConstraintsModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateDdlTab()));
+ connect(structureConstraintsModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(updateDdlTab()));
+
+ connect(structureConstraintsModel, SIGNAL(modifiyStateChanged()), this, SIGNAL(modifyStatusChanged()));
+ connect(structureConstraintsModel, SIGNAL(constraintOrderChanged()), this, SLOT(updateTableConstraintsToolbarState()));
+
+ ui->tableConstraintsView->setModel(structureConstraintsModel);
+ ui->tableConstraintsView->verticalHeader()->setDefaultSectionSize(ui->tableConstraintsView->fontMetrics().height() + 8);
+
+ // Constraint tab model signals
+ connect(structureModel, SIGNAL(rowsInserted(QModelIndex,int,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), constraintTabModel, SLOT(updateModel()));
+ connect(structureConstraintsModel, SIGNAL(rowsInserted(QModelIndex,int,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureConstraintsModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureConstraintsModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), constraintTabModel, SLOT(updateModel()));
+ connect(structureConstraintsModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), constraintTabModel, SLOT(updateModel()));
+
+ ui->constraintsView->setModel(constraintTabModel);
+
+ connect(ui->withoutRowIdCheck, SIGNAL(clicked()), this, SLOT(withOutRowIdChanged()));
+
+ ui->ddlEdit->setSqliteVersion(db->getVersion());
+ parseDdl();
+ updateIndexes();
+ updateTriggers();
+
+ // (Re)connect to DB signals
+ connect(db, SIGNAL(dbObjectDeleted(QString,QString,DbObjectType)), this, SLOT(checkIfTableDeleted(QString,QString,DbObjectType)));
+
+ // Selection model is recreated when setModel() is called on the view
+ connect(ui->structureView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(updateStructureToolbarState()));
+ connect(ui->tableConstraintsView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(updateTableConstraintsToolbarState()));
+}
+
+void TableWindow::setupCoverWidget()
+{
+ widgetCover = new WidgetCover(this);
+ widgetCover->initWithInterruptContainer();
+ widgetCover->hide();
+ connect(widgetCover, SIGNAL(cancelClicked()), structureExecutor, SLOT(interrupt()));
+}
+
+void TableWindow::parseDdl()
+{
+ if (existingTable)
+ {
+ SchemaResolver resolver(db);
+ SqliteQueryPtr parsedObject = resolver.getParsedObject(database, table, SchemaResolver::TABLE);
+ if (!parsedObject.dynamicCast<SqliteCreateTable>())
+ {
+ notifyError(tr("Could not process the %1 table correctly. Unable to open a table window.").arg(table));
+ invalid = true;
+ return;
+ }
+
+ createTable = parsedObject.dynamicCast<SqliteCreateTable>();
+ }
+ else
+ {
+ createTable = SqliteCreateTablePtr::create();
+ createTable->table = table;
+ createTable->dialect = db->getDialect();
+ }
+ originalCreateTable = SqliteCreateTablePtr::create(*createTable);
+ structureModel->setCreateTable(createTable.data());
+ structureConstraintsModel->setCreateTable(createTable.data());
+ constraintTabModel->setCreateTable(createTable.data());
+ ui->withoutRowIdCheck->setChecked(!createTable->withOutRowId.isNull());
+ ui->tableConstraintsView->resizeColumnsToContents();
+ ui->structureView->resizeColumnsToContents();
+ ui->constraintsView->resizeColumnsToContents();
+
+ updateStructureToolbarState();
+ updateTableConstraintsToolbarState();
+ updateDdlTab();
+}
+
+void TableWindow::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ ui->retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+QVariant TableWindow::saveSession()
+{
+ if (!db || DBLIST->isTemporary(db))
+ return QVariant();
+
+ QHash<QString,QVariant> sessionValue;
+ sessionValue["table"] = table;
+ sessionValue["db"] = db->getName();
+ return sessionValue;
+}
+
+bool TableWindow::restoreSession(const QVariant& sessionValue)
+{
+ QHash<QString, QVariant> value = sessionValue.toHash();
+ if (value.size() == 0)
+ {
+ notifyWarn("Could not restore window, because no database or table was stored in session for this window.");
+ return false;
+ }
+
+ if (!value.contains("db") || !value.contains("table"))
+ {
+ notifyWarn("Could not restore window, because no database or table was stored in session for this window.");
+ return false;
+ }
+
+ db = DBLIST->getByName(value["db"].toString());
+ if (!db || !db->isValid() || (!db->isOpen() && !db->open()))
+ {
+ notifyWarn(tr("Could not restore window, because database %1 could not be resolved.").arg(value["db"].toString()));
+ return false;
+ }
+
+ table = value["table"].toString();
+ database = value["database"].toString();
+ SchemaResolver resolver(db);
+ if (!resolver.getTables(database).contains(table, Qt::CaseInsensitive))
+ {
+ notifyWarn(tr("Could not restore window, because the table %1 doesn't exist in the database %2.").arg(table).arg(db->getName()));
+ return false;
+ }
+
+ initDbAndTable();
+ applyInitialTab();
+ return true;
+}
+
+Icon* TableWindow::getIconNameForMdiWindow()
+{
+ return ICONS.TABLE;
+}
+
+QString TableWindow::getTitleForMdiWindow()
+{
+ QString dbSuffix = (!db ? "" : (" (" + db->getName() + ")"));
+ if (existingTable)
+ return table + dbSuffix;
+
+ QStringList existingNames = MainWindow::getInstance()->getMdiArea()->getWindowTitles();
+ if (existingNames.contains(windowTitle()))
+ return windowTitle();
+
+ // Generate new name
+ QString title = tr("New table %1").arg(newTableWindowNum++);
+ while (existingNames.contains(title))
+ title = tr("New table %1").arg(newTableWindowNum++);
+
+ title += dbSuffix;
+ return title;
+}
+
+Db* TableWindow::getDb() const
+{
+ return db;
+}
+
+QString TableWindow::getTable() const
+{
+ return table;
+}
+
+void TableWindow::dbClosedFinalCleanup()
+{
+ db = nullptr;
+ dataModel->setDb(nullptr);
+ structureExecutor->setDb(nullptr);
+}
+
+void TableWindow::checkIfTableDeleted(const QString& database, const QString& object, DbObjectType type)
+{
+ UNUSED(database);
+
+ // TODO uncomment below when dbnames are supported
+// if (this->database != database)
+// return;
+
+ switch (type)
+ {
+ case DbObjectType::TABLE:
+ break;
+ case DbObjectType::INDEX:
+ checkIfIndexDeleted(object);
+ return;
+ case DbObjectType::TRIGGER:
+ checkIfTriggerDeleted(object);
+ return;
+ case DbObjectType::VIEW:
+ return;
+ }
+
+ if (modifyingThisTable)
+ return;
+
+ if (object.compare(table, Qt::CaseInsensitive) == 0)
+ {
+ dbClosedFinalCleanup();
+ getMdiWindow()->close();
+ }
+}
+
+void TableWindow::checkIfIndexDeleted(const QString& object)
+{
+ for (int i = 0, total = ui->indexList->rowCount(); i < total; ++i)
+ {
+ if (ui->indexList->item(i, 0)->text().compare(object, Qt::CaseInsensitive) == 0)
+ {
+ ui->indexList->removeRow(i);
+ return;
+ }
+ }
+}
+
+void TableWindow::checkIfTriggerDeleted(const QString& object)
+{
+ for (int i = 0, total = ui->triggerList->rowCount(); i < total; ++i)
+ {
+ if (ui->triggerList->item(i, 0)->text().compare(object, Qt::CaseInsensitive) == 0)
+ {
+ ui->triggerList->removeRow(i);
+ return;
+ }
+ }
+}
+
+void TableWindow::refreshStructure()
+{
+ parseDdl();
+ updateIndexes();
+ updateTriggers();
+}
+
+void TableWindow::commitStructure(bool skipWarning)
+{
+ if (!isModified())
+ {
+ qWarning() << "Called TableWindow::commitStructure(), but isModified() returned false.";
+ updateStructureCommitState();
+ return;
+ }
+
+ if (!validate(skipWarning))
+ return;
+
+ executeStructureChanges();
+}
+
+void TableWindow::changesSuccessfullyCommited()
+{
+ QStringList sqls = structureExecutor->getQueries();
+ CFG->addDdlHistory(sqls.join("\n"), db->getName(), db->getPath());
+
+ widgetCover->hide();
+
+ originalCreateTable = createTable;
+ structureModel->setCreateTable(createTable.data());
+ structureConstraintsModel->setCreateTable(createTable.data());
+ dataLoaded = false;
+
+ QString oldTable = table;
+ database = createTable->database;
+ table = createTable->table;
+ existingTable = true;
+ initDbAndTable();
+ updateStructureCommitState();
+ updateNewTableState();
+ updateWindowTitle();
+
+ DBTREE->refreshSchema(db);
+
+ if (tableModifier)
+ {
+ QList<QStringList> modifiedObjects = {
+ tableModifier->getModifiedTables(),
+ tableModifier->getModifiedIndexes(),
+ tableModifier->getModifiedTriggers(),
+ tableModifier->getModifiedViews()
+ };
+ NotifyManager* notifyManager = NotifyManager::getInstance();
+ foreach (const QStringList& objList, modifiedObjects)
+ {
+ foreach (const QString& obj, objList)
+ {
+ if (obj.compare(oldTable, Qt::CaseInsensitive) == 0)
+ continue;
+
+ notifyManager->modified(db, database, obj);
+ }
+ }
+ }
+}
+
+void TableWindow::changesFailedToCommit(int errorCode, const QString& errorText)
+{
+ qDebug() << "TableWindow::changesFailedToCommit:" << errorCode << errorText;
+
+ widgetCover->hide();
+ notifyError(tr("Could not commit table structure. Error message: %1", "table window").arg(errorText));
+}
+
+void TableWindow::rollbackStructure()
+{
+ createTable = SqliteCreateTablePtr::create(*originalCreateTable.data());
+ structureModel->setCreateTable(createTable.data());
+ structureConstraintsModel->setCreateTable(createTable.data());
+ constraintTabModel->setCreateTable(createTable.data());
+ ui->tableNameEdit->setText(createTable->table);
+
+ updateStructureCommitState();
+ updateStructureToolbarState();
+ updateTableConstraintsToolbarState();
+}
+
+void TableWindow::addColumn()
+{
+ SqliteCreateTable::Column column;
+ column.setParent(createTable.data());
+
+ ColumnDialog columnDialog(db, this);
+ columnDialog.setColumn(&column);
+ if (columnDialog.exec() != QDialog::Accepted)
+ return;
+
+ SqliteCreateTable::Column* modifiedColumn = columnDialog.getModifiedColumn();
+ structureModel->appendColumn(modifiedColumn);
+ ui->structureView->resizeColumnToContents(0);
+
+ ui->structureView->setCurrentIndex(structureModel->index(structureModel->rowCount()-1, 0));
+}
+
+void TableWindow::editColumn()
+{
+ editColumn(structureCurrentIndex());
+}
+
+void TableWindow::delColumn()
+{
+ QModelIndex idx = structureCurrentIndex();
+ delColumn(idx);
+}
+
+void TableWindow::moveColumnUp()
+{
+ QModelIndex idx = structureCurrentIndex();
+ if (!idx.isValid())
+ {
+ qWarning() << "Called TableWindow::moveColumnUp() with invalid index.";
+ return;
+ }
+
+ structureModel->moveColumnUp(idx.row());
+}
+
+void TableWindow::moveColumnDown()
+{
+ QModelIndex idx = structureCurrentIndex();
+ if (!idx.isValid())
+ {
+ qWarning() << "Called TableWindow::moveColumnDown() with invalid index.";
+ return;
+ }
+
+ structureModel->moveColumnDown(idx.row());
+}
+
+
+void TableWindow::addConstraint(ConstraintDialog::Constraint mode)
+{
+ NewConstraintDialog dialog(mode, createTable.data(), db, this);
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ SqliteStatement* constrStmt = dialog.getConstraint();
+ SqliteCreateTable::Constraint* tableConstr = dynamic_cast<SqliteCreateTable::Constraint*>(constrStmt);
+ if (!tableConstr)
+ {
+ qCritical() << "Constraint returned from ConstraintDialog was not of table type, while we're trying to add table constraint.";
+ return;
+ }
+
+ structureConstraintsModel->appendConstraint(tableConstr);
+ ui->tableConstraintsView->resizeColumnToContents(0);
+ ui->tableConstraintsView->resizeColumnToContents(1);
+}
+
+bool TableWindow::validate(bool skipWarning)
+{
+ if (!existingTable && !skipWarning && ui->tableNameEdit->text().isEmpty())
+ {
+ int res = QMessageBox::warning(this, tr("Empty name"), tr("A blank name for the table is allowed in SQLite, but it is not recommended.\n"
+ "Are you sure you want to create a table with blank name?"), QMessageBox::Yes, QMessageBox::No);
+
+ if (res != QMessageBox::Yes)
+ return false;
+ }
+
+ if (structureModel->rowCount() == 0)
+ {
+ notifyError(tr("Cannot create a table without at least one column."));
+ return false;
+ }
+
+ if (ui->withoutRowIdCheck->isChecked())
+ {
+ bool hasPk = false;
+ bool isPkAutoIncr = false;
+
+ if (createTable->getConstraints(SqliteCreateTable::Constraint::PRIMARY_KEY).size() > 0)
+ hasPk = true;
+
+ SqliteCreateTable::Column::Constraint* colConstraint = nullptr;
+ foreach (SqliteCreateTable::Column* column, createTable->columns)
+ {
+ colConstraint = column->getConstraint(SqliteCreateTable::Column::Constraint::PRIMARY_KEY);
+ if (colConstraint)
+ {
+ hasPk = true;
+ if (colConstraint->autoincrKw)
+ isPkAutoIncr = true;
+ }
+ }
+
+ if (!hasPk)
+ {
+ notifyError(tr("Cannot create table without ROWID, if it has no PRIMARY KEY defined."
+ " Either uncheck the WITHOUT ROWID, or define a PRIMARY KEY."));
+ return false;
+ }
+
+ if (isPkAutoIncr)
+ {
+ notifyError(tr("Cannot use AUTOINCREMENT for PRIMARY KEY when WITHOUT ROWID clause is used."
+ " Either uncheck the WITHOUT ROWID, or the AUTOINCREMENT in a PRIMARY KEY."));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TableWindow::isModified() const
+{
+ return (structureModel && structureModel->isModified()) ||
+ (structureConstraintsModel && structureConstraintsModel->isModified()) ||
+ (originalCreateTable &&
+ (originalCreateTable->table != ui->tableNameEdit->text() ||
+ originalCreateTable->withOutRowId != createTable->withOutRowId)
+ ) ||
+ !existingTable;
+}
+
+TokenList TableWindow::indexColumnTokens(SqliteCreateIndexPtr index)
+{
+ if (index->indexedColumns.size() == 0)
+ return TokenList();
+
+ SqliteIndexedColumn* firstCol = index->indexedColumns.first();
+ SqliteIndexedColumn* lastCol = index->indexedColumns.last();
+ if (firstCol->tokens.size() == 0)
+ return TokenList();
+
+ if (lastCol->tokens.size() == 0)
+ return TokenList();
+
+ int firstIdx = index->tokens.indexOf(firstCol->tokens.first());
+ int lastIdx = index->tokens.indexOf(lastCol->tokens.last());
+
+ return index->tokens.mid(firstIdx, lastIdx-firstIdx+1);
+}
+
+QString TableWindow::getCurrentIndex() const
+{
+ int row = ui->indexList->currentRow();
+ QTableWidgetItem* item = ui->indexList->item(row, 0);
+ if (!item)
+ return QString::null;
+
+ return item->text();
+}
+
+QString TableWindow::getCurrentTrigger() const
+{
+ int row = ui->triggerList->currentRow();
+ QTableWidgetItem* item = ui->triggerList->item(row, 0);
+ if (!item)
+ return QString::null;
+
+ return item->text();
+}
+
+void TableWindow::applyInitialTab()
+{
+ if (existingTable && !table.isNull() && CFG_UI.General.OpenTablesOnData.get())
+ ui->tabWidget->setCurrentIndex(1);
+ else
+ ui->tabWidget->setCurrentIndex(0);
+}
+
+void TableWindow::updateDdlTab()
+{
+ CodeFormatter* formatter = SQLITESTUDIO->getCodeFormatter();
+ createTable->rebuildTokens();
+ ui->ddlEdit->setPlainText(formatter->format("sql", createTable->detokenize(), db));
+}
+
+void TableWindow::updateNewTableState()
+{
+ for (int i = 1; i < 5; i++)
+ ui->tabWidget->setTabEnabled(i, existingTable);
+
+ actionMap[EXPORT]->setEnabled(existingTable);
+ actionMap[IMPORT]->setEnabled(existingTable);
+ actionMap[POPULATE]->setEnabled(existingTable);
+ actionMap[CREATE_SIMILAR]->setEnabled(existingTable);
+ actionMap[REFRESH_STRUCTURE]->setEnabled(existingTable);
+}
+
+void TableWindow::addConstraint()
+{
+ addConstraint(ConstraintDialog::UNKNOWN);
+}
+
+void TableWindow::editConstraint()
+{
+ QModelIndex idx = ui->tableConstraintsView->currentIndex();
+ editConstraint(idx);
+}
+
+void TableWindow::delConstraint()
+{
+ QModelIndex idx = ui->tableConstraintsView->currentIndex();
+ delConstraint(idx);
+}
+
+void TableWindow::editConstraint(const QModelIndex& idx)
+{
+ if (!idx.isValid())
+ return;
+
+ SqliteCreateTable::Constraint* constr = structureConstraintsModel->getConstraint(idx.row());
+ ConstraintDialog dialog(ConstraintDialog::EDIT, constr, createTable.data(), db, this);
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ structureConstraintsModel->constraintModified(idx.row());
+ ui->tableConstraintsView->resizeColumnToContents(0);
+ ui->tableConstraintsView->resizeColumnToContents(1);
+}
+
+void TableWindow::delConstraint(const QModelIndex& idx)
+{
+ if (!idx.isValid())
+ return;
+
+ SqliteCreateTable::Constraint* constr = structureConstraintsModel->getConstraint(idx.row());
+
+ QString arg = constr->name.isNull() ? constr->typeString() : constr->name;
+ QString msg = tr("Are you sure you want to delete table constraint '%1'?", "table window").arg(arg);
+ int btn = QMessageBox::question(this, tr("Delete constraint", "table window"), msg);
+ if (btn != QMessageBox::Yes)
+ return;
+
+ structureConstraintsModel->delConstraint(idx.row());
+ ui->structureView->resizeColumnToContents(0);
+}
+
+void TableWindow::moveConstraintUp()
+{
+ QModelIndex idx = ui->tableConstraintsView->currentIndex();
+ if (!idx.isValid())
+ return;
+
+ structureConstraintsModel->moveConstraintUp(idx.row());
+ updateTableConstraintsToolbarState();
+ updateStructureCommitState();
+}
+
+void TableWindow::moveConstraintDown()
+{
+ QModelIndex idx = ui->tableConstraintsView->currentIndex();
+ if (!idx.isValid())
+ return;
+
+ structureConstraintsModel->moveConstraintDown(idx.row());
+ updateTableConstraintsToolbarState();
+ updateStructureCommitState();
+}
+
+void TableWindow::addPk()
+{
+ addConstraint(ConstraintDialog::PK);
+}
+
+void TableWindow::addFk()
+{
+ addConstraint(ConstraintDialog::FK);
+}
+
+void TableWindow::addUnique()
+{
+ addConstraint(ConstraintDialog::UNIQUE);
+}
+
+void TableWindow::addCheck()
+{
+ addConstraint(ConstraintDialog::CHECK);
+}
+
+void TableWindow::exportTable()
+{
+ if (!ExportManager::isAnyPluginAvailable())
+ {
+ notifyError(tr("Cannot export, because no export plugin is loaded."));
+ return;
+ }
+
+ ExportDialog dialog(this);
+ dialog.setTableMode(db, table);
+ dialog.exec();
+}
+
+void TableWindow::importTable()
+{
+ if (!ImportManager::isAnyPluginAvailable())
+ {
+ notifyError(tr("Cannot import, because no import plugin is loaded."));
+ return;
+ }
+
+ ImportDialog dialog(this);
+ dialog.setDbAndTable(db, table);
+ if (dialog.exec() == QDialog::Accepted && dataLoaded)
+ ui->dataView->refreshData();
+}
+
+void TableWindow::populateTable()
+{
+ PopulateDialog dialog(this);
+ dialog.setDbAndTable(db, table);
+ if (dialog.exec() == QDialog::Accepted && dataLoaded)
+ ui->dataView->refreshData();
+}
+
+void TableWindow::createSimilarTable()
+{
+ DbObjectDialogs dialog(db);
+ dialog.addTableSimilarTo(QString(), table);
+}
+
+void TableWindow::tabChanged(int newTab)
+{
+ switch (newTab)
+ {
+ case 1:
+ {
+ if (isModified())
+ {
+ int res = QMessageBox::question(this, tr("Uncommited changes"),
+ tr("There are uncommited structure modifications. You cannot browse or edit data until you have "
+ "table structure settled.\n"
+ "Do you want to commit the structure, or do you want to go back to the structure tab?"),
+ tr("Go back to structure tab"), tr("Commit modifications and browse data."));
+
+ ui->tabWidget->setCurrentIndex(0);
+ if (res == 1)
+ commitStructure(true);
+
+ break;
+ }
+
+ if (!dataLoaded)
+ ui->dataView->refreshData();
+
+ break;
+ }
+ }
+}
+
+void TableWindow::on_structureView_doubleClicked(const QModelIndex &index)
+{
+ editColumn(index);
+}
+
+void TableWindow::on_tableConstraintsView_doubleClicked(const QModelIndex &index)
+{
+ editConstraint(index);
+}
+
+void TableWindow::nameChanged()
+{
+ if (!createTable)
+ return;
+
+ createTable->table = ui->tableNameEdit->text();
+ updateDdlTab();
+}
+
+void TableWindow::withOutRowIdChanged()
+{
+ if (!createTable)
+ return;
+
+ createTable->withOutRowId = ui->withoutRowIdCheck->isChecked() ? QStringLiteral("ROWID") : QString::null;
+ updateDdlTab();
+ emit modifyStatusChanged();
+}
+
+void TableWindow::addIndex()
+{
+ DbObjectDialogs dialogs(db, this);
+ dialogs.addIndex(table);
+ updateIndexes();
+}
+
+void TableWindow::editIndex()
+{
+ QString index = getCurrentIndex();
+ if (index.isNull())
+ return;
+
+ DbObjectDialogs dialogs(db, this);
+ dialogs.editIndex(index);
+ updateIndexes();
+}
+
+void TableWindow::delIndex()
+{
+ QString index = getCurrentIndex();
+ if (index.isNull())
+ return;
+
+ DbObjectDialogs dialogs(db, this);
+ dialogs.dropObject(index);
+ updateIndexes();
+}
+
+void TableWindow::addTrigger()
+{
+ DbObjectDialogs dialogs(db, this);
+ dialogs.addTriggerOnTable(table);
+ updateTriggers();
+}
+
+void TableWindow::editTrigger()
+{
+ QString trigger = getCurrentTrigger();
+ if (trigger.isNull())
+ return;
+
+ DbObjectDialogs dialogs(db, this);
+ dialogs.editTrigger(trigger);
+ updateTriggers();
+}
+
+void TableWindow::delTrigger()
+{
+ QString trigger = getCurrentTrigger();
+ if (trigger.isNull())
+ return;
+
+ DbObjectDialogs dialogs(db, this);
+ dialogs.dropObject(trigger);
+ updateTriggers();
+}
+
+void TableWindow::updateIndexesState()
+{
+ bool editDel = ui->indexList->currentItem() != nullptr;
+ actionMap[REFRESH_INDEXES]->setEnabled(existingTable);
+ actionMap[ADD_INDEX]->setEnabled(existingTable);
+ actionMap[EDIT_INDEX]->setEnabled(editDel);
+ actionMap[DEL_INDEX]->setEnabled(editDel);
+}
+
+void TableWindow::updateTriggersState()
+{
+ bool editDel = ui->triggerList->currentItem() != nullptr;
+ actionMap[REFRESH_TRIGGERS]->setEnabled(existingTable);
+ actionMap[ADD_TRIGGER]->setEnabled(existingTable);
+ actionMap[EDIT_TRIGGER]->setEnabled(editDel);
+ actionMap[DEL_TRIGGER]->setEnabled(editDel);
+}
+
+void TableWindow::nextTab()
+{
+ int idx = ui->tabWidget->currentIndex();
+ idx++;
+ ui->tabWidget->setCurrentIndex(idx);
+}
+
+void TableWindow::prevTab()
+{
+ int idx = ui->tabWidget->currentIndex();
+ idx--;
+ ui->tabWidget->setCurrentIndex(idx);
+}
+
+void TableWindow::updateIndexes()
+{
+ ui->indexList->clear();
+
+ if (!db || !db->isValid())
+ return;
+
+ SchemaResolver resolver(db);
+ resolver.setIgnoreSystemObjects(true);
+ QList<SqliteCreateIndexPtr> indexes = resolver.getParsedIndexesForTable(database, table);
+
+ ui->indexList->setColumnCount(4);
+ ui->indexList->setRowCount(indexes.size());
+ ui->indexList->setHorizontalHeaderLabels({
+ tr("Name", "table window indexes"),
+ tr("Unique", "table window indexes"),
+ tr("Columns", "table window indexes"),
+ tr("Partial index condition", "table window indexes"),
+ });
+
+ Dialect dialect= db->getDialect();
+ if (dialect == Dialect::Sqlite2)
+ ui->indexList->setColumnCount(3);
+
+ ui->indexList->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch);
+
+ QTableWidgetItem* item = nullptr;
+ int row = 0;
+ foreach (SqliteCreateIndexPtr index, indexes)
+ {
+ item = new QTableWidgetItem(index->index);
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->indexList->setItem(row, 0, item);
+
+ // TODO a delegate to make the checkbox in the center, or use setCellWidget()
+ item = new QTableWidgetItem();
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ item->setCheckState(index->uniqueKw ? Qt::Checked : Qt::Unchecked);
+ ui->indexList->setItem(row, 1, item);
+
+ item = new QTableWidgetItem(indexColumnTokens(index).detokenize());
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->indexList->setItem(row, 2, item);
+
+ if (dialect == Dialect::Sqlite3)
+ {
+ item = new QTableWidgetItem(index->where ? index->where->detokenize() : "");
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->indexList->setItem(row, 3, item);
+ }
+
+ row++;
+ }
+
+ ui->indexList->resizeColumnsToContents();
+ updateIndexesState();
+}
+
+void TableWindow::updateTriggers()
+{
+ if (!db || !db->isValid())
+ return;
+
+ SchemaResolver resolver(db);
+ QList<SqliteCreateTriggerPtr> triggers = resolver.getParsedTriggersForTable(database, table);
+
+ ui->triggerList->setColumnCount(4);
+ ui->triggerList->setRowCount(triggers.size());
+ ui->triggerList->horizontalHeader()->setMaximumSectionSize(200);
+ ui->triggerList->setHorizontalHeaderLabels({
+ tr("Name", "table window triggers"),
+ tr("Event", "table window triggers"),
+ tr("Condition", "table window triggers"),
+ tr("Details", "table window triggers")
+ });
+
+ QTableWidgetItem* item = nullptr;
+ QString timeAndEvent;
+ int row = 0;
+ foreach (SqliteCreateTriggerPtr trig, triggers)
+ {
+ item = new QTableWidgetItem(trig->trigger);
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->triggerList->setItem(row, 0, item);
+
+ timeAndEvent = trig->tokensMap["trigger_time"].detokenize() + trig->tokensMap["trigger_event"].detokenize();
+ item = new QTableWidgetItem(timeAndEvent);
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->triggerList->setItem(row, 1, item);
+
+ item = new QTableWidgetItem(trig->precondition ? trig->precondition->detokenize().trimmed() : "");
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->triggerList->setItem(row, 2, item);
+
+ item = new QTableWidgetItem(trig->tokensMap["trigger_cmd_list"].detokenize().trimmed());
+ item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable);
+ ui->triggerList->setItem(row, 3, item);
+
+ row++;
+ }
+
+ ui->triggerList->resizeColumnsToContents();
+ updateTriggersState();
+}
+
+void TableWindow::editColumn(const QString& columnName)
+{
+ QModelIndex colIdx = structureModel->findColumn(columnName);
+ if (!colIdx.isValid())
+ return;
+
+ editColumn(colIdx);
+}
+
+void TableWindow::delColumn(const QString& columnName)
+{
+ QModelIndex colIdx = structureModel->findColumn(columnName);
+ if (!colIdx.isValid())
+ return;
+
+ delColumn(colIdx);
+}
+
+bool TableWindow::restoreSessionNextTime()
+{
+ return existingTable && db && !DBLIST->isTemporary(db);
+}
+
+QToolBar* TableWindow::getToolBar(int toolbar) const
+{
+ switch (static_cast<ToolBar>(toolbar))
+ {
+ case TOOLBAR_STRUCTURE:
+ return ui->structureToolBar;
+ case TOOLBAR_INDEXES:
+ return ui->indexToolBar;
+ case TOOLBAR_TRIGGERS:
+ return ui->triggerToolBar;
+ }
+ return nullptr;
+}
+
+bool TableWindow::handleInitialFocus()
+{
+ if (!existingTable)
+ {
+ ui->tableNameEdit->setFocus();
+ return true;
+ }
+ return false;
+}
+
+bool TableWindow::isUncommited() const
+{
+ return ui->dataView->isUncommited() || isModified();
+}
+
+QString TableWindow::getQuitUncommitedConfirmMessage() const
+{
+ QString title = getMdiWindow()->windowTitle();
+ if (ui->dataView->isUncommited() && isModified())
+ return tr("Table window \"%1\" has uncommited structure modifications and data.").arg(title);
+ else if (ui->dataView->isUncommited())
+ return tr("Table window \"%1\" has uncommited data.").arg(title);
+ else if (isModified())
+ return tr("Table window \"%1\" has uncommited structure modifications.").arg(title);
+ else
+ {
+ qCritical() << "Unhandled message case in TableWindow::getQuitUncommitedConfirmMessage().";
+ return QString();
+ }
+}
+
+void TableWindow::useCurrentTableAsBaseForNew()
+{
+ newTable();
+ ui->tableNameEdit->clear();
+ updateWindowTitle();
+ ui->tableNameEdit->setFocus();
+ updateAfterInit();
+}
+
+Db* TableWindow::getAssociatedDb() const
+{
+ return db;
+}