Skip to content

Commit f33943f

Browse files
committed
Improvements for drag and drop of items from the DB Schema dock to editor
Two new options editable from a new context menu of the dock: - Drag & Drop Qualified Names: add table name to fields and schema name to other objects (except for "main" schema) - Drag & Drop Enquoted Names: whether to surround the identifiers by the configured quoting characters for identifiers. Support for dragging & dropping of attached databases names. Add "." as separator for multiple dropped objects other than fields (since they are not usually used in SQL as a list). This allows to compose qualified names by dropping the parent and the child items together. This is only generally useful when the "Qualified Names" option is disabled. See related issue #1433
1 parent 04bc7da commit f33943f

File tree

6 files changed

+127
-12
lines changed

6 files changed

+127
-12
lines changed

src/DbStructureModel.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
DbStructureModel::DbStructureModel(DBBrowserDB& db, QObject* parent)
1212
: QAbstractItemModel(parent),
13-
m_db(db)
13+
m_db(db),
14+
m_dropQualifiedNames(false),
15+
m_dropEnquotedNames(false)
1416
{
1517
// Create root item and use its columns to store the header strings
1618
QStringList header;
@@ -41,7 +43,7 @@ QVariant DbStructureModel::data(const QModelIndex& index, int role) const
4143
switch(role)
4244
{
4345
case Qt::DisplayRole:
44-
// For the display role and the browsabled branch of the tree we want to show the column name including the schema name if necessary (i.e.
46+
// For the display role and the browsable branch of the tree we want to show the column name including the schema name if necessary (i.e.
4547
// for schemata != "main"). For the normal structure branch of the tree we don't want to add the schema name because it's already obvious from
4648
// the position of the item in the tree.
4749
if(index.column() == ColumnName && item->parent() == browsablesRootItem)
@@ -66,9 +68,10 @@ Qt::ItemFlags DbStructureModel::flags(const QModelIndex &index) const
6668
// All items are enabled and selectable
6769
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
6870

69-
// Only enable dragging for entire table objects and for fields (composition in SQL text editor)
71+
// Only enable dragging for SQLite objects and for fields (composition in SQL text editor).
72+
// Grouping nodes have no type.
7073
QString type = data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString();
71-
if(type == "table" || type == "field" || type == "view" || type == "index" || type == "trigger")
74+
if(!type.isEmpty())
7275
flags |= Qt::ItemIsDragEnabled;
7376

7477
return flags;
@@ -143,8 +146,8 @@ void DbStructureModel::reloadData()
143146
}
144147

145148
// Create the nodes for browsables and for tables, indices, views and triggers. The idea here is to basically have two trees in one model:
146-
// In the root node there are two nodes: 'browsables' and 'all'. The first node contains a list of a all browsable objects, i.e. views and tables.
147-
// The seconds node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type.
149+
// In the root node there are two nodes: 'browsables' and 'all'. The first node contains a list of all browsable objects, i.e. views and tables.
150+
// The second node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type.
148151
// This way we only have to have and only have to update one model and can use it in all sorts of places, just by setting a different root node.
149152
browsablesRootItem = new QTreeWidgetItem(rootItem);
150153
browsablesRootItem->setIcon(ColumnName, QIcon(QString(":/icons/view")));
@@ -154,6 +157,7 @@ void DbStructureModel::reloadData()
154157
QTreeWidgetItem* itemAll = new QTreeWidgetItem(rootItem);
155158
itemAll->setIcon(ColumnName, QIcon(QString(":/icons/database")));
156159
itemAll->setText(ColumnName, tr("All"));
160+
itemAll->setText(ColumnObjectType, "database");
157161
buildTree(itemAll, "main");
158162

159163
// Add the temporary database as a node if it isn't empty. Make sure it's always second if it exists.
@@ -162,6 +166,7 @@ void DbStructureModel::reloadData()
162166
QTreeWidgetItem* itemTemp = new QTreeWidgetItem(itemAll);
163167
itemTemp->setIcon(ColumnName, QIcon(QString(":/icons/database")));
164168
itemTemp->setText(ColumnName, tr("Temporary"));
169+
itemTemp->setText(ColumnObjectType, "database");
165170
buildTree(itemTemp, "temp");
166171
}
167172

