See More

#include "DbStructureModel.h" #include "sqlitedb.h" #include "sqlitetablemodel.h" #include "Settings.h" #include #include #include #include DbStructureModel::DbStructureModel(DBBrowserDB& db, QObject* parent) : QAbstractItemModel(parent), m_db(db) { // 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(index.internalPointer()); // Depending on the role either return the text or the icon switch(role) { case Qt::DisplayRole: return Settings::getValue("db", "hideschemalinebreaks").toBool() ? item->text(index.column()).replace("\n", " ").simplified() : item->text(index.column()); case Qt::EditRole: case Qt::ToolTipRole: // Don't modify the text when it's supposed to be shown in a tooltip return item->text(index.column()); case Qt::DecorationRole: return item->icon(index.column()); default: return QVariant(); } } Qt::ItemFlags DbStructureModel::flags(const QModelIndex &index) const { if(!index.isValid()) return Qt::ItemIsDropEnabled; // All items are enabled and selectable Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled; // Only enable dragging for entire table objects QString type = data(index.sibling(index.row(), 1), Qt::DisplayRole).toString(); if(type == "table" || type == "view" || type == "index" || type == "trigger") flags |= Qt::ItemIsDragEnabled; return flags; } 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(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(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(parent.internalPointer())->childCount(); } void DbStructureModel::reloadData() { beginResetModel(); // Remove all data except for the root item while(rootItem->childCount()) delete rootItem->child(0); // Return here if no DB is opened if(!m_db.isOpen()) { endResetModel(); return; } // Create the nodes for browsables and for tables, indices, views and triggers. The idea here is to basically have two trees in one model: // 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. // The seconds node contains four sub-nodes (tables, indices, views and triggers), each containing a list of objects of that type. // 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. QMap typeToParentItem; QTreeWidgetItem* itemBrowsables = new QTreeWidgetItem(rootItem); itemBrowsables->setIcon(0, QIcon(QString(":/icons/view"))); itemBrowsables->setText(0, tr("Browsables (%1)").arg(m_db.objMap.values("table").count() + m_db.objMap.values("view").count())); typeToParentItem.insert("browsable", itemBrowsables); QTreeWidgetItem* itemAll = new QTreeWidgetItem(rootItem); itemAll->setIcon(0, QIcon(QString(":/icons/view"))); itemAll->setText(0, tr("All")); QTreeWidgetItem* itemTables = new QTreeWidgetItem(itemAll); itemTables->setIcon(0, QIcon(QString(":/icons/table"))); itemTables->setText(0, tr("Tables (%1)").arg(m_db.objMap.values("table").count())); typeToParentItem.insert("table", itemTables); QTreeWidgetItem* itemIndices = new QTreeWidgetItem(itemAll); itemIndices->setIcon(0, QIcon(QString(":/icons/index"))); itemIndices->setText(0, tr("Indices (%1)").arg(m_db.objMap.values("index").count())); typeToParentItem.insert("index", itemIndices); QTreeWidgetItem* itemViews = new QTreeWidgetItem(itemAll); itemViews->setIcon(0, QIcon(QString(":/icons/view"))); itemViews->setText(0, tr("Views (%1)").arg(m_db.objMap.values("view").count())); typeToParentItem.insert("view", itemViews); QTreeWidgetItem* itemTriggers = new QTreeWidgetItem(itemAll); itemTriggers->setIcon(0, QIcon(QString(":/icons/trigger"))); itemTriggers->setText(0, tr("Triggers (%1)").arg(m_db.objMap.values("trigger").count())); typeToParentItem.insert("trigger", itemTriggers); // Get all database objects and sort them by their name QMultiMap dbobjs; for(auto it=m_db.objMap.constBegin(); it != m_db.objMap.constEnd(); ++it) dbobjs.insert((*it)->name(), (*it)); // Add the actual table objects for(auto it=dbobjs.constBegin();it!=dbobjs.constEnd();++it) { // Object node QTreeWidgetItem* item = addNode(typeToParentItem.value(sqlb::Object::typeToString((*it)->type())), *it); // If it is a table or view add the field nodes, add an extra node for the browsable section if((*it)->type() == sqlb::Object::Types::Table || (*it)->type() == sqlb::Object::Types::View) addNode(typeToParentItem.value("browsable"), *it); // Add field nodes if there are any sqlb::FieldInfoList fieldList = (*it)->fieldInformation(); if(!fieldList.empty()) { QStringList pk_columns; if((*it)->type() == sqlb::Object::Types::Table) { sqlb::FieldVector pk = (*it).dynamicCast<:table>()->primaryKey(); foreach(sqlb::FieldPtr pk_col, pk) pk_columns.push_back(pk_col->name()); } foreach(const sqlb::FieldInfo& field, fieldList) { QTreeWidgetItem *fldItem = new QTreeWidgetItem(item); fldItem->setText(0, field.name); fldItem->setText(1, "field"); fldItem->setText(2, field.type); fldItem->setText(3, field.sql); if(pk_columns.contains(field.name)) fldItem->setIcon(0, QIcon(":/icons/field_key")); else fldItem->setIcon(0, QIcon(":/icons/field")); } } } // Refresh the view endResetModel(); } QStringList DbStructureModel::mimeTypes() const { QStringList types; types << "text/plain"; return types; } QMimeData* DbStructureModel::mimeData(const QModelIndexList& indices) const { // Loop through selected indices QByteArray d; foreach(QModelIndex index, indices) { // Only export data for valid indices and only for the SQL column, i.e. only once per row if(index.isValid() && index.column() == 3) { // Add the SQL code used to create the object d = d.append(data(index, Qt::DisplayRole).toString() + ";\n"); // If it is a table also add the content if(data(index.sibling(index.row(), 1), Qt::DisplayRole).toString() == "table") { SqliteTableModel tableModel(m_db); tableModel.setTable(data(index.sibling(index.row(), 0), Qt::DisplayRole).toString()); for(int i=0; i < tableModel.rowCount(); ++i) { QString insertStatement = "INSERT INTO " + sqlb::escapeIdentifier(data(index.sibling(index.row(), 0), Qt::DisplayRole).toString()) + " VALUES("; for(int j=1; j < tableModel.columnCount(); ++j) insertStatement += QString("'%1',").arg(tableModel.data(tableModel.index(i, j)).toString()); insertStatement.chop(1); insertStatement += ");\n"; d = d.append(insertStatement); } } } } // Create the MIME data object QMimeData* mime = new QMimeData(); 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 mime->setData("text/plain", d); return mime; } bool DbStructureModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int, int, const QModelIndex&) { if(action == Qt::IgnoreAction) return true; if(!data->hasFormat("text/plain")) return false; if(data->property("db_file") == m_db.currentFile()) return false; // Get data QByteArray d = data->data("text/plain"); // Try to execute the SQL statement if(m_db.executeMultiSQL(d, true, true)) { m_db.updateSchema(); reloadData(); return true; } else { QMessageBox::warning(0, QApplication::applicationName(), m_db.lastError()); return false; } } QTreeWidgetItem* DbStructureModel::addNode(QTreeWidgetItem* parent, const sqlb::ObjectPtr& object) { QString type = sqlb::Object::typeToString(object->type()); QTreeWidgetItem *item = new QTreeWidgetItem(parent); item->setIcon(0, QIcon(QString(":/icons/%1").arg(type))); item->setText(0, object->name()); item->setText(1, type); item->setText(3, object->originalSql()); return item; }