1 #include "sqlitedb.h"
2 #include "ForeignKeyEditorDelegate.h"
3
4 #include <QComboBox>
5 #include <QLineEdit>
6 #include <QPushButton>
7 #include <QHBoxLayout>
8
9 class ForeignKeyEditor : public QWidget
10 {
11 Q_OBJECT
12
13 public:
ForeignKeyEditor(QWidget * parent=nullptr)14 explicit ForeignKeyEditor(QWidget* parent = nullptr)
15 : QWidget(parent)
16 , tablesComboBox(new QComboBox(this))
17 , idsComboBox(new QComboBox(this))
18 , clauseEdit(new QLineEdit(this))
19 , m_btnReset(new QPushButton(tr("&Reset"), this))
20 {
21 idsComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
22 clauseEdit->setPlaceholderText(tr("Foreign key clauses (ON UPDATE, ON DELETE etc.)"));
23
24 QHBoxLayout* layout = new QHBoxLayout(this);
25 layout->addWidget(tablesComboBox);
26 layout->addWidget(idsComboBox);
27 layout->addWidget(clauseEdit);
28 layout->addWidget(m_btnReset);
29 layout->setSpacing(0);
30 layout->setMargin(0);
31 setLayout(layout);
32
33 connect(m_btnReset, &QPushButton::clicked, [&]
34 {
35 tablesComboBox->setCurrentIndex(-1);
36 idsComboBox->setCurrentIndex(-1);
37 clauseEdit->clear();
38 });
39
40 connect(tablesComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
41 [=](int index)
42 {
43 // reset ids combo box
44 idsComboBox->setCurrentIndex(-1);
45
46 // disable clauses editor if none of tables is selected
47 bool enableClausesEditor = (index!= -1);
48 clauseEdit->setEnabled(enableClausesEditor);
49 });
50 }
51
getSql() const52 QString getSql() const
53 {
54 if (tablesComboBox->currentText().isEmpty())
55 return QString();
56
57 const QString table = sqlb::escapeIdentifier(tablesComboBox->currentText());
58 const QString clauses = clauseEdit->text();
59
60 QString id = idsComboBox->currentText();
61 if (!id.isEmpty())
62 id = QString("(%1)").arg(sqlb::escapeIdentifier(id));
63
64 return QString("%1%2 %3").arg(
65 table,
66 id,
67 clauses)
68 .trimmed();
69 }
70
71 QComboBox* tablesComboBox;
72 QComboBox* idsComboBox;
73 QLineEdit* clauseEdit; // for ON CASCADE and such
74
75 private:
76 QPushButton* m_btnReset;
77 };
78
ForeignKeyEditorDelegate(const DBBrowserDB & db,sqlb::Table & table,QObject * parent)79 ForeignKeyEditorDelegate::ForeignKeyEditorDelegate(const DBBrowserDB& db, sqlb::Table& table, QObject* parent)
80 : QStyledItemDelegate(parent)
81 , m_db(db)
82 , m_table(table)
83 {
84 for(const auto& it : m_db.schemata)
85 {
86 for(const auto& jt : it.second)
87 {
88 // Don't insert the current table into the list. The name and fields of the current table are always taken from the m_table reference
89 if(jt.second->type() == sqlb::Object::Types::Table && jt.second->name() != m_table.name())
90 m_tablesIds.insert({jt.second->name(), std::dynamic_pointer_cast<sqlb::Table>(jt.second)->fieldNames()});
91 }
92 }
93 }
94
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const95 QWidget* ForeignKeyEditorDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
96 {
97 Q_UNUSED(option)
98 Q_UNUSED(index)
99
100 ForeignKeyEditor* editor = new ForeignKeyEditor(parent);
101 editor->setAutoFillBackground(true);
102
103 connect(editor->tablesComboBox, static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged),
104 [=](const QString& tableName)
105 {
106 QComboBox* box = editor->idsComboBox;
107 box->clear();
108 box->addItem(QString()); // for those heroes who don't like to specify key explicitly
109
110 // For recursive foreign keys get the field list from the m_table reference. For other foreign keys from the prepared field lists.
111 if(tableName.toStdString() == m_table.name())
112 {
113 for(const auto& n : m_table.fieldNames())
114 box->addItem(QString::fromStdString(n));
115 } else {
116 for(const auto& n : m_tablesIds[tableName.toStdString()])
117 box->addItem(QString::fromStdString(n));
118 }
119
120
121 box->setCurrentIndex(0);
122 });
123
124 editor->tablesComboBox->clear();
125 for(const auto& i : m_tablesIds)
126 editor->tablesComboBox->addItem(QString::fromStdString(i.first));
127 editor->tablesComboBox->addItem(QString::fromStdString(m_table.name())); // For recursive foreign keys
128
129 return editor;
130 }
131
setEditorData(QWidget * editor,const QModelIndex & index) const132 void ForeignKeyEditorDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
133 {
134 ForeignKeyEditor* fkEditor = static_cast<ForeignKeyEditor*>(editor);
135
136 size_t column = static_cast<size_t>(index.row()); // weird? I know right
137 const sqlb::Field& field = m_table.fields.at(column);
138 auto fk = std::dynamic_pointer_cast<sqlb::ForeignKeyClause>(m_table.constraint({field.name()}, sqlb::Constraint::ForeignKeyConstraintType));
139 if (fk) {
140 fkEditor->tablesComboBox->setCurrentText(QString::fromStdString(fk->table()));
141 fkEditor->clauseEdit->setText(QString::fromStdString(fk->constraint()));
142 if (!fk->columns().empty())
143 fkEditor->idsComboBox->setCurrentText(QString::fromStdString(fk->columns().front()));
144 } else {
145 fkEditor->tablesComboBox->setCurrentIndex(-1);
146 }
147 }
148
setModelData(QWidget * editor,QAbstractItemModel * model,const QModelIndex & index) const149 void ForeignKeyEditorDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
150 {
151 ForeignKeyEditor* fkEditor = static_cast<ForeignKeyEditor*>(editor);
152 QString sql = fkEditor->getSql();
153
154 size_t column = static_cast<size_t>(index.row());
155 const sqlb::Field& field = m_table.fields.at(column);
156 if (sql.isEmpty()) {
157 // Remove the foreign key
158 m_table.removeConstraints({field.name()}, sqlb::Constraint::ConstraintTypes::ForeignKeyConstraintType);
159 } else {
160 // Set the foreign key
161 sqlb::ForeignKeyClause* fk = new sqlb::ForeignKeyClause;
162
163 const QString table = fkEditor->tablesComboBox->currentText();
164 const std::string id = fkEditor->idsComboBox->currentText().toStdString();
165 const QString clause = fkEditor->clauseEdit->text();
166
167 fk->setTable(table.toStdString());
168 fk->setColumnList({ field.name() });
169
170 if (!id.empty())
171 fk->setColumns({id});
172
173 if (!clause.trimmed().isEmpty()) {
174 fk->setConstraint(clause.toStdString());
175 }
176
177 m_table.setConstraint(sqlb::ConstraintPtr(fk));
178 }
179
180 model->setData(index, sql);
181 }
182
updateEditorGeometry(QWidget * editor,const QStyleOptionViewItem & option,const QModelIndex & index) const183 void ForeignKeyEditorDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const
184 {
185 Q_UNUSED(index)
186
187 editor->setGeometry(option.rect);
188 }
189
190 #include "ForeignKeyEditorDelegate.moc"
191