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,811 @@
/**
* @RDE_HEADER@
* The MountingPointListDialog shows how many assets are needed for a specific schedule, needed elsewhere,
* totally possessed, free, and missing
* Modified version using the checkStateProxyModel to allow setting via the checkboxes of the tree structure hjl 5/19
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-11-03
*/
#include "ism_mounting_point_list_dialog.h"
#include "ui_ism_mounting_point_list_dialog.h"
#include "ism_mounting_point_list_dialog_model.h"
#include "ism_mounting_point_list_dialog_checkbox_proxy_model.h"
#include "ism_mounting_point_list_dialog_checkbox_delegate.h"
#include "ism_mounting_point_list_dialog_list_sort_model.h"
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsUtilsCore/dds_vector.h>
#include <InfrastructureManagement/ism_translation.h>
#include <InfrastructureManagement/ism_operator_rights.h>
#include <InfrastructureManagement/Shared/ism_mounting_point.h>
#include <FilterManagement/flt_building_cmpnt_helper.h> // getVisibleTypesOfFilter
#include <SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <SituationMap/Map/map.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsPrinting/printing.h>
#include <QItemDelegate>
#include <QTreeView>
#include <QListWidget>
#include <QMenu>
#include <QHeaderView>
#include <QScrollBar>
#include <QTimer>
using namespace LtsWidgets;
namespace InfrastructureManagement
{
/// DummyLtsColumnFilterProxyModel
class DummyLtsColumnFilterProxyModel : public LtsColumnFilterProxyModel
{
Q_OBJECT
public:
void setColumnStringFilter(const QString &, int ) override {}
void setColumnDataFilter(const QVariant &newFilterData, int column) override
{
emit signalSetColumnDataFilter(newFilterData, column);
}
bool filterAcceptsRow(int /*source_row*/, const QModelIndex &/*source_parent*/) const override
{
return true;
}
signals:
void signalSetColumnDataFilter(const QVariant &newFilterData, int column);
};
/// MountingPointListDialog
MountingPointListDialog::MountingPointListDialog(const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_requestHelper(new LtsUtils::RequestHelper(this))
, m_menuGroup(menuGroup)
, m_ismGuiIfc(nullptr)
, m_mapGuiInterface(nullptr)
, m_typeFilterColumn(0)
, m_typeFilterRow(0)
, m_ddsSelector(nullptr)
, m_scheduleId(-1)
, m_ui(new Ui::MountingPointListDialog)
, m_logger(getLogger("MountingPointListDialog"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_ui->setupUi(this);
setOnlineHelpFilename(QStringLiteral("Ressourcen-Mounting_Point_List"));
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &MountingPointListDialog::onPrint);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
MountingPointListDialog::~MountingPointListDialog()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_mapGuiInterface->removeObjectIndicator();
m_mapGuiInterface->setSkipMapObjectSelectionDialog(false);
if (m_ismGuiIfc)
m_ismGuiIfc->setSelector(nullptr);
if (m_buildingComponentSettings)
m_buildingComponentSettings->removeChangedCallback(this);
m_mapGuiInterface->setResourceManagerMode(m_oldResourceManagerMode);
delete m_ui;
delete m_ddsSelector;
delete m_requestHelper;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void MountingPointListDialog::retranslate()
{
translate(m_ui, m_dtc->mainSchedule(m_scheduleId), tr("Mounting Point List"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Unmount"));
}
bool MountingPointListDialog::init(LtsSession::LtsSession *session,
const std::shared_ptr<Interface::Dtc> &dtc,
const std::shared_ptr<Interface::Ftc> &ftc,
const std::shared_ptr<ForeignDataInterface> &dataInterface,
InfrastructureManagement::ForeignResourceManagementGuiInterface *ismGuiIfc,
const std::shared_ptr<SituationMap::ForeignGuiInterface> &mapIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> fltBuildingComponentIfc)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_dtc = dtc;
m_ftc = ftc;
m_dataIfc = dataInterface;
m_session = session;
m_mapGuiInterface = mapIfc;
m_ismGuiIfc = ismGuiIfc;
m_fltBuildingComponentIfc = fltBuildingComponentIfc;
disconnect(m_ui->mountingPointListView);
disconnect(this);
if (!m_ftc->enableInstrumentationAssetModify())
{
ERROR_LOG(m_logger) << "Could not enable FT InstrumentationAssetModify of ISM";
return false;
}
if (!m_ftc->enableInstrumentationAssetModify())
{
ERROR_LOG(m_logger) << "Could not enable FT enableInstrumentationAssetModify of ISM";
return false;
}
if (!m_ftc->enableInstrumentationAssetRemove())
{
ERROR_LOG(m_logger) << "Could not enable FT enableInstrumentationAssetRemove of ISM";
return false;
}
if (!m_ftc->enableInstrumentationAssetUnmount())
{
ERROR_LOG(m_logger) << "Could not enable FT enableInstrumentationAssetUnmount of ISM";
return false;
}
if (!m_ftc->enableMountingPointAssetModify())
{
ERROR_LOG(m_logger) << "Could not enable FT MountingPointAssetModify of ISM";
return false;
}
if (!m_ftc->enableMountingPointAssetRemove())
{
ERROR_LOG(m_logger) << "Could not enable FT MountingPointAssetRemove of ISM";
return false;
}
if (!m_ftc->enableStructureElementModify())
{
ERROR_LOG(m_logger) << "Could not enable FT enableStructureElementModify of ISM";
return false;
}
delete m_sourceModel;
m_sourceModel = new MountingPointListDialogModel(this);
const QSet<MountingPointType> filters = { MountingPointType::BuildingSensorModule,
MountingPointType::OutdoorCamera,
// MountingPointType::BuildingControlUnit,
MountingPointType::IndoorCamera,
MountingPointType::OutdoorIPS,
MountingPointType::IndoorIPS,
MountingPointType::EffectGenerationControlUnit,
#ifndef GUEZ_MODE
MountingPointType::AnnouncementUnit,
MountingPointType::BuildingNetworkSwitch
#endif
};
m_sourceModel->setMntPointFilterTypes(filters);
if (!m_sourceModel->initModel(m_dtc, m_dataIfc))
{
ERROR_LOG(m_logger) << "Could not init model";
delete m_sourceModel;
m_sourceModel = nullptr;
return false;
}
connect(m_sourceModel, &MountingPointListDialogModel::changesMade,
this, &MountingPointListDialog::slotChangesMade); /*[this]
{
slotChangesMade();
});*/
delete m_sortProxyModel;
m_sortProxyModel = new MountingPointListSortModel(this);
m_sortProxyModel->setTypeFilter(filters);
m_sortProxyModel->setSourceModel(m_sourceModel);
delete m_checkboxProxyModel;
m_checkboxProxyModel = new CheckboxProxyModel;
/* OKAY, get this...:
* We need LtsDataView's filtering mechanism. But the CheckBoxProxyModel needs to be the *last* Proxy Model
* because it checks which Checkbox state a leaf's parent and its parent's parents has. If the LtsDataView
* does the filtering, then the CheckboxProxyModel doesn't know what lines were hidden, so the Checkbox
* states don't match.
* If LtsDataView is given an LtsColumnFilterProxyModel instead of an ItemModel, it will not create a
* LtsColumnFilterProxyModel for filtering, but use the given one instead.
* Here's the super hacky solution:
* 1. First we need a barebones LtsColumnFilterProxyModel as the LAST model instance, for the LtsDataView,
* which just sends a signa - when setColumnStringFilter was called - to ...
* 2. the REAL LtsColumnFilterProxyModel, which does the real filtering. It needs to be put BEFORE
* the CheckboxFilterProxy. So the CheckboxFilterProxy will see ALL rows that remain visible.
*/
// Model => Sort => rcfpm => CheckboxProxy => dcfpm => View
m_sortProxyModel->setRecursiveFilteringEnabled(true); // non-leafs don't have a type to filter by, so tell LtsDataView's filter proxy model to ignore these and still show them if at least ONE child (recursive) of a non-leaf is visible
LtsColumnFilterProxyModel *rcfpm = new LtsColumnFilterProxyModel; // the REAL filter, put before CheckboxFilterProxy.
rcfpm->setSourceModel(m_sortProxyModel);
rcfpm->setRecursiveFilteringEnabled(true); // non-leafs don't have a type to filter by, so tell LtsDataView's filter proxy model to ignore these and still show them if at least ONE child (recursive) of a non-leaf is visible
m_checkboxProxyModel->setSourceModel(rcfpm);
DummyLtsColumnFilterProxyModel *dcfpm = new DummyLtsColumnFilterProxyModel; // the fake filter, put as LAST instance. Source model is the CheckboxFilterProxy.
dcfpm->setRecursiveFilteringEnabled(true); // non-leafs don't have a type to filter by, so tell LtsDataView's filter proxy model to ignore these and still show them if at least ONE child (recursive) of a non-leaf is visible
connect(dcfpm, &DummyLtsColumnFilterProxyModel::signalSetColumnDataFilter,
rcfpm, &LtsColumnFilterProxyModel::onSetColumnDataFilter);
dcfpm->setSourceModel(m_checkboxProxyModel);
m_ui->mountingPointListView->setModel(dcfpm);
// Needed as otherwise changes to the model do not affect the visibility of Rooms and Levels.
connect(m_sourceModel, &MountingPointListDialogModel::dataChanged, m_sortProxyModel, &MountingPointListSortModel::invalidateFilter);
if (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SystemConfiguration))
{
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load, this, &MountingPointListDialog::onUnmountButtonClicked);
m_buttonBox->setIcon(LtsWidgets::DialogButton::Load, QIcon(QLatin1String("icon:fixed_video_announcement.svg")));
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setEnabled(false);
}
// connect signals of TableView/CustomView: (for MapObjectIndicator
connect(m_ui->mountingPointListView, &LtsDataView::LtsDataView::clicked, this, &MountingPointListDialog::onDataViewClicked);
connect(m_ui->mountingPointListView, &LtsDataView::LtsDataView::signalEntered, this, &MountingPointListDialog::onDataViewEntered);
//------------- Filter ----------------
// SWPÄ 2/22 / GUEZE-1478:
// Diese Filter werden ersetzt durch übergeordnete BuildingComponentnFilter (Extras | SituationMap | BuildingComponent)
// subscribe to Filter:
if (m_fltBuildingComponentIfc)
{
const int operatorId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
m_buildingComponentSettings = m_fltBuildingComponentIfc->buildingComponentSettingsForOperator(operatorId, m_session->workspaceId());
if (m_buildingComponentSettings)
m_buildingComponentSettings->addChangedCallback(this); // will call onBuildingComponentSettingsChanged
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " addChangedCallback fltBuildingComponentIfc failed";
#ifdef LTS_DEBUG
Q_ASSERT(false);
#endif // LTS_DEBUG
}
// call on init:
onBuildingComponentSettingsChanged();
}else
{
ERROR_LOG(m_logger) << "No BuildingCOmponentIfc ";
}
// honor visibilityChanged (dialog toogles between foreground/background)
connect(this, &MountingPointListDialog::visibilityChanged, this, &MountingPointListDialog::onVisibilityChanged);
// show first tree level when activating the dialog
m_ui->mountingPointListView->treeView()->expandToDepth(0);
delete m_ddsSelector;
m_ddsSelector = nullptr;
m_ddsSelector = new DdsSelector(dtc, ftc, m_dataIfc, m_requestHelper);
// always select from the map when the dialog is visible
m_ismGuiIfc->setSelector(m_ddsSelector);
// SWPÄ GUEZE-1478 (?)
// Mit Aufschalten des Dialog wird umgeschaltet auf Modus SituationMap::ResourceManagerMode::RMM_PlanDefinitionView
// mit RMM_PlanDefinitionView sollte die Notification disabled werden...
// bis Ende des Dialoges...
m_oldResourceManagerMode = m_mapGuiInterface->resourceManagerMode();
// since switching _to_ RMM_PlanDefinitionView could be timeconsuming:
// (and give the GlobalWaitDialog time being displayed)
QTimer::singleShot(100, this, [this] () {
m_mapGuiInterface->setResourceManagerMode(SituationMap::ResourceManagerMode::RMM_PlanDefinitionView);
});
m_mapGuiInterface->setSkipMapObjectSelectionDialog(true);
m_scheduleId = m_dataIfc->scheduleId();
connect(m_dataIfc.get(), &ForeignDataInterface::scheduleIdChanged,
this, &MountingPointListDialog::onScheduleIdChanged);
m_ui->mountingPointListView->setContextMenuPolicyPerView(LtsDataView::LtsDataView::Page::TABLE, Qt::CustomContextMenu);
connect(m_ui->mountingPointListView->treeView(), &QTreeView::customContextMenuRequested,
this, &MountingPointListDialog::onCustomContextMenu);
m_ui->mountingPointListView->treeView()->setColumnWidth(0, 500);
m_ui->mountingPointListView->treeView()->setColumnWidth(1, 300);
m_ui->mountingPointListView->treeView()->setColumnWidth(2, 300);
m_ui->mountingPointListView->treeView()->header()->setSectionResizeMode(1, QHeaderView::Fixed);
m_ui->mountingPointListView->searchView()->setColumnWidth(0, 500);
m_ui->mountingPointListView->searchView()->setColumnWidth(1, 300);
m_ui->mountingPointListView->searchView()->setColumnWidth(2, 300);
m_ui->mountingPointListView->searchView()->header()->setSectionResizeMode(1, QHeaderView::Fixed);
connect(m_ui->mountingPointListView->searchView()->horizontalScrollBar(), &QScrollBar::rangeChanged,
this, [this]()
{
m_ui->mountingPointListView->slotSetHorizontalRange(0,0);
}, Qt::QueuedConnection);
connect(m_ui->mountingPointListView->treeView()->horizontalScrollBar(), &QScrollBar::rangeChanged,
this, [this]()
{
m_ui->mountingPointListView->slotSetHorizontalRange(0,0);
}, Qt::QueuedConnection);
m_ui->mountingPointListView->treeView()->setItemDelegateForColumn(MountingPointListDialogModel::Columns::NAME, new CheckboxDelegate);
// connect onEscapePressed of Map:
if (m_mapGuiInterface && m_mapGuiInterface->map())
connect(m_mapGuiInterface->map().data(), &SituationMap::Map::escapePressed,
this, &MountingPointListDialog::onEscapePressed, Qt::UniqueConnection);
// InfoMountButton:
connect(m_ui->tbMountMode, &QToolButton::clicked,
this, &MountingPointListDialog::onInfoMountButtonClicked, Qt::UniqueConnection);
onUpdateInfoMountButtonAndBehaviour();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void MountingPointListDialog::getMountingPointPlanData(const QModelIndex &index, std::vector<MountingPointPlan>&modify, const QHash<int, MountingPointPlan> &mppMap)
{
NGLTreeItem *item = m_sourceModel->privateData(index);
if (!item)
return;
if (item->data().canConvert<SimpleMountingPointData>())
{
const SimpleMountingPointData &data = item->data().value<SimpleMountingPointData>();
DEBUG_LOG(m_logger) << "Item: " << m_sourceModel->data(index, Qt::DisplayRole).toString()
<< ", Structure Element Id: " << data.id;
if (mppMap.contains(data.id))
{
if (data.aimState != mppMap[data.id].mountingPointState())
modify.push_back(MountingPointPlan(data.id, data.aimState));
}
else if (data.aimState != MountingPointState_def::Idle)
{
DEBUG_LOG(m_logger) << "Not available in Map. Desired: " << data.aimState;
modify.push_back(MountingPointPlan(data.id, data.aimState));
}
}
for (int i =0 ; i< m_sourceModel->rowCount(index); i++ )
getMountingPointPlanData(m_sourceModel->index(i, 0, index), modify, mppMap);
}
bool MountingPointListDialog::hasMountingPointMountedAsset(const QModelIndex &index)
{
NGLTreeItem *treeItem = m_sourceModel->privateData(mapToSourciest(index));
if (!treeItem)
return false;
QVariant v = treeItem->data();
if (v.canConvert<SimpleMountingPointData>())
{
// MountingPoint:
const SimpleMountingPointData itemData = v.value<SimpleMountingPointData>();
int assetId = m_dtc->getMountedAssetId(itemData.id);
return !(assetId == -1);
}
return false;
}
void MountingPointListDialog::onMountingPointPlanModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onInstrumentationAssetModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onInstrumentationAssetUnmountReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onStructureElementModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onMountingPointAssetModifyReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onMountingPointAssetRemoveReply(const Core::FunctionReplyObject &replyObject)
{
m_requestHelper->requestReply(replyObject);
}
void MountingPointListDialog::onBuildingComponentSettingsChanged()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
// update view via m_sortProxyModel:
if (!m_buildingComponentSettings)
return;
const FilterManagement::BuildingComponentSettings::BldCmpSettings &bldCmpSettings =
m_buildingComponentSettings->bldCmpSettings();
// 1) setTypeFilter:
{
const QSet<MountingPointType> visibleTypes = FilterManagement::getVisibleTypesOfFilter(bldCmpSettings);
m_sortProxyModel->setTypeFilter(visibleTypes);
}
// 2) floor:
{
// '-10' is floorId of Exterior / Outdoor
// Filter has HIDDEN floorIds
// (good reason: if filter not available (or nothing is checked), show all item on map...)
// so translate here to visibleFloors:
const QSet<int> &hiddenFloorIds = bldCmpSettings.buildingSettings.hiddenFloorIds;
const QSet<int> &availableFloors = m_sourceModel->getAvailableFloors();
QSet<int> visibleFloors;
for (int floor : availableFloors)
if (!hiddenFloorIds.contains(floor))
visibleFloors.insert(floor);
// qCritical() << " hiddenFloorIds" << hiddenFloorIds;
// qCritical() << " availableFloors" << availableFloors;
// qCritical() << " visibleFloors" << visibleFloors;
m_sortProxyModel->setFloorFilter(visibleFloors);
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " END";
}
bool MountingPointListDialog::apply()
{
std::vector<MountingPointPlan> modify;
const QModelIndex root = m_sourceModel->getIndexOfVisibleRootItem();
if (!root.isValid())
return false;
MainSchedule mainSchedule = m_dataIfc->mainSchedule(m_dataIfc->scheduleId());
QHash<int,MountingPointPlan> mppMap;
for(const auto &elem : mainSchedule.elements())
mppMap[elem.structureElementId()] = elem;
getMountingPointPlanData(root, modify, mppMap);
if (modify.empty())
return true;
MountingPointPlanModifyRequest req(m_dataIfc->scheduleId(), modify);
return m_requestHelper->startSingleRequest([req, this]()
{
return m_ftc->mountingPointPlanModifyRequest(req, this);
});
}
void MountingPointListDialog::onPrint()
{
MainSchedule ms = m_dtc->mainSchedule(m_dataIfc->scheduleId());
QString header;
header += QStringLiteral("<p>");
header += tr("Instrumentation Plan: %1").arg(QString::fromStdString(ms.name()));
header += QStringLiteral("<br/>");
header += tr("Status: %1").arg(toString(ms.statusId()));
header += QStringLiteral("<br/>");
header += tr("Created: %1").arg(DTG::toStringMSec(ms.timeCreated()));
header += QStringLiteral("<br/>");
if (ms.timeStart().is_set())
header += tr("START: %1").arg(DTG::toStringMSec(ms.timeStart().get()));
else
header += tr("START: %1").arg(tr("[N/A]"));
header += QStringLiteral(" - ");
if (ms.timeEnd().is_set())
header += tr("END: %1").arg(DTG::toStringMSec(ms.timeEnd().get()));
else
header += tr("END: %1").arg(tr("[N/A]"));
header += QStringLiteral("<br/>");
header += QStringLiteral("</p>");
m_ui->mountingPointListView->expandAll();
LtsPrinting::printView(m_session, this, m_ui->mountingPointListView->treeView(), header);
}
void MountingPointListDialog::onScheduleIdChanged(int32_t scheduleId)
{
if (scheduleId == m_scheduleId)
return;
// scheduleId changed, data is maybe invalid -> close dialog:
deleteLater();
}
void MountingPointListDialog::onVisibilityChanged(bool visible)
{
Q_UNUSED(visible);
// maybe dialog is put to background -> leave mount mode:
onUpdateInfoMountButtonAndBehaviour();
}
void MountingPointListDialog::onCustomContextMenu(QPoint pos)
{
QMenu *contextMenu = new QMenu(this);
contextMenu->setAttribute(Qt::WA_DeleteOnClose);
{
QAction *action = new QAction(tr("Expand All"), this);
action->setEnabled(true);
connect(action, &QAction::triggered, this, [this]()
{
m_ui->mountingPointListView->expandAll();
});
contextMenu->addAction(action);
}
{
QAction *action = new QAction(tr("Collapse All"), this);
action->setEnabled(true);
connect(action, &QAction::triggered, this, [this]()
{
m_ui->mountingPointListView->collapseAll();
});
contextMenu->addAction(action);
}
contextMenu->addSeparator();
QModelIndex index = m_ui->mountingPointListView->treeView()->indexAt(pos);
bool hasChildren = m_ui->mountingPointListView->treeView()->model()->rowCount(index) > 0;
{
QAction *action = new QAction(tr("Expand"), this);
action->setEnabled(hasChildren);
connect(action, &QAction::triggered, this, [this, index]()
{
#if QT_VERSION < QT_VERSION_CHECK(5,13,0)
m_ui->mountingPointListView->treeView()->expand(index);
#else
m_ui->mountingPointListView->treeView()->expandRecursively(index);
#endif
});
contextMenu->addAction(action);
}
{
QAction *action = new QAction(tr("Collapse"), this);
action->setEnabled(hasChildren);
connect(action, &QAction::triggered, this, [this, index]()
{
m_ui->mountingPointListView->treeView()->collapse(index);
});
contextMenu->addAction(action);
}
contextMenu->popup(m_ui->mountingPointListView->treeView()->mapToGlobal(pos));
}
void MountingPointListDialog::onDataViewClicked(const QModelIndex &mi)
{
m_clickedObjectStore.reset();
onDataViewEntered(mi);
// suppress Entered for a while:
m_clickedObjectStore.timestampMsec = QDateTime::currentMSecsSinceEpoch();
bool enabled = LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SystemConfiguration);
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setEnabled(hasMountingPointMountedAsset(mi) && enabled);
}
QModelIndex MountingPointListDialog::mapToSourciest(const QModelIndex &index)
{
QModelIndex currentIndex = index;
const QAbstractItemModel *model = index.model();
//qWarning() << "start" << model << currentIndex;
while (true)
{
const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel *>(model);
if (proxyModel == nullptr)
break;
model = proxyModel->sourceModel();
currentIndex = proxyModel->mapToSource(currentIndex);
//qWarning() << "new" << model << currentIndex;
}
//qWarning() << "done" << model << currentIndex;
return currentIndex;
}
void MountingPointListDialog::onDataViewEntered(const QModelIndex & mi)
{
if ( (QDateTime::currentMSecsSinceEpoch() - m_clickedObjectStore.timestampMsec) < 500)
return;
const QModelIndex &theIndex(mapToSourciest(mi));
NGLTreeItem *treeItem = m_sourceModel->privateData(theIndex);
if (!treeItem)
return;
const QVariant v(treeItem->data());
if (v.canConvert<SimpleStructureElementData>())
{
// Building:
const SimpleStructureElementData itemData = v.value<SimpleStructureElementData>();
m_clickedObjectStore.mot = SituationMap::MapObjectType::MOT_Building;
m_clickedObjectStore.entityId = itemData.mseId;
}
else if (v.canConvert<SimpleMountingPointData>())
{
// MountingPoint:
const SimpleMountingPointData itemData = v.value<SimpleMountingPointData>();
m_clickedObjectStore.mot = SituationMap::MapObjectType::MOT_MountingPoint;
m_clickedObjectStore.entityId = itemData.id;
}
else
{
m_clickedObjectStore.reset();
}
if (m_clickedObjectStore.entityId>=0)
{
m_mapGuiInterface->objectIndicatorToMapObject(m_clickedObjectStore.mot, m_clickedObjectStore.entityId, true);
if (QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ShiftModifier))
m_mapGuiInterface->centerMapToMapObject(m_clickedObjectStore.mot, m_clickedObjectStore.entityId);
}
}
void MountingPointListDialog::onEscapePressed()
{
if (!m_mapGuiInterface)
return;
m_mapGuiInterface->removeObjectIndicator();
}
void MountingPointListDialog::onUnmountButtonClicked()
{
if (m_ui->mountingPointListView->selectionModel()->selectedRows().size() < 1)
return;
NGLTreeItem *treeItem = m_sourceModel->privateData(mapToSourciest(m_ui->mountingPointListView->currentIndex()));
if (!treeItem)
return;
const QVariant v = treeItem->data();
if (v.canConvert<SimpleMountingPointData>())
{
const SimpleMountingPointData itemData = v.value<SimpleMountingPointData>();
LtsWidgets::Button button = LtsWidgets::LtsMessageDialog::showMessage(
this, tr("Do you really want to unmount asset on selected mounting point?"), tr("Unmount Assets"), LtsWidgets::Behaviour::Modal,
{ LtsWidgets::Button::Yes, LtsWidgets::Button::No });
if (button == LtsWidgets::Button::Yes)
{
const int assetId = m_dtc->getMountedAssetId(itemData.id);
if (assetId > 0)
{
InstrumentationAssetUnmountRequest req(assetId);
bool ret = m_requestHelper->startSingleRequest([req, this]()
{
return m_ftc->instrumentationAssetUnmountRequest(req, this);
});
if (!ret)
WARNING_LOG(m_logger)<<" couldn't unmount asset on MountingPoinAsset id: "<<req.id();
else
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load,false);
}
else // mounted asset id not found
{
LtsWidgets::LtsMessageDialog::showMessage(
this, tr("No mounted asset found on this mounting point"), tr("Unmount Assets"), LtsWidgets::Behaviour::Modal, LtsWidgets::Button::Ok);
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Load,false);
}
} // end if allow unmount asset
}
else
{
ERROR_LOG(m_logger)<<" Couldn't convert to SimpleMountingPointData: ";
}
}
void MountingPointListDialog::onInfoMountButtonClicked(bool)
{
// toggle btn and sitMap behaviour, but keep visibility in mind:
switch (m_infoMount) {
case InfoMount::IM_INFO:
m_infoMount = InfoMount::IM_MOUNT;
break;
case InfoMount::IM_MOUNT:
m_infoMount = InfoMount::IM_INFO;
break;
default:
// empty
break;
} // switch
onUpdateInfoMountButtonAndBehaviour();
}
void MountingPointListDialog::onUpdateInfoMountButtonAndBehaviour()
{
// // maybe dialog is put to background and is hidden now -> leave mount mode:
// // in that case switch to 'IM_INFO' tempoary:
InfoMount infoMountTemp = m_infoMount;
const bool bDialogIsVisi = this->isVisible();
if (!bDialogIsVisi)
infoMountTemp = InfoMount::IM_INFO;
switch (infoMountTemp) {
case InfoMount::IM_INFO:
{
QColor backgroundColor = QToolButton().palette().window().color();
QColor textColor = QGuiApplication::palette().color(QPalette::Text);
QString style = QStringLiteral("QToolButton { background-color: rgb(%1,%2,%3); color: rgb(%4,%5,%6) }")
.arg(backgroundColor.red()).arg(backgroundColor.green()).arg(backgroundColor.blue())
.arg(textColor.red()).arg(textColor.green()).arg(textColor.blue());
m_ui->tbMountMode->setText(tr("Info"));
m_ui->tbMountMode->setStyleSheet(style);
m_ui->tbMountMode->setToolTip(tr("Click on map object opens info dialog"));
m_ismGuiIfc->setSelector(nullptr);
m_mapGuiInterface->setSkipMapObjectSelectionDialog(false);
}
break;
case InfoMount::IM_MOUNT:
{
QColor color = QGuiApplication::palette().color(QPalette::Link);
QString style = QStringLiteral("QToolButton { background-color: rgb(%1,%2,%3); color: white }").arg(
QString::number(color.red()), QString::number(color.green()), QString::number(color.blue())
);
m_ui->tbMountMode->setStyleSheet(style);
m_ui->tbMountMode->setText(tr("Map Selection"));
m_ui->tbMountMode->setToolTip(tr("Click on map object toogles aim state"));
m_ismGuiIfc->setSelector(m_ddsSelector);
m_mapGuiInterface->setSkipMapObjectSelectionDialog(true);
}
break;
default:
// empty
break;
} // switch
}
//------------------------------------------------------------------------------
//( class ClickedObjectStore
//------------------------------------------------------------------------------
void MountingPointListDialog::ClickedObjectStore::reset()
{
mot = SituationMap::MapObjectType::MOT_Undefined;
entityId = -1;
timestampMsec = 0;
}
} // namespace InfrastructureManagement
#include "ism_mounting_point_list_dialog.moc"
@@ -0,0 +1,167 @@
/**
* @RDE_HEADER@
* The MountingPointListDialog shows how many assets are needed for a specific schedule, needed elsewhere,
* totally possessed, free, and missing
* Modified version using the checkStateProxyModel to allow setting via the checkboxes of the tree structure hjl 5/19
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-11-03
*/
#pragma once
#include <InfrastructureManagement/ism_dtc.h>
#include <InfrastructureManagement/ism_ftc.h>
#include <InfrastructureManagement/logger.h>
#include <InfrastructureManagement/ism_data_plugin_interface.h>
#include <SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <InfrastructureManagement/ism_resource_management_gui_plugin_interface.h>
#include <InfrastructureManagement/Gui/ResourceManagement/MountingPointMapSelection/ism_dds_selector.h>
#include <InfrastructureManagement/Gui/ResourceManagement/MountingPointMapSelection/ism_selector.h>
#include <FilterManagement/flt_building_cmpnt_plugin_interface.h> // honor current filter settings (menue SituationMap)
#include <FilterManagement/flt_building_cmpnt_settings.h> // hold current filter settings (and for addCSubscriberCallback)
#include <LtsWidgets/lts_dialog.h>
#include <LtsDataView/check_state_proxy_model.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <QItemDelegate>
class QCheckBox;
namespace Ui
{
class MountingPointListDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace LtsUtils
{
class RequestHelper;
}
namespace SituationMap
{
enum class MapObjectType;
}
namespace InfrastructureManagement
{
class MountingPointListDialogModel;
class MountingPointListSortModel;
class CheckboxProxyModel;
class MountingPointListDialog final
: public LtsWidgets::LtsDialog
, protected Interface::Ftc::MountingPointPlanModifyReplyHandler
, protected Interface::Ftc::InstrumentationAssetUnmountReplyHandler
, protected Interface::Ftc::StructureElementModifyReplyHandler
, protected Interface::Ftc::InstrumentationAssetModifyReplyHandler
, protected Interface::Ftc::MountingPointAssetModifyReplyHandler
, protected Interface::Ftc::MountingPointAssetRemoveReplyHandler
, protected FilterManagement::BuildingComponentSettings::SettingsChangedCallback
{
Q_OBJECT
public:
explicit MountingPointListDialog(const QString &menuGroup, QWidget *parent = nullptr);
~MountingPointListDialog();
bool init(LtsSession::LtsSession *session,
const std::shared_ptr<Interface::Dtc> &dtc,
const std::shared_ptr<Interface::Ftc> &ftc,
const std::shared_ptr<ForeignDataInterface> &dataInterface,
InfrastructureManagement::ForeignResourceManagementGuiInterface *ismGuiIfc,
const std::shared_ptr<SituationMap::ForeignGuiInterface> &mapIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> fltBuildingComponentIfc
);
void onMountingPointPlanModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void onInstrumentationAssetModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void onInstrumentationAssetUnmountReply(const Core::FunctionReplyObject &replyObject) override final;
void onStructureElementModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void onMountingPointAssetModifyReply(const Core::FunctionReplyObject &replyObject) override final;
void onMountingPointAssetRemoveReply(const Core::FunctionReplyObject &replyObject) override final;
void onBuildingComponentSettingsChanged() override final;
void getMountingPointPlanData(const QModelIndex &index, std::vector<MountingPointPlan> &add, const QHash<int, MountingPointPlan> &mppMap);
bool hasMountingPointMountedAsset(const QModelIndex &index);
friend class MountingPointSelector;
private slots:
void onVisibilityChanged(bool visible);
void onPrint();
void onScheduleIdChanged(int32_t scheduleId);
void onCustomContextMenu(QPoint pos);
void onDataViewClicked(const QModelIndex &mi);
void onDataViewEntered(const QModelIndex &mi);
void onEscapePressed();
void onUnmountButtonClicked();
void onInfoMountButtonClicked(bool);
void onUpdateInfoMountButtonAndBehaviour();
private: // methods
void retranslate() override;
bool apply() override final;
bool isIncludedId(int32_t mountingPointId) const;
QModelIndex mapToSourciest(const QModelIndex &index);
private: // attributes
MountingPointListDialogModel *m_sourceModel{nullptr};
MountingPointListSortModel *m_sortProxyModel{nullptr};
CheckboxProxyModel *m_checkboxProxyModel{nullptr};
std::shared_ptr<Interface::Dtc> m_dtc;
std::shared_ptr<Interface::Ftc> m_ftc;
std::shared_ptr<ForeignDataInterface> m_dataIfc;
LtsUtils::RequestHelper *m_requestHelper{nullptr};
QString m_menuGroup;
LtsSession::LtsSession *m_session{nullptr};
ForeignResourceManagementGuiInterface *m_ismGuiIfc{nullptr};
std::shared_ptr<SituationMap::ForeignGuiInterface> m_mapGuiInterface;
struct ClickedObjectStore
{
SituationMap::MapObjectType mot {SituationMap::MapObjectType::MOT_Undefined};
int entityId {-1};
qint64 timestampMsec {-1};
void reset();
} m_clickedObjectStore;
unsigned int m_typeFilterColumn;
unsigned int m_typeFilterRow;
DdsSelector *m_ddsSelector{nullptr};
int32_t m_scheduleId;
// click leads to a) InfoDialog or b) mounting/changing aimState (Idle, Equipped, Empty)
enum class InfoMount
{
IM_INFO, ///! on Click on MapObject, normal map behaviour: info dialog
IM_MOUNT ///! on Click on MapObject, change aim state
}
m_infoMount{InfoMount::IM_INFO};
SituationMap::ResourceManagerMode m_oldResourceManagerMode{SituationMap::ResourceManagerMode::Undefined};
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> m_fltBuildingComponentIfc;
std::shared_ptr<FilterManagement::BuildingComponentSettings> m_buildingComponentSettings;
Ui::MountingPointListDialog *m_ui{nullptr};
LOGGER_T m_logger;
}; // class MountingPointListDialog
} // namespace InfrastructureManagement
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MountingPointListDialog</class>
<widget class="QWidget" name="MountingPointListDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>833</width>
<height>478</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Asset Stocks</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1" colspan="2">
<widget class="LtsDataView::LtsDataView" name="mountingPointListView" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="rootIsDecorated" stdset="0">
<bool>true</bool>
</property>
<property name="showColumnCount" stdset="0">
<bool>false</bool>
</property>
<property name="toolButtonsVisible" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>439</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<widget class="QToolButton" name="tbMountMode">
<property name="minimumSize">
<size>
<width>150</width>
<height>50</height>
</size>
</property>
<property name="text">
<string notr="true">mountMode</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,52 @@
#include "ism_mounting_point_list_dialog_checkbox_delegate.h"
#include "ism_mounting_point_list_dialog_model.h"
#include <QPainter>
namespace InfrastructureManagement
{
CheckboxDelegate::CheckboxDelegate(QObject *parent)
: QItemDelegate(parent)
{}
void CheckboxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QItemDelegate::paint(painter, option, index);
if (index.column() != MountingPointListDialogModel::Columns::NAME)
return;
painter->save();
painter->setClipRect(option.rect);
painter->setClipping(true);
const QVariant value(index.data(Qt::UserRole + 1));
if (value.isValid() && value.canConvert<int>())
{
QPixmap x(QStringLiteral(":/style/checkbox-multi-state.svg"));
switch (value.toInt())
{
case MountingPointState::Equipped:
x = QPixmap(QStringLiteral(":/style/checkbox-true.svg"));
break;
case MountingPointState::Idle:
x = QPixmap(QStringLiteral(":/style/checkbox-empty.svg"));
break;
case MountingPointState::Empty:
x = QPixmap(QStringLiteral(":/style/checkbox-false.svg"));
break;
}
const QStyleOptionViewItem opt = setOptions(index, option);
const QRect checkRect(QItemDelegate::doCheck(opt, opt.rect, /*value*/1));
painter->drawPixmap(checkRect.adjusted(3,0,3,0), x, QRect());
//QItemDelegate::drawCheck(painter, opt, checkRect, Qt::CheckState::Checked); // test
}
painter->restore();
}
} // namespace InfrastructureManagement
@@ -0,0 +1,18 @@
#pragma once
#include "ism_mounting_point_list_dialog_checkbox_delegate.h"
#include <QItemDelegate>
namespace InfrastructureManagement
{
class CheckboxDelegate : public QItemDelegate
{
Q_OBJECT
public:
CheckboxDelegate(QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
};
} // namespace InfrastructureManagement
@@ -0,0 +1,168 @@
#include "ism_mounting_point_list_dialog_checkbox_proxy_model.h"
#include "ism_mounting_point_list_dialog_model.h"
namespace InfrastructureManagement
{
Qt::ItemFlags CheckboxProxyModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return sourceModel()->flags(index); // delegate to source model for further processing if necessary
// Sets only the checkbox nodes on user checkable,
// checkbox leaves must be handled in source model.
QModelIndex sourceIndex = mapToSource(index);
Qt::ItemFlags fl = sourceModel()->flags(sourceIndex);
if (index.column() == 0)
return fl | Qt::ItemIsUserCheckable;
return fl;
}
QVariant CheckboxProxyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
const QModelIndex sourceIndex(mapToSource(index));
// delegate everything except CheckStateRole to source model
if (role != (Qt::UserRole + 1))
return sourceModel()->data(sourceIndex, role);
// delegate a leaf too with CheckStateRole to source model
if (isLeaf(index))
return sourceModel()->data(sourceIndex, role);
// delegate to source model for further processing of further columns if necessary
if (index.column() != MountingPointListDialogModel::Columns::NAME)
return sourceModel()->data(sourceIndex, role);
// sets the node checkboxes and decoration depending on the entries in the tree structure
bool lastMpsSet = false; // std::optional >_>
int lastMps = -2;
int childCount = rowCount(index);
for (int childRow = 0; childRow < childCount; ++childRow)
{
const QModelIndex childIndex(this->index(childRow, 0, index));
const QVariant value(data(childIndex, (Qt::UserRole + 1)));
if (value.isValid() && value.canConvert<int>())
{
int state = value.toInt();
if (!lastMpsSet)
{
lastMpsSet = true;
lastMps = state;
continue;
}
if (lastMps != state) // if at one point there is a different status inside one parent, show the 'partial' checkbox state as there are mixed states
return -2;
}
}
return lastMps;
}
QString mpsToString(const int mps)
{
switch ((int)mps)
{
case -2:
return QStringLiteral("PARTIALLY");
case -1:
return QStringLiteral("EMPTY");
case 0:
return QStringLiteral("IDLE");
case 1:
return QStringLiteral("EQUIPPED");
default:
return QStringLiteral("( !UNKNOWN! )");
}
}
QString mpsToString(const MountingPointState_def::type mps)
{
return mpsToString((int)mps);
}
bool CheckboxProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
/* this setData is called if the user clicks on a checkbox
* then this setData calls the sourceModel's setData to finally
* set the underlying checkboxes */
if (!index.isValid())
return false;
const QModelIndex sourceIndex(mapToSource(index));
// delegate to source model for further processing of further columns if necessary
if (index.column() != MountingPointListDialogModel::Columns::NAME)
return sourceModel()->setData(sourceIndex, value, role);
/* Qt calls setData with role == Qt::CheckStateRole if the user clicks on a checkbox.
* We need to know at this point if wither the call to this method was called by Qt,
* or if this method called setData recursively. If this is the first call, the new
* checkbox state has to be determined at this point. To determine if the call to
* setData is a recursive call, we set role to Qt::UserRole+1. Kind of a hack. */
int newMps = value.toInt();
if (role == Qt::CheckStateRole)
{
//newMps = (int)MountingPointState_def::Idle;
int mps = index.data(Qt::UserRole + 1).toInt();
switch (mps)
{
case -2: // SPECIAL CASE, not a real MPS, but the indicator that there are mixed states on the underlying children
newMps = (int)MountingPointState_def::Idle;
break;
case (int)MountingPointState_def::Idle:
newMps = (int)MountingPointState_def::Equipped;
break;
case (int)MountingPointState_def::Equipped:
newMps = (int)MountingPointState_def::Empty;
break;
case (int)MountingPointState_def::Empty:
newMps = (int)MountingPointState_def::Idle;
break;
default:
return false;
}
}
if (isLeaf(index))
{
// delegate a leaf to source model
sourceModel()->setData(sourceIndex, newMps, role);
}
else
{
// update underlying children recursively (Additional nodes with leaves may also exist)
const int childCount = rowCount(index);
for (int childRow = 0; childRow < childCount; ++childRow)
{
const QModelIndex childIndex(this->index(childRow, 0, index));
setData(childIndex, newMps, (Qt::UserRole + 1));
}
}
return true;
}
bool CheckboxProxyModel::isLeaf(const QModelIndex &index) const
{
const QModelIndex sourceIndex(mapToSource(index));
return sourceModel()->flags(sourceIndex).testFlag(Qt::ItemIsUserCheckable);
}
} // namespace InfrastructureManagement
@@ -0,0 +1,19 @@
#pragma once
#include <QIdentityProxyModel>
namespace InfrastructureManagement
{
class CheckboxProxyModel : public QIdentityProxyModel
{
public:
using QIdentityProxyModel::QIdentityProxyModel;
Qt::ItemFlags flags(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
private:
bool isLeaf(const QModelIndex &index) const;
};
} // namespace InfrastructureManagement
@@ -0,0 +1,120 @@
#include "ism_mounting_point_list_dialog_list_sort_model.h"
#include <Plugins/InfrastructureManagement/Shared/ism_mounting_point.h>
namespace InfrastructureManagement
{
void MountingPointListSortModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<MountingPointListDialogModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(m_sourceModel);
}
void MountingPointListSortModel::setTypeFilter(const QSet<MountingPointType> &visibleTypes)
{
if (m_visibleTypes == visibleTypes)
return;
m_visibleTypes = visibleTypes;
invalidateFilter();
}
void MountingPointListSortModel::addToTypeFilter(MountingPointType mpt, bool bAdd)
{
auto visibleTypes = m_visibleTypes;
if (bAdd)
{
if(mpt == MountingPointType::IndoorIPS)
{
visibleTypes.insert(MountingPointType::OutdoorIPS);
}
visibleTypes.insert(mpt);
}
else
{
if(mpt == MountingPointType::IndoorIPS)
{
visibleTypes.remove(MountingPointType::OutdoorIPS);
}
visibleTypes.remove(mpt);
}
setTypeFilter(visibleTypes);
}
void MountingPointListSortModel::setFloorFilter(const QSet<int> &visibleFloors)
{
if (m_visibleFloors == visibleFloors)
return;
m_visibleFloors = visibleFloors;
invalidateFilter();
}
void MountingPointListSortModel::addToFloorFilter(int floor, bool bAdd)
{
auto visibleFloors = m_visibleFloors;
if (bAdd)
visibleFloors.insert(floor);
else
visibleFloors.remove(floor);
setFloorFilter(visibleFloors);
}
bool MountingPointListSortModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel)
return true;
bool isRootItem = !source_parent.isValid();
if (isRootItem)
{
Q_ASSERT(source_row == 0);
return true; // root item shall always be visible
}
const QModelIndex childIdx = m_sourceModel->index(source_row, 0, source_parent);
const NGLTreeItem *itemChild = m_sourceModel->privateData(childIdx);
Q_ASSERT(itemChild);
bool isLeaf = itemChild->data().canConvert<SimpleMountingPointData>();
if (isLeaf)
{
const SimpleMountingPointData data = itemChild->data().value<SimpleMountingPointData>();
Q_ASSERT(data.isMountingPoint());
return m_visibleTypes.contains(data.mpType) && m_visibleFloors.contains(data.floor);
}
else
{
if (!itemChild->data().canConvert<SimpleStructureElementData>())
return true;
const SimpleStructureElementData ssed = itemChild->data().value<SimpleStructureElementData>();
if (ssed.floor != -20 && !m_visibleFloors.contains(ssed.floor))
return false;
}
// Qt5.11: setRecursiveFilteringEnabled(true);
for (int i = 0; i < itemChild->childCount(); ++i)
{
if (filterAcceptsRow(i, childIdx))
return true;
}
return false;
}
bool MountingPointListSortModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel || source_left.column() != source_right.column())
return BaseClass::lessThan(source_left, source_right);
const NGLTreeItem *itemLeft = m_sourceModel->privateData(source_left);
const NGLTreeItem *itemRight = m_sourceModel->privateData(source_right);
if (!itemLeft || !itemRight)
return BaseClass::lessThan(source_left, source_right);
const SimpleMountingPointData left = itemLeft->data().value<SimpleMountingPointData>();
const SimpleMountingPointData right = itemRight->data().value<SimpleMountingPointData>();
if (left.name != right.name)
return left.name < right.name;
return left.id < right.id;
}
} // namespace InfrastructureManagement
@@ -0,0 +1,38 @@
#pragma once
#include "ism_mounting_point_list_dialog_model.h"
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <QSet>
namespace InfrastructureManagement
{
class MountingPointListSortModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
using BaseClass = LtsWidgets::LtsColumnFilterProxyModel;
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
void setTypeFilter(const QSet<MountingPointType> &visibleTypes);
void addToTypeFilter(MountingPointType mpt, bool bAdd);
void setFloorFilter(const QSet<int> &visibleFloors);
void addToFloorFilter(int floor, bool bAdd);
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
using LtsWidgets::LtsColumnFilterProxyModel::invalidateFilter;
private:
MountingPointListDialogModel *m_sourceModel = nullptr;
QSet<MountingPointType> m_visibleTypes;
QSet<int> m_visibleFloors;
};
} // namespace InfrastructureManagement
@@ -0,0 +1,478 @@
/**
* @RDE_HEADER@
* The MountingPointListDialogModel provices data for MountingPointListDialog
* Modified version using the checkStateProxyModel to allow setting via the checkboxes of the tree structure hjl 5/19
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-11-03
*/
#include "ism_mounting_point_list_dialog_model.h"
#include "ism_mounting_point_list_dialog_list_sort_model.h"
#include <InfrastructureManagement/ism_translation.h>
#include <Plugins/InfrastructureManagement/Shared/ism_mounting_point.h>
#include <QTimer>
// #define BENCHMARK
#ifdef BENCHMARK
# include <QElapsedTimer>
# include <QDebug>
#endif
namespace InfrastructureManagement
{
MountingPointListDialogModel::MountingPointListDialogModel(QObject *parent)
: NGLTreeModel(parent, new NGLTreeItem(QVariant()), { tr("Name"), tr("Type"), tr("Asset"), tr("Aim State") })
, m_visRoot(new NGLTreeItem(tr("Mounting Points")))
, m_dtc(nullptr)
, m_scheduleId(-1)
, m_dataChangedTimer(new QTimer(this))
, m_logger(getLogger("MountingPointListDialogModel"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_dataChangedTimer->setSingleShot(true);
m_dataChangedTimer->setInterval(100);
connect(m_dataChangedTimer, &QTimer::timeout, this, &MountingPointListDialogModel::emitDataChanged);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QVariant MountingPointListDialogModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
NGLTreeItem *item = privateData(index);
const int indexColumn = index.column();
if (role == Qt::DisplayRole)
{
if (indexColumn != NAME && !item->data().canConvert<SimpleMountingPointData>())
return QVariant();
const SimpleMountingPointData itemData = item->data().value<SimpleMountingPointData>();
switch (indexColumn)
{
case NAME:
if (item->data().canConvert<SimpleMountingPointData>())
return item->data().value<SimpleMountingPointData>().name;
if (item->data().canConvert<SimpleStructureElementData>())
return item->data().value<SimpleStructureElementData>().name;
if (item->data().canConvert<SimpleBuildingGroupData>())
return item->data().value<SimpleBuildingGroupData>().name;
return tr("all");
case STATE:
if (itemData.seType == StructureElementType::MountingPoint)
return toString(itemData.aimState);
break;
case ASSET_BARCODE:
if (item->data().canConvert<SimpleMountingPointData>())
return item->data().value<SimpleMountingPointData>().assetBarcode;
break;
case TYPE:
if (itemData.seType == StructureElementType::MountingPoint)
{
if(itemData.mpType == MountingPointType::OutdoorIPS)
return toString(MountingPointType::IndoorIPS);
return toString(itemData.mpType);
}
break;
}
}
if (role == LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole
&& indexColumn == TYPE)
{
const SimpleMountingPointData itemData = item->data().value<SimpleMountingPointData>();
if (itemData.mpType.underlying() == MountingPointType::Unknown)
{
ERROR_LOG(m_logger) << "unknown!!!";
return QVariant(); // return INVALID QVariant to let LtsDataView's proxy filter model ignore this row while filtering / aka. don't hide it when filtering
}
ERROR_LOG(m_logger) << "mpType = " << itemData.mpType.underlying();
if(itemData.mpType == MountingPointType::OutdoorIPS)
return MountingPointType::IndoorIPS;
return itemData.mpType.underlying();
}
if (role == Qt::CheckStateRole && index.column() == NAME)
return Qt::Unchecked; // dummy
if (role == Qt::UserRole && item->data().canConvert<SimpleMountingPointData>())
return item->data().value<SimpleMountingPointData>().id;
if (role == (Qt::UserRole + 1) && item->data().canConvert<SimpleMountingPointData>())
return item->data().value<SimpleMountingPointData>().aimState.underlying();
return {};
}
bool MountingPointListDialogModel::initModel(const std::shared_ptr<Interface::Dtc> &dtc, const std::shared_ptr<ForeignDataInterface> &dataInterface)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (m_dtc == dtc)
return true;
if (!(dtc->enableMainStructureElement() &&
dtc->enableMountingPointAsset()))
{
ERROR_LOG(m_logger) << "Could not enable Datatopics";
return false;
}
m_dtc = dtc;
m_dataInterface = dataInterface;
m_scheduleId = m_dataInterface->scheduleId();
connect(m_dataInterface.get(), &ForeignDataInterface::mountingPointChanged,
this, &MountingPointListDialogModel::onMountingPointChanged);
DEBUG_LOG(m_logger) << "initModel successful";
initData();
beginResetModel();
endResetModel();
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
return true;
}
QModelIndex MountingPointListDialogModel::getIndexOfVisibleRootItem() const
{
return indexFromItem(m_visRoot);
}
void MountingPointListDialogModel::initData()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
#ifdef BENCHMARK
QElapsedTimer elapsedTimer1;
elapsedTimer1.start();
#endif // BENCHMARK
NGLTreeModel::resetModel();
m_rootItem->appendChild(m_visRoot);
// const InfrastructureManagement::MainSchedule &currentSchedule = m_dtc->mainSchedule(m_scheduleId);
const QMap<int, InfrastructureManagement::MainStructureElement> &mainStructEles = m_dtc->mainStructureElements();
// cfroebel 2018-11-07:
// The algorithm of building the tree structure assumes that
// the StructureElements are hierarchically ordered, i.e. that the
// MountingPoint comes after its Room, Level and Building.
// Is that guaranteed by the service?
m_mountingPointIndexes.reserve(mainStructEles.size());
m_availableBuildingGroups.reserve(mainStructEles.size());
for (const MainStructureElement &mse : mainStructEles)
{
// only consider buildings
if (mse.structureElementTypeId() != StructureElementType::Building)
continue;
// 1- if a buildingGroup modelIndex for current building dosen't exist
// a new one will be first added and used as parentIndex for the blg_Index.
QModelIndex groupIndex;
const StructureElement bse= setBuildingGroup(mse.groupId());
// Outdoor camera will not be displayed
if (mse.groupId() != BuildingGroup::OutdoorCameras)
{
if (m_availableBuildingGroups.contains(bse.id()) == false)
{
SimpleBuildingGroupData buildingGroup;
buildingGroup.name = QString::fromStdString(bse.displayName());
NGLTreeItem *groupItem = new NGLTreeItem(QVariant::fromValue(buildingGroup));
QModelIndex groupParentIndex;
groupParentIndex = indexFromItem(m_visRoot);
insertRow(rowCount(groupParentIndex), groupParentIndex, groupItem);
groupIndex = index(groupItem->row(), 0, groupParentIndex);
m_mountingPointIndexes.insert(bse.id(), groupIndex);
m_availableBuildingGroups.insert(bse.id());
}
else
{
groupIndex = m_mountingPointIndexes.value(bse.id(), indexFromItem(m_visRoot));
}
}
for (const StructureElement &currentElement : mse.elements())
{
NGLTreeItem *item;
if (currentElement.structureElementTypeId() == StructureElementType::MountingPoint)
{
const MountingPoint &mountingPoint = m_dataInterface->mountingPoint(currentElement.id());
if (!mountingPoint.isValid())
continue;
if (mountingPoint.type() == MountingPointType::Unknown ||
mountingPoint.type() == MountingPointType::BuildingControlUnit ||
mountingPoint.type() == MountingPointType::VirtualIPS ||
mountingPoint.type() == MountingPointType::MeteoDataUnit ||
mountingPoint.type() == MountingPointType::BuildingNetworkSwitch ||
mountingPoint.type() == MountingPointType::OutdoorCamera)
continue;
SimpleMountingPointData mpData;
mpData.seType = currentElement.structureElementTypeId();
mpData.id = mountingPoint.id();
mpData.name = mountingPoint.name();
mpData.aimState = mountingPoint.aimState();
mpData.mpType = mountingPoint.type();
mpData.floor = mountingPoint.floor();
// attention: getMountedAssetId could be expensive
// const int assetId = m_dtc->getMountedAssetId(mountingPoint.data().id());
// use already available mountingPoint instead: :-)
const int assetId = mountingPoint.associatedAssetId();
mpData.assetBarcode = QStringLiteral("-");
if(assetId != -1)
{
mpData.assetBarcode = QString::fromStdString(m_dtc->instrumentationAsset(assetId).barcode());
}
item = new NGLTreeItem(QVariant::fromValue(mpData));
}
else
{
SimpleStructureElementData ssed;
ssed.mseId = mse.id();
if (currentElement.structureElementTypeId() == StructureElementType::Floor)
ssed.floor = QString::fromStdString(currentElement.name()).split(QLatin1Literal("_")).value(1, QLatin1String("-10")).toInt();
else
ssed.floor = -20;
ssed.name = currentElement.displayName().empty() ? QString::fromStdString(currentElement.name()) : QString::fromStdString(currentElement.displayName());
item = new NGLTreeItem(QVariant::fromValue(ssed));
m_availableFloors << ssed.floor;
}
const auto &path = currentElement.path();
QModelIndex parentIndex;
if (path.size() > 1)
{
parentIndex = m_mountingPointIndexes.value(path.at(path.size() - 2), indexFromItem(m_visRoot)); //default sollte nie erreicht werden
if (!parentIndex.isValid())
{
ERROR_LOG(m_logger) << "Did not find parent for structure element with id " << currentElement.id() << " mseid: " << mse.id();
for (const StructureElement &se : mse.elements())
ERROR_LOG(m_logger) << " se: " << se.id() << ", " << se.displayName() << ", " << se.name();
}
}
else
{
// 2- Append building to it section index
if(groupIndex.isValid() == false)
{
parentIndex = indexFromItem(m_visRoot);
}
else
{
parentIndex = groupIndex;
}
}
insertRow(rowCount(parentIndex), parentIndex, item);
m_mountingPointIndexes.insert(currentElement.id(), index(item->row(), 0, parentIndex));
}
}
m_availableFloors << -10;
#ifdef BENCHMARK
qCritical() << " MountingPointListDialogModel::init-BENCHMARK: took " << elapsedTimer1.elapsed() << " [ms]";
#endif // BENCHMARK
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QSet<int> MountingPointListDialogModel::getAvailableFloors() const
{
return m_availableFloors;
}
StructureElement MountingPointListDialogModel::setBuildingGroup(int groupId)
{
StructureElement se{};
InfrastructureManagement::BuildingGroup_def::type buildingGroup = static_cast<InfrastructureManagement::BuildingGroup_def::type>(groupId);
if(buildingGroup == 0)
{
buildingGroup = InfrastructureManagement::BuildingGroup_def::UnknownGroup;
}
se.id() = maxIntValue - static_cast<int>(buildingGroup);
se.name() = InfrastructureManagement::toString(buildingGroup).toStdString();
se.displayName() = InfrastructureManagement::toString(buildingGroup).toStdString();
se.structureElementTypeId() = InfrastructureManagement::StructureElementType::Section;
return se;
}
Qt::ItemFlags MountingPointListDialogModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags fl = NGLTreeModel::flags(index);
if (!index.isValid())
return fl;
NGLTreeItem *item = privateData(index);
bool isLeaf = item->data().canConvert<SimpleMountingPointData>();
if (isLeaf && index.column() == Columns::NAME)
return fl | Qt::ItemIsUserCheckable | Qt::ItemIsUserTristate;
return fl;
}
bool MountingPointListDialogModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// is called by the CheckboxProxyModel's setData method
if (index.column() != NAME || !index.isValid())
return NGLTreeModel::/**/setData(index, value, role);
auto lambda = [this, &value](const QModelIndex &idx)
{
NGLTreeItem *item = privateData(idx);
if (!item->data().canConvert<SimpleMountingPointData>()) // not a leaf?
{
Q_ASSERT(false); // CheckboxProxyModel never calls this model's setData if the index is not a leaf
/* we don't know what state the parent is, wo don't know how many children there REALLY are.
* all this is hidden behind multiple proxy models. so send a signal to the dialog,
* get the clicked parent's state and set all its (visible!) children.
* at least that's the plan .... */
return;
}
SimpleMountingPointData smpd = item->data().value<SimpleMountingPointData>();
smpd.aimState = (MountingPointState::inner_enum)value.toInt();
item->setData(QVariant::fromValue(smpd));
emit changesMade();
};
if (index.parent().isValid())
{
lambda(index);
}
else
{
for (const QModelIndex &idx : qAsConst(m_mountingPointIndexes))
lambda(idx);
}
m_dataChangedTimer->start(); // ugly hack
return true;
}
QVariant MountingPointListDialogModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal
&& role == LtsWidgets::LtsColumnFilterProxyModel::ComboboxFilterRole
&& section == TYPE)
return m_cbxFilterType;
return BaseClass::headerData(section, orientation, role);
}
void MountingPointListDialogModel::setMntPointFilterTypes(QSet<MountingPointType> types)
{
LtsWidgets::ComboboxFilterContent cfc;
LtsWidgets::ComboboxFilterData cfd;
for (const auto es : qAsConst(types))
{
if (es == MountingPointType::OutdoorIPS)
continue;
cfd.data = es.underlying();
cfd.text = toString(es);
cfc.data.push_back(cfd);
// DEBUG_LOG(m_logger)<<" cfd.text: "<<cfd.text;
}
cfc.sortItems = true;
m_cbxFilterType = cfc.toVariant();
}
void MountingPointListDialogModel::onMountingPointChanged(const MountingPoint &mountingPoint, DataModification modType)
{
if (modType != DataModification::eModified)
return;
QModelIndex index = m_mountingPointIndexes.value(mountingPoint.id());
if (!index.isValid())
return;
NGLTreeItem *item = privateData(index);
if (!(item && item->data().canConvert<SimpleMountingPointData>()))
return;
SimpleMountingPointData mpData = item->data().value<SimpleMountingPointData>();
mpData.name = mountingPoint.name();
mpData.aimState = mountingPoint.aimState();
const int assetId = mountingPoint.associatedAssetId(); // (m_dtc->getMountedAssetId(mountingPoint.data().id());
mpData.assetBarcode = QStringLiteral("-");
mpData.floor = mountingPoint.floor();
if(assetId != -1)
{
mpData.assetBarcode = QString::fromStdString(m_dtc->instrumentationAsset(assetId).barcode());
}
item->setData(QVariant::fromValue(mpData));
m_dataChangedTimer->start();
}
void MountingPointListDialogModel::onScheduleIdChanged(int32_t scheduleId)
{
// is this unreachable code?
if (scheduleId == m_scheduleId)
return;
DEBUG_LOG(m_logger) << "onScheduleIdChanged: " << scheduleId;
m_scheduleId = scheduleId;
// !!! Maybe this is critical !!!
// !!! Maybe this is critical !!!
// !!! Maybe this is critical !!!
// m_dataInterface::setScheduleId will update the MountingPoints (aimState) according schedule!!!
m_dataInterface->setScheduleId(m_scheduleId);
initData();
}
void MountingPointListDialogModel::emitDataChanged()
{
QModelIndex vri = getIndexOfVisibleRootItem();
emit dataChanged(vri, index(rowCount(vri) -1, Columns::STATE, vri), {Qt::DisplayRole, Qt::CheckStateRole});
}
//
// MountingPointListSortModel
//
} // namespace InfrastructureManagement
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
* The MountingPointListDialogModel provices data for MountingPointListDialog
* Modified version using the checkStateProxyModel to allow setting via the checkboxes of the tree structure hjl 5/19
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Baeuerle, SLSE, 2016-11-03
*/
#pragma once
#include <InfrastructureManagement/ism_dtc.h>
#include <InfrastructureManagement/logger.h>
#include <QSet>
#include <nglwidgets/ngltreemodel.h>
#include <nglwidgets/ngltreeitem.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
#include <LtsDataView/check_state_proxy_model.h>
namespace InfrastructureManagement
{
struct SimpleMountingPointData
{
QString name;
int32_t id = 0; // StructureElementId
StructureElementType seType;
MountingPointType mpType;
MountingPointState aimState;
// int assetId; Chance to only reload assetBarcode, if assetId changes...
QString assetBarcode;
int32_t floor;
bool isMountingPoint() const { return seType == StructureElementType::MountingPoint; }
};
struct SimpleStructureElementData
{
int mseId;
QString name;
int floor;
};
struct SimpleBuildingGroupData
{
QString name;
};
class MountingPointListDialogModel final : public NGLTreeModel
{
Q_OBJECT
using BaseClass = NGLTreeModel;
public:
enum Columns
{
NAME,
TYPE,
ASSET_BARCODE,
STATE
};
explicit MountingPointListDialogModel(QObject *parent = nullptr);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
/**
* Handles the representation of data.
* In case of CheckStateRole only leafs will be handled.
* Nodes are handled in the upstream CheckStateProxyModel class.
* @param[in] index item index to be handled
* @param[in] role role to which the data relates
*
*/
QVariant data(const QModelIndex &index, int role) const override final;
bool initModel(const std::shared_ptr<Interface::Dtc> &dtc, const std::shared_ptr<ForeignDataInterface> &dataInterface);
QModelIndex getIndexOfVisibleRootItem() const;
/**
* Sets only the leafs of the tree view to Qt::ItemIsUserCheckable.
* Nodes are handled in the upstream CheckStateProxyModel class.
* @param[in] index item index to be handled
*
*/
Qt::ItemFlags flags(const QModelIndex &index) const override final;
/**
* Sets the check state data according to their check box in the internal model.
* CheckState data can be set for leafs only.
* Nodes are handled in the upstream CheckStateProxyModel class.
* @param[in] index item index to be handled
* @param[in] value value to be set
* @param[in] role role to which the data relates (here especially Qt::CheckStateRole)
*
*/
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
void setMntPointFilterTypes(QSet<InfrastructureManagement::MountingPointType> types);
QSet<int> getAvailableFloors() const;
StructureElement setBuildingGroup(int groupId);
static const int maxIntValue = std::numeric_limits<int>::max();
signals:
void changesMade();
public slots:
void onMountingPointChanged(const MountingPoint &mountingPoint, DataModification modType);
private slots:
void onScheduleIdChanged(int32_t scheduleId);
void emitDataChanged();
private:
void initData();
friend class MountingPointListSortModel;
NGLTreeItem *m_visRoot;
std::shared_ptr<Interface::Dtc> m_dtc;
std::shared_ptr<InfrastructureManagement::ForeignDataInterface> m_dataInterface;
int32_t m_scheduleId;
QHash<int, QModelIndex> m_mountingPointIndexes;
QTimer *m_dataChangedTimer{nullptr};
QSet<int> m_availableFloors;
QSet<int> m_availableBuildingGroups;
QVariant m_cbxFilterType;
LOGGER_T m_logger;
}; // class MountingPointListDialogModel
} // namespace InfrastructureManagement
Q_DECLARE_METATYPE(InfrastructureManagement::SimpleMountingPointData)
Q_DECLARE_METATYPE(InfrastructureManagement::SimpleStructureElementData)
Q_DECLARE_METATYPE(InfrastructureManagement::SimpleBuildingGroupData)