mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-20 19:11:39 -06:00
Foreign key combo-box in table browser This adds a combo-box for the editor delegate when the associated column has a foreign key constraint. In this way the user can choose the key value based from a combo-box. To minimise possible performance problems, the model only loads the foreign key field, instead of the whole related table. Except for not-null columns, a NULL value is given as possible value in the foreign-key combo-box. See issue #614
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
#include "FilterTableHeader.h"
|
||||
#include "sql/sqlitetypes.h"
|
||||
#include "Settings.h"
|
||||
#include "sqlitedb.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
@@ -20,6 +21,7 @@
|
||||
#include <QPrintPreviewDialog>
|
||||
#include <QTextDocument>
|
||||
#include <QCompleter>
|
||||
#include <QComboBox>
|
||||
|
||||
#include <limits>
|
||||
|
||||
@@ -126,44 +128,93 @@ ExtendedTableWidgetEditorDelegate::ExtendedTableWidgetEditorDelegate(QObject* pa
|
||||
|
||||
QWidget* ExtendedTableWidgetEditorDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& index) const
|
||||
{
|
||||
QLineEdit* editor = new QLineEdit(parent);
|
||||
// If the row count is not greater than the complete threshold setting, set a completer of values based on current values in the column.
|
||||
if (index.model()->rowCount() <= Settings::getValue("databrowser", "complete_threshold").toInt()) {
|
||||
QCompleter* completer = new QCompleter(editor);
|
||||
UniqueFilterModel* completerFilter = new UniqueFilterModel(completer);
|
||||
// Provide a filter for the source model, so only unique and non-empty values are accepted.
|
||||
completerFilter->setSourceModel(const_cast<QAbstractItemModel*>(index.model()));
|
||||
completerFilter->setFilterKeyColumn(index.column());
|
||||
completer->setModel(completerFilter);
|
||||
// Complete on this column, using a popup and case-insensitively.
|
||||
completer->setCompletionColumn(index.column());
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
editor->setCompleter(completer);
|
||||
|
||||
SqliteTableModel* m = qobject_cast<SqliteTableModel*>(const_cast<QAbstractItemModel*>(index.model()));
|
||||
sqlb::ForeignKeyClause fk = m->getForeignKeyClause(index.column()-1);
|
||||
|
||||
if(fk.isSet()) {
|
||||
|
||||
sqlb::ObjectIdentifier foreignTable = sqlb::ObjectIdentifier(m->currentTableName().schema(), fk.table());
|
||||
|
||||
QString column;
|
||||
// If no column name is set, assume the primary key is meant
|
||||
if(fk.columns().isEmpty()) {
|
||||
sqlb::TablePtr obj = m->db().getObjectByName<sqlb::Table>(foreignTable);
|
||||
column = obj->findPk()->name();
|
||||
} else
|
||||
column = fk.columns().at(0);
|
||||
|
||||
sqlb::TablePtr currentTable = m->db().getObjectByName<sqlb::Table>(m->currentTableName());
|
||||
QString query = QString("SELECT %1 FROM %2").arg(sqlb::escapeIdentifier(column)).arg(foreignTable.toString());
|
||||
|
||||
// if the current column of the current table does NOT have not-null constraint,
|
||||
// the NULL is united to the query to get the possible values in the combo-box.
|
||||
if (!currentTable->fields.at(index.column()-1).notnull())
|
||||
query.append (" UNION SELECT NULL");
|
||||
|
||||
SqliteTableModel* fkModel = new SqliteTableModel(m->db(), parent, m->chunkSize(), m->encoding());
|
||||
fkModel->setQuery(query);
|
||||
|
||||
QComboBox* combo = new QComboBox(parent);
|
||||
|
||||
// Complete cache so it is ready when setEditorData is invoked.
|
||||
fkModel->completeCache();
|
||||
combo->setModel(fkModel);
|
||||
|
||||
return combo;
|
||||
} else {
|
||||
|
||||
QLineEdit* editor = new QLineEdit(parent);
|
||||
// If the row count is not greater than the complete threshold setting, set a completer of values based on current values in the column.
|
||||
if (index.model()->rowCount() <= Settings::getValue("databrowser", "complete_threshold").toInt()) {
|
||||
QCompleter* completer = new QCompleter(editor);
|
||||
UniqueFilterModel* completerFilter = new UniqueFilterModel(completer);
|
||||
// Provide a filter for the source model, so only unique and non-empty values are accepted.
|
||||
completerFilter->setSourceModel(const_cast<QAbstractItemModel*>(index.model()));
|
||||
completerFilter->setFilterKeyColumn(index.column());
|
||||
completer->setModel(completerFilter);
|
||||
// Complete on this column, using a popup and case-insensitively.
|
||||
completer->setCompletionColumn(index.column());
|
||||
completer->setCompletionMode(QCompleter::PopupCompletion);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
editor->setCompleter(completer);
|
||||
}
|
||||
// Set the maximum length to the highest possible value instead of the default 32768.
|
||||
editor->setMaxLength(std::numeric_limits<int>::max());
|
||||
return editor;
|
||||
}
|
||||
// Set the maximum length to the highest possible value instead of the default 32768.
|
||||
editor->setMaxLength(std::numeric_limits<int>::max());
|
||||
return editor;
|
||||
}
|
||||
|
||||
void ExtendedTableWidgetEditorDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
|
||||
{
|
||||
QLineEdit* lineedit = static_cast<QLineEdit*>(editor);
|
||||
|
||||
// Set the data for the line editor
|
||||
QLineEdit* lineedit = dynamic_cast<QLineEdit*>(editor);
|
||||
// Set the data for the editor
|
||||
QString data = index.data(Qt::EditRole).toString();
|
||||
lineedit->setText(data);
|
||||
|
||||
// Put the editor in read only mode if the actual data is larger than the maximum length to avoid accidental truncation of the data
|
||||
lineedit->setReadOnly(data.size() > lineedit->maxLength());
|
||||
if(!lineedit) {
|
||||
QComboBox* combo = static_cast<QComboBox*>(editor);
|
||||
int comboIndex = combo->findText(data);
|
||||
if (comboIndex >= 0)
|
||||
// if it is valid, adjust the combobox
|
||||
combo->setCurrentIndex(comboIndex);
|
||||
} else {
|
||||
lineedit->setText(data);
|
||||
|
||||
// Put the editor in read only mode if the actual data is larger than the maximum length to avoid accidental truncation of the data
|
||||
lineedit->setReadOnly(data.size() > lineedit->maxLength());
|
||||
}
|
||||
}
|
||||
|
||||
void ExtendedTableWidgetEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
|
||||
{
|
||||
// Only apply the data back to the model if the editor is not in read only mode to avoid accidental truncation of the data
|
||||
QLineEdit* lineedit = static_cast<QLineEdit*>(editor);
|
||||
if(!lineedit->isReadOnly())
|
||||
model->setData(index, lineedit->text());
|
||||
QLineEdit* lineedit = dynamic_cast<QLineEdit*>(editor);
|
||||
if(!lineedit) {
|
||||
QComboBox* combo = static_cast<QComboBox*>(editor);
|
||||
model->setData(index, combo->currentData(Qt::EditRole), Qt::EditRole);
|
||||
} else
|
||||
if(!lineedit->isReadOnly())
|
||||
model->setData(index, lineedit->text());
|
||||
}
|
||||
|
||||
void ExtendedTableWidgetEditorDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const
|
||||
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
void setQuery(const sqlb::Query& query);
|
||||
|
||||
void setChunkSize(size_t chunksize);
|
||||
size_t chunkSize() { return m_chunkSize; };
|
||||
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
|
||||
sqlb::ObjectIdentifier currentTableName() const { return m_query.table(); }
|
||||
|
||||
@@ -114,6 +115,8 @@ public:
|
||||
void addCondFormat(int column, const CondFormat& condFormat);
|
||||
void setCondFormats(int column, const QVector<CondFormat>& condFormats);
|
||||
|
||||
DBBrowserDB& db() { return m_db; };
|
||||
|
||||
public slots:
|
||||
void updateFilter(int column, const QString& value);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user