同步操作将从 陈炳煌/sqlitebrowser 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#include "EditTableDialog.h"#include "Settings.h"#include "ForeignKeyEditorDelegate.h"#include "ui_EditTableDialog.h"#include "sqlitetablemodel.h"#include "sqlitedb.h"#include "SelectItemsPopup.h"#include <QMessageBox>#include <QPushButton>#include <QComboBox>#include <QDateTime>#include <QKeyEvent>#include <algorithm>Q_DECLARE_METATYPE(sqlb::ConstraintPtr)EditTableDialog::EditTableDialog(DBBrowserDB& db, const sqlb::ObjectIdentifier& tableName, bool createTable, QWidget* parent): QDialog(parent),ui(new Ui::EditTableDialog),pdb(db),curTable(tableName),m_table(tableName.name()),m_bNewTable(createTable),m_sRestorePointName(pdb.generateSavepointName("edittable")){// Create UIui->setupUi(this);ui->widgetExtension->setVisible(false);connect(ui->treeWidget, &QTreeWidget::itemChanged, this, &EditTableDialog::fieldItemChanged);connect(ui->tableConstraints, &QTableWidget::itemChanged, this, &EditTableDialog::constraintItemChanged);// TODO Remove this once we have added support for this buttonui->buttonAddConstraint->setVisible(false);// Set item delegate for foreign key columnm_fkEditorDelegate = new ForeignKeyEditorDelegate(db, m_table, this);ui->treeWidget->setItemDelegateForColumn(kForeignKey, m_fkEditorDelegate);// Editing an existing table?if(m_bNewTable == false){// Existing table, so load and set the current layoutm_table = *(pdb.getObjectByName<sqlb::Table>(curTable));ui->labelEditWarning->setVisible(!m_table.fullyParsed());// Initialise the list of tracked columns for table layout changesfor(const auto& field : m_table.fields)trackColumns[QString::fromStdString(field.name())] = QString::fromStdString(field.name());// Set without rowid checkbox and schema dropdown. No need to trigger any events here as we're only loading a table exactly as it is stored by SQLite, so no need// for error checking etc.ui->checkWithoutRowid->blockSignals(true);ui->checkWithoutRowid->setChecked(m_table.withoutRowidTable());ui->checkWithoutRowid->blockSignals(false);ui->comboSchema->blockSignals(true);for(const auto& n : pdb.schemata) // Load list of database schemataui->comboSchema->addItem(QString::fromStdString(n.first));ui->comboSchema->setCurrentText(QString::fromStdString(curTable.schema()));ui->comboSchema->blockSignals(false);populateFields();populateConstraints();} else {for(const auto& n : pdb.schemata) // Load list of database schemataui->comboSchema->addItem(QString::fromStdString(n.first));ui->comboSchema->setCurrentText("main"); // Always create tables in the main schema by defaultui->labelEditWarning->setVisible(false);}// Enable/disable remove constraint button depending on whether a constraint is selectedconnect(ui->tableConstraints, &QTableWidget::itemSelectionChanged, [this]() {bool hasSelection = ui->tableConstraints->selectionModel()->hasSelection();ui->buttonRemoveConstraint->setEnabled(hasSelection);});// And create a savepointpdb.setSavepoint(m_sRestorePointName);// Update UIui->editTableName->setText(QString::fromStdString(curTable.name()));updateColumnWidth();// Allow editing of constraint columns by double clicking the columns column of the constraints tableconnect(ui->tableConstraints, &QTableWidget::itemDoubleClicked, [this](QTableWidgetItem* item) {// Check whether the double clicked item is in the columns columnif(item->column() == kConstraintColumns){sqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();// Do not allow editing the columns list of a CHECK constraint because CHECK constraints are independent of column listsif(constraint->type() == sqlb::Constraint::CheckConstraintType)return;// Show the select items popup dialogSelectItemsPopup* dialog = new SelectItemsPopup(m_table.fieldNames(), item->data(Qt::UserRole).value<sqlb::ConstraintPtr>()->column_list, this);QRect item_rect = ui->tableConstraints->visualItemRect(item);dialog->move(ui->tableConstraints->mapToGlobal(QPoint(ui->tableConstraints->x() + item_rect.x(),ui->tableConstraints->y() + item_rect.y() + item_rect.height() / 2)));dialog->show();// When clicking the Apply button in the popup dialog, save the new columns listconnect(dialog, &SelectItemsPopup::accepted, [this, dialog, constraint]() {// Check if column selection changed at allsqlb::StringVector new_columns = dialog->selectedItems();if(constraint->column_list != new_columns){// Remove the constraint with the old columns and add a new one with the new columnsm_table.removeConstraint(constraint);constraint->column_list = new_columns;m_table.addConstraint(constraint);// Update the UIpopulateFields();populateConstraints();updateSqlText();}});}});// (De-)activate fieldscheckInput();}EditTableDialog::~EditTableDialog(){delete ui;}void EditTableDialog::keyPressEvent(QKeyEvent *evt){if((evt->modifiers() & Qt::ControlModifier)&& (evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)){accept();return;}if(evt->key() == Qt::Key_Enter || evt->key() == Qt::Key_Return)return;QDialog::keyPressEvent(evt);}void EditTableDialog::updateColumnWidth(){ui->treeWidget->setColumnWidth(kName, 190);ui->treeWidget->setColumnWidth(kType, 100);ui->treeWidget->setColumnWidth(kNotNull, 30);ui->treeWidget->setColumnWidth(kPrimaryKey, 30);ui->treeWidget->setColumnWidth(kAutoIncrement, 30);ui->treeWidget->setColumnWidth(kUnique, 30);ui->treeWidget->setColumnWidth(kForeignKey, 500);ui->tableConstraints->setColumnWidth(kConstraintColumns, 180);ui->tableConstraints->setColumnWidth(kConstraintType, 130);ui->tableConstraints->setColumnWidth(kConstraintName, 130);ui->tableConstraints->setColumnWidth(kConstraintSql, 300);}void EditTableDialog::populateFields(){// Disable the itemChanged signal or the table item will be updated while filling the treewidgetui->treeWidget->blockSignals(true);ui->treeWidget->clear();const auto& fields = m_table.fields;sqlb::StringVector pk = m_table.primaryKey();for(const sqlb::Field& f : fields){QTreeWidgetItem *tbitem = new QTreeWidgetItem(ui->treeWidget);tbitem->setFlags(tbitem->flags() | Qt::ItemIsEditable);tbitem->setText(kName, QString::fromStdString(f.name()));QComboBox* typeBox = new QComboBox(ui->treeWidget);typeBox->setProperty("column", QString::fromStdString(f.name()));typeBox->setEditable(true);typeBox->addItems(DBBrowserDB::Datatypes);int index = typeBox->findText(QString::fromStdString(f.type()), Qt::MatchExactly);if(index == -1){// non standard named typetypeBox->addItem(QString::fromStdString(f.type()));index = typeBox->count() - 1;}typeBox->setCurrentIndex(index);typeBox->installEventFilter(this);connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes()));ui->treeWidget->setItemWidget(tbitem, kType, typeBox);tbitem->setCheckState(kNotNull, f.notnull() ? Qt::Checked : Qt::Unchecked);tbitem->setCheckState(kPrimaryKey, contains(pk, f.name()) ? Qt::Checked : Qt::Unchecked);tbitem->setCheckState(kAutoIncrement, f.autoIncrement() ? Qt::Checked : Qt::Unchecked);tbitem->setCheckState(kUnique, f.unique() ? Qt::Checked : Qt::Unchecked);// For the default value check if it is surrounded by parentheses and if that's the case// add a '=' character before the entire string to match the input format we're expecting// from the user when using functions in the default value field.if(f.defaultValue().front() == '(' && f.defaultValue().back() == ')')tbitem->setText(kDefault, "=" + QString::fromStdString(f.defaultValue()));elsetbitem->setText(kDefault, QString::fromStdString(f.defaultValue()));tbitem->setText(kCheck, QString::fromStdString(f.check()));auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(m_table.constraint({f.name()}, sqlb::Constraint::ForeignKeyConstraintType));if(fk)tbitem->setText(kForeignKey, QString::fromStdString(fk->toString()));ui->treeWidget->addTopLevelItem(tbitem);}// and reconnectui->treeWidget->blockSignals(false);}void EditTableDialog::populateConstraints(){// Disable the itemChanged signal or the table item will be updated while filling the treewidgetui->tableConstraints->blockSignals(true);const auto& constraints = m_table.allConstraints();ui->tableConstraints->setRowCount(static_cast<int>(constraints.size()));int row = 0;for(const auto& constraint : constraints){const auto& columns = constraint->column_list;// ColumnsQTableWidgetItem* column = new QTableWidgetItem(QString::fromStdString(sqlb::joinStringVector(columns, ",")));column->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);column->setData(Qt::UserRole, QVariant::fromValue<sqlb::ConstraintPtr>(constraint)); // Remember address of constraint object. This is used for modifying it laterui->tableConstraints->setItem(row, kConstraintColumns, column);// TypeQComboBox* type = new QComboBox(this);type->addItem(tr("Primary Key")); // NOTE: The order of the items here have to match the order in the sqlb::Constraint::ConstraintTypes enum!type->addItem(tr("Unique"));type->addItem(tr("Foreign Key"));type->addItem(tr("Check"));type->setCurrentIndex(constraint->type());connect(type, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this, type, constraint](int index) {// Handle change of constraint type. Effectively this means removing the old constraint and replacing it by an entirely new one.// Only the column list and the name can be migrated to the new constraint.// Create new constraint depending on selected typesqlb::ConstraintPtr new_constraint;switch(index){case 0:// Make sure there is only one primary key at a timeif(!m_table.primaryKey().empty()){QMessageBox::warning(this, qApp->applicationName(), tr("There can only be one primary key for each table. Please modify the existing primary ""key instead."));// Set combo box back to original constraint typetype->blockSignals(true);type->setCurrentIndex(constraint->type());type->blockSignals(false);return;}new_constraint = sqlb::ConstraintPtr(new sqlb::PrimaryKeyConstraint());break;case 1:new_constraint = sqlb::ConstraintPtr(new sqlb::UniqueConstraint());break;case 2:new_constraint = sqlb::ConstraintPtr(new sqlb::ForeignKeyClause());break;case 3:new_constraint = sqlb::ConstraintPtr(new sqlb::CheckConstraint());break;default:return;}new_constraint->setName(constraint->name());new_constraint->column_list = constraint->column_list;// Replace old by new constraintm_table.replaceConstraint(constraint, new_constraint);// Update SQL and viewpopulateFields();populateConstraints();updateSqlText();});ui->tableConstraints->setCellWidget(row, kConstraintType, type);// NameQTableWidgetItem* name = new QTableWidgetItem(QString::fromStdString(constraint->name()));name->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);ui->tableConstraints->setItem(row, kConstraintName, name);// SQLQTableWidgetItem* sql = new QTableWidgetItem(QString::fromStdString(constraint->toSql()));sql->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);ui->tableConstraints->setItem(row, kConstraintSql, sql);row++;}ui->tableConstraints->blockSignals(false);}void EditTableDialog::accept(){// Are we editing an already existing table or designing a new one? In the first case there is a table name set,// in the latter the current table name is emptyif(m_bNewTable){// Creation of new tableif(!pdb.executeSQL(QString::fromStdString(m_table.sql(ui->comboSchema->currentText().toStdString())))){QMessageBox::warning(this,QApplication::applicationName(),tr("Error creating table. Message from database engine:\n%1").arg(pdb.lastError()));return;}} else {// Editing of old table// Apply all changes to the actual table in the databaseif(!pdb.alterTable(curTable, m_table, trackColumns, ui->comboSchema->currentText().toStdString())){QMessageBox::warning(this, QApplication::applicationName(), pdb.lastError());return;}}QDialog::accept();}void EditTableDialog::reject(){// Then rollback to our savepointpdb.revertToSavepoint(m_sRestorePointName);QDialog::reject();}void EditTableDialog::updateSqlText(){ui->sqlTextEdit->setText(QString::fromStdString(m_table.sql(ui->comboSchema->currentText().toStdString())));}void EditTableDialog::checkInput(){std::string normTableName = ui->editTableName->text().toStdString();bool valid = true;if(normTableName.empty())valid = false;if(ui->treeWidget->topLevelItemCount() == 0)valid = false;if (normTableName != m_table.name()) {const std::string oldTableName = m_table.name();m_table.setName(normTableName);m_fkEditorDelegate->updateTablesList(oldTableName);// update fk's that refer to table itself recursivelyconst auto& fields = m_table.fields;for(const sqlb::Field& f : fields) {auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(m_table.constraint({f.name()}, sqlb::Constraint::ForeignKeyConstraintType));if(fk && oldTableName == fk->table())fk->setTable(normTableName);}populateFields();}updateSqlText();ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);}void EditTableDialog::updateTypes(QObject *object){QComboBox* typeBox = qobject_cast<QComboBox*>(object);if(typeBox){QString type = typeBox->currentText();std::string column = typeBox->property("column").toString().toStdString();for(size_t index=0; index < m_table.fields.size(); ++index){if(m_table.fields.at(index).name() == column){m_table.fields.at(index).setType(type.toStdString());break;}}checkInput();}}void EditTableDialog::updateTypes(){updateTypes(sender());}bool EditTableDialog::eventFilter(QObject *object, QEvent *event){if(event->type() == QEvent::FocusOut){updateTypes(object);}return false;}void EditTableDialog::fieldItemChanged(QTreeWidgetItem *item, int column){size_t index = static_cast<size_t>(ui->treeWidget->indexOfTopLevelItem(item));if(index < m_table.fields.size()){sqlb::Field& field = m_table.fields.at(index);QString oldFieldName = QString::fromStdString(field.name());switch(column){case kName:{// When a field of that name already exists, show a warning to the user and don't apply the new name. This is done by searching for an// existing field with the new name. There is one exception, however, to this rule: if the field that we have found is the same field// as the one we are currently trying to rename, this means the user is trying to rename the field to essentially the same name but// with different case. Example: if I rename column 'COLUMN' to 'column', findField() is going to return the current field number// because it's doing a case-independent search and it can't return another field number because SQLite prohibits duplicate field// names (no matter the case). So when this happens we just allow the renaming because there's no harm to be expected from it.auto foundField = sqlb::findField(m_table, item->text(column).toStdString());if(foundField != m_table.fields.end() && foundField-m_table.fields.begin() != static_cast<int>(index)){QMessageBox::warning(this, qApp->applicationName(), tr("There already is a field with that name. Please rename it first or choose a different ""name for this field."));// Reset the name to the old value but avoid calling this method again for that automatic changeui->treeWidget->blockSignals(true);item->setText(column, oldFieldName);ui->treeWidget->blockSignals(false);return;}// When editing an exiting table, check if any foreign keys would cause trouble in case this name is editedif(!m_bNewTable){sqlb::StringVector pk = m_table.primaryKey();const auto tables = pdb.schemata[curTable.schema()].equal_range("table");for(auto it=tables.first;it!=tables.second;++it){const sqlb::ObjectPtr& fkobj = it->second;auto fks = std::dynamic_pointer_cast<sqlb::Table>(fkobj)->constraints(sqlb::StringVector(), sqlb::Constraint::ForeignKeyConstraintType);for(const sqlb::ConstraintPtr& fkptr : fks){auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(fkptr);if(fk->table() == m_table.name()){if(contains(fk->columns(), field.name()) || contains(pk, field.name())){QMessageBox::warning(this, qApp->applicationName(), tr("This column is referenced in a foreign key in table %1 and thus ""its name cannot be changed.").arg(QString::fromStdString(fkobj->name())));// Reset the name to the old value but avoid calling this method again for that automatic changeui->treeWidget->blockSignals(true);item->setText(column, oldFieldName);ui->treeWidget->blockSignals(false);return;}}}}}field.setName(item->text(column).toStdString());m_table.renameKeyInAllConstraints(oldFieldName.toStdString(), item->text(column).toStdString());qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(item, kType))->setProperty("column", item->text(column));// Update the field name in the map of old column names to new column namesif(!m_bNewTable){for(const auto& it : trackColumns){if(trackColumns[it.first] == oldFieldName)trackColumns[it.first] = QString::fromStdString(field.name());}}// Update the constraints viewpopulateConstraints();} break;case kType:// see updateTypes() SLOTbreak;case kPrimaryKey:{// Check if there already is a primary keyif(m_table.constraint(sqlb::StringVector(), sqlb::Constraint::PrimaryKeyConstraintType)){// There already is a primary key for this table. So edit that one as there always can only be one primary key anyway.sqlb::StringVector& pk = m_table.primaryKeyRef();if(item->checkState(column) == Qt::Checked){pk.push_back(field.name());} else {pk.erase(std::remove(pk.begin(), pk.end(), field.name()), pk.end());// If this is now a primary key constraint without any columns, remove it entirelyif(pk.empty())m_table.removeConstraints({}, sqlb::Constraint::PrimaryKeyConstraintType);}} else if(item->checkState(column) == Qt::Checked) {// There is no primary key in the table yet. This means we need to add a default one.m_table.addConstraint(sqlb::ConstraintPtr(new sqlb::PrimaryKeyConstraint({field.name()})));}if(item->checkState(column) == Qt::Checked){// this will unset any other autoincrementfor(int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i){QTreeWidgetItem* tbitem = ui->treeWidget->topLevelItem(i);if(tbitem != item)tbitem->setCheckState(kAutoIncrement, Qt::Unchecked);}} else {item->setCheckState(kAutoIncrement, Qt::Unchecked);}// Update the constraints viewpopulateConstraints();}break;case kNotNull:{// When editing an existing table and trying to set a column to Not Null an extra check is neededif(!m_bNewTable && item->checkState(column) == Qt::Checked){// Because our renameColumn() function fails when setting a column to Not Null when it already contains some NULL values// we need to check for this case and cancel here. Maybe we can think of some way to modify the INSERT INTO ... SELECT statement// to at least replace all troublesome NULL values by the default valueSqliteTableModel m(pdb, this);m.setQuery(QString("SELECT COUNT(%1) FROM %2 WHERE coalesce(NULL,%3) IS NULL;").arg(QString::fromStdString(sqlb::joinStringVector(sqlb::escapeIdentifier(pdb.getObjectByName<sqlb::Table>(curTable)->rowidColumns()), ","))).arg(QString::fromStdString(curTable.toString())).arg(QString::fromStdString(sqlb::escapeIdentifier(field.name()))));if(!m.completeCache()){// If we couldn't load all data because the cancel button was clicked, just unset the checkbox again and stop.item->setCheckState(column, Qt::Unchecked);return;}if(m.data(m.index(0, 0)).toInt() > 0){// There is a NULL value, so print an error message, uncheck the combobox, and return hereQMessageBox::information(this, qApp->applicationName(), tr("There is at least one row with this field set to NULL. ""This makes it impossible to set this flag. Please change the table data first."));item->setCheckState(column, Qt::Unchecked);return;}}field.setNotNull(item->checkState(column) == Qt::Checked);}break;case kAutoIncrement:{bool ischecked = item->checkState(column) == Qt::Checked;if(ischecked){// First check if the contents of this column are all integers. If not this field cannot be set to AIif(!m_bNewTable){SqliteTableModel m(pdb, this);m.setQuery(QString("SELECT COUNT(*) FROM %1 WHERE %2 <> CAST(%3 AS INTEGER);").arg(QString::fromStdString(curTable.toString())).arg(QString::fromStdString(sqlb::escapeIdentifier(field.name()))).arg(QString::fromStdString(sqlb::escapeIdentifier(field.name()))));if(!m.completeCache()){// If we couldn't load all data because the cancel button was clicked, just unset the checkbox again and stop.item->setCheckState(column, Qt::Unchecked);return;}if(m.data(m.index(0, 0)).toInt() > 0){// There is a non-integer value, so print an error message, uncheck the combobox, and return hereQMessageBox::information(this, qApp->applicationName(), tr("There is at least one row with a non-integer value in this field. ""This makes it impossible to set the AI flag. Please change the table data first."));item->setCheckState(column, Qt::Unchecked);return;}}// Make sure the data type is set to integerQComboBox* comboType = qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(item, kType));comboType->setCurrentIndex(comboType->findText("INTEGER"));item->setCheckState(kPrimaryKey, Qt::Checked);// this will unset all other primary keys// there can't be more than one autoincrement pkfor(int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i){QTreeWidgetItem* tbitem = ui->treeWidget->topLevelItem(i);if(tbitem != item){tbitem->setCheckState(kAutoIncrement, Qt::Unchecked);tbitem->setCheckState(kPrimaryKey, Qt::Unchecked);}}}field.setAutoIncrement(ischecked);}break;case kUnique:{// When editing an existing table and trying to set a column to unique an extra check is neededif(!m_bNewTable && item->checkState(column) == Qt::Checked){// Because our renameColumn() function fails when setting a column to unique when it already contains the same valuesSqliteTableModel m(pdb, this);m.setQuery(QString("SELECT COUNT(%2) FROM %1;").arg(QString::fromStdString(curTable.toString())).arg(QString::fromStdString(sqlb::escapeIdentifier(field.name()))));if(!m.completeCache()){// If we couldn't load all data because the cancel button was clicked, just unset the checkbox again and stop.item->setCheckState(column, Qt::Unchecked);return;}int rowcount = m.data(m.index(0, 0)).toInt();m.setQuery(QString("SELECT COUNT(DISTINCT %2) FROM %1;").arg(QString::fromStdString(curTable.toString())).arg(QString::fromStdString(sqlb::escapeIdentifier(field.name()))));if(!m.completeCache()){// If we couldn't load all data because the cancel button was clicked, just unset the checkbox again and stop.item->setCheckState(column, Qt::Unchecked);return;}int uniquecount = m.data(m.index(0, 0)).toInt();if(rowcount != uniquecount){// There is a NULL value, so print an error message, uncheck the combobox, and return hereQMessageBox::information(this, qApp->applicationName(), tr("Column '%1' has duplicate data.\n").arg(QString::fromStdString(field.name()))+ tr("This makes it impossible to enable the 'Unique' flag. Please remove the duplicate data, which will allow the 'Unique' flag to then be enabled."));item->setCheckState(column, Qt::Unchecked);return;}}field.setUnique(item->checkState(column) == Qt::Checked);}break;case kDefault:{QString new_value = item->text(column);// If the default value isn't a SQL keyword perform an extra check: If it isn't numeric but doesn't start and end with quotes,// add the quotesif(new_value.size() && new_value.compare("null", Qt::CaseInsensitive) &&new_value.compare("current_time", Qt::CaseInsensitive) &&new_value.compare("current_date", Qt::CaseInsensitive) &&new_value.compare("current_timestamp", Qt::CaseInsensitive)){QChar first_char = new_value.trimmed().at(0);if(!((first_char == '\'' || first_char == '"') && new_value.trimmed().endsWith(first_char))){bool is_numeric;new_value.toDouble(&is_numeric);if(!is_numeric){if(new_value.trimmed().startsWith("=(") && new_value.trimmed().endsWith(')')){new_value = new_value.trimmed().mid(1); // Leave the brackets as they are needed for a valid SQL expression} else {new_value = QString("'%1'").arg(new_value.replace("'", "''"));item->setText(column, new_value);}}}}field.setDefaultValue(new_value.toStdString());}break;case kCheck:field.setCheck(item->text(column).toStdString());break;case kForeignKey:// handled in delegatebreak;}}checkInput();}void EditTableDialog::constraintItemChanged(QTableWidgetItem* item){// Find modified constraintsqlb::ConstraintPtr constraint = ui->tableConstraints->item(item->row(), kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();// Which column has been modified?switch(item->column()){case kConstraintName:constraint->setName(item->text().toStdString());break;};// Update SQLui->tableConstraints->item(item->row(), kConstraintSql)->setText(QString::fromStdString(constraint->toSql()));checkInput();}void EditTableDialog::addField(){QTreeWidgetItem *tbitem = new QTreeWidgetItem(ui->treeWidget);tbitem->setFlags(tbitem->flags() | Qt::ItemIsEditable);// Find an unused name for the field by starting with 'Fieldx' where x is the number of fields + 1.// If this name happens to exist already, increase x by one until we find an unused name.{int field_number = ui->treeWidget->topLevelItemCount();std::string field_name;do{field_name = "Field" + std::to_string(field_number);field_number++;} while(sqlb::findField(m_table, field_name) != m_table.fields.end());tbitem->setText(kName, QString::fromStdString(field_name));}QComboBox* typeBox = new QComboBox(ui->treeWidget);typeBox->setProperty("column", tbitem->text(kName));typeBox->setEditable(true);typeBox->addItems(DBBrowserDB::Datatypes);int defaultFieldTypeIndex = Settings::getValue("db", "defaultfieldtype").toInt();if (defaultFieldTypeIndex < DBBrowserDB::Datatypes.count()){typeBox->setCurrentIndex(defaultFieldTypeIndex);}ui->treeWidget->setItemWidget(tbitem, kType, typeBox);typeBox->installEventFilter(this);connect(typeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes()));tbitem->setCheckState(kNotNull, Qt::Unchecked);tbitem->setCheckState(kPrimaryKey, Qt::Unchecked);tbitem->setCheckState(kAutoIncrement, Qt::Unchecked);tbitem->setCheckState(kUnique, Qt::Unchecked);ui->treeWidget->addTopLevelItem(tbitem);ui->treeWidget->scrollToBottom();ui->treeWidget->editItem(tbitem, 0);// add field to table objectm_table.fields.emplace_back(tbitem->text(kName).toStdString(), typeBox->currentText().toStdString());// Add the new column to the list of tracked columns to indicate it has been addedif(!m_bNewTable)trackColumns.insert({QString(), tbitem->text(kName)});checkInput();}void EditTableDialog::removeField(){// Is there any item selected to delete?if(!ui->treeWidget->currentItem())return;// If we are editing an existing table, ask the user for confirmationif(!m_bNewTable){QString msg = tr("Are you sure you want to delete the field '%1'?\nAll data currently stored in this field will be lost.").arg(ui->treeWidget->currentItem()->text(0));if(QMessageBox::warning(this, QApplication::applicationName(), msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No)return;// Update the map of tracked columns to indicate the column is deletedQString name = ui->treeWidget->currentItem()->text(0);for(const auto& it : trackColumns){if(trackColumns[it.first] == name)trackColumns[it.first] = QString();}}// Just delete that item. At this point there is no DB table to edit or data to be lost anywaym_table.fields.erase(m_table.fields.begin() + ui->treeWidget->indexOfTopLevelItem(ui->treeWidget->currentItem()));m_table.removeKeyFromAllConstraints(ui->treeWidget->currentItem()->text(kName).toStdString());delete ui->treeWidget->currentItem();// Update the constraints viewpopulateConstraints();checkInput();}void EditTableDialog::fieldSelectionChanged(){bool hasSelection = ui->treeWidget->selectionModel()->hasSelection();// Enable the remove and the move up/down buttons if a field is selected, disable it otherwiseui->removeFieldButton->setEnabled(hasSelection);ui->buttonMoveUp->setEnabled(hasSelection);ui->buttonMoveDown->setEnabled(hasSelection);// If the selected line is the first one disable the move up button, it it's the last one disable the move down buttonif(hasSelection){ui->buttonMoveUp->setEnabled(ui->treeWidget->selectionModel()->currentIndex().row() != 0);ui->buttonMoveDown->setEnabled(ui->treeWidget->selectionModel()->currentIndex().row() != ui->treeWidget->topLevelItemCount() - 1);}}void EditTableDialog::moveUp(){moveCurrentField(false);}void EditTableDialog::moveDown(){moveCurrentField(true);}void EditTableDialog::moveCurrentField(bool down){int currentRow = ui->treeWidget->currentIndex().row();int newRow = currentRow + (down ? 1 : -1);// Save the combobox first by making a copyQComboBox* oldCombo = qobject_cast<QComboBox*>(ui->treeWidget->itemWidget(ui->treeWidget->topLevelItem(currentRow), kType));QComboBox* newCombo = new QComboBox(ui->treeWidget);newCombo->setProperty("column", oldCombo->property("column"));newCombo->installEventFilter(this);connect(newCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypes()));newCombo->setEditable(true);for(int i=0; i < oldCombo->count(); ++i)newCombo->addItem(oldCombo->itemText(i));newCombo->setCurrentIndex(oldCombo->currentIndex());// Now, just remove the item and insert it at it's new position, then restore the comboboxQTreeWidgetItem* item = ui->treeWidget->takeTopLevelItem(currentRow);ui->treeWidget->insertTopLevelItem(newRow, item);ui->treeWidget->setItemWidget(item, kType, newCombo);// Select the old item at its new positionui->treeWidget->setCurrentIndex(ui->treeWidget->currentIndex().sibling(newRow, 0));// Finally update the table SQLstd::swap(m_table.fields[static_cast<size_t>(newRow)], m_table.fields[static_cast<size_t>(currentRow)]);// Update the SQL previewupdateSqlText();}void EditTableDialog::setWithoutRowid(bool without_rowid){if(without_rowid){// Before setting the without rowid flag, first perform a check to see if the table meets all the required criteria for without rowid tablesauto pks = m_table.primaryKey();for(const auto& pk_name : pks){auto pk = sqlb::findField(m_table, pk_name);if(pk == m_table.fields.end() || pk->autoIncrement()){QMessageBox::information(this, QApplication::applicationName(),tr("Please add a field which meets the following criteria before setting the without rowid flag:\n"" - Primary key flag set\n"" - Auto increment disabled"));// Reset checkbox state to unchecked. Block any signals while doing this in order to avoid an extra call to// this function being triggered.ui->checkWithoutRowid->blockSignals(true);ui->checkWithoutRowid->setChecked(false);ui->checkWithoutRowid->blockSignals(false);return;}}// If it does, set the without rowid flag of the tablem_table.setWithoutRowidTable(true);} else {// If the without rowid flag is unset no further checks are required. Just unset the without rowid flagm_table.setWithoutRowidTable(false);}// Update the SQL previewupdateSqlText();}void EditTableDialog::changeSchema(const QString& /*schema*/){// Update the SQL previewupdateSqlText();}void EditTableDialog::removeConstraint(){// Is there any item selected to delete?if(!ui->tableConstraints->currentItem())return;// Find constraint to deleteint row = ui->tableConstraints->currentRow();sqlb::ConstraintPtr constraint = ui->tableConstraints->item(row, kConstraintColumns)->data(Qt::UserRole).value<sqlb::ConstraintPtr>();// Remove the constraint. If there is more than one constraint with this combination of columns and constraint type, only delete the first one.m_table.removeConstraint(constraint);ui->tableConstraints->removeRow(ui->tableConstraints->currentRow());// Update SQL and viewupdateSqlText();populateFields();}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。