#include "ngltreemodel.h" #include "ngltreeitem.h" #include #include #include #include #include NGLTreeModel::NGLTreeModel(QObject *parent, NGLTreeItem *rootItem, const QStringList &titles) : QAbstractItemModel(parent) , m_rootItem(rootItem) , m_titles(titles) , m_mimeType(QStringLiteral("NGLTreeModel/dragDrop")) , m_bMimeDataWithicon(true) { connect(this, &NGLTreeModel::dataChanged, this, &NGLTreeModel::modelDataChanged); connect(this, &NGLTreeModel::rowsInserted, this, &NGLTreeModel::modelDataChanged); connect(this, &NGLTreeModel::rowsMoved, this, &NGLTreeModel::modelDataChanged); connect(this, &NGLTreeModel::rowsRemoved, this, &NGLTreeModel::modelDataChanged); } NGLTreeModel::~NGLTreeModel() { resetModel(); delete m_rootItem; } void NGLTreeModel::setRootItem(NGLTreeItem *rootItem) { resetModel(); delete m_rootItem; m_rootItem = nullptr; beginResetModel(); endResetModel(); m_rootItem = rootItem; } NGLTreeItem *NGLTreeModel::getRootItem() const { return m_rootItem; } NGLTreeItem *NGLTreeModel::createItem(const QVariant &data, const QIcon &icon) const { return new NGLTreeItem(data, icon); } void NGLTreeModel::resetModel() { if (m_rootItem) { beginResetModel(); m_rootItem->removeAllChildren(); endResetModel(); } } Qt::ItemFlags NGLTreeModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; return defaultFlags; } QVariant NGLTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if ((section >= 0) && (section < m_titles.size())) return m_titles.at(section); } return QVariant(); } QModelIndex NGLTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return QModelIndex(); } NGLTreeItem *parentItem = privateData(parent); NGLTreeItem *childItem = parentItem->child(row); if (childItem) { return createIndex(row, column, childItem); } return QModelIndex(); } QModelIndex NGLTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); NGLTreeItem *childItem = privateData(index); NGLTreeItem *parentItem = childItem->parent(); if (!parentItem || parentItem == m_rootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } int NGLTreeModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_titles.size(); } int NGLTreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; NGLTreeItem *ti = privateData(parent); if (!ti) return 0; return ti->childCount(); } bool NGLTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { Q_UNUSED(index) Q_UNUSED(value) Q_UNUSED(role) return false; } void NGLTreeModel::setMimeType(const QString &mimeType) { m_mimeType = mimeType; } void NGLTreeModel::resetModifedFlag(NGLTreeItem *rootItem) { if (!rootItem) return; rootItem->setModified(false); for (int i = 0; i < rootItem->childCount(); ++i) resetModifedFlag(rootItem->child(i)); } bool NGLTreeModel::setItemData(const QModelIndex &index, const QVariant &value, const QIcon &icon) { if (!index.isValid()) return false; NGLTreeItem *item = privateData(index); item->setData(value); item->setIcon(icon); item->setModified(true); emit dataChanged(index, index); return true; } QVariant NGLTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DecorationRole) { if (index.column() == 0) { NGLTreeItem *item = privateData(index); if (item) return item->icon(); } } return QVariant(); } QMimeData *NGLTreeModel::mimeData(const QModelIndexList &indexes) const { return getMimeData(indexes, m_mimeType); } QMimeData *NGLTreeModel::getMimeData(const QModelIndexList &indexes, const QString &mimeType) const { QMimeData *mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); stream << m_bMimeDataWithicon; bool dataStreamed = false; for (const QModelIndex &index : indexes) { if (index.isValid() && (index.column() == 0)) { streamAddChilds(stream, privateData(index)); dataStreamed = true; } } if (dataStreamed) mimeData->setData(mimeType, encodedData); return mimeData; } void NGLTreeModel::streamAddChilds(QDataStream &stream, const NGLTreeItem *item) const { stream << qint64(item->parent()) << qint64(item) << getItemData(item); if (m_bMimeDataWithicon) stream << item->icon(); for (int i = 0; i < item->childCount(); i++) streamAddChilds(stream, item->child(i)); } bool NGLTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { qDebug() << "dropMimeData: action = " << action << ", row = " << row << ", column = " << column << ", parent: valid = " << parent.isValid(); if ((action != Qt::MoveAction) && (action != Qt::CopyAction)) return false; if (!data->hasFormat(m_mimeType)) return false; int beginRow; QModelIndex parentR; if (row != -1) { parentR = parent; beginRow = row; } else if (parent.isValid()) { parentR = parent; beginRow = rowCount(parentR); } else { parentR = QModelIndex(); beginRow = rowCount(parentR); } return insertMimeData(data, m_mimeType, parentR, beginRow); } bool NGLTreeModel::insertMimeData(const QMimeData *data, const QString &mimeType, QModelIndex parentR, int beginRow) { bool bStreamContainsIcons; QByteArray encodedData = data->data(mimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); qint64 idParent, idItem; QHash rowCache; QVariant itemData; QIcon icon; stream >> bStreamContainsIcons; bool first = true; qint64 prevIdParent = 0; while (!stream.atEnd()) { stream >> idParent >> idItem >> itemData; if (bStreamContainsIcons) stream >> icon; NGLTreeItem *item = createItem(itemData, icon); if (first) { first = false; rowCache.insert(idParent, parentR); } else if (idParent == prevIdParent) { parentR = rowCache[idParent]; beginRow++; } else { parentR = rowCache[idParent]; beginRow = privateData(rowCache[idParent])->childCount(); } prevIdParent = idParent; if (!insertRow(beginRow, parentR, item)) { delete item; return false; } rowCache.insert(idItem, index(beginRow, 0, parentR)); emit signalDataDropped(beginRow, parentR); } return true; } QVector NGLTreeModel::getMimeDataItems(const QMimeData *data, const QString &mimeType) const { QVector ret; QByteArray encodedData = data->data(mimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); qint64 idParent, idItem; QVariant itemData; QIcon icon; bool bStreamContainsIcons; stream >> bStreamContainsIcons; while (!stream.atEnd()) { stream >> idParent >> idItem >> itemData; if (bStreamContainsIcons) stream >> icon; ret += createItem(itemData, icon); } return ret; } NGLTreeItem *NGLTreeModel::privateData(const QModelIndex &index) const { if (!index.isValid()) return m_rootItem; Q_ASSERT(index.model() == this); return static_cast(index.internalPointer()); } bool NGLTreeModel::moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destParent, int destRow) { Q_ASSERT(sourceParent.column() == 0 || !sourceParent.isValid()); Q_ASSERT(destParent.column() == 0 || !destParent.isValid()); NGLTreeItem *sourceParentItem = privateData(sourceParent); NGLTreeItem *destParentItem = privateData(destParent); if ((sourceRow >= 0) && (sourceRow < sourceParentItem->childCount()) && (destRow >= 0) && (destRow < destParentItem->childCount())) { beginRemoveRows(sourceParent, sourceRow, sourceRow); NGLTreeItem *item = sourceParentItem->takeChild(sourceRow); endRemoveRows(); if (item) { beginInsertRows(destParent, destRow, destRow); bool ret = destParentItem->insertChild(destRow, item); endInsertRows(); return ret; } } return false; } bool NGLTreeModel::removeRows(int row, int count, const QModelIndex &parent) { qDebug() << "removeRows: row = " << row << ", " << "count = " << count; NGLTreeItem *parentItem = privateData(parent); if ((row < 0 && row >= parentItem->childCount()) || count <= 0) return false; Q_ASSERT(parent.column() == 0 || !parent.isValid()); if (parent.column() != 0 && parent.isValid()) return false; beginRemoveRows(parent, row, row + count - 1); bool ret = parentItem->removeChildren(row, count); if (ret) { for (int r = row; r < parentItem->childCount(); ++r) parentItem->child(r)->setModified(true); } endRemoveRows(); return ret; } bool NGLTreeModel::insertRow(int row, const QModelIndex &parent, NGLTreeItem *item) { NGLTreeItem *parentItem = privateData(parent); if (!parentItem) return false; Q_ASSERT(parent.column() == 0 || !parent.isValid()); if (parent.column() != 0 && parent.isValid()) return false; parentItem->setModified(true); beginInsertRows(parent, row, row); bool ret = parentItem->insertChild(row, item); for (int r = row; r < parentItem->childCount(); ++r) parentItem->child(r)->setModified(true); endInsertRows(); return ret; } QModelIndex NGLTreeModel::indexFromItem(const NGLTreeItem *item, int column) const { if (item == m_rootItem || item == nullptr) return QModelIndex(); const NGLTreeItem *parent = item->parent(); const QModelIndex parentIdx = indexFromItem(parent); return index(item->row(), column, parentIdx); } QVariant NGLTreeModel::getItemData(const NGLTreeItem *item) const { return item->data(); }