Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,390 @@
/**
* @RDE_HEADER@
* The ExerciseAssignmentDialog shows how many assets are needed for a specific schedule, needed elsewhere,
* totally possessed, free, and missing
* $Id: ism_exercise_assignment_dialog.cpp 57783 2022-08-03 11:07:05Z ojog_i $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/22_09_resmgmt2.0/Lts/Plugins/InfrastructureManagement/Gui/AudioVideoConfig/ExerciseAssignment/ism_exercise_assignment_dialog.cpp $
* $Author: ojog_i $
* Created by Hannes Baeuerle, SLSE, 2017-06-29
* Refreshed by Mike Wille, SLTE, 2022-09-26
*/
#include "ism_exercise_assignment_dialog.h"
#include "ui_ism_exercise_assignment_dialog.h"
#include "ism_exercise_assignment_dialog_requesters.h"
#include "ism_exercise_assignment_dialog_proxy_model.h"
#include <LtsUtilsCore/json_object.h>
#include <ExerciseManagement/exm_data_plugin_interface.h>
#include <InfrastructureManagement/ism_data_plugin_interface.h>
#include <LtsSession/lts_session.h>
#include <LtsPrinting/printing.h>
#include <LtsWidgets/lts_overlay_loading.h>
#include <LtsUtilsSettings/settings.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
using namespace LtsUtils;
using namespace LtsWidgets;
namespace InfrastructureManagement
{
ExerciseAssignmentDialog::ExerciseAssignmentDialog(LtsSession::LtsSession *session, QWidget *parent) noexcept(false)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_session(session)
, m_ui(new Ui::ExerciseAssignmentDialog)
, m_model(nullptr)
, m_modelAssignedFilter(new ExerciseAssignmentDialogModelFilterProxy(this))
, m_modelUnassignedFilter(new ExerciseAssignmentDialogModelFilterProxy(this))
, m_logger(getLogger("ExerciseAssignmentDialog"))
{
m_ui->setupUi(this);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &ExerciseAssignmentDialog::onPrint);
setOnlineHelpFilename(QStringLiteral("Ressourcen-Exercise_Assignment"));
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc;
if (!m_session->getDtc<DataInterface>(m_dtc) ||
!m_session->getFtc<DataInterface>(m_ftc) ||
!m_session->getDtc<ExerciseManagement::DataInterface>(exmDtc))
throw std::exception();
if (!m_ftc->enableMainStructureElementModify())
{
ERROR_LOG(m_logger) << "Could not enable FT MainStructureElementModify of ISM";
throw std::exception();
}
if (!exmDtc->enableExercise())
{
ERROR_LOG(m_logger) << "Could not enable DT Exercise of EXM";
throw std::exception();
}
// Add all available exercises to comboBox (do not add deleted or terminated exercises)
exmDtc->exercises([this](const ExerciseManagement::Exercise &data)
{
if (data.isDeleted())
return;
switch(data.state().underlying())
{
case ExerciseManagement::ExerciseState_def::Created:
case ExerciseManagement::ExerciseState_def::Started:
case ExerciseManagement::ExerciseState_def::Paused:
case ExerciseManagement::ExerciseState_def::Stopped:
{
m_exercises.insert(data.id(), data);
break;
}
default:
return;
}
});
for (const auto &exercise : qAsConst(m_exercises))
m_ui->cbExercises->addItem(QString::fromStdString(exercise.name()), exercise.id());
if (m_ui->cbExercises->count() == 0)
{
setEnabled(false);
setUnchangeable();
m_buttonBox->getButton(LtsWidgets::DialogButton::Print)->setEnabled(false);
return;
}
else
m_ui->label->setVisible(false);
m_model = new ExerciseAssignmentDialogModel(m_exercises, this);
// set initial exercise filter to the assigned exercises so that only buildings for exercise x are visible
auto exerciseChangedFn = [this](int index = -1)
{
Q_UNUSED(index);
m_modelAssignedFilter->setExerciseId(m_ui->cbExercises->currentData().toInt());
//m_ui->assignedExercises->expandAll();
};
connect(m_ui->cbExercises, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, exerciseChangedFn);
exerciseChangedFn();
// NOT live, otherwise:
connect(m_dtc.get(), &Interface::Dtc::mainStructureElementChanged,
this, &ExerciseAssignmentDialog::onMainStructureElementChanged);
auto cb = [this](const InfrastructureManagement::MainStructureElement &mse)
{
onMainStructureElementChanged(mse, DataModification::eAdded);
};
m_dtc->mainStructureElements(cb);
m_modelUnassignedFilter->setSourceModel(m_model);
m_ui->availableExercises->setModel(m_modelUnassignedFilter);
m_ui->availableExercises->setSortingEnabled(true);
m_ui->availableExercises->sortByColumn(0, Qt::SortOrder::AscendingOrder);
m_ui->availableExercises->setDragEnabled(true);
m_ui->availableExercises->viewport()->setAcceptDrops(true);
// m_ui->availableExercises->setDropIndicatorShown(true);
m_ui->availableExercises->setDragDropMode(QAbstractItemView::DragDrop);
m_ui->availableExercises->setDefaultDropAction(Qt::DropAction::MoveAction);
m_modelAssignedFilter->setSourceModel(m_model);
m_ui->assignedExercises->setModel(m_modelAssignedFilter);
m_ui->assignedExercises->setSortingEnabled(true);
m_ui->assignedExercises->sortByColumn(0, Qt::SortOrder::AscendingOrder);
m_ui->assignedExercises->setDragEnabled(true);
m_ui->assignedExercises->viewport()->setAcceptDrops(true);
// m_ui->assignedExercises->setDropIndicatorShown(true);
m_ui->assignedExercises->setDragDropMode(QAbstractItemView::DragDrop);
m_ui->assignedExercises->setDefaultDropAction(Qt::DropAction::MoveAction);
connect(m_modelAssignedFilter, &ExerciseAssignmentDialogModelFilterProxy::doRefilter,
this, &ExerciseAssignmentDialog::onUnassignedNeedsUpdate);
connect(m_modelUnassignedFilter, &ExerciseAssignmentDialogModelFilterProxy::doRefilter,
this, &ExerciseAssignmentDialog::onAssignedNeedsUpdate);
}
ExerciseAssignmentDialog::~ExerciseAssignmentDialog()
{
delete m_ui;
}
void ExerciseAssignmentDialog::onUnassignedNeedsUpdate(const QSet<QModelIndex> &setToExpand)
{
m_modelUnassignedFilter->invalidate();
for(auto indexToExpand : setToExpand)
m_ui->assignedExercises->expand(m_modelAssignedFilter->mapFromSource(indexToExpand));
slotChangesMade();
}
void ExerciseAssignmentDialog::onAssignedNeedsUpdate(const QSet<QModelIndex> &setToExpand)
{
m_modelAssignedFilter->invalidate();
for(auto indexToExpand : setToExpand)
m_ui->availableExercises->expand(m_modelUnassignedFilter->mapFromSource(indexToExpand));
slotChangesMade();
}
void ExerciseAssignmentDialog::retranslate()
{
translate(m_ui, tr("Exercise Assignment"));
}
void ExerciseAssignmentDialog::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &mse, DataModification modType)
{
auto seti = mse.structureElementTypeId();
if (seti != StructureElementType::Building &&
seti != StructureElementType::Announcement)
return;
// read exercise id and add to assigned exercises
QString displayName(m_dtc->getBuildingDisplayName(mse.id()));
if (displayName.isEmpty())
displayName = QString::fromStdString(mse.name());
int exerciseId;
try
{
exerciseId = JsonObject::fromJson(mse.properties()).value<JsonArray>(QStringLiteral("available_in_exercise")).at<int>(0); // get exercise id from properties json
}
catch(...)
{
exerciseId = 0;
}
if (!m_exercises.contains(exerciseId)) // if building is part of a deleted / terminated exercise, just add it to the unassigned buildings model
exerciseId = 0;
if (modType == DataModification::eModified)
m_model->updateMse(mse.id(), mse.groupId(), displayName, exerciseId);
else if (modType == DataModification::eAdded)
m_model->addMse(mse.id(), mse.groupId(), displayName, exerciseId);
else
m_model->removeMse(mse.id());
}
bool ExerciseAssignmentDialog::apply()
{
// XXX: HACK we don't know if user pressed ok or apply - apply() has to emidiately return, but the operation is async... so mimic ok/apply later, based on what was clicked
bool wasOkay = (sender() == static_cast<QObject *>(m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)));
LtsOverlayLoading *lol = new LtsOverlayLoading;
setOverlay(lol);
MSEModifyRequestCollector *collector = new MSEModifyRequestCollector(m_ftc, this);
{
QPushButton *pbCancel = new QPushButton;
pbCancel->setText(tr("Cancel"));
connect(pbCancel, &QPushButton::clicked,
this, [pbCancel, collector, lol]()
{
collector->cancel();
lol->setText(tr("Canceling ..."));
pbCancel->setEnabled(false);
});
qobject_cast<QVBoxLayout *>(lol->layout())->addWidget(pbCancel, 0, Qt::AlignmentFlag::AlignCenter);
}
const QVector<RequestInfos> &requestInfos = m_model->getRequestInfos();
for (const RequestInfos &ri : qAsConst(requestInfos))
{
MainStructureElementModifyRequest request;
MainStructureElement &mse = request;
mse = m_dtc->mainStructureElement(ri.mseId);
QJsonObject root;
try
{
root = JsonObject::fromJson(mse.properties()).object();
}
catch (...)
{
ERROR_LOG(m_logger) << "can't parse json for mseId " << ri.mseId << " and excId " << ri.exerciseId << " - using new json";
Q_ASSERT(false);
}
if (LtsUtils::ProjectSettings::getInstance()->value(QStringLiteral("ism"), QStringLiteral("enableSetRecordingDialog")).toBool())
setRecording(mse,ri.exerciseId != 0);
if (ri.exerciseId != 0)
root[QStringLiteral("available_in_exercise")] = QJsonArray({{ri.exerciseId}});
else
root.remove(QStringLiteral("available_in_exercise"));
mse.properties(QString::fromUtf8(QJsonDocument(root).toJson()).toStdString());
collector->addRequest(ri, request);
}
int initialCount = collector->count();
const QString leText(QStringLiteral("%1<br />%3 / %2").arg(tr("Assigning building(s) to exercise(s)")).arg(initialCount));
lol->setText(leText.arg(0));
connect(collector, &MSEModifyRequestCollector::requestComplete,
this, [collector, lol, leText, initialCount]()
{
lol->setText(leText.arg(initialCount - collector->count()));
});
connect(collector, &MSEModifyRequestCollector::finished,
this, [this, collector, wasOkay, lol](bool canceled)
{
unsetOverlay();
lol->deleteLater();
collector->deleteLater();
int failedCount = collector->failedCount();
if (failedCount > 0)
{
new LtsWidgets::LtsMessageDialogNew(this, tr("Error: %1 building(s) could not be assigned!").arg(failedCount));
return;
}
if (canceled)
return;
__unchange__(); // 'apply' behaviour
if (!wasOkay) // 'ok' behaviour
return;
closeRequest(); // we 'unchanged', so it will close directly
});
collector->go();
return false; // our request is async, say false, show a blocking dialog and do ok-/apply-button stuff later
}
/*!
* \brief ExerciseAssignmentDialog::setRecoding
* The property of an element with type indoorcamera or outdoorcamera can be updated
* \param mse
* \param state
*/
void ExerciseAssignmentDialog::setRecording(MainStructureElement &mse, bool state)
{
for(auto &el : mse.elements())
{
if(el.mountingPointTypeId() != MountingPointType::IndoorCamera &&
el.mountingPointTypeId() != MountingPointType::OutdoorCamera)
continue;
QJsonDocument propsDoc = QJsonDocument::fromJson(QByteArray::fromStdString(el.properties()));
QJsonObject propsObj;
if (propsDoc.isObject()) // if the given properties are no valid JSON object, the properties will be made from scratch
propsObj = propsDoc.object();
propsObj.insert(QLatin1String("record"), state);
el.properties(QJsonDocument(propsObj).toJson(QJsonDocument::Compact).toStdString());
}
}
void ExerciseAssignmentDialog::onPrint()
{
QTreeView tv;
tv.setModel(m_model);
tv.expandAll();
LtsPrinting::printView(m_session, this, &tv);
}
} // namespace InfrastructureManagement
//
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* The AssetListDialog shows how many assets are needed for a specific schedule, needed elsewhere,
* totally possessed, free, and missing
* $Id: ism_exercise_assignment_dialog.h 45935 2019-03-08 08:36:11Z ehrlicher $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/22_09_resmgmt2.0/Lts/Plugins/InfrastructureManagement/Gui/AudioVideoConfig/ExerciseAssignment/ism_exercise_assignment_dialog.h $
* $Author: ehrlicher $
* Created by Hannes Baeuerle, SLSE, 2017-06-29
* Refreshed by Mike Wille, SLTE, 2022-09-26
*/
#pragma once
#include <ExerciseManagement/exm_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <InfrastructureManagement/ism_ftc.h>
#include <InfrastructureManagement/logger.h>
#include <LtsWidgets/lts_dialog.h>
namespace Ui
{
class ExerciseAssignmentDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace InfrastructureManagement
{
class ExerciseAssignmentDialogModel;
class ExerciseAssignmentDialogModelFilterProxy;
class ExerciseAssignmentDialog
: public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit ExerciseAssignmentDialog(LtsSession::LtsSession *session, QWidget *parent = nullptr) noexcept(false);
~ExerciseAssignmentDialog();
private slots:
void onUnassignedNeedsUpdate(const QSet<QModelIndex> &setToExpand);
void onAssignedNeedsUpdate(const QSet<QModelIndex> &setToExpand);
void onPrint();
private:
void onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &mse, DataModification modType);
void retranslate() override;
bool apply() override;
void setRecording(MainStructureElement &mse, bool state);
private:
LtsSession::LtsSession *m_session;
Ui::ExerciseAssignmentDialog *m_ui;
std::shared_ptr<Interface::Dtc> m_dtc;
std::shared_ptr<Interface::Ftc> m_ftc;
ExerciseAssignmentDialogModel *m_model;
ExerciseAssignmentDialogModelFilterProxy *m_modelAssignedFilter;
ExerciseAssignmentDialogModelFilterProxy *m_modelUnassignedFilter;
QMap<qint32, ExerciseManagement::Exercise> m_exercises;
LOGGER_T m_logger;
};
} // namespace InfrastructureManagement
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExerciseAssignmentDialog</class>
<widget class="QWidget" name="ExerciseAssignmentDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>953</width>
<height>681</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#aa0000;&quot;&gt;WARNING: There are no exercises available. You can't assign buildings.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="2">
<layout class="QGridLayout" name="gridLayout"/>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lbExercises">
<property name="text">
<string>Assign to exercise:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cbExercises">
<property name="currentText">
<string notr="true"/>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTreeView" name="availableExercises">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTreeView" name="assignedExercises">
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::CopyAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,353 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#include "ism_exercise_assignment_dialog_model.h"
#include <QFont>
#include <QGuiApplication>
#include <InfrastructureManagement/ism_translation.h>
#ifdef LTS_DEBUG
#include <QTimer> // ftc delay
#endif
namespace InfrastructureManagement
{
ExerciseAssignmentDialogModel::ExerciseAssignmentDialogModel(const QMap<qint32, ExerciseManagement::Exercise> &exercises, QObject *parent) noexcept(false)
: QAbstractItemModel(parent)
, m_rootItem(new TreeItem(0, 0, tr("InvisibleRootItem"), -1))
, m_exercises(exercises)
, m_logger(getLogger("ExerciseAssignmentDialogModel"))
{
for (int it = BuildingGroup_def::UnknownGroup;
it <= BuildingGroup_def::Salchau; // WARNING: Change this, when enum changes!
++it)
{
m_rootItem->appendChild(new TreeItem(0, it, toString(static_cast<BuildingGroup_def::type>(it)), -1, m_rootItem));
}
}
ExerciseAssignmentDialogModel::~ExerciseAssignmentDialogModel()
{
delete m_rootItem;
}
void ExerciseAssignmentDialogModel::addMse(int mseId, int sectionId, const QString &displayName, int exerciseId)
{
const QList<TreeItem*> &childItems = m_rootItem->childItems();
for (auto sectionItem : qAsConst(childItems))
{
if (sectionItem->sectionId() == sectionId)
{
beginInsertRows(index(sectionItem), sectionItem->childCount(), sectionItem->childCount());
sectionItem->appendChild(new TreeItem(mseId, sectionId, displayName, exerciseId, sectionItem));
endInsertRows();
return;
}
}
ERROR_LOG(m_logger) << "Did not find section with enum value " << sectionId;
return;
}
void ExerciseAssignmentDialogModel::updateMse(int mseId, int sectionId, const QString &displayName, int exerciseId)
{
// Todo: Change of display name or moving to another section not yet implemented
Q_UNUSED(sectionId);
Q_UNUSED(displayName);
QModelIndex mi = findItemByMseId(mseId);
TreeItem* item = treeItem(mi);
if (!item)
return;
if (exerciseId != item->originalExerciseId())
{
item->setOriginalExerciseId(exerciseId);
emit dataChanged(mi, mi);
}
}
void ExerciseAssignmentDialogModel::removeMse(int mseId)
{
QModelIndex mi = findItemByMseId(mseId);
TreeItem* item = treeItem(mi);
if (!item)
return;
TreeItem* parent = item->parentItem();
if (!parent)
return;
beginRemoveRows(index(parent),item->childNumber(),item->childNumber());
parent->removeChild(item->childNumber());
endRemoveRows();
}
Qt::DropActions ExerciseAssignmentDialogModel::supportedDropActions() const
{
return Qt::MoveAction;
}
QVariant ExerciseAssignmentDialogModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() ||
(role != Qt::DisplayRole
&& role != Qt::FontRole)
)
return {};
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (!item)
return {};
if (role == Qt::FontRole)
{
if (item->originalExerciseId() == item->exerciseId())
return {};
QFont f;
f.setBold(true);
return f;
}
if (role == Qt::DisplayRole)
{
if (index.column() == 0)
return item->displayName();
if (index.column() == 1)
{
const int exerciseId = item->exerciseId();
if (exerciseId < 0)
return {};
if (exerciseId == 0)
return QStringLiteral("-");
return QString::fromStdString(m_exercises.value(exerciseId).name());
}
}
return {};
}
QVariant ExerciseAssignmentDialogModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Orientation::Horizontal || role != Qt::DisplayRole)
return QAbstractItemModel::headerData(section, orientation, role);
if (section == 0)
return tr("Building");
if (section == 1)
return tr("Assigned Exercises");
return {};
}
QModelIndex ExerciseAssignmentDialogModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return {};
TreeItem *parentItem;
if (!parent.isValid())
parentItem = m_rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
return {};
}
QModelIndex ExerciseAssignmentDialogModel::parent(const QModelIndex &mi) const
{
if (!mi.isValid())
return {};
TreeItem *childItem = static_cast<TreeItem*>(mi.internalPointer());
TreeItem *parentItem = childItem->parentItem();
return index(parentItem);
}
QModelIndex ExerciseAssignmentDialogModel::index(TreeItem *item) const
{
if (item == m_rootItem || !item)
return {};
return createIndex(item->childNumber(),0,item);
}
int ExerciseAssignmentDialogModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (!parent.isValid())
parentItem = m_rootItem;
else
parentItem = treeItem(parent);
return parentItem->childCount();
}
QSet<int> ExerciseAssignmentDialogModel::childMses(const QModelIndex &mi, int exerciseId) const
{
QSet<int> mses;
const QList<TreeItem*> &childItems = treeItem(mi)->childItems();
for(auto myChild : qAsConst(childItems))
{
if (myChild->exerciseId() == exerciseId)
mses << myChild->mseId();
}
return mses;
}
TreeItem *ExerciseAssignmentDialogModel::treeItem(const QModelIndex &index) const
{
return static_cast<TreeItem*>(index.internalPointer());
}
QModelIndex ExerciseAssignmentDialogModel::findItemByMseId(int mseId) const
{
// This only works as long as first level is section and second level is Building!
// Otherwise we need to go into recursion!
const QList<TreeItem*> &childItems = m_rootItem->childItems();
for (auto sectionItem : qAsConst(childItems))
{
const QList<TreeItem*> &sectionChildItems= sectionItem->childItems();
for (auto mseItem : qAsConst(sectionChildItems))
{
if (mseItem->mseId() == mseId)
return index(mseItem);
}
}
return {};
}
void ExerciseAssignmentDialogModel::setExerciseId(const QModelIndex &mi, int exerciseId)
{
TreeItem *ti = treeItem(mi);
if (!ti)
return;
ti->setExerciseId(exerciseId);
emit dataChanged(mi, mi, {Qt::DisplayRole, Qt::FontRole});
}
QVector<RequestInfos> ExerciseAssignmentDialogModel::getRequestInfos(bool all) const
{
QVector<RequestInfos> requestInfos;
m_rootItem->getAllUnderlyingRequestInfos(requestInfos, all);
return requestInfos;
}
TreeItem::TreeItem(int mseId, int sectionId, const QString &displayName, int exerciseId, TreeItem *parentItem)
: m_mseId(mseId)
, m_sectionId(sectionId)
, m_displayName(displayName)
, m_exerciseId(exerciseId)
, m_originalExerciseId(exerciseId)
, m_parentItem(parentItem)
{
}
TreeItem::~TreeItem()
{
qDeleteAll(m_childItems);
}
void TreeItem::appendChild(TreeItem *child)
{
m_childItems.append(child);
}
TreeItem *TreeItem::child(int row)
{
if (row < 0 || row >= m_childItems.size())
return nullptr;
return m_childItems.at(row);
}
int TreeItem::childNumber() const
{
if (m_parentItem)
return m_parentItem->childItems().indexOf(const_cast<TreeItem*>(this));
return 0;
}
int TreeItem::childCount() const
{
return m_childItems.count();
}
void TreeItem::removeChild(int id)
{
delete(m_childItems.value(id));
m_childItems.removeAt(id);
}
void TreeItem::getAllUnderlyingRequestInfos(QVector<RequestInfos> &requestInfos, bool all) const
{
const QList<TreeItem*> &childItemsRef = childItems();
for (auto myChild : qAsConst(childItemsRef))
{
if (all || (myChild->exerciseId() != myChild->originalExerciseId()))
requestInfos.append({myChild->mseId(), myChild->exerciseId()});
myChild->getAllUnderlyingRequestInfos(requestInfos, all);
}
}
} // namespace InfrastructureManagement
//
@@ -0,0 +1,109 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#pragma once
#include <InfrastructureManagement/logger.h>
#include <ExerciseManagement/exm.hpp>
#include <QAbstractItemModel>
#include <QSet>
namespace InfrastructureManagement
{
/// RequestInfos
struct RequestInfos
{
int mseId;
int exerciseId;
};
// class ExerciseAssignmentDialogModel;
class TreeItem
{
public:
explicit TreeItem(int mseId, int sectionId, const QString &displayName, int exerciseId, TreeItem *parentItem = nullptr);
~TreeItem();
void appendChild(TreeItem *child);
void removeChild(int id);
TreeItem* child(int row);
int childNumber() const;
int childCount() const;
const QList<TreeItem*> childItems() const { return m_childItems; }
int sectionId() const { return m_sectionId; }
int mseId() const { return m_mseId; }
QString displayName() const { return m_displayName; }
void setExerciseId(int exerciseId) { m_exerciseId = exerciseId; }
int exerciseId() const { return m_exerciseId; }
void setOriginalExerciseId(int exerciseId) { m_originalExerciseId = exerciseId; }
int originalExerciseId() const { return m_originalExerciseId; }
TreeItem* parentItem() { return m_parentItem; }
void getAllUnderlyingRequestInfos(QVector<RequestInfos> &requestInfos, bool all = false) const;
private:
int m_mseId;
int m_sectionId;
QString m_displayName;
int m_exerciseId;
int m_originalExerciseId;
QList<TreeItem*> m_childItems;
TreeItem* m_parentItem;
};
/// ExerciseAssignmentDialogModel
class ExerciseAssignmentDialogModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit ExerciseAssignmentDialogModel(const QMap<qint32, ExerciseManagement::Exercise> &exercises, QObject *parent = nullptr) noexcept(false);
~ExerciseAssignmentDialogModel();
void addMse(int mseId, int sectionId, const QString &displayName, int exerciseId);
void updateMse(int mseId, int sectionId, const QString &displayName, int exerciseId);
void removeMse(int mseId);
QModelIndex findItemByMseId(int mseId) const;
void setExerciseId(const QModelIndex &mi, int exerciseId);
int exerciseId(const QModelIndex &mi) const { return treeItem(mi) ? treeItem(mi)->exerciseId() : -1; }
int mseId(const QModelIndex &mi) const { return treeItem(mi) ? treeItem(mi)->mseId() : 0; }
QVector<RequestInfos> getRequestInfos(bool all = false) const;
QSet<int> childMses(const QModelIndex &mi, int exerciseId) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
protected:
int rowCount(const QModelIndex &parent = {}) const override;
QVariant data(const QModelIndex &index, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &index) const override;
int columnCount(const QModelIndex &) const override { return 2; }
Qt::DropActions supportedDropActions() const override;
private:
TreeItem* treeItem(const QModelIndex &index) const;
QModelIndex index(TreeItem *item) const;
TreeItem* m_rootItem;
QMap<qint32, ExerciseManagement::Exercise> m_exercises;
LOGGER_T m_logger;
};
} // namespace InfrastructureManagement
@@ -0,0 +1,183 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#include "ism_exercise_assignment_dialog_proxy_model.h"
#include "ism_exercise_assignment_dialog_model.h"
#include <QMimeData>
namespace InfrastructureManagement
{
void ExerciseAssignmentDialogModelFilterProxy::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<ExerciseAssignmentDialogModel *>(sourceModel);
Q_ASSERT(m_sourceModel != nullptr);
QSortFilterProxyModel::setSourceModel(sourceModel);
}
void ExerciseAssignmentDialogModelFilterProxy::setExerciseId(int exerciseId)
{
m_exerciseId = exerciseId;
invalidateFilter();
}
bool ExerciseAssignmentDialogModelFilterProxy::filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const
{
const QModelIndex sourceIndex = sourceModel()->index(sourceRow, 0, sourceParent);
if (!sourceIndex.isValid())
return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);
// Parents are true automatically...
if (m_sourceModel->mseId(sourceIndex) == 0)
return false;
return (m_sourceModel->exerciseId(sourceIndex) == m_exerciseId);
}
bool ExerciseAssignmentDialogModelFilterProxy::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
return QString::localeAwareCompare(sourceModel()->data(left).toString(), sourceModel()->data(right).toString()) < 0;
}
QVariant ExerciseAssignmentDialogModelFilterProxy::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section == 0 && orientation == Qt::Orientation::Horizontal && role == Qt::DisplayRole)
return m_exerciseId > 0 ? tr("Assigned Buildings") : tr("Unassigned Buildings");
return m_sourceModel->headerData(section, orientation, role);
}
QMimeData *ExerciseAssignmentDialogModelFilterProxy::mimeData(const QModelIndexList &indexes) const
{
QJsonObject root;
QJsonArray mseIds;
for(const QModelIndex mi : indexes)
{
QModelIndex miSource = mapToSource(mi);
int mseId = m_sourceModel->mseId(miSource);
if (mseId <= 0)
{
const QSet<int> childMses = m_sourceModel->childMses(miSource, m_exerciseId);
for (int mse : qAsConst(childMses))
mseIds << mse;
}
else
mseIds << mseId;
}
root[QStringLiteral("eams")] = mseIds;
root[QStringLiteral("oldExerciseId")] = m_exerciseId;
QMimeData *mimeData = new QMimeData;
mimeData->setData(QStringLiteral("rheinmetall/exercise-assignment"), QJsonDocument(root).toJson());
return mimeData;
}
Qt::ItemFlags ExerciseAssignmentDialogModelFilterProxy::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = Qt::ItemIsDropEnabled | QAbstractItemModel::flags(index);
if (index.isValid())
f |= Qt::ItemIsDragEnabled;
return f;
}
bool ExerciseAssignmentDialogModelFilterProxy::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (!canDropMimeData(data, action, row, column, parent))
return false;
const JsonObject root(JsonObject::fromJson(data->data(QStringLiteral("rheinmetall/exercise-assignment"))));
const JsonArray eams(root.value<JsonArray>(QStringLiteral("eams")));
QSet<QModelIndex> setToExpand;
const QVector<int> &eamsVec = eams.toVector<int>();
for (int mseId : qAsConst(eamsVec))
{
QModelIndex sourceMi = m_sourceModel->findItemByMseId(mseId);
setToExpand.insert(sourceMi.parent());
if (sourceMi.isValid())
m_sourceModel->setExerciseId(sourceMi, m_exerciseId);
}
// Don't know why we have to do this, as always "dataChanged()" is emitted...
invalidateFilter();
emit doRefilter(setToExpand);
return true;
}
Qt::DropActions ExerciseAssignmentDialogModelFilterProxy::supportedDropActions() const
{
return Qt::MoveAction;
}
bool ExerciseAssignmentDialogModelFilterProxy::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(parent)
if (action != supportedDropActions())
return false;
const JsonObject root(JsonObject::fromJson(data->data(QStringLiteral("rheinmetall/exercise-assignment"))));
int oldExerciseId = root.value<int>(QStringLiteral("oldExerciseId"));
return oldExerciseId != m_exerciseId;
}
} // namespace InfrastructureManagement
//
@@ -0,0 +1,122 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#pragma once
#include <InfrastructureManagement/logger.h>
#include <QSortFilterProxyModel>
#include <LtsUtilsCore/json_object.h>
using namespace LtsUtils;
namespace InfrastructureManagement
{
class ExerciseAssignmentDialogModel;
/// ExerciseAssignmentDialogModelFilterProxy
class ExerciseAssignmentDialogModelFilterProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
ExerciseAssignmentDialogModelFilterProxy(QObject *parent)
: QSortFilterProxyModel(parent)
, m_exerciseId(0)
, m_logger(getLogger("ExerciseAssignmentDialogModelFilterProxy"))
{
setRecursiveFilteringEnabled(true);
}
void setSourceModel(QAbstractItemModel *sourceModel) override;
void setExerciseId(int exerciseId);
signals:
void doRefilter(const QSet<QModelIndex> &setToExpand);
protected:
bool filterAcceptsRow(const int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &rioght) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int columnCount(const QModelIndex &) const override { return 1; }
QMimeData* mimeData(const QModelIndexList &_indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
Qt::DropActions supportedDropActions() const override;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override;
QStringList mimeTypes() const override
{
return { QStringLiteral("rheinmetall/exercise-assignment") };
}
class ExerciseAssignmentMime
{
public:
ExerciseAssignmentMime(const JsonObject &root) noexcept(false) // drop
: m_mseId(root.value<int>(QStringLiteral("mseId")))
{}
ExerciseAssignmentMime(int mseId) noexcept(false) // drag
: m_mseId(mseId)
{}
QJsonObject toJson() const
{
return { { QStringLiteral("mseId"), m_mseId } };
}
int mseId() const { return m_mseId; }
private:
int m_mseId;
};
private:
int m_exerciseId;
ExerciseAssignmentDialogModel *m_sourceModel;
LOGGER_T m_logger;
};
} // namespace InfrastructureManagement
//
@@ -0,0 +1,119 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#include "ism_exercise_assignment_dialog_requesters.h"
#include "ism_exercise_assignment_dialog_model.h"
#include <QFont>
#include <QGuiApplication>
#include <QMimeData>
#ifdef LTS_DEBUG
#include <QTimer> // ftc delay
#endif
namespace InfrastructureManagement
{
/// MSEModifyRequestCollector
void MSEModifyRequestCollector::next()
{
if (m_requests.isEmpty())
{
emit finished(m_canceled);
return;
}
MSEModifyRequester *requester = new MSEModifyRequester;
const QPair<RequestInfos, MainStructureElementModifyRequest> request(m_requests.takeFirst());
connect(requester, &MSEModifyRequester::finished,
this, [this](const QString &msg)
{
bool failed = (!msg.isEmpty());
if (failed)
{
m_failedCount++;
}
else
{
// ri.model->unchange(ri.rowId);
}
emit requestComplete(failed);
if (m_canceled)
m_requests.clear();
next();
});
auto go = [this, request, requester]()
{
m_ftc->mainStructureElementModifyRequest(request.second, requester);
};
#ifdef LTS_DEBUG
if (QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier))
{
QTimer::singleShot(3000, this, go);
return;
}
#endif
go();
}
} // namespace InfrastructureManagement
//
@@ -0,0 +1,132 @@
/**
* @RDE_HEADER@
* $Id: $
* $HeadURL: $
* $Author: $
*
* Created by Mike Wille, SLTE, 2022-09-27
*/
#pragma once
#include "ism_exercise_assignment_dialog_model.h"
#include <InfrastructureManagement/ism_ftc.h>
#include <QObject>
#include <QVector>
#include <QPair>
namespace InfrastructureManagement
{
/// MSEModifyRequester
class MSEModifyRequester : public QObject,
public Interface::Ftc::MainStructureElementModifyReplyHandler
{
Q_OBJECT
public:
MSEModifyRequester()
: QObject() // do NOT parent!
{}
void onMainStructureElementModifyReply(const Core::FunctionReplyObject &reply) override
{
if (reply.error())
emit finished(reply.translated());
else
emit finished({});
deleteLater();
}
signals:
void finished(const QString &msg);
};
/// MSEModifyRequestCollector
class MSEModifyRequestCollector : public QObject
{
Q_OBJECT
public:
MSEModifyRequestCollector(std::shared_ptr<Interface::Ftc> &ftc, QObject *parent)
: QObject(parent)
, m_ftc(ftc)
{}
void addRequest(const RequestInfos &requestInfos, const MainStructureElementModifyRequest &request)
{
m_requests += QPair<RequestInfos, MainStructureElementModifyRequest>(requestInfos, request);
}
void cancel()
{
m_canceled = true;
}
void go()
{
next();
}
int count() const
{
return m_requests.count();
}
int failedCount() const
{
return m_failedCount;
}
private slots:
void next();
signals:
void finished(bool canceled);
void requestComplete(bool failed);
private:
QVector<QPair<RequestInfos, MainStructureElementModifyRequest> > m_requests;
std::shared_ptr<Interface::Ftc> m_ftc;
bool m_canceled = false;
int m_failedCount = 0;
};
} // namespace InfrastructureManagement
//