@@ -174,6 +179,7 @@ void DbStructureModel::reloadData()
174179
QTreeWidgetItem* itemSchema = new QTreeWidgetItem(itemAll);
175180
itemSchema->setIcon(ColumnName, QIcon(QString(":/icons/database")));
176181
itemSchema->setText(ColumnName, it.key());
182+
itemSchema->setText(ColumnObjectType, "database");
177183
buildTree(itemSchema, it.key());
178184
}
179185
}
@@ -198,14 +204,26 @@ QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const
198204
// Loop through selected indices
199205
for(const QModelIndex& index : indices)
200206
{
207+
// Get the item the index points at
208+
QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());
209+
201210
// Only export data for valid indices and only once per row (SQL column or Name column).
202-
// For names, export an escaped identifier of the item for statement composition in SQL editor.
203-
// Commas are included for a list of identifiers.
204211
if(index.isValid()) {
205212
QString objectType = data(index.sibling(index.row(), ColumnObjectType), Qt::DisplayRole).toString();
206213

214+
// For names, export an escaped identifier of the item for statement composition in SQL editor.
215+
// Commas are included for a list of fields. A dot is added after other items, in order to allow composing
216+
// a fully qualified name by selecting together and dropping a parent item and a child (e.g. select with
217+
// control an attached database and a table, and drag and drop them to get "schema1"."table1".)
218+
// Note that this only makes sense when the "Drop Qualified Names" option is not set.
207219
if(index.column() == ColumnName)
208-
namesData.append(sqlb::escapeIdentifier(data(index, Qt::DisplayRole).toString()) + ", ");
220+
if(objectType == "field")
221+
namesData.append(getNameForDropping(item->parent()->text(ColumnName), item->text(ColumnName)) + ", ");
222+
else if(objectType != "")
223+
if(item->text(ColumnSchema) == "main")
224+
namesData.append(getNameForDropping("", item->text(ColumnName)) + ".");
225+
else
226+
namesData.append(getNameForDropping(item->text(ColumnSchema), item->text(ColumnName)) + ".");
209227

210228
if(objectType != "field" && index.column() == ColumnSQL)
211229
{
@@ -243,8 +261,12 @@ QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const
243261
mime->setProperty("db_file", m_db.currentFile()); // Also save the file name to avoid dropping an object on the same database as it comes from
244262
// When we have both SQL and Names data (probable row selection mode) we give precedence to the SQL data
245263
if (sqlData.length() == 0 && namesData.length() > 0) {
246-
// Remove last ", "
247-
namesData.chop(2);
264+
// Remove last ", " or "."
265+
if (namesData.endsWith(", "))
266+
namesData.chop(2);
267+
else if (namesData.endsWith("."))
268+
namesData.chop(1);
269+
248270
mime->setData("text/plain", namesData);
249271
} else
250272
mime->setData("text/plain", sqlData);
@@ -362,3 +384,14 @@ QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::
362384

363385
return item;
364386
}
387+
388+
QString DbStructureModel::getNameForDropping(const QString& parentName, const QString& itemName) const
389+
{
390+
QString name;
391+
if (m_dropQualifiedNames && parentName != "")
392+
name = m_dropEnquotedNames ? sqlb::escapeIdentifier(parentName) + "." : parentName + ".";
393+
394+
name += m_dropEnquotedNames ? sqlb::escapeIdentifier(itemName) : itemName;
395+
396+
return name;
397+
}

src/DbStructureModel.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class DbStructureModel : public QAbstractItemModel
3838

3939
public slots:
4040
void reloadData();
41+
void setDropQualifiedNames(bool value) { m_dropQualifiedNames = value; };
42+
void setDropEnquotedNames(bool value) { m_dropEnquotedNames = value; };
4143

4244
signals:
4345
void structureUpdated();
@@ -46,9 +48,12 @@ public slots:
4648
DBBrowserDB& m_db;
4749
QTreeWidgetItem* rootItem;
4850
QTreeWidgetItem* browsablesRootItem;
51+
bool m_dropQualifiedNames;
52+
bool m_dropEnquotedNames;
4953

5054
void buildTree(QTreeWidgetItem* parent, const QString& schema);
5155
QTreeWidgetItem* addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object, const QString& schema);
56+
QString getNameForDropping(const QString& parentName, const QString& itemName) const;
5257
};
5358

