/** ** ** @RDE_HEADER@ ** plan_overlay_model.h ** $Id$ ** $HeadURL$ ** $Author$ ** ** ----------------------------------------------------------------------------------------------- ** ** Hardware-Environment: Intel-PC ** Software-Environment: Windows 7, Qt 5.7. ArcGIS 100 ** ** Language ........: C++ 11 ** ** ----------------------------------------------------------------------------------------------- ** ** Author ...........: Jörg Turla, SPD G, Tel. 3132 ** ** ----------------------------------------------------------------------------------------------- ** ** Notes .........: ** ** 10.07.2017, JT : First edition for use with Qt5 ** ** ----------------------------------------------------------------------------------------------- */ #include "plan_overlay_model.h" #include #include #include #include #include #include template inline _Ty doExchange(_Ty &_Val, _Other && _New_val) { // assign _New_val to _Val, return previous _Val _Ty _Old_val = std::move(_Val); _Val = std::forward<_Other>(_New_val); return (_Old_val); } namespace PlanOverlaysPlugin { const QLatin1String REORDER_MIMETYPE("application/LtsPlanOverlayItemData/Reorder"); class PlanOverlayModel::InternalData { public: QMap m_planStore; QVector m_planOrder; QString m_fileDialogMimeType; InternalData() : m_planStore(), m_planOrder(), m_fileDialogMimeType() { } // InternalData(const std::shared_ptr & sitMapGui) ~InternalData() = default; }; // PlanOverLayModel::InternalData PlanOverlayModel::PlanOverlayModel() : QAbstractItemModel(), m_internal(new InternalData()) , m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("PlanOverlayModel")) {;} PlanOverlayModel::~PlanOverlayModel() { delete m_internal; } // PlanOverlayModel::~PlanOverlayModel #ifdef LTS_DEBUG // show exercise id and phase id, last 2 columns in Debug mode int PlanOverlayModel::columnCount(const QModelIndex &) const { return int(ColumnSection::CustomSection) + 3; } #else int PlanOverlayModel::columnCount(const QModelIndex &) const { return int(ColumnSection::CustomSection) + 1; } #endif int PlanOverlayModel::rowCount(const QModelIndex &) const { return m_internal->m_planStore.size(); } int PlanOverlayModel::size() const { return m_internal->m_planStore.size(); } QMap PlanOverlayModel::planStore() const { return m_internal->m_planStore; } bool PlanOverlayModel::isEmpty() const { return m_internal->m_planStore.isEmpty(); } QVariant PlanOverlayModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation == Qt::Orientation::Vertical) return QVariant(); switch (static_cast(section)) { case ColumnSection::OrderButtons: return QVariant(); case ColumnSection::PlanName: return tr("Plan"); case ColumnSection::MinScale: return tr("Min. Scale"); case ColumnSection::MaxScale: return tr("Max. Scale"); case ColumnSection::Opacity: return tr("Opacity"); case ColumnSection::CustomSection: return QVariant(); case ColumnSection::ExerciseId: return tr("Exercise"); case ColumnSection::PhaseId: return tr("Phase"); } return QVariant(); } // QVariant PlanOverlayModel::headerData( int section, Qt::Orientation orientation, int role ) const QVariant PlanOverlayModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= m_internal->m_planOrder.size()) return QVariant(); if (Qt::ItemDataRole::TextAlignmentRole == role) { switch (static_cast(index.column())) { case ColumnSection::PlanName: return QVariant(Qt::Alignment::enum_type::AlignCenter); case ColumnSection::MinScale: return QVariant(Qt::Alignment::enum_type::AlignRight | Qt::Alignment::enum_type::AlignHCenter); case ColumnSection::MaxScale: return QVariant(Qt::Alignment::enum_type::AlignRight | Qt::Alignment::enum_type::AlignHCenter); case ColumnSection::OrderButtons: case ColumnSection::Opacity: case ColumnSection::CustomSection: case ColumnSection::ExerciseId: case ColumnSection::PhaseId: return QVariant(); } } if (Qt::ItemDataRole::DisplayRole == role) { const PlanItem &planItem = m_internal->m_planStore[m_internal->m_planOrder.at(index.row())]; switch (static_cast(index.column())) { case ColumnSection::OrderButtons: return QString(); case ColumnSection::PlanName: return planItem.m_fileName; case ColumnSection::MinScale: return (planItem.m_minScale > 0) ? QStringLiteral("1:%1").arg(planItem.m_minScale) : tr("undefined"); case ColumnSection::MaxScale: return (planItem.m_maxScale > 0) ? QStringLiteral("1:%1").arg(planItem.m_maxScale) : tr("undefined"); case ColumnSection::Opacity: return planItem.m_opacity; case ColumnSection::CustomSection: return QString(); case ColumnSection::ExerciseId: return planItem.m_exerciseId > 0 ? QVariant(planItem.m_exerciseId) : QVariant(); case ColumnSection::PhaseId: return planItem.m_phaseId > 0 ? QVariant(planItem.m_phaseId) : QVariant(); } } return QVariant(); } // QVariant PlanOverlayModel::data( const QModelIndex & index, int role ) const QStringList PlanOverlayModel::mimeTypes() const { QStringList typeList; typeList << REORDER_MIMETYPE; if (!m_internal->m_fileDialogMimeType.isEmpty()) typeList << m_internal->m_fileDialogMimeType; return typeList; } // QStringList PlanOverlayModel::mimeTypes() const void PlanOverlayModel::setFileDialogMimeType(const QString &mimeType) { m_internal->m_fileDialogMimeType = mimeType; } // void PlanOverlayModel::setFileDialogMimeType( const QString & mimeType ) QMimeData *PlanOverlayModel::mimeData(const QModelIndexList &indexes) const { if (indexes.isEmpty()) return nullptr; int row = indexes.first().row(); unsigned int keyId = m_internal->m_planOrder.at(row); QMimeData *mimeData = new QMimeData(); QByteArray encodedData; mimeData->setData(REORDER_MIMETYPE, encodedData.setNum(keyId)); return mimeData; } // QMimeData * PlanOverlayModel::mimeData( const QModelIndexList & indexes ) const bool PlanOverlayModel::canDropMimeData(const QMimeData *data, Qt::DropAction, int row, int, const QModelIndex &) const { if ((row >= 0) && (row < m_internal->m_planOrder.size())) { if (data->hasFormat(REORDER_MIMETYPE)) { QByteArray encodedData = data->data(REORDER_MIMETYPE); bool ok = false; unsigned int keyId = encodedData.toUInt(&ok); if (ok) return (m_internal->m_planOrder.indexOf(keyId) != row); } if ((!m_internal->m_fileDialogMimeType.isEmpty()) && (data->hasFormat(m_internal->m_fileDialogMimeType))) { QByteArray encodedData = data->data(m_internal->m_fileDialogMimeType); bool ok = false; int keyId = encodedData.toUInt(&ok); if (ok) return (keyId > 0); } } return false; } // bool PlanOverlayModel::canDropMimeData( const QMimeData * data, Qt::DropAction, int, int, const QModelIndex & ) const bool PlanOverlayModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (!canDropMimeData(data, action, row, column, parent)) return false; if (action == Qt::IgnoreAction) return true; emit dropPlanAccepted(); if (data->hasFormat(REORDER_MIMETYPE)) { QByteArray encodedData = data->data(REORDER_MIMETYPE); bool ok = false; int keyId = encodedData.toInt(&ok); if (!ok) return false; int rowToMove = m_internal->m_planOrder.indexOf(keyId); if (row != rowToMove) { if (row < rowToMove) { for (; row <= rowToMove; ++row) { unsigned int idSwap = m_internal->m_planOrder[row]; m_internal->m_planOrder[row] = keyId; keyId = idSwap; } } else { for (--row; row >= rowToMove; --row) { unsigned int idSwap = m_internal->m_planOrder[row]; m_internal->m_planOrder[row] = keyId; keyId = idSwap; } } emit dataChanged(createIndex(0, 0), createIndex(m_internal->m_planOrder.size() - 1, columnCount(QModelIndex()) - 1)); emit layoutChanged(); emit planOrderChanged(m_internal->m_planOrder); } return true; } if ((!m_internal->m_fileDialogMimeType.isEmpty()) && (data->hasFormat(m_internal->m_fileDialogMimeType))) { QByteArray encodedData = data->data(m_internal->m_fileDialogMimeType); bool ok = false; int fileId = encodedData.toInt(&ok); if ((ok) && (fileId > 0)) { emit requestLoadPlan(fileId); // so the plan is now loaded at end of overlay list, move it a the right position int rowToMove = m_internal->m_planOrder.size() - 1; int keyId = m_internal->m_planOrder[rowToMove]; if (row != rowToMove) { for (; row <= rowToMove; ++row) keyId = doExchange(m_internal->m_planOrder[row], keyId); emit dataChanged(createIndex(0, 0), createIndex(m_internal->m_planOrder.size() - 1, columnCount(QModelIndex()) - 1)); emit layoutChanged(); emit planOrderChanged(m_internal->m_planOrder); } return true; } } return false; } QJsonArray PlanOverlayModel::toJson() const { QJsonArray result; if (m_internal->m_planStore.count() != m_internal->m_planOrder.count()) { Q_ASSERT(m_internal->m_planStore.count() == m_internal->m_planOrder.count()); return result; } // save in reverse order, because loadPlan appends plans instead of prepending for (int i = m_internal->m_planStore.count() - 1; i >= 0; i--) { PlanItem planItem = m_internal->m_planStore[m_internal->m_planOrder.at(i)]; result += planItem.toJson(); } return result; } PlanOverlayModel::PlanItem PlanOverlayModel::planItem(int keyId) { if (keyId < 1) { static int nextKey = 1; keyId = nextKey++; } PlanItem &planItem = m_internal->m_planStore[keyId]; planItem.m_keyId = keyId; return planItem; } PlanOverlayModel::PlanItem &PlanOverlayModel::planItemRef(int keyId) { return m_internal->m_planStore[keyId]; } PlanOverlayModel::PlanItem PlanOverlayModel::planItemValue(int keyId) { QMap::Iterator found = m_internal->m_planStore.find(keyId); return (found == m_internal->m_planStore.end()) ? PlanItem() : found.value(); } PlanOverlayModel::PlanItem PlanOverlayModel::planItemAtRow(int row) { if ((row < 0) || (row >= m_internal->m_planOrder.size())) return PlanItem(); unsigned int planKeyId = m_internal->m_planOrder.at(row); return m_internal->m_planStore[planKeyId]; } PlanOverlayModel::PlanItem PlanOverlayModel::findPlanItemByFileId(int fileId) { if (fileId < 1) return PlanItem(); QMap::const_iterator it; for (it = m_internal->m_planStore.constBegin(); it != m_internal->m_planStore.constEnd(); ++it) { if (it.value().m_fileId == fileId) return it.value(); } return PlanItem(); } Qt::ItemFlags PlanOverlayModel::flags(const QModelIndex &modelIndex) const { Qt::ItemFlags flags = QAbstractItemModel::flags(modelIndex) | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; switch (static_cast(modelIndex.column())) { case ColumnSection::OrderButtons: case ColumnSection::PlanName: case ColumnSection::MinScale: case ColumnSection::MaxScale: case ColumnSection::Opacity: flags |= Qt::ItemIsEditable; break; case ColumnSection::CustomSection: case ColumnSection::ExerciseId: case ColumnSection::PhaseId: break; } return flags; } // Qt::ItemFlags PlanOverlayModel::flags( const QModelIndex & modelIndex ) const void PlanOverlayModel::updatePlanItem(const PlanItem &planItem) { if (!planItem.isValid()) { ERROR_LOG(m_logger) << "planItem has invalid key"; return; } m_internal->m_planStore[planItem.m_keyId] = planItem; beginResetModel(); { if (!m_internal->m_planOrder.contains(planItem.m_keyId)) m_internal->m_planOrder.prepend(planItem.m_keyId); } endResetModel(); int row = m_internal->m_planOrder.size() - 1; emit dataChanged(index(row, 0, QModelIndex()), index(row, columnCount(QModelIndex()) - 1, QModelIndex())); emit layoutChanged(); } // void PlanOverlayModel::updatePlanItem(PlanItem & planItem) void PlanOverlayModel::swapRow(int row1, int row2) { if (row1 < 0) return; if (row2 < 0) return; if (row1 >= m_internal->m_planOrder.size()) return; if (row2 >= m_internal->m_planOrder.size()) return; m_internal->m_planOrder[row2] = doExchange(m_internal->m_planOrder[row1], m_internal->m_planOrder[row2]); QModelIndex topLeftIndex; QModelIndex bottomRightIndex; if (row1 < row2) { topLeftIndex = createIndex(row1, 0); bottomRightIndex = createIndex(row2, columnCount(QModelIndex()) - 1); } else { topLeftIndex = createIndex(row2, 0); bottomRightIndex = createIndex(row1, columnCount(QModelIndex()) - 1); } emit dataChanged(topLeftIndex, bottomRightIndex); emit planOrderChanged(m_internal->m_planOrder); } // void PlanOverlayModel::swapRow(int row1, int row2) int PlanOverlayModel::getRowOfId(int keyId) const { return m_internal->m_planOrder.indexOf(keyId); } // PlanOverlayModel::getRowOfId( int id ) void PlanOverlayModel::setMinScale(int row, double scale) { if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; const int id = m_internal->m_planOrder[row]; planItemRef(id).m_minScale = scale; emit planMinScaleChanged(id, scale); const QModelIndex &modelIndex = createIndex(row, int(ColumnSection::MinScale)); emit dataChanged(modelIndex, modelIndex); } // void PlanOverlayModel::setMinScale( int row, double scale ) void PlanOverlayModel::setMaxScale(int row, double scale) { if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; const int id = m_internal->m_planOrder[row]; planItemRef(id).m_maxScale = scale; emit planMaxScaleChanged(id, scale); const QModelIndex &modelIndex = createIndex(row, int(ColumnSection::MaxScale)); emit dataChanged(modelIndex, modelIndex); } // void PlanOverlayModel::setMaxScale( int row, double scale ) void PlanOverlayModel::setOpacity(int row, double opacity) { if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; if (opacity < 0) opacity = 0.0; if (opacity > 1) opacity = 1.0; const int id = m_internal->m_planOrder[row]; planItemRef(id).m_opacity = opacity; emit planOpacityChanged(id, opacity); const QModelIndex &modelIndex = createIndex(row, int(ColumnSection::Opacity)); emit dataChanged(modelIndex, modelIndex); } // void PlanOverlayModel::setAlpha( int row, double alpha ) void PlanOverlayModel::updateOverlay(int keyId) { // keep in mimd fileId is same as planId here int row = m_internal->m_planOrder.indexOf(keyId); if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; emit dataChanged(createIndex(row, 0), createIndex(row, columnCount(QModelIndex()) - 1)); emit requestUpdatePlan(keyId); } // void PlanOverlayModel::updateOverlay(int fileId) void PlanOverlayModel::renewPlanItem(const PlanItem &planItem) { int row = m_internal->m_planOrder.indexOf(planItem.m_keyId); if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; m_internal->m_planStore[planItem.m_keyId] = planItem; emit dataChanged(createIndex(row, 0), createIndex(row, columnCount(QModelIndex()) - 1)); } // void PlanOverlayModel::renewPlanItem( const PlanItem & planItem ) void PlanOverlayModel::removeOverlay(int keyId) { int row = m_internal->m_planOrder.indexOf(keyId); if (row < 0) return; if (row >= m_internal->m_planOrder.size()) return; emit requestRemovePlan(keyId); // Important: remove first from situation map and there for we need information from model entry beginResetModel(); { m_internal->m_planStore.remove(keyId); m_internal->m_planOrder.remove(row); } endResetModel(); emit dataChanged(createIndex(0, 0), createIndex(m_internal->m_planOrder.size() - 1, columnCount(QModelIndex()) - 1)); emit layoutChanged(); } // void PlanOverlayModel::removeOverlay( int keyId ) bool PlanOverlayModel::reInitModel(int exerciseId, int phaseId) { DEBUG_LOG(m_logger) << "ReInit Model for exerciseId = " << exerciseId << ", phaseId = " << phaseId; if ((exerciseId < 1) || (phaseId < 1)) { // what's to do here?? WARNING_LOG(m_logger) << LOGFUNCTION << " has been called with undefined exercise and/or phaseId"; return false; } QVector removeList; removeList.reserve(m_internal->m_planOrder.size()); QMap::const_iterator it; for (it = m_internal->m_planStore.constBegin(); it != m_internal->m_planStore.constEnd(); ++it) { int planExId = it.value().m_exerciseId; int planPhId = it.value().m_phaseId; DEBUG_LOG(m_logger) << "Plan name = " << it.value().m_fileName << " exercise = " << planExId << " phase = " << planPhId; if ((planExId < 1) || (planPhId < 1) || ((planExId == exerciseId) && (planPhId == phaseId))) { // nothing to do, exercise and phase did not change or it is a global or user plan continue; } bool removeFromModel(false); emit requestReloadPlanByName(it.value(), exerciseId, phaseId, &removeFromModel); if (removeFromModel) removeList << it.value().m_keyId; } // for m_planOder // remove all plans which doesn't exist any more from model for (auto id : qAsConst(removeList)) removeOverlay(id); emit dataChanged(createIndex(0, 0), createIndex(m_internal->m_planOrder.size() - 1, columnCount(QModelIndex()) - 1)); emit layoutChanged(); return true; } // void PlanOverlayModel::reInitModel(int exerciseId, int phaseId) void PlanOverlayModel::updateOrderOfPlans() const { emit const_cast(this)->planOrderChanged(m_internal->m_planOrder); } // void PlanOverlayModel::updateOrderOfPlans() const } // namespace PlanOverlaysPlugin