aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2016-06-13 18:42:57 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2016-06-13 18:42:57 -0400
commit65d5f68cc6dc81799c5a5d90400a2c1f0dd02547 (patch)
tree6f245ba901b87ef42fed69965aea22f5eea6b590 /SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp
parentaeb6bad01630d325a4e768e798a7a6d44e18fdaf (diff)
parent5d9314f134ddd3dc4c853e398ac90ba247fb2e4f (diff)
Merge tag 'upstream/3.1.0'
Upstream version 3.1.0 # gpg: Signature made Mon 13 Jun 2016 06:42:54 PM EDT using RSA key ID EBE9BD91 # gpg: Good signature from "Unit 193 <unit193@gmail.com>" # gpg: aka "Unit 193 <unit193@ninthfloor.org>" # gpg: aka "Unit 193 <unit193@ubuntu.com>" # gpg: aka "Unit 193 <unit193@ninthfloor.com>"
Diffstat (limited to 'SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp')
-rw-r--r--SQLiteStudio3/guiSQLiteStudio/windows/viewwindow.cpp311
1 files changed, 286 insertions, 25 deletions
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;