mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-19 10:20:17 -06:00
Add custom model for tree view in Database Structure tab
Use a custom model for the tree view in the "Database Structure" tab in the main window, i.e. change from a QTreeWidget to a QTreeView and do all the item management stuff manually. This might add some code and complexity but also offers some more flexibility for us.
This commit is contained in:
155
src/DbStructureModel.cpp
Normal file
155
src/DbStructureModel.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include "DbStructureModel.h"
|
||||
#include "sqlitedb.h"
|
||||
#include <QTreeWidgetItem>
|
||||
|
||||
DbStructureModel::DbStructureModel(QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
{
|
||||
// Create root item and use its columns to store the header strings
|
||||
QStringList header;
|
||||
header << tr("Name") << tr("Object") << tr("Type") << tr("Schema");
|
||||
rootItem = new QTreeWidgetItem(header);
|
||||
}
|
||||
|
||||
DbStructureModel::~DbStructureModel()
|
||||
{
|
||||
delete rootItem;
|
||||
}
|
||||
|
||||
int DbStructureModel::columnCount(const QModelIndex&) const
|
||||
{
|
||||
return rootItem->columnCount();
|
||||
}
|
||||
|
||||
QVariant DbStructureModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
// Get the item the index points at
|
||||
QTreeWidgetItem* item = static_cast<QTreeWidgetItem*>(index.internalPointer());
|
||||
|
||||
// Depending on the role either return the text or the icon
|
||||
if(role == Qt::DisplayRole)
|
||||
return item->text(index.column());
|
||||
else if(role == Qt::DecorationRole)
|
||||
return item->icon(index.column());
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags DbStructureModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return 0;
|
||||
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
QVariant DbStructureModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
// Get the header string from the root item
|
||||
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
return rootItem->data(section, role);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QModelIndex DbStructureModel::index(int row, int column, const QModelIndex& parent) const
|
||||
{
|
||||
if(!hasIndex(row, column, parent))
|
||||
return QModelIndex();
|
||||
|
||||
QTreeWidgetItem *parentItem;
|
||||
if(!parent.isValid())
|
||||
parentItem = rootItem;
|
||||
else
|
||||
parentItem = static_cast<QTreeWidgetItem*>(parent.internalPointer());
|
||||
|
||||
QTreeWidgetItem* childItem = parentItem->child(row);
|
||||
if(childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex DbStructureModel::parent(const QModelIndex& index) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
return QModelIndex();
|
||||
|
||||
QTreeWidgetItem* childItem = static_cast<QTreeWidgetItem*>(index.internalPointer());
|
||||
QTreeWidgetItem* parentItem = childItem->parent();
|
||||
|
||||
if(parentItem == rootItem)
|
||||
return QModelIndex();
|
||||
else
|
||||
return createIndex(0, 0, parentItem);
|
||||
}
|
||||
|
||||
int DbStructureModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if(parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if(!parent.isValid())
|
||||
return rootItem->childCount();
|
||||
else
|
||||
return static_cast<QTreeWidgetItem*>(parent.internalPointer())->childCount();
|
||||
}
|
||||
|
||||
void DbStructureModel::reloadData(DBBrowserDB* db)
|
||||
{
|
||||
// Remove all data except for the root item
|
||||
while(rootItem->childCount())
|
||||
{
|
||||
delete rootItem->child(0);
|
||||
rootItem->removeChild(rootItem->child(0));
|
||||
}
|
||||
|
||||
// Create the nodes for tables, indices, views and triggers
|
||||
QMap<QString, QTreeWidgetItem*> typeToParentItem;
|
||||
QTreeWidgetItem* itemTables = new QTreeWidgetItem(rootItem);
|
||||
itemTables->setIcon(0, QIcon(QString(":/icons/table")));
|
||||
itemTables->setText(0, tr("Tables (%1)").arg(db->objMap.values("table").count()));
|
||||
typeToParentItem.insert("table", itemTables);
|
||||
QTreeWidgetItem* itemIndices = new QTreeWidgetItem(rootItem);
|
||||
itemIndices->setIcon(0, QIcon(QString(":/icons/index")));
|
||||
itemIndices->setText(0, tr("Indices (%1)").arg(db->objMap.values("index").count()));
|
||||
typeToParentItem.insert("index", itemIndices);
|
||||
QTreeWidgetItem* itemViews = new QTreeWidgetItem(rootItem);
|
||||
itemViews->setIcon(0, QIcon(QString(":/icons/view")));
|
||||
itemViews->setText(0, tr("Views (%1)").arg(db->objMap.values("view").count()));
|
||||
typeToParentItem.insert("view", itemViews);
|
||||
QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(rootItem);
|
||||
itemTriggers->setIcon(0, QIcon(QString(":/icons/trigger")));
|
||||
itemTriggers->setText(0, tr("Triggers (%1)").arg(db->objMap.values("trigger").count()));
|
||||
typeToParentItem.insert("trigger", itemTriggers);
|
||||
|
||||
// Add the actual table objects
|
||||
for(objectMap::ConstIterator it=db->objMap.begin();it!=db->objMap.end();++it)
|
||||
{
|
||||
// Object node
|
||||
QTreeWidgetItem *tableItem = new QTreeWidgetItem(typeToParentItem.value((*it).gettype()));
|
||||
tableItem->setIcon(0, QIcon(QString(":/icons/%1").arg((*it).gettype())));
|
||||
tableItem->setText(0, (*it).getname());
|
||||
tableItem->setText(1, (*it).gettype());
|
||||
tableItem->setText(3, (*it).getsql());
|
||||
|
||||
// If it is a table or view add the field Nodes
|
||||
if((*it).gettype() == "table" || (*it).gettype() == "view")
|
||||
{
|
||||
for(int i=0;i<(*it).fldmap.size();i++)
|
||||
{
|
||||
QTreeWidgetItem *fldItem = new QTreeWidgetItem(tableItem);
|
||||
fldItem->setText(0, (*it).fldmap.at(i)->name());
|
||||
fldItem->setText(1, "field");
|
||||
fldItem->setText(2, (*it).fldmap.at(i)->type());
|
||||
fldItem->setIcon(0, QIcon(":/icons/field"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the view
|
||||
reset();
|
||||
}
|
||||
30
src/DbStructureModel.h
Normal file
30
src/DbStructureModel.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef __DBSTRUCTUREMODEL_H__
|
||||
#define __DBSTRUCTUREMODEL_H__
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
class DBBrowserDB;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
class DbStructureModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DbStructureModel(QObject* parent = 0);
|
||||
~DbStructureModel();
|
||||
|
||||
void reloadData(DBBrowserDB* db);
|
||||
|
||||
QVariant data(const QModelIndex& index, int role) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex& index) const;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex& = QModelIndex()) const;
|
||||
|
||||
private:
|
||||
QTreeWidgetItem* rootItem;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "FilterTableHeader.h"
|
||||
#include "SqlExecutionArea.h"
|
||||
#include "VacuumDialog.h"
|
||||
#include "DbStructureModel.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget* parent)
|
||||
: QMainWindow(parent),
|
||||
@@ -54,16 +55,19 @@ void MainWindow::init()
|
||||
// Init the SQL log dock
|
||||
db.mainWindow = this;
|
||||
|
||||
// Set up the db tree widget
|
||||
ui->dbTreeWidget->setColumnHidden(1, true);
|
||||
ui->dbTreeWidget->setColumnWidth(0, 300);
|
||||
|
||||
// Set the validator for the goto line edit
|
||||
ui->editGoto->setValidator(gotoValidator);
|
||||
|
||||
// Set up DB models
|
||||
ui->dataTable->setModel(m_browseTableModel);
|
||||
|
||||
// Set up DB structure tab
|
||||
dbStructureModel = new DbStructureModel(ui->dbTreeWidget);
|
||||
ui->dbTreeWidget->setModel(dbStructureModel);
|
||||
ui->dbTreeWidget->setColumnHidden(1, true);
|
||||
ui->dbTreeWidget->setColumnWidth(0, 300);
|
||||
|
||||
// Set up filter row
|
||||
FilterTableHeader* tableHeader = new FilterTableHeader(ui->dataTable);
|
||||
connect(tableHeader, SIGNAL(filterChanged(int,QString)), m_browseTableModel, SLOT(updateFilter(int,QString)));
|
||||
connect(tableHeader, SIGNAL(filterChanged(int,QString)), this, SLOT(setRecordsetLabel()));
|
||||
@@ -103,6 +107,7 @@ void MainWindow::init()
|
||||
connect(ui->dataTable->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(setRecordsetLabel()));
|
||||
connect(editWin, SIGNAL(goingAway()), this, SLOT(editWinAway()));
|
||||
connect(editWin, SIGNAL(updateRecordText(int, int, QByteArray)), this, SLOT(updateRecordText(int, int, QByteArray)));
|
||||
connect(ui->dbTreeWidget->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(changeTreeSelection()));
|
||||
|
||||
// Load window settings
|
||||
restoreGeometry(PreferencesDialog::getSettingsValue("MainWindow", "geometry").toByteArray());
|
||||
@@ -175,10 +180,8 @@ void MainWindow::fileNew()
|
||||
}
|
||||
}
|
||||
|
||||
//** Populate DbTree Structure
|
||||
void MainWindow::populateStructure()
|
||||
{
|
||||
ui->dbTreeWidget->model()->removeRows(0, ui->dbTreeWidget->model()->rowCount());
|
||||
completerModelTables.clear();
|
||||
completerModelsFields.clear();
|
||||
if(!db.isOpen())
|
||||
@@ -229,51 +232,9 @@ void MainWindow::populateStructure()
|
||||
sqlarea->getEditor()->insertFieldCompleterModels(completerModelsFields);
|
||||
}
|
||||
|
||||
// fill the structure tab
|
||||
QMap<QString, QTreeWidgetItem*> typeToParentItem;
|
||||
QTreeWidgetItem* itemTables = new QTreeWidgetItem(ui->dbTreeWidget);
|
||||
itemTables->setIcon(0, QIcon(QString(":/icons/table")));
|
||||
itemTables->setText(0, tr("Tables (%1)").arg(db.objMap.values("table").count()));
|
||||
typeToParentItem.insert("table", itemTables);
|
||||
QTreeWidgetItem* itemIndices = new QTreeWidgetItem(ui->dbTreeWidget);
|
||||
itemIndices->setIcon(0, QIcon(QString(":/icons/index")));
|
||||
itemIndices->setText(0, tr("Indices (%1)").arg(db.objMap.values("index").count()));
|
||||
typeToParentItem.insert("index", itemIndices);
|
||||
QTreeWidgetItem* itemViews = new QTreeWidgetItem(ui->dbTreeWidget);
|
||||
itemViews->setIcon(0, QIcon(QString(":/icons/view")));
|
||||
itemViews->setText(0, tr("Views (%1)").arg(db.objMap.values("view").count()));
|
||||
typeToParentItem.insert("view", itemViews);
|
||||
QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(ui->dbTreeWidget);
|
||||
itemTriggers->setIcon(0, QIcon(QString(":/icons/trigger")));
|
||||
itemTriggers->setText(0, tr("Triggers (%1)").arg(db.objMap.values("trigger").count()));
|
||||
typeToParentItem.insert("trigger", itemTriggers);
|
||||
ui->dbTreeWidget->setItemExpanded(itemTables, true);
|
||||
ui->dbTreeWidget->setItemExpanded(itemIndices, true);
|
||||
ui->dbTreeWidget->setItemExpanded(itemViews, true);
|
||||
ui->dbTreeWidget->setItemExpanded(itemTriggers, true);
|
||||
|
||||
for(objectMap::ConstIterator it=db.objMap.begin();it!=db.objMap.end();++it)
|
||||
{
|
||||
// Object node
|
||||
QTreeWidgetItem *tableItem = new QTreeWidgetItem(typeToParentItem.value((*it).gettype()));
|
||||
tableItem->setIcon(0, QIcon(QString(":/icons/%1").arg((*it).gettype())));
|
||||
tableItem->setText(0, (*it).getname());
|
||||
tableItem->setText(1, (*it).gettype());
|
||||
tableItem->setText(3, (*it).getsql());
|
||||
|
||||
// If it is a table add the field Nodes
|
||||
if((*it).gettype() == "table" || (*it).gettype() == "view")
|
||||
{
|
||||
for(int i=0;i<(*it).fldmap.size();i++)
|
||||
{
|
||||
QTreeWidgetItem *fldItem = new QTreeWidgetItem(tableItem);
|
||||
fldItem->setText(0, (*it).fldmap.at(i)->name());
|
||||
fldItem->setText(1, "field");
|
||||
fldItem->setText(2, (*it).fldmap.at(i)->type());
|
||||
fldItem->setIcon(0, QIcon(":/icons/field"));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Refresh the structure tab
|
||||
dbStructureModel->reloadData(&db);
|
||||
ui->dbTreeWidget->expandToDepth(0);
|
||||
}
|
||||
|
||||
void MainWindow::populateTable( const QString & tablename)
|
||||
@@ -517,9 +478,9 @@ void MainWindow::compact()
|
||||
|
||||
void MainWindow::deleteObject()
|
||||
{
|
||||
// Get name of table to delete
|
||||
QString table = ui->dbTreeWidget->currentItem()->text(0);
|
||||
QString type = ui->dbTreeWidget->currentItem()->text(1);
|
||||
// Get name and type of object to delete
|
||||
QString table = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0)).toString();
|
||||
QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString();
|
||||
|
||||
// Ask user if he really wants to delete that table
|
||||
if(QMessageBox::warning(this, QApplication::applicationName(), tr("Are you sure you want to delete the %1 '%2'?\nAll data associated with the %1 will be lost.").arg(type).arg(table),
|
||||
@@ -547,7 +508,7 @@ void MainWindow::editTable()
|
||||
if(!ui->dbTreeWidget->selectionModel()->hasSelection()){
|
||||
return;
|
||||
}
|
||||
QString tableToEdit = ui->dbTreeWidget->currentItem()->text(0);
|
||||
QString tableToEdit = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 0)).toString();
|
||||
|
||||
EditTableDialog dialog(&db, tableToEdit, this);
|
||||
if(dialog.exec())
|
||||
@@ -867,9 +828,9 @@ void MainWindow::createTreeContextMenu(const QPoint &qPoint)
|
||||
if(!ui->dbTreeWidget->selectionModel()->hasSelection())
|
||||
return;
|
||||
|
||||
QTreeWidgetItem *cItem = ui->dbTreeWidget->currentItem();
|
||||
QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString();
|
||||
|
||||
if(cItem->text(1) == "table" || cItem->text(1) == "view" || cItem->text(1) == "trigger" || cItem->text(1) == "index")
|
||||
if(type == "table" || type == "view" || type == "trigger" || type == "index")
|
||||
popupTableMenu->exec(ui->dbTreeWidget->mapToGlobal(qPoint));
|
||||
}
|
||||
//** Tree selection changed
|
||||
@@ -879,26 +840,27 @@ void MainWindow::changeTreeSelection()
|
||||
ui->editDeleteObjectAction->setEnabled(false);
|
||||
ui->editModifyTableAction->setEnabled(false);
|
||||
|
||||
if(ui->dbTreeWidget->currentItem() == 0)
|
||||
if(!ui->dbTreeWidget->currentIndex().isValid())
|
||||
return;
|
||||
|
||||
// Change the text of the actions
|
||||
ui->editDeleteObjectAction->setIcon(QIcon(QString(":icons/%1_delete").arg(ui->dbTreeWidget->currentItem()->text(1))));
|
||||
if(ui->dbTreeWidget->currentItem()->text(1) == "view")
|
||||
QString type = ui->dbTreeWidget->model()->data(ui->dbTreeWidget->currentIndex().sibling(ui->dbTreeWidget->currentIndex().row(), 1)).toString();
|
||||
ui->editDeleteObjectAction->setIcon(QIcon(QString(":icons/%1_delete").arg(type)));
|
||||
if(type == "view")
|
||||
ui->editDeleteObjectAction->setText(tr("Delete View"));
|
||||
else if(ui->dbTreeWidget->currentItem()->text(1) == "trigger")
|
||||
else if(type == "trigger")
|
||||
ui->editDeleteObjectAction->setText(tr("Delete Trigger"));
|
||||
else if(ui->dbTreeWidget->currentItem()->text(1) == "index")
|
||||
else if(type == "index")
|
||||
ui->editDeleteObjectAction->setText(tr("Delete Index"));
|
||||
else
|
||||
ui->editDeleteObjectAction->setText(tr("Delete Table"));
|
||||
|
||||
// Activate actions
|
||||
if(ui->dbTreeWidget->currentItem()->text(1) == "table")
|
||||
if(type == "table")
|
||||
{
|
||||
ui->editDeleteObjectAction->setEnabled(true);
|
||||
ui->editModifyTableAction->setEnabled(true);
|
||||
} else if(ui->dbTreeWidget->currentItem()->text(1) == "view" || ui->dbTreeWidget->currentItem()->text(1) == "trigger" || ui->dbTreeWidget->currentItem()->text(1) == "index") {
|
||||
} else if(type == "view" || type == "trigger" || type == "index") {
|
||||
ui->editDeleteObjectAction->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class QIntValidator;
|
||||
class QLabel;
|
||||
class QModelIndex;
|
||||
class SqliteTableModel;
|
||||
class DbStructureModel;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
@@ -62,6 +63,8 @@ private:
|
||||
SQLiteSyntaxHighlighter* sqliteHighlighterLogUser;
|
||||
SQLiteSyntaxHighlighter* sqliteHighlighterLogApp;
|
||||
|
||||
DbStructureModel* dbStructureModel;
|
||||
|
||||
enum { MaxRecentFiles = 5 };
|
||||
QAction *recentFileActs[MaxRecentFiles];
|
||||
QAction *recentSeparatorAct;
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="dbTreeWidget">
|
||||
<widget class="QTreeView" name="dbTreeWidget">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
@@ -53,26 +53,6 @@
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Object</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Schema</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -286,7 +266,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>763</width>
|
||||
<width>278</width>
|
||||
<height>444</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1757,22 +1737,6 @@
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>dbTreeWidget</sender>
|
||||
<signal>itemSelectionChanged()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>changeTreeSelection()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>399</x>
|
||||
<y>341</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>399</x>
|
||||
<y>299</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>viewDBToolbarAction</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
|
||||
@@ -46,7 +46,8 @@ HEADERS += \
|
||||
FilterTableHeader.h \
|
||||
gen_version.h \
|
||||
SqlExecutionArea.h \
|
||||
VacuumDialog.h
|
||||
VacuumDialog.h \
|
||||
DbStructureModel.h
|
||||
|
||||
SOURCES += \
|
||||
sqlitedb.cpp \
|
||||
@@ -67,7 +68,8 @@ SOURCES += \
|
||||
sqlitetablemodel.cpp \
|
||||
FilterTableHeader.cpp \
|
||||
SqlExecutionArea.cpp \
|
||||
VacuumDialog.cpp
|
||||
VacuumDialog.cpp \
|
||||
DbStructureModel.cpp
|
||||
|
||||
RESOURCES += icons/icons.qrc
|
||||
|
||||
|
||||
Reference in New Issue
Block a user