#include "multiverseitem.h" #include "multiversemodel.h" #include "qsacnnode.h" #include /** * @brief MultiverseModel::MultiverseModel * @param parent * @param node */ MultiverseModel::MultiverseModel(QObject *parent, QSacnNode *node) : QAbstractItemModel(parent) , node_(node) , rootItem_(new MultiverseItem()) { // create items for the category headers QMetaEnum categories = QMetaEnum::fromType(); for (int itr = 0; itr < categories.keyCount(); itr++) { auto header = new MultiverseItem(rootItem_); header->setOverrideData(QString(categories.key(itr))); auto idx = MultiverseModel::index(itr, 0, QModelIndex()); categoryIndexes.insert(static_cast(categories.value(itr)), idx); } // connect node changes connect(node, &QSacnNode::discoveryUpdates, this, &MultiverseModel::doDiscovery); connect(node, &QSacnNode::subscribing, this, [this](QSacnUniverse *universe){ insert_(categoryIndexes.value(Receiver), universe); }); connect(node, &QSacnNode::unsubscribing, this, [this](QSacnUniverse *universe){ remove_(categoryIndexes.value(Receiver), universe); }); connect(node, &QSacnNode::creating, this, [this](QSacnUniverse *universe){ insert_(categoryIndexes.value(Source), universe); }); connect(node, &QSacnNode::terminating, this, [this](QSacnUniverse *universe){ remove_(categoryIndexes.value(Source), universe); }); } /** * @brief MultiverseModel::~MultiverseModel */ MultiverseModel::~MultiverseModel() { delete rootItem_; } /** * @brief MultiverseModel::getItem * @param index * @return */ MultiverseItem * MultiverseModel::getItem(const QModelIndex &index) const { if (index.isValid()) { auto item = static_cast(index.internalPointer()); if (item) return item; } return rootItem_; } /** * @brief MultiverseModel::headerData * @param section * @param orientation * @param role * @return */ QVariant MultiverseModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { switch (static_cast(section)) { case Universe: return tr("Universe"); case Status: return tr("Status"); case Priority: return tr("Priority"); case Channels: return tr("Channels"); case SourceName: return tr("Source Name"); } } return QVariant(); } /** * @brief MultiverseModel::index * @param row * @param column * @param parent * @return */ QModelIndex MultiverseModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() && parent.column() != 0) return QModelIndex(); MultiverseItem *parentItem = getItem(parent); if (!parentItem) return QModelIndex(); auto childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); return QModelIndex(); } /** * @brief MultiverseModel::parent * @param index * @return */ QModelIndex MultiverseModel::parent(const QModelIndex &index) const { if (!checkIndex(index, CheckIndexOption::IndexIsValid | CheckIndexOption::DoNotUseParent)) return QModelIndex(); auto childItem = getItem(index); auto parentItem = childItem ? childItem->parentItem() : nullptr; if (parentItem == rootItem_ || !parentItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } /** * @brief MultiverseModel::rowCount * @param parent * @return */ int MultiverseModel::rowCount(const QModelIndex &parent) const { auto parentItem = getItem(parent); return parentItem ? parentItem->childCount() : 0; } /** * @brief MultiverseModel::columnCount * @param parent * @return */ int MultiverseModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return QMetaEnum::fromType().keyCount(); } /** * @brief MultiverseModel::data * @param index * @param role * @return */ QVariant MultiverseModel::data(const QModelIndex &index, int role) const { if (!checkIndex(index, CheckIndexOption::IndexIsValid)) return QVariant(); return getItem(index)->data(index.column(), role); } /** * @brief MultiverseModel::setData * @param index * @param value * @param role * @return */ bool MultiverseModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!checkIndex(index, CheckIndexOption::IndexIsValid)) return false; if (getItem(index)->setData(index.column(), value, role)) { emit dataChanged(index, index, QVector() << role); return true; } return false; } /** * @brief MultiverseModel::flags * @param index * @return */ Qt::ItemFlags MultiverseModel::flags(const QModelIndex &index) const { if (!checkIndex(index, CheckIndexOption::IndexIsValid)) return Qt::NoItemFlags; auto item = getItem(index); if (!item) return Qt::NoItemFlags; return item->flags(index.column(), QAbstractItemModel::flags(index)); } /** * @brief MultiverseModel::insert * @param universe * @param parent * @return */ void MultiverseModel::insert_(const QModelIndex &parent, QSacnUniverse* universe, QSacnDiscoveredUniverse *discovery) { auto item = getItem(parent); beginInsertRows(parent, item->childCount(), item->childCount()); auto child = new MultiverseItem(item, universe, discovery); endInsertRows(); auto index = QPersistentModelIndex(createIndex(item->childCount() - 1, 0, child)); auto refreshRow = [this] (QPersistentModelIndex index) { auto begin = index.sibling(index.row(), 0); auto end = begin.siblingAtColumn(columnCount() - 1); emit dataChanged(begin, end); }; connect(universe, &QSacnUniverse::dataChanged, this, [refreshRow, index](){ refreshRow(index); }); connect(universe, &QSacnUniverse::statusChanged, this, [refreshRow, index](){ refreshRow(index); }); connect(universe, &QSacnUniverse::sourceListChanged, this, [this, universe, child, index, refreshRow]() { // remove the old children beginRemoveRows(index, 0, child->childCount() - 1); child->removeChildren(); endRemoveRows(); // add the new children beginInsertRows(index, 0, universe->sources().size() - 1); child->createChildren(); endInsertRows(); // refresh row of new child when data arrives for (int i = 0; i < child->childCount(); i++) { auto grandchild = child->child(i); auto index = QPersistentModelIndex(createIndex(i, 0, grandchild)); auto data = grandchild->data(Column::Universe, Qt::EditRole); if (data.metaType().id() != qMetaTypeId()) return; auto universe = data.value(); connect(universe, &QSacnUniverse::dataChanged, this, [refreshRow, index](){ refreshRow(index); }); connect(universe, &QSacnUniverse::statusChanged, this, [refreshRow, index](){ refreshRow(index); }); } }); } /** * @brief MultiverseModel::remove * @param parent * @param data */ void MultiverseModel::remove_(const QModelIndex &parent, const QSacnUniverse *universe) { auto item = static_cast(parent.internalPointer()); auto row = item->childRow(Column::Universe, QVariant::fromValue(universe)); if (row < 0) return; auto child = item->child(row); if (!child) return; beginRemoveRows(parent, row, row); item->removeChild(child); endRemoveRows(); } void MultiverseModel::doDiscovery() { auto parentIndex = categoryIndexes.value(MultiverseModel::Discovery); auto parent = static_cast(parentIndex.internalPointer()); beginRemoveRows(parentIndex, 0, parent->childCount() - 1); parent->removeChildren(); endRemoveRows(); beginInsertRows(parentIndex, 0, node_->discovered.size() - 1); for (auto& discovery : node_->discovered) new MultiverseItem(parent, nullptr, discovery.get()); endInsertRows(); }