Skip to content

Commit

Permalink
Add custom model for tree view in Database Structure tab
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MKleusberg committed Jul 19, 2013
1 parent afc53f7 commit 64a9387
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 104 deletions.
155 changes: 155 additions & 0 deletions src/DbStructureModel.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 30 additions & 0 deletions src/DbStructureModel.h
Original file line number Diff line number Diff line change
@@ -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
90 changes: 26 additions & 64 deletions src/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "FilterTableHeader.h"
#include "SqlExecutionArea.h"
#include "VacuumDialog.h"
#include "DbStructureModel.h"

MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent),
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class QIntValidator;
class QLabel;
class QModelIndex;
class SqliteTableModel;
class DbStructureModel;

namespace Ui {
class MainWindow;
Expand Down Expand Up @@ -62,6 +63,8 @@ class MainWindow : public QMainWindow
SQLiteSyntaxHighlighter* sqliteHighlighterLogUser;
SQLiteSyntaxHighlighter* sqliteHighlighterLogApp;

DbStructureModel* dbStructureModel;

enum { MaxRecentFiles = 5 };
QAction *recentFileActs[MaxRecentFiles];
QAction *recentSeparatorAct;
Expand Down
Loading

0 comments on commit 64a9387

Please sign in to comment.