5459
#endif

src/MainWindow.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ void MainWindow::init()
176176
popupTableMenu->addAction(ui->actionEditCopyCreateStatement);
177177
popupTableMenu->addAction(ui->actionExportCsvPopup);
178178

179+
popupSchemaDockMenu = new QMenu(this);
180+
popupSchemaDockMenu->addAction(ui->actionDropQualifiedCheck);
181+
popupSchemaDockMenu->addAction(ui->actionEnquoteNamesCheck);
182+
179183
popupOpenDbMenu = new QMenu(this);
180184
popupOpenDbMenu->addAction(ui->fileOpenAction);
181185
popupOpenDbMenu->addAction(ui->fileOpenReadOnlyAction);
@@ -303,6 +307,10 @@ void MainWindow::init()
303307
connect(m_remoteDb, &RemoteDatabase::gotCurrentVersion, this, &MainWindow::checkNewVersion);
304308
connect(m_browseTableModel, &SqliteTableModel::finishedFetch, this, &MainWindow::setRecordsetLabel);
305309
connect(ui->dataTable, &ExtendedTableWidget::selectedRowsToBeDeleted, this, &MainWindow::deleteRecord);
310+
connect(ui->actionDropQualifiedCheck, &QAction::toggled, dbStructureModel, &DbStructureModel::setDropQualifiedNames);
311+
connect(ui->actionEnquoteNamesCheck, &QAction::toggled, dbStructureModel, &DbStructureModel::setDropEnquotedNames);
312+
ui->actionDropQualifiedCheck->setChecked(Settings::getValue("SchemaDock", "dropQualifiedNames").toBool());
313+
ui->actionEnquoteNamesCheck->setChecked(Settings::getValue("SchemaDock", "dropEnquotedNames").toBool());
306314

