Files
qt5-legatus-legacy/Lts/Plugins/PlanOverlays/Gui/plan_overlay_model.cpp
T
2026-08-13 18:33:10 +02:00

617 lines
18 KiB
C++
Executable File

/**
**
** @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 <QByteArray>
#include <QDataStream>
#include <QMimeData>
#include <QJsonArray>
#include <FileManagement/flm_dtc.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
template<class _Ty, class _Other = _Ty> 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<unsigned int, PlanOverlayModel::PlanItem> m_planStore;
QVector<int> m_planOrder;
QString m_fileDialogMimeType;
InternalData()
: m_planStore(), m_planOrder(), m_fileDialogMimeType()
{
} // InternalData(const std::shared_ptr<SituationMap::ForeignGuiInterface> & 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<unsigned int, PlanOverlayModel::PlanItem> 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<ColumnSection>(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<ColumnSection>(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<ColumnSection>(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<unsigned int, PlanOverlayModel::PlanItem>::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<unsigned int, PlanOverlayModel::PlanItem>::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<ColumnSection>(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<int> removeList;
removeList.reserve(m_internal->m_planOrder.size());
QMap<unsigned int, PlanOverlayModel::PlanItem>::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<PlanOverlayModel *>(this)->planOrderChanged(m_internal->m_planOrder);
} // void PlanOverlayModel::updateOrderOfPlans() const
} // namespace PlanOverlaysPlugin