/** * @RDE_HEADER@ * Radio TreeModel * $Id$ * $HeadURL$ * $Author$ */ #include "avm_used_streams_model.h" #include "avm_available_streams_model.h" #include #include #include #include namespace AudioVideoManagement { const QString UsedStreamsModel::s_MimeTypeOI = QStringLiteral("UsedStreamsModel/OrderItem"); UsedStreamsModel::UsedStreamsModel(const std::shared_ptr avmDtc, const std::shared_ptr ismDtc, const MenuStruct &ms, StreamNameMapping *streamNameMapping, QObject *parent) : NGLTreeModel(parent, nullptr, {tr("Streams")}) , m_avmDtc(avmDtc) , m_ismDtc(ismDtc) , m_dummyId(-10000000) , m_menuStruct(ms) , m_streamNameMapping(streamNameMapping) , m_logger(getLogger("UsedStreamsModel")) { Q_ASSERT(streamNameMapping); initModel(); } QVariant UsedStreamsModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); NGLTreeItem *item = privateData(index); if (!item->data().canConvert()) return QVariant(); const StreamModelData availStream = item->data().value(); switch (role) { case Qt::DisplayRole: case Qt::EditRole: { switch (index.column()) { case 0: return availStream.name; default: return QStringLiteral("??"); } break; } case Qt::DecorationRole: { switch (index.column()) { case 0: { static const QIcon folder(QStringLiteral("icon:evaluation_folder.svg")); static const QIcon camera(QStringLiteral("icon:camera_control.svg")); return availStream.isGroup ? folder : camera; } default: break; } break; } default: break; } // switch return NGLTreeModel::data(index, role); } Qt::ItemFlags UsedStreamsModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = NGLTreeModel::flags(index); if (index.isValid()) { defaultFlags ^= Qt::ItemIsDropEnabled; NGLTreeItem *item = privateData(index); if (item->data().canConvert()) { StreamModelData menuEntry = item->data().value(); defaultFlags |= Qt::ItemIsDragEnabled; if (menuEntry.isGroup) { defaultFlags |= Qt::ItemIsEditable; defaultFlags |= Qt::ItemIsDropEnabled; } } } else defaultFlags |= Qt::ItemIsDropEnabled; return defaultFlags; } QMimeData *UsedStreamsModel::mimeData(const QModelIndexList &indexes) const { QString type = AvailableStreamsModel::s_MimeType; for (const QModelIndex &mi : indexes) { if (mimeType(mi) == s_MimeTypeOI) { type = s_MimeTypeOI; break; } } return getMimeData(indexes, type); } bool UsedStreamsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(column); if (action == Qt::IgnoreAction) return true; if (!checkDrop(data, index(row, column), parent, action)) return false; INFO_LOG(m_logger) << "dropMimeData: action = " << action << ", row = " << row << ", parent: valid = " << parent.isValid(); //DEBUG_LOG(m_logger) << "row: " << row << ", column: " << column << ", parent: " << parent.row(); // insert or append (or replace ?) rows : int beginRow; QModelIndex parentR; if (row != -1) { // insert rows: parentR = parent; beginRow = row; } else if (parent.isValid()) { // parent is current item: // if(canHaveChildren(parent)) : parentR = parent; // beginRow = 0; prepend? beginRow = rowCount(parentR); // append to last child } else { // no parent, so append row: (take the toplevel parent) parentR = QModelIndex(); beginRow = rowCount(parentR); } QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType; bool ret = insertMimeData(data, mimeType, parentR, beginRow); if (ret) { //fixup data fixUpData(m_rootItem); } return ret; } bool UsedStreamsModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const { if (!NGLTreeModel::canDropMimeData(data, action, row, column, parent)) return false; if (!data->hasFormat(AvailableStreamsModel::s_MimeType) && !data->hasFormat(s_MimeTypeOI)) return false; if ((action != Qt::MoveAction) && (action != Qt::CopyAction)) return false; if (!checkDrop(data, parent, parent, action)) return false; if (!parent.isValid()) return true; NGLTreeItem *item = privateData(parent); QVariant v = item->data(); StreamModelData ms = v.value(); if (!ms.isGroup) return false; QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType; QByteArray encodedData = data->data(mimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); qint64 idParent, idItem, mountId; bool group; QVariant itemData; stream >> idParent >> idItem >> group >> itemData >> mountId; ms = itemData.value(); INFO_LOG(m_logger) << "Insert:" << "\n ms.name=" << ms.name << "\n ms.isGroup=" << ms.isGroup << "\n ms.parentId=" << ms.parentId << "\n ms.mountingPointId=" << ms.mountingPointId; if (ms.isGroup) // do not drop group on group return false; return true; } QStringList UsedStreamsModel::mimeTypes() const { return { AvailableStreamsModel::s_MimeType, s_MimeTypeOI }; } QString UsedStreamsModel::mimeType(const QModelIndex &index) const { if (index.isValid()) { NGLTreeItem *item = privateData(index); QVariant v = item->data(); StreamModelData ms = v.value(); if (ms.parentId == -1) return s_MimeTypeOI; return AvailableStreamsModel::s_MimeType; } return s_MimeTypeOI; } static void getItems(const NGLTreeItem *item, int rowId, int parentId, std::vector &contToAdd, int &newId) { if (item->data().canConvert()) { StreamModelData s = item->data().value(); AvMenuEntry me; me.id(s.id ? s.id : newId++); me.parentId(parentId); me.isGroup(s.isGroup); if (s.isGroup) me.name().set(s.name.toStdString()); else me.mountingPointId(s.mountingPointId); me.rowId(rowId); contToAdd.push_back(me); if (s.isGroup) { for (int row = 0; row < item->childCount(); ++row) getItems(item->child(row), row + 1, me.id(), contToAdd, newId); } } } MenuStruct UsedStreamsModel::getMenuStruct() const { MenuStruct ms; ms.id(m_menuStruct.id()); ms.name(m_menuStruct.name()); int newId = -10000000; for (int row = 0; row < m_rootItem->childCount(); ++row) getItems(m_rootItem->child(row), row + 1, ms.id(), ms.menuEntries(), newId); return ms; } void UsedStreamsModel::insertChildren(NGLTreeItem *parent, int parentId, const std::vector &entries) { QVariant data; QMap rowIds; // no QSet, must be sorted! for (const AudioVideoManagement::AvMenuEntry &me : entries) { if (me.parentId() != parentId) continue; data.setValue(StreamModelData::createFromServiceItem(me, m_streamNameMapping, m_avmDtc->videoTeams())); NGLTreeItem *treeItem = new NGLTreeItem(data); // find the correct row to insert the data in respect to rowId auto hierIt = rowIds.insert(me.rowId(), me.rowId()); const int rowToInsert = std::distance(rowIds.begin(), hierIt); parent->insertChild(rowToInsert, treeItem); if (me.isGroup()) { DEBUG_LOG(m_logger) << "insertChildren" << me.mountingPointId() << me.name(); insertChildren(treeItem, me.id(), entries); } } } NGLTreeItem *UsedStreamsModel::getItemToCheck(const QModelIndex &index, const QModelIndex &parentIndex) const { NGLTreeItem *item = privateData(index); QVariant v = item->data(); StreamModelData ms = v.value(); NGLTreeItem *itemParent = privateData(parentIndex); const QVariant &variantParent = itemParent->data(); NGLTreeItem *itemToCheck = itemParent; const StreamModelData &msParent = variantParent.value(); if (!ms.isGroup && msParent.isGroup) itemToCheck = itemParent; if (!ms.isGroup && !msParent.isGroup) itemToCheck = m_rootItem; return itemToCheck; } bool UsedStreamsModel::checkSingleItem(NGLTreeItem *target, StreamModelData &toCheck) const { bool retValue = true; if (!target) { ERROR_LOG(m_logger) << "Nullptr exception"; return false; } else { StreamModelData msTarget = target->data().value(); if (msTarget.id == toCheck.parentId) DEBUG_LOG(m_logger) << "Drag&drag in same folder."; else if (msTarget.isGroup && toCheck.isGroup) { DEBUG_LOG(m_logger) << "Trying to drop folder into folder"; retValue = false; } else { //Check for duplicates:msSource const QVector &children = target->children(); for (const NGLTreeItem *childData : children) { const QVariant &variantCheck = childData->data(); const StreamModelData &msChild = variantCheck.value(); if ((msChild.name == toCheck.name) && (msChild.id != toCheck.id)) // { retValue = false; break; } } } } return retValue; } bool UsedStreamsModel::checkDrop(const QMimeData *data, const QModelIndex &index, const QModelIndex &parentIndex, Qt::DropAction action) const { /* * WHY ? You can use the Stream in different Menus aren't you? * You just need check if its in it CURRENT Dropped parent * First check if we drop to a folder or directly * If it's a folder DONT Check the parrent * We still need to allow reordering */ //check alle verschiedenen NGLTreeItem *itemToCheck = getItemToCheck(index, parentIndex); //const QVariant &variantCheck = itemToChek->data(); // const StreamModelData &data = data-> QString mimeType = data->hasFormat(s_MimeTypeOI) ? s_MimeTypeOI : AvailableStreamsModel::s_MimeType; QByteArray encodedData = data->data(mimeType); QList items; QDataStream stream(&encodedData, QIODevice::ReadOnly); qint64 idParent, idItem; bool containsIcon; QVariant itemData; QIcon tmp; stream >> containsIcon; //Contains icons while (!stream.atEnd()) { stream >> idParent >> idItem >> itemData; if (containsIcon) stream >> tmp; items.push_back(itemData.value()); } //Size check int countDropped = 0; //count to be dropped int countAgainst = 0; //count in folder const QVector &children = itemToCheck->children(); for (const auto &item : children) { const QVariant &variantCheck = item->data(); const StreamModelData &msCheck = variantCheck.value(); if (msCheck.isGroup) continue; ++countAgainst; // prefer pre-increment operator } QList movedFolders; bool onlyOneFolder = false; for (StreamModelData &item : items) { StreamModelData msToCheck = itemToCheck->data().value(); if (movedFolders.size() == 0) { //first item in list, at parentID of current item movedFolders.push_back(item.parentId); } if ((item.isGroup) && movedFolders.contains(item.parentId)) movedFolders.push_back(item.id); //different sub folder than top level, but not in case of copying from left to right view (Why is ParentId in those cases the ID of the folder else if ((movedFolders.at(0) != item.parentId) && (movedFolders.contains(item.parentId)) && (action == Qt::MoveAction)) { DEBUG_LOG(m_logger) << "Ignoring items in sub folder while moving."; continue; } //Check if only items from the same folder (including the folder are moved) (Not checked when copying from left to right view) else if (!movedFolders.contains(item.parentId) && (action == Qt::MoveAction)) { DEBUG_LOG(m_logger) << "Only allow items from same folder to be moved."; return false; } if (!checkSingleItem(itemToCheck, item)) { DEBUG_LOG(m_logger) << "Not able to add item with name: " << item.name; return false; } if (item.isGroup) { if (!onlyOneFolder) { onlyOneFolder = true; continue; } else { DEBUG_LOG(m_logger) << "More than one folder selected."; return false; } } if ((msToCheck.id == item.parentId) && (Qt::MoveAction == action)) //Moving item in same folder, dont count them continue; ++countDropped; // prefer pre-increment operator } if (countDropped + countAgainst > 12) { DEBUG_LOG(m_logger) << "Trying to add to many items to folder"; return false; } return true; } QSet UsedStreamsModel::fillTakenCameras(NGLTreeItem *getItem) const { QSet ret; const QVector &children = getItem->children(); for (const auto &item : qAsConst(children)) { if (item->childCount()) ret += fillTakenCameras(item); else { const QVariant &variantCheck = item->data(); const StreamModelData &msCheck = variantCheck.value(); if (msCheck.isGroup) continue; ret.insert(msCheck.mountingPointId); } } return ret; } bool UsedStreamsModel::initModel() { beginResetModel(); StreamModelData ms; ms.parentId = -1; ms.id = -2; QVariant data; data.setValue(ms); setRootItem(new NGLTreeItem(data)); insertChildren(m_rootItem, m_menuStruct.id(), m_menuStruct.menuEntries()); connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this, &UsedStreamsModel::onDataChanged); connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::videoTeamChanged, this, &UsedStreamsModel::onDataChangedMvs); // connect(m_avmDtc.get(), &AudioVideoManagement::Interface::Dtc::streamNameChanged, // this, &UsedStreamsModel::onStreamNameDataChanged); connect(m_streamNameMapping, &StreamNameMapping::changed, this, &UsedStreamsModel::onStreamNameDataChanged); endResetModel(); return true; } NGLTreeItem *UsedStreamsModel::createItem(const QVariant &data, const QIcon &icon) const { StreamModelData s = data.value(); // need to be adjusted later on /Yeah BUT WHY Did you never DONE it , well we will never know s.id = 0; s.parentId = 0; QVariant newData; newData.setValue(s); return new NGLTreeItem(newData, icon); } bool UsedStreamsModel::setData(const QModelIndex &idx, const QVariant &value, int role) { Q_UNUSED(role); if (idx.isValid()) { QVariant data; NGLTreeItem *ti = privateData(idx); StreamModelData ms = ti->data().value(); if (idx.column() == 0) ms.name = value.toString(); data.setValue(ms); ti->setData(data); emit dataChanged(index(idx.row(), 0), index(idx.row(), columnCount(QModelIndex()) - 1)); return true; } return false; } void UsedStreamsModel::onDataChanged(const InfrastructureManagement::MainStructureElement &data, const DataModification modType) { beginResetModel(); INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount() << " data.elements().size()=" << data.elements().size(); switch (modType) { case DataModification::eRemoved: break; case DataModification::eModified: case DataModification::eAdded: for (const InfrastructureManagement::StructureElement &se : data.elements()) { INFO_LOG(m_logger) << "se.id()" << se.id() << " se.displayName()" << se.displayName() << " m_rootItem->childCount()" << m_rootItem->childCount(); const QString newName = QString::fromStdString(se.displayName()); updateStreamNames(se.id(), newName, m_rootItem); } } endResetModel(); emit modelDataChanged(); } void UsedStreamsModel::onDataChangedMvs(const VideoTeam &data, const DataModification modType) { beginResetModel(); INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount(); switch (modType) { case DataModification::eRemoved: break; case DataModification::eModified: case DataModification::eAdded: INFO_LOG(m_logger) << "se.id()" << data.id() << " se.displayName()" << data.streamName() << " m_rootItem->childCount()" << m_rootItem->childCount(); const QString newName = QString::fromStdString(data.streamName()); updateStreamNames(-1 * data.id(), newName, m_rootItem); } endResetModel(); emit modelDataChanged(); } void UsedStreamsModel::onStreamNameDataChanged(int32_t mountingPointId, const QString &internalName, const QString &displayName) { Q_UNUSED(internalName) beginResetModel(); INFO_LOG(m_logger) << "onDataChanged m_rootItem->childCount()" << m_rootItem->childCount(); INFO_LOG(m_logger) << "se.id()" << mountingPointId << " se.displayName()" << displayName << " m_rootItem->childCount()" << m_rootItem->childCount(); updateStreamNames(mountingPointId, displayName, m_rootItem); endResetModel(); emit modelDataChanged(); } void UsedStreamsModel::updateStreamNames(const int mountingPointId, const QString &newName, NGLTreeItem *item) { INFO_LOG(m_logger) << "updateStreamNames mountingPointId=" << mountingPointId << " newName=" << newName; int childCount = item->childCount(); for (int i = 0; i < childCount; ++i) { NGLTreeItem *child = item->child(i); ////////////////////// if (!child->data().canConvert()) ERROR_LOG(m_logger) << "Error convert item"; StreamModelData usedStream = child->data().value(); INFO_LOG(m_logger) << "i=" << i << " usedStream.mountingPointId=" << usedStream.mountingPointId << " usedStream.name=" << usedStream.name << " mountingPointId=" << mountingPointId << " newName=" << newName; if (mountingPointId == usedStream.mountingPointId) { INFO_LOG(m_logger) << "Set new DisplayName id=" << usedStream.id << " mountingPointId=" << usedStream.mountingPointId << " name=" << usedStream.name << " new Name =" << newName; usedStream.name = newName; QVariant dataVar; dataVar.setValue(usedStream); child->setData(dataVar); break; } ////////////////////// // Recursively call the function for each child node. updateStreamNames(mountingPointId, newName, item->child(i)); } } void UsedStreamsModel::fixUpData(NGLTreeItem *item) { //ERROR_LOG(m_logger) << "FIXUP "; const QVector &children = item->children(); for (NGLTreeItem *child : children) { const StreamModelData parentStream = item->data().value(); StreamModelData childStream = child->data().value(); if (childStream.isGroup && childStream.id == 0) childStream.id = m_dummyId++; if (!childStream.isGroup && childStream.id == 0) childStream.id = m_dummyId++; childStream.parentId = parentStream.id; QVariant data; data.setValue(childStream); child->setData(data); if (child->childCount() > 0) fixUpData(child); } } } // namespace AudioVideoManagement