459 lines
11 KiB
C++
Executable File
459 lines
11 KiB
C++
Executable File
|
|
|
|
|
|
#include "ngltreemodel.h"
|
|
#include "ngltreeitem.h"
|
|
|
|
#include <QtCore/QHash>
|
|
#include <QtCore/QMetaType>
|
|
#include <QtCore/QMimeData>
|
|
#include <QtCore/QDebug>
|
|
#include <QtGui/QIcon>
|
|
|
|
|
|
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<qint64, QModelIndex> 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<NGLTreeItem*> NGLTreeModel::getMimeDataItems(const QMimeData *data,
|
|
const QString &mimeType) const
|
|
{
|
|
QVector<NGLTreeItem*> 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<NGLTreeItem *>(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();
|
|
}
|