307315
connect(m_browseTableModel, &SqliteTableModel::finishedFetch, [this](){
308316
auto & settings = browseTableSettings[currentlyBrowsedTableName()];
@@ -715,6 +723,9 @@ void MainWindow::closeEvent( QCloseEvent* event )
715723
Settings::setValue("MainWindow", "geometry", saveGeometry());
716724
Settings::setValue("MainWindow", "windowState", saveState());
717725
Settings::setValue("SQLLogDock", "Log", ui->comboLogSubmittedBy->currentText());
726+
Settings::setValue("SchemaDock", "dropQualifiedNames", ui->actionDropQualifiedCheck->isChecked());
727+
Settings::setValue("SchemaDock", "dropEnquotedNames", ui->actionEnquoteNamesCheck->isChecked());
728+
718729
QMainWindow::closeEvent(event);
719730
} else {
720731
event->ignore();
@@ -1652,6 +1663,12 @@ void MainWindow::createTreeContextMenu(const QPoint &qPoint)
16521663
popupTableMenu->exec(ui->dbTreeWidget->mapToGlobal(qPoint));
16531664
}
16541665

1666+
//** DB Schema Dock Context Menu
1667+
void MainWindow::createSchemaDockContextMenu(const QPoint &qPoint)
1668+
{
1669+
popupSchemaDockMenu->exec(ui->treeSchemaDock->mapToGlobal(qPoint));
1670+
}
1671+
16551672
void MainWindow::changeTreeSelection()
16561673
{
16571674
// Just assume first that something's selected that can not be edited at all

src/MainWindow.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class MainWindow : public QMainWindow
145145
SqliteTableModel* m_currentTabTableModel;
146146

147147
QMenu* popupTableMenu;
148+
QMenu* popupSchemaDockMenu;
148149
QMenu* recentFilesMenu;
149150
QMenu* popupOpenDbMenu;
150151
QMenu* popupNewRecordMenu;
@@ -214,6 +215,7 @@ public slots:
214215

215216
private slots:
216217
void createTreeContextMenu(const QPoint & qPoint);
218+
void createSchemaDockContextMenu(const QPoint & qPoint);
217219
void changeTreeSelection();
218220
void fileNew();
219221
void fileNewInMemoryDatabase();

src/MainWindow.ui

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,9 +1207,12 @@ You can drag SQL statements from an object row and drop them into other applicat
12071207
<layout class="QVBoxLayout" name="verticalLayout_9">
12081208
<item>
12091209
<widget class="QTreeView" name="treeSchemaDock">
1210+
<property name="contextMenuPolicy">
1211+
<enum>Qt::CustomContextMenu</enum>
1212+
</property>
12101213
<property name="whatsThis">
12111214
<string>This is the structure of the opened database.
1212-
You can drag multiple object names from the Name column and drop them into the SQL editor.
1215+
You can drag multiple object names from the Name column and drop them into the SQL editor and you can adjust the properties of the dropped names using the context menu. This would help you in composing SQL statements.
12131216
You can drag SQL statements from the Schema column and drop them into the SQL editor or into other applications.
12141217
</string>
12151218
</property>
@@ -2203,6 +2206,34 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
22032206
<string>New In-&amp;Memory Database</string>
22042207
</property>
22052208
</action>
2209+
<action name="actionDropQualifiedCheck">
2210+
<property name="checkable">
2211+
<bool>true</bool>
2212+
</property>
2213+
<property name="text">
2214+
<string>Drag &amp;&amp; Drop Qualified Names</string>
2215+
</property>
2216+
<property name="statusTip">
2217+
<string>Use qualified names (e.g. &quot;Table&quot;.&quot;Field&quot;) when dragging the objects and dropping them into the editor </string>
2218+
</property>
2219+
<property name="whatsThis">
2220+
<string>Use qualified names (e.g. &quot;Table&quot;.&quot;Field&quot;) when dragging the objects and dropping them into the editor </string>
2221+
</property>
2222+
</action>
2223+
<action name="actionEnquoteNamesCheck">
2224+
<property name="checkable">
2225+
<bool>true</bool>
2226+
</property>
2227+
<property name="text">
2228+
<string>Drag &amp;&amp; Drop Enquoted Names</string>
2229+
</property>
2230+
<property name="statusTip">
2231+
<string>Use escaped identifiers (e.g. &quot;Table1&quot;) when dragging the objects and dropping them into the editor </string>
2232+
</property>
2233+
<property name="whatsThis">
2234+
<string>Use escaped identifiers (e.g. &quot;Table1&quot;) when dragging the objects and dropping them into the editor </string>
2235+
</property>
2236+
</action>
22062237
</widget>
22072238
<customwidgets>
22082239
<customwidget>
@@ -2696,6 +2727,22 @@ You can drag SQL statements from the Schema column and drop them into the SQL ed
26962727
</hint>
26972728
</hints>
26982729
</connection>
2730+
<connection>
2731+
<sender>treeSchemaDock</sender>
2732+
<signal>customContextMenuRequested(QPoint)</signal>
2733+
<receiver>MainWindow</receiver>
2734+
<slot>createSchemaDockContextMenu(QPoint)</slot>
2735+
<hints>
2736+
<hint type="sourcelabel">
2737+
<x>111</x>
2738+
<y>261</y>
2739+
</hint>
2740+
<hint type="destinationlabel">
2741+
<x>399</x>
2742+
<y>299</y>
2743+
</hint>
2744+
</hints>
2745+
</connection>
26992746
<connection>
27002747
<sender>viewDBToolbarAction</sender>
27012748
<signal>toggled(bool)</signal>

src/Settings.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,17 @@ QVariant Settings::getDefaultValue(const QString& group, const QString& name)
334334
return 4;
335335
}
336336

337+
338+
// SchemaDock Drag & drop settings
339+
if(group == "SchemaDock")
340+
{
341+
if(name == "dropQualifiedNames")
342+
return false;
343+
344+
if(name == "dropEnquotedNames")
345+
return true;
346+
}
347+
337348
// Remote settings?
338349
if(group == "remote")
339350
{

0 commit comments

Comments
 (0)