Files
sqlitebrowser/src/EditIndexDialog.cpp
Martin Kleusberg 7a9f310778 Don't allow editing the SQL preview in the Edit Index dialog
Don't allow the user to edit the SQL preview in the Edit Index dialog
because it's just supposed to be a preview.
2018-07-15 23:39:40 +02:00

352 lines
12 KiB
C++

#include "EditIndexDialog.h"
#include "ui_EditIndexDialog.h"
#include "sqlitedb.h"
#include <QMessageBox>
#include <QPushButton>
EditIndexDialog::EditIndexDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& indexName, bool createIndex, QWidget* parent)
: QDialog(parent),
pdb(db),
curIndex(indexName),
index(indexName.name()),
newIndex(createIndex),
ui(new Ui::EditIndexDialog),
m_sRestorePointName(pdb.generateSavepointName("editindex"))
{
// Create UI
ui->setupUi(this);
ui->sqlTextEdit->setReadOnly(true);
// Get list of tables, sort it alphabetically and fill the combobox
QMap<QString, sqlb::ObjectIdentifier> dbobjs; // Map from display name to full object identifier
if(newIndex) // If this is a new index, offer all tables of all database schemata
{
for(auto it=pdb.schemata.constBegin();it!=pdb.schemata.constEnd();++it)
{
QList<sqlb::ObjectPtr> tables = it->values("table");
for(auto jt=tables.constBegin();jt!=tables.constEnd();++jt)
{
// Only show the schema name for non-main schemata
sqlb::ObjectIdentifier obj(it.key(), (*jt)->name());
dbobjs.insert(obj.toDisplayString(), obj);
}
}
} else { // If this is an existing index, only offer tables of the current database schema
QList<sqlb::ObjectPtr> tables = pdb.schemata[curIndex.schema()].values("table");
for(auto it : tables)
{
// Only show the schema name for non-main schemata
sqlb::ObjectIdentifier obj(curIndex.schema(), it->name());
dbobjs.insert(obj.toDisplayString(), obj);
}
}
ui->comboTableName->blockSignals(true);
for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it)
ui->comboTableName->addItem(QIcon(QString(":icons/table")), it.key(), it.value().toVariant());
ui->comboTableName->blockSignals(false);
QHeaderView *tableHeaderView = ui->tableIndexColumns->horizontalHeader();
tableHeaderView->setSectionResizeMode(0, QHeaderView::Stretch);
// Editing an existing index?
if(!newIndex)
{
// Load the current layout and fill in the dialog fields
index = *(pdb.getObjectByName(curIndex).dynamicCast<sqlb::Index>());
ui->editIndexName->blockSignals(true);
ui->editIndexName->setText(index.name());
ui->editIndexName->blockSignals(false);
ui->checkIndexUnique->blockSignals(true);
ui->checkIndexUnique->setChecked(index.unique());
ui->checkIndexUnique->blockSignals(false);
ui->comboTableName->blockSignals(true);
ui->comboTableName->setCurrentText(index.table());
ui->comboTableName->blockSignals(false);
ui->editPartialClause->blockSignals(true);
ui->editPartialClause->setText(index.whereExpr());
ui->editPartialClause->blockSignals(false);
tableChanged(index.table(), true);
} else {
tableChanged(ui->comboTableName->currentText(), false);
}
// Add event handler for index column name changes. These are only allowed for expression columns, though.
connect(ui->tableIndexColumns, static_cast<void(QTableWidget::*)(QTableWidgetItem*)>(&QTableWidget::itemChanged),
[=](QTableWidgetItem* item)
{
index.columns().at(item->row())->setName(item->text());
updateSqlText();
});
// Create a savepoint to revert back to
pdb.setSavepoint(m_sRestorePointName);
}
EditIndexDialog::~EditIndexDialog()
{
delete ui;
}
void EditIndexDialog::tableChanged(const QString& new_table, bool initialLoad)
{
// Set the table name and clear all index columns
if(!initialLoad)
{
index.setTable(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).name());
index.clearColumns();
}
// Stop here if table name is empty
if(new_table.isEmpty())
{
// Call checkInput() before to make sure the OK button is disabled
checkInput();
return;
}
updateColumnLists();
}
void EditIndexDialog::updateColumnLists()
{
// Fill the table column list
sqlb::TablePtr table = pdb.getObjectByName(sqlb::ObjectIdentifier(ui->comboTableName->currentData())).dynamicCast<sqlb::Table>();
if(!table)
return;
sqlb::FieldInfoList tableFields = table->fieldInformation();
ui->tableTableColumns->setRowCount(tableFields.size());
int tableRows = 0;
for(int i=0;i<tableFields.size();++i)
{
// When we're doing the initial loading and this field already is in the index to edit, then don't add it to the
// list of table columns. It will be added to the list of index columns in the next step. When this is not the initial
// loading, the index column list is empty, so this check will always be true.
if(index.findColumn(tableFields.at(i).name) == -1)
{
// Put the name of the field in the first column
QTableWidgetItem* name = new QTableWidgetItem(tableFields.at(i).name);
name->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableTableColumns->setItem(tableRows, 0, name);
// Put the data type in the second column
QTableWidgetItem* type = new QTableWidgetItem(tableFields.at(i).type);
type->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->tableTableColumns->setItem(tableRows, 1, type);
tableRows++;
}
}
// Set row count to actual count. This is needed for the intial loading, when some rows might have been omitted because they were used in the index
ui->tableTableColumns->setRowCount(tableRows);
// Fill the index column list. This is done separately from the table column to include expression columns (these are not found in the original
// table) and to preserve the order of the index columns
auto indexFields = index.columns();
ui->tableIndexColumns->blockSignals(true);
ui->tableIndexColumns->setRowCount(indexFields.size());
for(int i=0;i<indexFields.size();++i)
{
// Put the name of the field in the first column
QTableWidgetItem* name = new QTableWidgetItem(indexFields.at(i)->name());
Qt::ItemFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
if(indexFields.at(i)->expression())
flags |= Qt::ItemIsEditable;
name->setFlags(flags);
ui->tableIndexColumns->setItem(i, 0, name);
// And put a combobox to select the order in which to index the field in the last column
QComboBox* order = new QComboBox(this);
order->addItem("");
order->addItem("ASC");
order->addItem("DESC");
order->setCurrentText(indexFields.at(i)->order().toUpper());
ui->tableIndexColumns->setCellWidget(i, 1, order);
connect(order, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentTextChanged),
[=](QString new_order)
{
int colnum = index.findColumn(indexFields.at(i)->name());
if(colnum != -1)
{
index.column(colnum)->setOrder(new_order);
updateSqlText();
}
});
}
ui->tableIndexColumns->blockSignals(false);
checkInput();
}
void EditIndexDialog::addToIndex(const QModelIndex& idx)
{
// Get current row number
int row;
if(idx.isValid())
row = idx.row();
else
row = ui->tableTableColumns->currentRow();
// No row selected? Abort.
if(row == -1)
return;
// Add field to index
index.addColumn(sqlb::IndexedColumnPtr(new sqlb::IndexedColumn(
ui->tableTableColumns->item(row, 0)->text(), // Column name
false, // Is expression
""))); // Order
// Update UI
updateColumnLists();
}
void EditIndexDialog::removeFromIndex(const QModelIndex& idx)
{
// Get current row number
int row;
if(idx.isValid())
row = idx.row();
else
row = ui->tableIndexColumns->currentRow();
// No row selected? Abort.
if(row == -1)
return;
// If this is an expression column and the action was triggered by a double click event instead of a button click,
// we won't remove the expression column because it's too likely that this was only done by accident by the user.
// Instead just open the expression column for editing.
if(index.column(row)->expression() && sender() != ui->buttonFromIndex)
{
ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0));
return;
}
// Remove column from index
index.removeColumn(ui->tableIndexColumns->item(row, 0)->text());
// Update UI
updateColumnLists();
}
void EditIndexDialog::checkInput()
{
// Check if index name is set
bool valid = true;
if(ui->editIndexName->text().isEmpty())
valid = false;
// Check if a table is selected (this is especially important in the case where there are no tables in the database yet).
if(ui->comboTableName->currentText().isNull())
valid = false;
// Check if index has any columns
if(index.columns().size() == 0)
valid = false;
// Only activate OK button if index data is valid
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
// Set the index name and the unique flag
index.setName(ui->editIndexName->text());
index.setUnique(ui->checkIndexUnique->isChecked());
index.setWhereExpr(ui->editPartialClause->text());
updateSqlText();
}
void EditIndexDialog::accept()
{
// When editing an index, delete the old one first
if(!newIndex)
{
if(!pdb.executeSQL(QString("DROP INDEX IF EXISTS %1;").arg(curIndex.toString())))
{
QMessageBox::warning(this, qApp->applicationName(), tr("Deleting the old index failed:\n%1").arg(pdb.lastError()));
return;
}
}
// Create the new index in the schema of the selected table
if(pdb.executeSQL(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).schema())))
QDialog::accept();
else
QMessageBox::warning(this, QApplication::applicationName(), tr("Creating the index failed:\n%1").arg(pdb.lastError()));
}
void EditIndexDialog::reject()
{
// Rollback to our savepoint
pdb.revertToSavepoint(m_sRestorePointName);
QDialog::reject();
}
void EditIndexDialog::updateSqlText()
{
ui->sqlTextEdit->setText(index.sql(sqlb::ObjectIdentifier(ui->comboTableName->currentData()).schema()));
}
void EditIndexDialog::moveColumnUp()
{
moveCurrentColumn(false);
}
void EditIndexDialog::moveColumnDown()
{
moveCurrentColumn(true);
}
void EditIndexDialog::moveCurrentColumn(bool down)
{
// Get current row number and calculate row number after the movement. Check the values
int currentRow = ui->tableIndexColumns->currentRow();
if(currentRow == -1)
return;
int newRow = currentRow + (down ? 1 : -1);
if(newRow < 0)
return;
if(newRow >= ui->tableIndexColumns->rowCount())
return;
// Get the column information, swap the columns, and save the new column list back in the index
auto columns = index.columns();
std::swap(columns[currentRow], columns[newRow]);
index.setColumns(columns);
// Update UI
updateColumnLists();
// Select old row at new position
ui->tableIndexColumns->selectRow(newRow);
}
void EditIndexDialog::addExpressionColumn()
{
// Check if there already is an empty expression column
int row = index.findColumn("");
if(row == -1)
{
// There is no empty expression column yet, so add one.
// Add new expression column to the index
index.addColumn(sqlb::IndexedColumnPtr(new sqlb::IndexedColumn(
"", // Column name
true, // Is expression
""))); // Order
// Update UI
updateColumnLists();
// Get row number of new column
row = ui->tableIndexColumns->rowCount() - 1;
}
// Now we should have the row number of the empty expression column, no matter if it was newly added or it already existed.
// Select the row for editing
ui->tableIndexColumns->editItem(ui->tableIndexColumns->item(row, 0));
}