diff options
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows')
7 files changed, 553 insertions, 83 deletions
diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp index 80c4567..1c817de 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablestructuremodel.cpp @@ -128,14 +128,16 @@ QVariant TableStructureModel::data(const QModelIndex& index, int role) const QVariant TableStructureModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) - return QAbstractTableModel::headerData(section, orientation, role); + if (role == Qt::DisplayRole) + { + if (orientation == Qt::Vertical) + return section + 1; - if (orientation == Qt::Vertical) - return section + 1; + // Now it's horizontal orientation with DisplayRole + return columnLabel(section); + } - // Now it's horizontal orientation with DisplayRole - return columnLabel(section); + return QAbstractTableModel::headerData(section, orientation, role); } TableStructureModel::Columns TableStructureModel::getHeaderColumn(int colIdx) const @@ -300,17 +302,17 @@ QString TableStructureModel::columnLabel(int column) const case Columns::TYPE: return tr("Data type", "table structure columns"); case Columns::PK: - return "P"; + return tr("Primary\nKey", "table structure columns"); case Columns::FK: - return "F"; + return tr("Foreign\nKey", "table structure columns"); case Columns::UNIQUE: - return "U"; + return tr("Unique", "table structure columns"); case Columns::CHECK: - return "H"; + return tr("Check", "table structure columns"); case Columns::NOTNULL: - return "N"; + return tr("Not\nNULL", "table structure columns"); case Columns::COLLATE: - return "C"; + return tr("Collate", "table structure columns"); case Columns::DEFAULT: return tr("Default value", "table structure columns"); } diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp index cd1ba72..fd344e8 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.cpp @@ -33,9 +33,11 @@ #include "services/importmanager.h" #include "dbobjectdialogs.h" #include "dialogs/exportdialog.h" +#include "common/centerediconitemdelegate.h" #include "themetuner.h" #include "dialogs/importdialog.h" #include "dialogs/populatedialog.h" +#include "datagrid/sqlqueryitem.h" #include <QMenu> #include <QToolButton> #include <QLabel> @@ -46,7 +48,6 @@ #include <QPushButton> #include <QDebug> #include <QStyleFactory> -#include <datagrid/sqlqueryitem.h> // TODO extend QTableView for columns and constraints, so they show full-row-width drop indicator, // instead of single column drop indicator. @@ -142,6 +143,9 @@ void TableWindow::init() { ui->setupUi(this); ui->structureSplitter->setStretchFactor(0, 2); + ui->structureView->horizontalHeader()->setSectionsClickable(false); + ui->structureView->verticalHeader()->setSectionsClickable(false); + constraintColumnsDelegate = new CenteredIconItemDelegate(this); #ifdef Q_OS_MACX QStyle *fusion = QStyleFactory::create("Fusion"); @@ -159,6 +163,7 @@ void TableWindow::init() ui->dataView->init(dataModel); initActions(); + updateTabsOrder(); connect(dataModel, SIGNAL(executionSuccessful()), this, SLOT(executionSuccessful())); connect(dataModel, SIGNAL(executionFailed(QString)), this, SLOT(executionFailed(QString))); @@ -168,6 +173,9 @@ void TableWindow::init() 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())); + connect(CFG_UI.General.DataTabAsFirstInTables, SIGNAL(changed(const QVariant&)), this, SLOT(updateTabsOrder())); + connect(ui->structureView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(structureViewDoubleClicked(const QModelIndex&))); + connect(ui->tableConstraintsView, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(constraintsViewDoubleClicked(const QModelIndex&))); structureExecutor = new ChainExecutor(this); connect(structureExecutor, SIGNAL(success()), this, SLOT(changesSuccessfullyCommited())); @@ -253,9 +261,9 @@ 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(EDIT_INDEX, ICONS.INDEX_EDIT, tr("Edit index", "table window"), this, SLOT(editCurrentIndex()), 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())); + connect(ui->indexList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(indexViewDoubleClicked(QModelIndex))); } void TableWindow::createTriggerActions() @@ -265,14 +273,14 @@ void TableWindow::createTriggerActions() 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())); + connect(ui->triggerList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(triggerViewDoubleClicked(QModelIndex))); } void TableWindow::editColumn(const QModelIndex& idx) { if (!idx.isValid()) { - qWarning() << "Called TableWindow::editColumn() with invalid index."; + addColumn(); return; } @@ -352,6 +360,8 @@ void TableWindow::executeStructureChanges() modifyingThisTable = true; structureExecutor->setDb(db); structureExecutor->setQueries(sqls); + structureExecutor->setDisableForeignKeys(true); + structureExecutor->setDisableObjectDropsDetection(true); widgetCover->show(); structureExecutor->exec(); } @@ -458,23 +468,27 @@ void TableWindow::setupDefShortcuts() 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() { + int totalConstrCols = 6; if (db->getVersion() == 2) { ui->withoutRowIdCheck->setVisible(false); + totalConstrCols -= 2; } + totalConstrCols += 2; // we start at 3rd column + for (int colIdx = 2; colIdx < totalConstrCols; colIdx++) + ui->structureView->setItemDelegateForColumn(colIdx, constraintColumnsDelegate); + if (existingTable) { dataModel->setDb(db); @@ -785,6 +799,8 @@ void TableWindow::commitStructure(bool skipWarning) void TableWindow::changesSuccessfullyCommited() { + modifyingThisTable = false; + QStringList sqls = structureExecutor->getQueries(); CFG->addDdlHistory(sqls.join("\n"), db->getName(), db->getPath()); @@ -804,6 +820,11 @@ void TableWindow::changesSuccessfullyCommited() updateNewTableState(); updateWindowTitle(); + if (oldTable.compare(table, Qt::CaseInsensitive) == 0 || oldTable.isEmpty()) + notifyInfo(tr("Commited changes for table '%1' successfly.").arg(table)); + else + notifyInfo(tr("Commited changes for table '%1' (named before '%2') successfly.").arg(table, oldTable)); + DBTREE->refreshSchema(db); if (tableModifier) @@ -832,6 +853,7 @@ void TableWindow::changesFailedToCommit(int errorCode, const QString& errorText) { qDebug() << "TableWindow::changesFailedToCommit:" << errorCode << errorText; + modifyingThisTable = false; widgetCover->hide(); notifyError(tr("Could not commit table structure. Error message: %1", "table window").arg(errorText)); } @@ -1011,8 +1033,8 @@ TokenList TableWindow::indexColumnTokens(SqliteCreateIndexPtr index) if (index->indexedColumns.size() == 0) return TokenList(); - SqliteIndexedColumn* firstCol = index->indexedColumns.first(); - SqliteIndexedColumn* lastCol = index->indexedColumns.last(); + SqliteOrderBy* firstCol = index->indexedColumns.first(); + SqliteOrderBy* lastCol = index->indexedColumns.last(); if (firstCol->tokens.size() == 0) return TokenList(); @@ -1048,9 +1070,9 @@ QString TableWindow::getCurrentTrigger() const void TableWindow::applyInitialTab() { if (existingTable && !table.isNull() && CFG_UI.General.OpenTablesOnData.get()) - ui->tabWidget->setCurrentIndex(1); + ui->tabWidget->setCurrentIndex(getDataTabIdx()); else - ui->tabWidget->setCurrentIndex(0); + ui->tabWidget->setCurrentIndex(getStructureTabIdx()); } void TableWindow::resizeStructureViewColumns() @@ -1060,6 +1082,16 @@ void TableWindow::resizeStructureViewColumns() ui->structureView->resizeColumnToContents(c); } +int TableWindow::getDataTabIdx() const +{ + return ui->tabWidget->indexOf(ui->dataTab); +} + +int TableWindow::getStructureTabIdx() const +{ + return ui->tabWidget->indexOf(ui->structureTab); +} + void TableWindow::updateDdlTab() { createTable->rebuildTokens(); @@ -1103,7 +1135,10 @@ void TableWindow::delConstraint() void TableWindow::editConstraint(const QModelIndex& idx) { if (!idx.isValid()) + { + addConstraint(); return; + } SqliteCreateTable::Constraint* constr = structureConstraintsModel->getConstraint(idx.row()); ConstraintDialog dialog(ConstraintDialog::EDIT, constr, createTable.data(), db, this); @@ -1217,39 +1252,37 @@ void TableWindow::createSimilarTable() void TableWindow::tabChanged(int newTab) { - switch (newTab) + if (tabsMoving) + return; + + if (newTab == getDataTabIdx()) { - case 1: + if (isModified()) { - 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; - } + 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.")); - if (!dataLoaded) - ui->dataView->refreshData(); + ui->tabWidget->setCurrentIndex(0); + if (res == 1) + commitStructure(true); - break; + return; } + + if (!dataLoaded) + ui->dataView->refreshData(); } } -void TableWindow::on_structureView_doubleClicked(const QModelIndex &index) +void TableWindow::structureViewDoubleClicked(const QModelIndex &index) { editColumn(index); } -void TableWindow::on_tableConstraintsView_doubleClicked(const QModelIndex &index) +void TableWindow::constraintsViewDoubleClicked(const QModelIndex &index) { editConstraint(index); } @@ -1280,7 +1313,7 @@ void TableWindow::addIndex() updateIndexes(); } -void TableWindow::editIndex() +void TableWindow::editCurrentIndex() { QString index = getCurrentIndex(); if (index.isNull()) @@ -1291,6 +1324,22 @@ void TableWindow::editIndex() updateIndexes(); } +void TableWindow::indexViewDoubleClicked(const QModelIndex& idx) +{ + if (!idx.isValid()) + { + addIndex(); + return; + } + + QString index = ui->indexList->item(idx.row(), 0)->text(); + + DbObjectDialogs dialogs(db, this); + dialogs.editIndex(index); + updateIndexes(); +} + + void TableWindow::delIndex() { QString index = getCurrentIndex(); @@ -1313,7 +1362,25 @@ void TableWindow::editTrigger() { QString trigger = getCurrentTrigger(); if (trigger.isNull()) + { + addTrigger(); + return; + } + + DbObjectDialogs dialogs(db, this); + dialogs.editTrigger(trigger); + updateTriggers(); +} + +void TableWindow::triggerViewDoubleClicked(const QModelIndex& idx) +{ + if (!idx.isValid()) + { + addTrigger(); return; + } + + QString trigger = ui->triggerList->item(idx.row(), 0)->text(); DbObjectDialogs dialogs(db, this); dialogs.editTrigger(trigger); @@ -1488,6 +1555,18 @@ void TableWindow::delColumn(const QString& columnName) delColumn(colIdx); } +void TableWindow::updateTabsOrder() +{ + tabsMoving = true; + ui->tabWidget->removeTab(getDataTabIdx()); + int idx = 1; + if (CFG_UI.General.DataTabAsFirstInTables.get()) + idx = 0; + + ui->tabWidget->insertTab(idx, ui->dataTab, tr("Data")); + tabsMoving = false; +} + bool TableWindow::restoreSessionNextTime() { return existingTable && db && !DBLIST->isTemporary(db); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h index 69f210b..d8738f1 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.h @@ -21,6 +21,7 @@ class TableConstraintsModel; class QProgressBar; class WidgetCover; class SqliteSyntaxHighlighter; +class CenteredIconItemDelegate; class ConstraintTabModel; namespace Ui { @@ -159,6 +160,8 @@ class GUI_API_EXPORT TableWindow : public MdiChild QString getCurrentTrigger() const; void applyInitialTab(); void resizeStructureViewColumns(); + int getDataTabIdx() const; + int getStructureTabIdx() const; int newTableWindowNum = 1; @@ -178,6 +181,8 @@ class GUI_API_EXPORT TableWindow : public MdiChild ChainExecutor* structureExecutor = nullptr; TableModifier* tableModifier = nullptr; bool modifyingThisTable = false; + CenteredIconItemDelegate* constraintColumnsDelegate = nullptr; + bool tabsMoving = false; private slots: void executionSuccessful(); @@ -215,12 +220,14 @@ class GUI_API_EXPORT TableWindow : public MdiChild void updateTableConstraintsToolbarState(); void updateDdlTab(); void updateNewTableState(); - void on_structureView_doubleClicked(const QModelIndex &index); - void on_tableConstraintsView_doubleClicked(const QModelIndex &index); + void structureViewDoubleClicked(const QModelIndex &index); + void constraintsViewDoubleClicked(const QModelIndex &index); void nameChanged(); void withOutRowIdChanged(); void addIndex(); - void editIndex(); + void editCurrentIndex(); + void indexViewDoubleClicked(const QModelIndex& idx); + void triggerViewDoubleClicked(const QModelIndex& idx); void delIndex(); void addTrigger(); void editTrigger(); @@ -229,6 +236,7 @@ class GUI_API_EXPORT TableWindow : public MdiChild void updateTriggersState(); void nextTab(); void prevTab(); + void updateTabsOrder(); public slots: void updateIndexes(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui index 0dd7fe6..14e278a 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/tablewindow.ui @@ -105,7 +105,7 @@ <property name="childrenCollapsible"> <bool>false</bool> </property> - <widget class="QTableView" name="structureView"> + <widget class="ExtTableView" name="structureView"> <property name="dragEnabled"> <bool>true</bool> </property> @@ -152,7 +152,7 @@ <widget class="QToolBar" name="tableConstraintsToolbar"/> </item> <item> - <widget class="QTableView" name="tableConstraintsView"> + <widget class="ExtTableView" name="tableConstraintsView"> <property name="dragEnabled"> <bool>true</bool> </property> @@ -225,7 +225,7 @@ <widget class="QToolBar" name="indexToolBar"/> </item> <item> - <widget class="QTableWidget" name="indexList"> + <widget class="ExtTableWidget" name="indexList"> <property name="editTriggers"> <set>QAbstractItemView::NoEditTriggers</set> </property> @@ -254,7 +254,7 @@ <widget class="QToolBar" name="triggerToolBar"/> </item> <item> - <widget class="QTableWidget" name="triggerList"> + <widget class="ExtTableWidget" name="triggerList"> <property name="editTriggers"> <set>QAbstractItemView::NoEditTriggers</set> </property> @@ -307,6 +307,16 @@ <header>dataview.h</header> <container>1</container> </customwidget> + <customwidget> + <class>ExtTableView</class> + <extends>QTableView</extends> + <header>common/exttableview.h</header> + </customwidget> + <customwidget> + <class>ExtTableWidget</class> + <extends>QTableWidget</extends> + <header>common/exttablewidget.h</header> + </customwidget> </customwidgets> <resources/> <connections/> diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp index cb3a11e..07c927c 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp @@ -21,10 +21,12 @@ #include "services/config.h" #include "services/codeformatter.h" #include "themetuner.h" +#include "datagrid/sqlviewmodel.h" #include <QPushButton> #include <QProgressBar> #include <QDebug> #include <QMessageBox> +#include <QCheckBox> CFG_KEYS_DEFINE(ViewWindow) @@ -44,6 +46,7 @@ ViewWindow::ViewWindow(Db* db, QWidget* parent) : newView(); init(); applyInitialTab(); + updateDbRelatedUiElements(); } ViewWindow::ViewWindow(const ViewWindow& win) : @@ -56,6 +59,7 @@ ViewWindow::ViewWindow(const ViewWindow& win) : init(); initView(); applyInitialTab(); + updateDbRelatedUiElements(); } ViewWindow::ViewWindow(QWidget* parent, Db* db, const QString& database, const QString& view) : @@ -68,6 +72,7 @@ ViewWindow::ViewWindow(QWidget* parent, Db* db, const QString& database, const Q init(); initView(); applyInitialTab(); + updateDbRelatedUiElements(); } ViewWindow::~ViewWindow() @@ -134,6 +139,7 @@ bool ViewWindow::restoreSession(const QVariant& sessionValue) initView(); applyInitialTab(); + updateDbRelatedUiElements(); return true; } @@ -209,10 +215,10 @@ void ViewWindow::init() ui->queryTab, ui->dataTab, ui->triggersTab, - ui->ddl + ui->ddlTab }); - dataModel = new SqlQueryModel(this); + dataModel = new SqlViewModel(this); ui->dataView->init(dataModel); ui->queryEdit->setVirtualSqlExpression("CREATE VIEW name AS %1"); @@ -225,6 +231,12 @@ void ViewWindow::init() connect(ui->queryEdit, SIGNAL(textChanged()), this, SLOT(updateQueryToolbarStatus())); connect(ui->queryEdit, SIGNAL(errorsChecked(bool)), this, SLOT(updateQueryToolbarStatus())); connect(ui->triggersList, SIGNAL(itemSelectionChanged()), this, SLOT(updateTriggersState())); + connect(ui->triggersList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(triggerViewDoubleClicked(QModelIndex))); + connect(ui->outputColumnsTable, SIGNAL(currentRowChanged(int)), this, SLOT(updateColumnButtons())); + connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateColumnButtons())); + connect(ui->outputColumnsTable->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int)), this, SLOT(updateQueryToolbarStatus())); + connect(ui->outputColumnsTable, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(updateQueryToolbarStatus())); + connect(CFG_UI.General.DataTabAsFirstInViews, SIGNAL(changed(const QVariant&)), this, SLOT(updateTabsOrder())); structureExecutor = new ChainExecutor(this); connect(structureExecutor, SIGNAL(success()), this, SLOT(changesSuccessfullyCommited())); @@ -234,9 +246,17 @@ void ViewWindow::init() initActions(); + ui->splitter->setStretchFactor(0, 1); + ui->splitter->setStretchFactor(1, 3); + + updateOutputColumnsVisibility(); + + updateTabsOrder(); + refreshTriggers(); updateQueryToolbarStatus(); updateTriggersState(); + updateColumnButtons(); } void ViewWindow::newView() @@ -258,10 +278,18 @@ void ViewWindow::initView() { dataModel->setDb(db); dataModel->setQuery(originalCreateView->select->detokenize()); + dataModel->setView(view); } ui->queryEdit->setDb(db); ui->queryEdit->setPlainText(createView->select->detokenize()); + + if (createView->columns.size() > 0) + { + columnsFromViewToList(); + outputColumnsCheck->setChecked(true); + } + updateDdlTab(); ui->ddlEdit->setSqliteVersion(db->getVersion()); @@ -286,6 +314,19 @@ void ViewWindow::createQueryTabActions() createAction(ROLLBACK_QUERY, ICONS.ROLLBACK, tr("Rollback the view changes", "view window"), this, SLOT(rollbackView()), ui->queryToolbar); ui->queryToolbar->addSeparator(); ui->queryToolbar->addAction(ui->queryEdit->getAction(SqlEditor::FORMAT_SQL)); + + outputColumnsCheck = new QAction(ICONS.COLUMNS, tr("Explicit column names"), this); + outputColumnsCheck->setCheckable(true); + connect(outputColumnsCheck, SIGNAL(toggled(bool)), this, SLOT(updateOutputColumnsVisibility())); + + outputColumnsSeparator = ui->queryToolbar->addSeparator(); + ui->queryToolbar->addAction(outputColumnsCheck); + createAction(GENERATE_OUTPUT_COLUMNS, ICONS.GENERATE_COLUMNS, tr("Generate output column names automatically basing on result columns of the view."), this, SLOT(generateOutputColumns()), ui->queryToolbar); + createAction(ADD_COLUMN, ICONS.TABLE_COLUMN_ADD, tr("Add column", "view window"), this, SLOT(addColumn()), ui->queryToolbar); + createAction(EDIT_COLUMN, ICONS.TABLE_COLUMN_EDIT, tr("Edit column", "view window"), this, SLOT(editColumn()), ui->queryToolbar); + createAction(DEL_COLUMN, ICONS.TABLE_COLUMN_DELETE, tr("Delete column", "view window"), this, SLOT(delColumn()), ui->queryToolbar); + createAction(MOVE_COLUMN_UP, ICONS.MOVE_UP, tr("Move column up", "view window"), this, SLOT(moveColumnUp()), ui->queryToolbar); + createAction(MOVE_COLUMN_DOWN, ICONS.MOVE_DOWN, tr("Move column down", "view window"), this, SLOT(moveColumnDown()), ui->queryToolbar); } void ViewWindow::createTriggersTabActions() @@ -394,6 +435,7 @@ void ViewWindow::rollbackView() ui->nameEdit->setText(createView->view); ui->queryEdit->setPlainText(createView->select->detokenize()); + columnsFromViewToList(); updateQueryToolbarStatus(); updateDdlTab(); } @@ -411,15 +453,70 @@ QString ViewWindow::getCurrentTrigger() const void ViewWindow::applyInitialTab() { if (existingView && !view.isNull() && CFG_UI.General.OpenViewsOnData.get()) - ui->tabWidget->setCurrentIndex(1); + ui->tabWidget->setCurrentIndex(getDataTabIdx()); else - ui->tabWidget->setCurrentIndex(0); + ui->tabWidget->setCurrentIndex(getQueryTabIdx()); } QString ViewWindow::getCurrentDdl() const { - static_qstring(ddlTpl, "CREATE VIEW %1 AS %2"); - return ddlTpl.arg(wrapObjIfNeeded(ui->nameEdit->text(), db->getDialect())).arg(ui->queryEdit->toPlainText()); + static_qstring(ddlTpl, "CREATE VIEW %1%2 AS %3"); + QString columnsStr = ""; + if (outputColumnsCheck->isChecked() && ui->outputColumnsTable->count() > 0) + columnsStr = "(" + collectColumnNames().join(", ") + ")"; + + return ddlTpl.arg( + wrapObjIfNeeded(ui->nameEdit->text(), db->getDialect()), + columnsStr, + ui->queryEdit->toPlainText() + ); +} + +QStringList ViewWindow::indexedColumnsToNamesOnly(const QList<SqliteIndexedColumn*>& columns) const +{ + QStringList names; + for (SqliteIndexedColumn* col : columns) + names << col->name; + + return names; +} + +QStringList ViewWindow::collectColumnNames() const +{ + Dialect dialect = db ? db->getDialect() : Dialect::Sqlite3; + QStringList cols; + for (int row = 0; row < ui->outputColumnsTable->count(); row++) + cols << wrapObjIfNeeded(ui->outputColumnsTable->item(row)->text(), dialect); + + return cols; +} + +void ViewWindow::columnsFromViewToList() +{ + ui->outputColumnsTable->clear(); + ui->outputColumnsTable->addItems(indexedColumnsToNamesOnly(createView->columns)); + + QListWidgetItem* item = nullptr; + for (int row = 0; row < ui->outputColumnsTable->count(); row++) + { + item = ui->outputColumnsTable->item(row); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } +} + +int ViewWindow::getDataTabIdx() const +{ + return ui->tabWidget->indexOf(ui->dataTab); +} + +int ViewWindow::getQueryTabIdx() const +{ + return ui->tabWidget->indexOf(ui->queryTab); +} + +int ViewWindow::getDdlTabIdx() const +{ + return ui->tabWidget->indexOf(ui->ddlTab); } void ViewWindow::addTrigger() @@ -465,10 +562,11 @@ void ViewWindow::executionFailed(const QString& errorMessage) void ViewWindow::tabChanged(int tabIdx) { - switch (tabIdx) + if (tabsMoving) + return; + + if (tabIdx == getDataTabIdx()) { - case 1: - { if (isModified()) { int res = QMessageBox::question(this, tr("Uncommited changes"), @@ -481,19 +579,19 @@ void ViewWindow::tabChanged(int tabIdx) if (res == 1) commitView(true); - break; + return; } if (!dataLoaded) ui->dataView->refreshData(); - break; - } - case 3: - { - updateDdlTab(); - break; - } + return; + } + + if (tabIdx == getDdlTabIdx()) + { + updateDdlTab(); + return; } } @@ -518,12 +616,18 @@ void ViewWindow::changesSuccessfullyCommited() //QString oldView = view; // uncomment when implementing notify manager call database = createView->database; + QString oldView = view; view = createView->view; existingView = true; initView(); updateQueryToolbarStatus(); updateWindowTitle(); + if (oldView.compare(view, Qt::CaseInsensitive) == 0) + notifyInfo(tr("Commited changes for view '%1' successfly.").arg(view)); + else + notifyInfo(tr("Commited changes for view '%1' (named before '%2') successfly.").arg(view, oldView)); + DBTREE->refreshSchema(db); } @@ -599,6 +703,154 @@ void ViewWindow::checkIfViewDeleted(const QString& database, const QString& obje } } +void ViewWindow::updateOutputColumnsVisibility() +{ + bool enabled = outputColumnsCheck->isChecked(); + + ui->outputColumnsContainer->setVisible(enabled); + actionMap[Action::GENERATE_OUTPUT_COLUMNS]->setVisible(enabled); + actionMap[Action::ADD_COLUMN]->setVisible(enabled); + actionMap[Action::EDIT_COLUMN]->setVisible(enabled); + actionMap[Action::DEL_COLUMN]->setVisible(enabled); + actionMap[Action::MOVE_COLUMN_UP]->setVisible(enabled); + actionMap[Action::MOVE_COLUMN_DOWN]->setVisible(enabled); + + updateQueryToolbarStatus(); +} + +void ViewWindow::addColumn() +{ + QListWidgetItem* item = new QListWidgetItem(); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->outputColumnsTable->addItem(item); + ui->outputColumnsTable->editItem(item); + ui->outputColumnsTable->setCurrentItem(item); + updateColumnButtons(); +} + +void ViewWindow::editColumn() +{ + QListWidgetItem* item = ui->outputColumnsTable->currentItem(); + ui->outputColumnsTable->editItem(item); + updateColumnButtons(); +} + +void ViewWindow::delColumn() +{ + QListWidgetItem* item = ui->outputColumnsTable->takeItem(ui->outputColumnsTable->currentRow()); + delete item; + updateColumnButtons(); +} + +void ViewWindow::moveColumnUp() +{ + int row = ui->outputColumnsTable->currentRow(); + if (row <= 0) + return; + + QListWidgetItem* item = ui->outputColumnsTable->takeItem(row); + ui->outputColumnsTable->insertItem(--row, item); + ui->outputColumnsTable->setCurrentItem(item); +} + +void ViewWindow::moveColumnDown() +{ + int row = ui->outputColumnsTable->currentRow(); + if (row + 1 >= ui->outputColumnsTable->count()) + return; + + QListWidgetItem* item = ui->outputColumnsTable->takeItem(row); + ui->outputColumnsTable->insertItem(++row, item); + ui->outputColumnsTable->setCurrentItem(item); +} + +void ViewWindow::updateColumnButtons() +{ + QListWidgetItem* item = ui->outputColumnsTable->currentItem(); + int row = ui->outputColumnsTable->currentRow(); + + actionMap[MOVE_COLUMN_UP]->setEnabled(row > 0); + actionMap[MOVE_COLUMN_DOWN]->setEnabled(row + 1 < ui->outputColumnsTable->count()); + actionMap[EDIT_COLUMN]->setEnabled(item != nullptr); + actionMap[DEL_COLUMN]->setEnabled(item != nullptr); +} + +void ViewWindow::generateOutputColumns() +{ + if (ui->outputColumnsTable->count() > 0) + { + QMessageBox::StandardButton res = QMessageBox::question(this, tr("Override columns"), tr("Currently defined columns will be overriden. Do you want to continue?")); + if (res != QMessageBox::Yes) + return; + } + + // Validate and generate fresh createView instance + bool validated = validate(true); + if (!validated) + return; + + // Make copy of CREATE statement and remove columns + SqliteCreateView* stmt = dynamic_cast<SqliteCreateView*>(createView->clone()); + for (SqliteIndexedColumn* col : stmt->columns) + delete col; + + stmt->columns.clear(); + + // Indentify columns + SchemaResolver resolver(db); + QStringList columns = resolver.getColumnsUsingPragma(stmt); + delete stmt; + if (columns.isEmpty()) + { + notifyWarn(tr("Could not determinate columns returned from the view. The query is problably incomplete or contains errors.")); + return; + } + + ui->outputColumnsTable->clear(); + ui->outputColumnsTable->addItems(columns); + + QListWidgetItem* item = nullptr; + for (int row = 0; row < columns.size(); row++) + { + item = ui->outputColumnsTable->item(row); + item->setFlags(item->flags() | Qt::ItemIsEditable); + } +} + +void ViewWindow::updateDbRelatedUiElements() +{ + bool enabled = db->getDialect() == Dialect::Sqlite3; + outputColumnsCheck->setVisible(enabled); + outputColumnsSeparator->setVisible(enabled); +} + +void ViewWindow::updateTabsOrder() +{ + tabsMoving = true; + ui->tabWidget->removeTab(getDataTabIdx()); + int idx = 1; + if (CFG_UI.General.DataTabAsFirstInViews.get()) + idx = 0; + + ui->tabWidget->insertTab(idx, ui->dataTab, tr("Data")); + tabsMoving = false; +} + +void ViewWindow::triggerViewDoubleClicked(const QModelIndex& idx) +{ + if (!idx.isValid()) + { + addTrigger(); + return; + } + + QString trigger = ui->triggersList->item(idx.row(), 0)->text(); + + DbObjectDialogs dialogs(db, this); + dialogs.editTrigger(trigger); + refreshTriggers(); +} + void ViewWindow::refreshTriggers() { if (!db || !db->isValid()) @@ -680,9 +932,21 @@ void ViewWindow::updateDdlTab() bool ViewWindow::isModified() const { - return (originalCreateView && originalCreateView->view != ui->nameEdit->text()) || - ui->queryEdit->toPlainText() != originalQuery || - !existingView; + // Quick checks first + bool modified = !existingView || (originalCreateView && originalCreateView->view != ui->nameEdit->text()) || + ui->queryEdit->toPlainText() != originalQuery; + + if (modified) + return modified; + + // And now a bit slower check + QStringList origCols = createView ? indexedColumnsToNamesOnly(createView->columns) : QStringList(); + QStringList currentCols; + if (outputColumnsCheck->isChecked()) + currentCols = collectColumnNames(); + + bool colsModified = origCols != currentCols; + return colsModified; } bool ViewWindow::validate(bool skipWarnings) @@ -697,12 +961,9 @@ bool ViewWindow::validate(bool skipWarnings) } // Rebuilding createView statement and validating it on the fly. - QString ddl = "CREATE VIEW %1 AS %2"; - QString viewName = wrapObjIfNeeded(ui->nameEdit->text(), db->getDialect()); - QString select = ui->queryEdit->toPlainText(); - + QString ddl = getCurrentDdl(); Parser parser(db->getDialect()); - if (!parser.parse(ddl.arg(viewName).arg(select)) || parser.getQueries().size() < 1) + if (!parser.parse(ddl) || parser.getQueries().size() < 1) { notifyError(tr("The SELECT statement could not be parsed. Please correct the query and retry.\nDetails: %1").arg(parser.getErrorString())); return false; diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h index 62e1218..65c3260 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.h @@ -13,12 +13,12 @@ namespace Ui { } class SqliteSyntaxHighlighter; -class SqlQueryModel; class WidgetCover; class QPushButton; class QProgressBar; class ChainExecutor; class ViewModifier; +class SqlViewModel; CFG_KEY_LIST(ViewWindow, QObject::tr("A view window"), CFG_KEY_ENTRY(REFRESH_TRIGGERS, Qt::Key_F5, QObject::tr("Refresh view trigger list")) @@ -41,6 +41,12 @@ class GUI_API_EXPORT ViewWindow : public MdiChild REFRESH_QUERY, COMMIT_QUERY, ROLLBACK_QUERY, + ADD_COLUMN, + EDIT_COLUMN, + DEL_COLUMN, + MOVE_COLUMN_UP, + MOVE_COLUMN_DOWN, + GENERATE_OUTPUT_COLUMNS, // Triggers tab REFRESH_TRIGGERS, ADD_TRIGGER, @@ -103,6 +109,12 @@ class GUI_API_EXPORT ViewWindow : public MdiChild QString getCurrentTrigger() const; void applyInitialTab(); QString getCurrentDdl() const; + QStringList indexedColumnsToNamesOnly(const QList<SqliteIndexedColumn*>& columns) const; + QStringList collectColumnNames() const; + void columnsFromViewToList(); + int getDataTabIdx() const; + int getQueryTabIdx() const; + int getDdlTabIdx() const; Db* db = nullptr; QString database; @@ -113,13 +125,16 @@ class GUI_API_EXPORT ViewWindow : public MdiChild bool modified = false; SqliteCreateViewPtr originalCreateView; SqliteCreateViewPtr createView; - SqlQueryModel* dataModel = nullptr; + SqlViewModel* dataModel = nullptr; QString originalQuery; WidgetCover* widgetCover = nullptr; ChainExecutor* structureExecutor = nullptr; ViewModifier* viewModifier = nullptr; Ui::ViewWindow *ui = nullptr; bool modifyingThisView = false; + QAction* outputColumnsCheck = nullptr; + QAction* outputColumnsSeparator = nullptr; + bool tabsMoving = false; private slots: void refreshView(); @@ -139,6 +154,17 @@ class GUI_API_EXPORT ViewWindow : public MdiChild void prevTab(); void dbClosedFinalCleanup(); void checkIfViewDeleted(const QString& database, const QString& object, DbObjectType type); + void updateOutputColumnsVisibility(); + void addColumn(); + void editColumn(); + void delColumn(); + void moveColumnUp(); + void moveColumnDown(); + void updateColumnButtons(); + void generateOutputColumns(); + void updateDbRelatedUiElements(); + void updateTabsOrder(); + void triggerViewDoubleClicked(const QModelIndex& idx); public slots: void refreshTriggers(); diff --git a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui index 0fdccc3..9112280 100644 --- a/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui +++ b/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.ui @@ -29,7 +29,7 @@ <item> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> - <number>0</number> + <number>2</number> </property> <widget class="QWidget" name="queryTab"> <attribute name="title"> @@ -68,7 +68,86 @@ </widget> </item> <item> - <widget class="SqlEditor" name="queryEdit"/> + <widget class="QWidget" name="widget" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <property name="spacing"> + <number>0</number> + </property> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QSplitter" name="splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QWidget" name="outputColumnsContainer" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <property name="spacing"> + <number>4</number> + </property> + <property name="leftMargin"> + <number>4</number> + </property> + <property name="topMargin"> + <number>4</number> + </property> + <property name="rightMargin"> + <number>4</number> + </property> + <property name="bottomMargin"> + <number>4</number> + </property> + <item> + <widget class="QGroupBox" name="outputColumnsGroup"> + <property name="title"> + <string>Output column names</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QListWidget" name="outputColumnsTable"> + <property name="dragEnabled"> + <bool>true</bool> + </property> + <property name="dragDropMode"> + <enum>QAbstractItemView::InternalMove</enum> + </property> + <property name="defaultDropAction"> + <enum>Qt::MoveAction</enum> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="selectionRectVisible"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="SqlEditor" name="queryEdit"/> + </widget> + </item> + </layout> + </widget> </item> </layout> </widget> @@ -91,7 +170,7 @@ <widget class="QToolBar" name="triggersToolbar"/> </item> <item> - <widget class="QTableWidget" name="triggersList"> + <widget class="ExtTableWidget" name="triggersList"> <property name="editTriggers"> <set>QAbstractItemView::NoEditTriggers</set> </property> @@ -114,7 +193,7 @@ </item> </layout> </widget> - <widget class="QWidget" name="ddl"> + <widget class="QWidget" name="ddlTab"> <attribute name="title"> <string>DDL</string> </attribute> @@ -149,6 +228,11 @@ <extends>QPlainTextEdit</extends> <header>sqleditor.h</header> </customwidget> + <customwidget> + <class>ExtTableWidget</class> + <extends>QTableWidget</extends> + <header>common/exttablewidget.h</header> + </customwidget> </customwidgets> <resources/> <connections/> |
