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,135 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLSE, 2017-02-10
#
# EventFilter
SET(EventFilter_SRCS
EventFilter/AttributeEditingModeUI.h
EventFilter/AttributeEditingModeUI.cpp
EventFilter/AttributeSelectionModeUI.h
EventFilter/AttributeSelectionModeUI.cpp
EventFilter/FilterWindow.h
EventFilter/FilterWindow.cpp
EventFilter/FilterTableEntry.h
EventFilter/FilterTableEntry.cpp
EventFilter/FilterTableEntryAttribute.h
EventFilter/FilterTableEntryAttribute.cpp
EventFilter/interpreter/FilterWorker.h
EventFilter/interpreter/FilterWorker.cpp
EventFilter/widgets/AbstractAttributeWidget.cpp
EventFilter/widgets/ComboBox.cpp
EventFilter/widgets/TimeRangeWidget.cpp
EventFilter/widgets/AreaWidget.cpp
EventFilter/widgets/DefaultRangeWidget.cpp
EventFilter/widgets/CheckBoxWidget.cpp
EventFilter/widgets/RadiusWidget.cpp
EventFilter/interpreter/InfoWindow.h
EventFilter/interpreter/InfoWindow.cpp
EventFilter/interpreter/FilterEquationFunctions.cpp
EventFilter/interpreter/FilterValueInterpreter.cpp
EventFilter/widgets/AbstractAttributeWidget.h
EventFilter/widgets/ComboBox.h
EventFilter/widgets/TimeRangeWidget.h
EventFilter/widgets/AreaWidget.h
EventFilter/widgets/DefaultRangeWidget.h
EventFilter/widgets/CheckBoxWidget.h
EventFilter/widgets/RadiusWidget.h
EventFilter/interpreter/FilterEquationFunctions.h
EventFilter/interpreter/FilterValueInterpreter.h
detail_data.h
detail_data.cpp
excon_filter.h
excon_filter.cpp
logger.h
logger.cpp
)
SET(EventFilter_UICS
EventFilter/ui/FilterWindowUi.ui
)
# Timeline
SET(tln_view_gui_SRCS
attribute_value_lookup.cpp
attribute_value_lookup.h
tln_view_gui_plugin.cpp
tln_view_gui_plugin.h
tln_dialog.cpp
tln_dialog.h
tln_dialog_model.cpp
tln_dialog_model.h
tln_cache_dialog.cpp
tln_cache_dialog.h
tln_show_details.cpp
tln_show_details.h
tln_event_list.h
tln_event_list.cpp
tln_event_list_model.h
tln_event_list_model.cpp
tln_event_list_columns.h
tln_event_list_columns.cpp
tln_legend.h
tln_legend.cpp
${EventFilter_SRCS}
)
NGL_WRAP_UI(tln_view_gui_SRCS
tln_dialog.ui
tln_cache_dialog.ui
tln_show_details.ui
tln_event_list.ui
tln_event_list_columns.ui
tln_legend.ui
${EventFilter_UICS}
)
NGL_ADD_LIBRARY(WS TlnViewGuiPlugin MODULE ${tln_view_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(TlnViewGuiPlugin
Qt5::Widgets
Qt5::WebSockets
LtsAppManager
LtsDataView
LtsTimelineClassic
LtsPrinting
LtsSession
LtsSessionData
LtsUtilsCore # data mod
LtsUtilsGuiDds
LtsUtilsSettings
LtsWidgets
NGLLogger
ngltools # dtg
AvmDataPluginInterface
ExmDataPluginInterface
IsmDataPluginInterface
MrkDataPluginInterface
OpmDataPluginInterface
PdmDataPluginInterface
PpmDataPluginInterface
FlmExplorerGuiPluginInterface
FltAesGuiPluginInterface # SitMap Filter
TlnDataPluginInterface
TlnViewGuiPluginInterface
MrkCommon # SitMap Filter Helper
)
NGL_INSTALL_TARGET(TlnViewGuiPlugin)
@@ -0,0 +1,226 @@
/**
* @RDE_HEADER@
* provides user-management for attributes for a filter in filter table entry
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
//#include "FilterWindow.h" // for debug messagebox
#include "AttributeEditingModeUI.h"
#include "../detail_data.h"
#include "widgets/TimeRangeWidget.h"
#include "widgets/DefaultRangeWidget.h"
#include "widgets/ComboBox.h"
#include "widgets/CheckBoxWidget.h"
#include "widgets/AreaWidget.h"
#include "widgets/RadiusWidget.h"
#include "logger.h"
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
namespace Timeline
{
AbstractAttributeWidget *EditingModeUI::createAttributeUIWidget(DetailData *detailData, const QString &attributeType,
int eventType, const QString &initialValue) // create a widget, suitable for the attribute
{
AbstractAttributeWidget *resultWidget = nullptr;
QString concreteAttributeType = detailData->attributeRedirectionLookup.getRedirection(attributeType);
int attributeUIType = detailData->attributeUITypeLookup.getUIType(concreteAttributeType);
if (attributeUIType == -1) // not found? abort!
return nullptr;
//WARNING_LOG(m_logger) << "createAttributeUIWidget: for attributeType " << attributeType << " with string " << initialValue << " and attribute ui type " << attributeUIType;
const DetailData::AttributeParameterLookup::ParameterMap &pm = detailData->attributeParameterLookup.getParameterMap(concreteAttributeType); //const DetailData::UIInitialValuesLookup::ParameterValuesMap& pvm = filterData->uiInitialValuesLookup.getUIInitialValuesMap(attributeUIType); // initial values to be used to create widgets with intiaial values.....
switch (attributeUIType)
{
case DetailData::ATTRIBUTE_UI_TYPE_COMBO_BOX: // do not use pm !
{
resultWidget = new ComboBox(detailData, concreteAttributeType, eventType, initialValue); // get the combobox for the attribute (which is then filled with possible values)
break;
}
case DetailData::ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET:
{
bool minb = false;
unsigned int min = pm.value(DetailData::UI_PARAM_TYPE_MIN).toUInt(&minb);
bool maxb = false;
unsigned int max = pm.value(DetailData::UI_PARAM_TYPE_MAX).toUInt(&maxb);
QString prefix = pm.value(DetailData::UI_PARAM_TYPE_PREFIX).toString();
QString suffix = pm.value(DetailData::UI_PARAM_TYPE_SUFFIX).toString();
if (!minb || !maxb) // || prefix.isEmpty() || suffix.isEmpty())
{
WARNING_LOG(m_logger) << "createAttributeUIWidget: !minb (" << minb << ") || !maxb (" << maxb << ")"; // || prefix.isEmpty ("<<prefix.isEmpty()<<") || suffix.isEmpty ("<<suffix.isEmpty()<<")" << ;
break;
}
resultWidget = new DefaultRangeWidget(min, max, prefix, suffix, initialValue);
break;
}
case DetailData::ATTRIBUTE_UI_TYPE_RAD_RANGE_WIDGET:
{
bool n1k = false;
int n1 = pm.value(DetailData::UI_PARAM_TYPE_N1).toUInt(&n1k);
bool n2k = false;
int n2 = pm.value(DetailData::UI_PARAM_TYPE_N2).toUInt(&n2k);
bool o1k = false;
int o1 = pm.value(DetailData::UI_PARAM_TYPE_O1).toUInt(&o1k);
bool o2k = false;
int o2 = pm.value(DetailData::UI_PARAM_TYPE_O2).toUInt(&o2k);
bool rk = false;
int r = pm.value(DetailData::UI_PARAM_TYPE_RAD).toUInt(&rk);
if (n1k && n2k && o1k && o2k && rk)
{
resultWidget = new RadiusWidget(n1, n2, o1, o2, r); // set by zwmstl_timelineFilterData TODO: initial value !
break;
}
WARNING_LOG(m_logger) << "DetailData::ATTRIBUTE_UI_TYPE_RAD_RANGE_WIDGET: errorious values!";
break;
}
case DetailData::ATTRIBUTE_UI_TYPE_AREA_RANGE_WIDGET:
{
bool n1k = false;
int n1 = pm.value(DetailData::UI_PARAM_TYPE_N1).toUInt(&n1k);
bool n2k = false;
int n2 = pm.value(DetailData::UI_PARAM_TYPE_N2).toUInt(&n2k);
bool o1k = false;
int o1 = pm.value(DetailData::UI_PARAM_TYPE_O1).toUInt(&o1k);
bool o2k = false;
int o2 = pm.value(DetailData::UI_PARAM_TYPE_O2).toUInt(&o2k);
if (n1k && n2k && o1k && o2k)
{
resultWidget = new AreaWidget(n1, n2, o1, o2); // set by zwmstl_timelineFilterData TODO: initial value !
break;
}
WARNING_LOG(m_logger) << "DetailData::ATTRIBUTE_UI_TYPE_AREA_RANGE_WIDGET: errorious values!";
break;
}
case DetailData::ATTRIBUTE_UI_TYPE_CHECKBOX:
{
resultWidget = new CheckBoxWidget(detailData, concreteAttributeType, initialValue); // 1 = 'true'-value from the list (must not have that wordly negative sound)
break;
}
case DetailData::ATTRIBUTE_UI_TYPE_TIME_RANGE_WIDGET:
{
TimeRangeWidget *trw = new TimeRangeWidget(detailData, initialValue); // update parameter lists filled by EventFilter-using component for specific widgets
detailData->createRetrieveCurrentTimeCallback(trw->m_edit1);
detailData->createRetrieveCurrentTimeCallback(trw->m_edit2);
resultWidget = trw;
break;
}
default: // invalid attribute type!
{
WARNING_LOG(m_logger) << "createAttributeUIWidget: invalid attribute type #" << attributeUIType << "!";
//return 0;
break;
}
}
//resultWidget->setStyleSheet("background-color:#CC9B97;"); // layer 3
return resultWidget;
}
void EditingModeUI::addAttributeContainer(int eventType, const QString &initialValue)
{
//attributeValuesLayout->addWidget(createAttributeUIWidget(attributeType), 0, Qt::AlignTop);
AbstractAttributeWidget *attributeUIWidget = createAttributeUIWidget(m_detailData, attributeType, eventType, initialValue);
if (attributeUIWidget == nullptr)
{
DEBUG_LOG(m_logger) << "addAttributeContainer: !attributeUIWidget";
return;
}
connect(attributeUIWidget, &AbstractAttributeWidget::destroyed, this, &EditingModeUI::signalChanges);
connect(attributeUIWidget, &AbstractAttributeWidget::signalChanges, this, &EditingModeUI::signalChanges);
attributeValuesLayout->insertWidget(0, attributeUIWidget, 0, Qt::AlignTop);
attributeUIWidget->setFocus(); // set focus to new widget
}
EditingModeUI::EditingModeUI(DetailData *detailData, const QString &attributeType, QWidget *parent)
: QWidget(parent)
, attributeType(attributeType)
, m_detailData(detailData)
, m_logger(getLogger("EventFilter.EditingModeUI"))
{
QVBoxLayout *editingModeLayout = new QVBoxLayout;
QWidget *filterTextWidget = new QWidget; // contains the filter text + widget to delete whole filter
QHBoxLayout *filterTextLayout = new QHBoxLayout;
filterTextWidget->setLayout(filterTextLayout);
pbDelete = new QPushButton(QStringLiteral("-"));
pbDelete->setToolTip(tr("Delete this attribute and all its values"));
pbDelete->setPalette(QPalette(QColor("red")));
pbDelete->setMaximumWidth(30);
lbFilterText = new QLabel;
filterTextLayout->addWidget(lbFilterText, 0, Qt::AlignLeft);
filterTextLayout->addWidget(pbDelete, 1, Qt::AlignLeft);
QWidget *attributeValuesWidget = new QWidget;
attributeValuesLayout = new QVBoxLayout();
attributeValuesWidget->setLayout(attributeValuesLayout); // add layout to widget
pbAddAttributeContainer = new QPushButton(tr("Add Value"));
pbAddAttributeContainer->setPalette(QPalette(QColor("green")));
// add all widgets to editing mode main widget
setLayout(editingModeLayout);
editingModeLayout->addWidget(filterTextWidget, 0, Qt::AlignTop);
// horizontal line after filter text
QWidget *horizontalLineWidget = new QWidget;
horizontalLineWidget->setFixedHeight(2);
horizontalLineWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
horizontalLineWidget->setStyleSheet(QStringLiteral("background-color: #c0c0c0;"));
editingModeLayout->addWidget(horizontalLineWidget, 0, Qt::AlignTop);
editingModeLayout->addWidget(pbAddAttributeContainer, 0, Qt::AlignTop | Qt::AlignLeft);
editingModeLayout->addWidget(attributeValuesWidget, 0, Qt::AlignTop); // add attribute values "list" to this, so it can be searched through for attribute values
}
EditingModeUI::~EditingModeUI()
{
const auto children = findChildren<AbstractAttributeWidget*>();
for (auto child : children)
child->disconnect(this);
}
}
@@ -0,0 +1,50 @@
/**
* @RDE_HEADER@
* provides user-management for attributes for a filter in filter table entry
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include <QWidget>
class QLabel;
class QPushButton;
class QVBoxLayout;
namespace Timeline
{
class DetailData;
class AbstractAttributeWidget;
class EditingModeUI : public QWidget
{
Q_OBJECT
public:
explicit EditingModeUI(DetailData *detailData, const QString &attributeType, QWidget *parent);
~EditingModeUI();
void addAttributeContainer(int eventType, const QString &initialValue = QString()); // 0 = default val (do not set -1 invalid !)
QLabel *lbFilterText;
QPushButton *pbDelete;
QVBoxLayout *attributeValuesLayout;
QPushButton *pbAddAttributeContainer; // does not end this mode, adds an attribute container
private:
Timeline::AbstractAttributeWidget *createAttributeUIWidget(DetailData *detailData, const QString &attributeType,
int eventType, const QString &initialValue);
signals:
void signalChanges();
private:
QString attributeType;
DetailData *m_detailData;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* provides user-management for events for a filter in filter table entry
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "../detail_data.h"
#include "AttributeSelectionModeUI.h"
#include "logger.h"
namespace Timeline
{
QComboBox *SelectionModeUI::createAttributesComboBox(int eventType)
{
QComboBox *cbAttributes = new QComboBox();
// 1. get all event type's attribute types!
// 2. get all attribute type's attribute texts! getAttributeText(..)
DetailData::AttributeEventLookup::AttributeList attributeList = m_detailData->attributeEventLookup.getEventsAttributeList(eventType);
for (auto i = attributeList.constBegin(); i != attributeList.constEnd(); ++i)
{
const QString &attributeKey = m_detailData->attributeRedirectionLookup.getRedirection(*i); // key (derefd')!
// Do not allow filtering for INTERPRETER_TYPE_NONE (e.g. Marker)
if (m_detailData->attributeInterpreterLookup.getInterpreterType(attributeKey) == DetailData::INTERPRETER_TYPE_NONE)
continue;
QString cbAttribute = m_detailData->attributeTypeLookup.getAttributeText(attributeKey);
if (cbAttribute.isEmpty())
{
WARNING_LOG(m_logger) << "SelectionModeUI::createAttributesComboBox: event type " << eventType << " has no attribute text for attribute " << attributeKey << " for attribute event lookup - not added";
}
else
{
cbAttributes->addItem(cbAttribute);
}
}
return cbAttributes;
}
SelectionModeUI::SelectionModeUI(DetailData *detailData, const int eventType)
: eventType(eventType)
, m_detailData(detailData)
, m_logger(getLogger("EventFilter.AttributeEditingModeUI"))
{
selectionModeWidget = new QWidget();
selectionModeLayout = new QHBoxLayout();
lbFilterText = new QLabel(tr("Select attribute type to filter:"));
cbAttributeType = createAttributesComboBox(eventType); // create a combobox with all attributes for the given event type
pbAddEditingMode = new QPushButton(tr("OK"));
pbAddEditingMode->setToolTip(tr("Add this attribute and start adding values"));
pbAddEditingMode->setPalette(QPalette(QColor("green")));
pbAddEditingMode->setMaximumWidth(30);
pbDeleteFilter = new QPushButton(QStringLiteral("-"));
pbDeleteFilter->setPalette(QPalette(QColor("red")));
pbDeleteFilter->setToolTip(tr("Delete this attribute and all its values"));
pbDeleteFilter->setMaximumWidth(30);
selectionModeLayout->addWidget(lbFilterText, 0, Qt::AlignCenter);
selectionModeLayout->addWidget(cbAttributeType, 0, Qt::AlignTop);
selectionModeLayout->addWidget(pbAddEditingMode, 0, Qt::AlignTop);
selectionModeLayout->addWidget(pbDeleteFilter, 1, Qt::AlignTop);
selectionModeWidget->setLayout(selectionModeLayout);
}
} // namespace Timeline
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* provides user-management for events for a filter in filter table entry
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include <QComboBox>
#include <QWidget>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
namespace Timeline
{
class DetailData;
class SelectionModeUI : public QObject
{
Q_OBJECT
private:
SelectionModeUI &operator=(const SelectionModeUI &); // no implementation
public:
QComboBox *createAttributesComboBox(int eventType);
// >>>>>>> (EVENT_TYPE has (ATTRIBUTE_TYPE has (FilterData::ATTRIBUTE_UI_TYPE and POSSIBLE_VALUES)))
explicit SelectionModeUI(DetailData *detailData, const int eventType);
~SelectionModeUI()
{
delete lbFilterText;
delete cbAttributeType;
delete pbAddEditingMode;
delete selectionModeLayout;
delete selectionModeWidget;
}
int eventType;
QWidget *selectionModeWidget;
QHBoxLayout *selectionModeLayout;
QLabel *lbFilterText;
QComboBox *cbAttributeType;
QPushButton *pbAddEditingMode;
QPushButton *pbDeleteFilter;
DetailData *m_detailData;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,334 @@
/**
* @RDE_HEADER@
* entry to manage a specific filter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "FilterTableEntry.h"
#include "FilterTableEntryAttribute.h"
#include "interpreter/FilterWorker.h"
#include "../detail_data.h"
#include "logger.h"
#include <QColor>
#include <QSizePolicy>
#include <QJsonArray>
#include <QDebug>
namespace Timeline
{
FilterTableEntry::FilterTableEntry(DetailData *detailData, const int &eventType, const bool &active, QWidget *parent) // directly creates ftw with selected event type
: QGroupBox(parent)
, m_logger(getLogger("EventFilter.FilterTableEntry"))
, m_eventType(eventType)
// ui
, m_mainLayout(new QVBoxLayout())
, m_topLayout(new QHBoxLayout())
, m_centerLayout(new QHBoxLayout())
, m_cbEventSelector(nullptr) // prepare cb
, m_pbDelete(new QPushButton(QStringLiteral("-")))
, m_pbFoundEvents(new QPushButton(tr("Show Events")))
, m_pbFilterActive(new QPushButton(tr("Active")))
, m_pbOK(new QPushButton(tr("OK")))
, m_lbSelectText(new QLabel(tr("Select event type to filter:")))
, m_detailData(detailData)
{
m_cbEventSelector = getEventTypesComboBox();
m_pbDelete->setPalette(QPalette(QColor("red")));
m_pbDelete->setToolTip(tr("Delete this filter and all its attributes"));
m_pbFilterActive->setPalette(QPalette(QColor("orange")));
m_pbFoundEvents->setPalette(QPalette(QColor("orange")));
m_pbOK->setPalette(QPalette(QColor("green")));
m_pbOK->setToolTip(tr("Add this filter and start adding attributes (when needed)"));
connect(m_pbFilterActive, &QPushButton::clicked, this, &FilterTableEntry::signalChanges);
// soft init: only init cb & ok-button (mini-selection mode)
setLayout(m_mainLayout);
// top layout
QWidget *topLayoutWidget = new QWidget(this);
//topLayoutWidget->setStyleSheet("background-color:#97B0CC"); //#D6FFFF;"); // layer 1
topLayoutWidget->setLayout(m_topLayout);
m_mainLayout->addWidget(topLayoutWidget, 0, Qt::AlignTop); // add top widget with top alingment
// start mini-selection mode
m_topLayout->addWidget(m_lbSelectText, 0, Qt::AlignLeft);
m_topLayout->addWidget(m_cbEventSelector, 0, Qt::AlignLeft);
m_pbOK->setMinimumWidth(30);
m_pbOK->setMaximumWidth(30);
m_topLayout->addWidget(m_pbOK, 0, Qt::AlignLeft);
connect(m_pbOK, &QPushButton::clicked, this, &FilterTableEntry::slotSelectEventType); // end selection mode => start editing mode
connect(m_pbDelete, &QPushButton::clicked, this, &FilterTableEntry::deleteLater);
m_pbDelete->setMinimumWidth(30);
m_pbDelete->setMaximumWidth(30);
m_topLayout->addWidget(m_pbDelete, 1, Qt::AlignLeft);
QWidget *centerLayoutWidget = new QWidget(this);
centerLayoutWidget->setStyleSheet(topLayoutWidget->styleSheet());
centerLayoutWidget->setLayout(m_centerLayout);
m_mainLayout->addWidget(centerLayoutWidget, 0);
if (m_eventType > -1)
slotSelectEventType(); // start making it a ready to use event filter for a specific event
m_pbFoundEvents->setEnabled(active);
m_pbFilterActive->setCheckable(true);
m_pbFilterActive->setChecked(active);
m_pbFilterActive->setToolTip(m_pbFilterActive->isChecked() ? tr("Filter is active") : tr("Filter is inactive"));
}
FilterTableEntry::~FilterTableEntry()
{
const auto children = findChildren<FilterTableEntryAttribute *>();
for (auto child : children)
child->disconnect(this);
}
void FilterTableEntry::updateData()
{
for (int i = 0; i < m_centerLayout->count(); i++)
{
FilterTableEntryAttribute *ftec = dynamic_cast<FilterTableEntryAttribute *>(m_centerLayout->itemAt(i)->widget()); // try to convert to fte
if (!ftec) // not an ftec
{
WARNING_LOG(m_logger) << "FilterTableEntry::filterText(): !ftec";
continue;
}
ftec->updateData(m_detailData);
}
}
QComboBox *FilterTableEntry::getEventTypesComboBox()
{
QComboBox *cbEventTypes = new QComboBox();
cbEventTypes->addItems(m_detailData->eventTypeLookup.getEventTexts());
cbEventTypes->model()->sort(0);
connect(cbEventTypes, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &FilterTableEntry::signalChanges);
return cbEventTypes;
}
const QJsonObject FilterTableEntry::filter(bool typeSafe) const
{
// get all attribute filters
if (m_eventType == -1/* || !m_filterActive*/)
{
WARNING_LOG(m_logger) << "FilterTableEntry has no event type.";
//warn("fte: fte has no event type (still in selection mode?)");
return {};
}
if (!m_centerLayout->count()) // if there are no children, do not worry, it should be possible to just filter by the event type
{
//info("fte: empty");
}
QJsonObject result;
QString eventTypeString;
if (typeSafe)
eventTypeString = QString::number(m_eventType);
else
eventTypeString = m_detailData->eventTypeLookup.getEventText(m_eventType); // getEventTypeString(eventType);
if (!eventTypeString.length())
return QJsonObject();
//info("fte: event type #" + QString::number(eventType) + " = " + eventTypeString);
result[QStringLiteral("EventType")] = eventTypeString;
QJsonArray attributes;
for (int i = 0; i < m_centerLayout->count(); i++)
{
FilterTableEntryAttribute *ftec = dynamic_cast<FilterTableEntryAttribute *>(m_centerLayout->itemAt(i)->widget()); // try to convert to fte
if (!ftec) // not an ftec
{
WARNING_LOG(m_logger) << "FilterTableEntry::filterText(): !ftec";
continue;
}
//info("fte: ftec found");
QJsonObject filterTile = ftec->filter(typeSafe); // the typeSafe parameter kills off the STR_COMPARE as we only safe ID's or ints, so do not implement it
if (filterTile.isEmpty()) // ftec has no result, continue!
continue;
attributes.append(filterTile);
}
result[QStringLiteral("Attributes")] = attributes;
result[QStringLiteral("Active")] = m_pbFilterActive->isChecked();
return result;
}
void FilterTableEntry::slotSelectEventType() // end selection mode => start editing mode
{
delete m_lbSelectText; // delete help text (not needed anymore)
// end selection mode
if (m_cbEventSelector) // if there are no attribute filters yet..
{
if (m_eventType == -1) // do not reload if already set (set by constructor when set-function is used)
{
m_eventType = m_detailData->eventTypeLookup.getEventType(m_cbEventSelector->currentText()); // DOES NOT WORK ANYMORE: m_cbEventSelector->currentIndex();
}
m_lbEventName = new QLabel(QString(tr("Filter by event type: <b>%1</b>")).arg(m_detailData->eventTypeLookup.getEventText(m_eventType))); //m_cbEventSelector->currentText()); // get event name
m_lbEventName->setMinimumWidth(m_cbEventSelector->width()); // gets the optics straight! FIXME: width was never set
delete m_cbEventSelector; // event type is now fixed, replace cb with lb
m_cbEventSelector = nullptr; // don't forget to set to 0 or this if-clause can't see that cb is already deleted => crash
m_topLayout->insertWidget(0, m_lbEventName); // add where the cb was
}
if (m_eventType == -1)
{
WARNING_LOG(m_logger) << "slotSelectEventType: m_eventType == -1";
return;
}
delete m_pbOK; // delete ok button
// start editing mode
// horizontal line under add button
QWidget *horizontalLineWidget = new QWidget();
horizontalLineWidget->setFixedHeight(2);
horizontalLineWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
horizontalLineWidget->setStyleSheet(QStringLiteral("background-color: #c0c0c0;"));
m_mainLayout->insertWidget(1, horizontalLineWidget, 0, Qt::AlignTop);
delete m_pbDelete; // must be recreated because of the stretch at the end whil in selection mode
m_pbDelete = new QPushButton(QStringLiteral("-"));
m_pbDelete->setToolTip(tr("Delete this filter and all its attributes"));
m_pbDelete->setPalette(QPalette(QColor("red")));
connect(m_pbDelete, &QPushButton::clicked, this, &FilterTableEntry::deleteLater);
m_pbDelete->setMinimumWidth(30);
m_pbDelete->setMaximumWidth(30);
m_topLayout->addWidget(m_pbDelete, 0, Qt::AlignLeft);
m_topLayout->addWidget(m_pbFilterActive, 0, Qt::AlignLeft);
connect(m_pbFilterActive, &QPushButton::clicked, this, &FilterTableEntry::slotFilterActiveClicked);
connect(m_pbFoundEvents, &QPushButton::clicked, this, &FilterTableEntry::slotShowEvents);
m_pbFoundEvents->setToolTip(tr("Show all events that fit to this filter"));
m_topLayout->addWidget(m_pbFoundEvents, 1, Qt::AlignLeft);
// do not add attributes if there are none
DetailData::AttributeEventLookup::AttributeList attributeList = m_detailData->attributeEventLookup.getEventsAttributeList(m_eventType);
// remove unfilterables:
QMutableListIterator<QString> it(attributeList);
while (it.hasNext())
{
const QString attributeType = it.next();
if (m_detailData->attributeInterpreterLookup.getInterpreterType(attributeType) == DetailData::INTERPRETER_TYPE_NONE)
it.remove();
}
if (attributeList.isEmpty())
return;
QPushButton *pbAddAttribute = new QPushButton(tr("Add Attribute"));
pbAddAttribute->setPalette(QPalette(QColor("green")));
connect(pbAddAttribute, &QPushButton::clicked, this, &FilterTableEntry::slotAddAttribute);
//pbAddFilter->setMinimumWidth(30);
//pbAddFilter->setMaximumWidth(30);
m_mainLayout->insertWidget(2, pbAddAttribute, 0, Qt::AlignLeft);
}
void FilterTableEntry::slotShowEvents()
{
QJsonArray a;
a.append(filter(true /* type safe! */));
emit signalShowEvents(a);
}
void FilterTableEntry::slotFilterActiveClicked()
{
if (m_pbFilterActive->isChecked())
{
m_pbFilterActive->setPalette(QPalette(QColor("orange")));
m_pbFilterActive->setToolTip(tr("Filter is active"));
m_pbFoundEvents->setPalette(QPalette(QColor("orange")));
m_pbFoundEvents->setEnabled(true);
}
else
{
m_pbFilterActive->setPalette(QPalette(QColor("grey")));
m_pbFilterActive->setToolTip(tr("Filter is inactive"));
m_pbFoundEvents->setPalette(QPalette(QColor("grey")));
m_pbFoundEvents->setEnabled(false);
}
}
void FilterTableEntry::slotAddAttribute()
{
addAttribute();
}
bool FilterTableEntry::hasAttributes() const
{
qWarning() << "hasAttributes? children: " << m_centerLayout->children().count();
return !!m_centerLayout->children().count();
}
FilterTableEntryAttribute *FilterTableEntry::addAttribute(const QString &attributeType)
{
FilterTableEntryAttribute *ftec = new FilterTableEntryAttribute(m_detailData, m_eventType, attributeType, this);
connect(ftec, &FilterTableEntryAttribute::signalChanges, this, &FilterTableEntry::signalChanges);
connect(ftec, &FilterTableEntryAttribute::destroyed, this, &FilterTableEntry::signalChanges);
m_centerLayout->insertWidget(0, ftec, 1, Qt::AlignLeft); // add all FTECs to the left
return ftec;
}
}
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* entry to manage a specific filter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "FilterTableEntryAttribute.h"
#include <QWidget>
#include <QHBoxLayout>
#include <QPushButton>
#include <QComboBox>
#include <QPaintEvent>
#include <QPainter>
#include <QList>
#include <QGroupBox>
namespace Timeline
{
class DetailData;
class FilterTableEntry : public QGroupBox //QWidget
{
Q_OBJECT
public:
explicit FilterTableEntry(DetailData *detailData, const int &eventType = -1, const bool &active = true, QWidget *parent = nullptr);
~FilterTableEntry();
const QJsonObject filter(bool typeSafe = false) const;
/**
* @brief addFilter adds a filter to the filter table layout and returns the new pointer (for set-functionality)
* @return new created ftw pointer
*/
FilterTableEntryAttribute *addAttribute(const QString &attributeType = QString());
bool hasAttributes() const;
void updateData();
private:
NGL::INGLLogger &m_logger;
QComboBox *getEventTypesComboBox();
int m_eventType;
// ui
QVBoxLayout *m_mainLayout{nullptr}; // for topLayout,
QHBoxLayout *m_topLayout{nullptr}; // for event, delete, show events
QHBoxLayout *m_centerLayout{nullptr}; // for many filtering types
QComboBox *m_cbEventSelector{nullptr};
QLabel *m_lbEventName{nullptr};
QPushButton *m_pbDelete{nullptr};
QPushButton *m_pbFoundEvents{nullptr};
QPushButton *m_pbFilterActive{nullptr};
QPushButton *m_pbOK{nullptr};
QLabel *m_lbSelectText{nullptr};
DetailData *m_detailData{nullptr};
signals:
void signalShowEvents(const QJsonArray &filter); // provided by filter text (use filter, get all fitting events by setting SHOW/HIDE to false and returning only the list of affected events)
void signalChanges();
private slots:
void slotAddAttribute();
void slotFilterActiveClicked();
void slotSelectEventType();
void slotShowEvents();
public slots:
/**
* @brief slotSetFoundEventsCount sets the variable for how much events could be found with this filter (called by parent, holding this fte)
* @param foundEvents count of events found with this filter
*/
//void slotSetFoundEventsCount(int foundEvents);
};
}
@@ -0,0 +1,244 @@
/**
* @RDE_HEADER@
* attribute entry to manage a specific attribute
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "interpreter/FilterWorker.h"
#include "FilterTableEntryAttribute.h"
#include "widgets/AbstractAttributeWidget.h"
#include "logger.h"
#include "../detail_data.h"
#include <QVBoxLayout>
#include <QJsonObject>
#include <QJsonArray>
namespace Timeline
{
FilterTableEntryAttribute::FilterTableEntryAttribute(DetailData *detailData, int eventType, const QString &attributeType, QWidget *parent)
: QGroupBox(parent)
, m_eventType(eventType)
, m_attributeType(attributeType) // CAN be invalid
, m_selectionModeUI(nullptr)
, m_editionModeUI(nullptr)
, m_attributesLayout(new QVBoxLayout())
, m_detailData(detailData)
, m_logger(getLogger("EventFilter.FilterTableEntryAttribute"))
{
//this->setStyleSheet("background-color:#436C99;"); // layer 2
this->setTitle(QString());
setLayout(m_attributesLayout);
/* Selection Mode Explanation:
*
* 1. Mode: Empty Mode DEPRECATED & DELETED!
*
* > Empty Widget
* > PushButton "+" to end Empty Mode and goto Selection Mode
* > goes onto 2. Mode
*
*
* 2. Mode: Selection Mode
*
* > ComboBox with Event-specific types: (Munition, Players, etc.)
* > PushButton "+" to END Selection Mode (turns ComboBox to QLabel, so that Event-specific Type is FIXED)
*
*
* 3. Mode: Editing Mode
*
* > Useable Widget
* > Label with chosen Event-specific Type
* > ComboBox with Type-specific attributes: (adding Munition-Types if the Event-specific type was Munition)
* >> (has to load Type-specific attributes relating to the selected Event-specific type! Munition -> Munition types)
* > PushButton "+" to add another Type-specific attribute:
* >> adds another ComboBox and PushButton for further adding more attributes
*
*/
//setStyleSheet("FilterTableEntryAttribute { border: 1px solid black }");
slotAddSelectionMode();
if (!attributeType.isEmpty())
{
m_selectionModeUI->cbAttributeType->setCurrentIndex(m_selectionModeUI->cbAttributeType->findData(QVariant::fromValue<QString>(attributeType))); // set initial value by id
slotAddEditingMode(false);
}
}
FilterTableEntryAttribute::~FilterTableEntryAttribute()
{
delete m_editionModeUI; // not a widget, not parented
}
void FilterTableEntryAttribute::updateData(DetailData *detailData)
{
if (!m_editionModeUI)
return;
for (int i = 0; i < m_editionModeUI->attributeValuesLayout->count(); i++) // now get values for attribute value(s) list (from attribute layout, only available in editing mode)
{
AbstractAttributeWidget *aaw = /*qobject_cast*/dynamic_cast<AbstractAttributeWidget *>(m_editionModeUI->attributeValuesLayout->itemAt(i)->widget());
if (!aaw) // not a aaw derived class
continue;
aaw->updateData(detailData);
connect(aaw, &AbstractAttributeWidget::deleteClicked, this, [this]()
{
emit attributeChanged(false);
});
}
}
const QJsonObject FilterTableEntryAttribute::filter(bool typeSafe) const // ftea alias "ftec"
{
if (m_attributeType.isEmpty())
{
WARNING_LOG(m_logger) << "filterText: m_attributeType == -1";
//warn("ftec: missing attribute type while getting filter text from attribute (still in selection mode?)");
return QJsonObject();
}
QString attributeTypeName;
if (typeSafe)
attributeTypeName = m_attributeType;
else
{
attributeTypeName = m_detailData->attributeTypeLookup.getAttributeText(m_attributeType); //getAttributeTypeString(m_attributeType);
}
if (attributeTypeName.isEmpty())
{
WARNING_LOG(m_logger) << "filterText: !attributeTypeName.length()";
//warn("ftec: missing attribute text while getting filter text from attribute");
return QJsonObject();
}
QJsonObject result;
result[QStringLiteral("AttributeType")] = attributeTypeName;
// translate all attributes + their value(s)
// must be: * Attributes : [Attr Type]{[Attr Value],[Attr Value], ...}
QJsonArray values;
if (m_editionModeUI)
{
for (int i = 0; i < m_editionModeUI->attributeValuesLayout->count(); i++) // now get values for attribute value(s) list (from attribute layout, only available in editing mode)
{
AbstractAttributeWidget *aaw = /*qobject_cast*/dynamic_cast<AbstractAttributeWidget *>(m_editionModeUI->attributeValuesLayout->itemAt(i)->widget());
if (!aaw) // not a aaw derived class
continue;
//info("ftec: aaw derived class found");
QString value = aaw->filterText(typeSafe); // ishygddt without a derived class
if (value.isEmpty())
continue;
values.append(value);
}
}
result[QStringLiteral("AttributeValues")] = values;
return result;
}
void FilterTableEntryAttribute::slotAddSelectionMode()
{
// end Empty Mode
/*if (!m_emptyModeUI)
return;
delete m_emptyModeUI;
m_emptyModeUI = 0;*/
// 2. Mode: Selection Mode
m_selectionModeUI = new SelectionModeUI(m_detailData, m_eventType);
connect(m_selectionModeUI->pbAddEditingMode, &QPushButton::clicked, this, &FilterTableEntryAttribute::slotAddEditingMode);
connect(m_selectionModeUI->pbDeleteFilter, &QPushButton::clicked, this, &FilterTableEntryAttribute::slotDeleteAttributeContainer);
m_attributesLayout->addWidget(m_selectionModeUI->selectionModeWidget, 0, Qt::AlignTop);
}
void FilterTableEntryAttribute::slotAddEditingMode(bool createBlankContainer)
{
// end selection mode
if (!m_selectionModeUI)
return;
// 3. Mode: Editing Mode
QString attributeTypeString;
if (m_attributeType.isEmpty()) // get text from combobox, when attribute type not given
{
attributeTypeString = m_selectionModeUI->cbAttributeType->currentText();
m_attributeType = m_detailData->attributeTypeLookup.getAttributeType(attributeTypeString);
}
else // get text from filter data, when attribute type given by set-function
{
attributeTypeString = m_detailData->attributeTypeLookup.getAttributeText(m_attributeType);
// do not set attribute type when it is already given!
}
delete m_selectionModeUI;
m_selectionModeUI = nullptr;
if (m_attributeType.isEmpty())
{
WARNING_LOG(m_logger) << "slotAddEditingMode: invalid attribute type string!";
return;
}
m_editionModeUI = new EditingModeUI(m_detailData, m_attributeType, nullptr); // munition
connect(m_editionModeUI, &EditingModeUI::signalChanges, this, &FilterTableEntryAttribute::signalChanges);
connect(m_editionModeUI, &EditingModeUI::destroyed, this, &FilterTableEntryAttribute::signalChanges);
connect(m_editionModeUI->pbAddAttributeContainer, &QPushButton::clicked, this, &FilterTableEntryAttribute::slotAddAttributeContainer);
connect(m_editionModeUI->pbDelete, &QPushButton::clicked, this, &FilterTableEntryAttribute::slotDeleteAttributeContainer);
m_editionModeUI->lbFilterText->setText(tr("Filter by attribute type: <b>%1</b>").arg(attributeTypeString));
m_attributesLayout->addWidget(m_editionModeUI, 0, Qt::AlignTop);
// add first attribute container to events layout
if (createBlankContainer)
slotAddAttributeContainer();
}
void FilterTableEntryAttribute::slotDeleteAttributeContainer() // delete attribute (munition, player, etc.)
{
emit attributeChanged(false);
deleteLater();
}
void FilterTableEntryAttribute::slotAddAttributeContainer()
{
emit signalChanges();
emit attributeChanged(true);
if (m_editionModeUI != nullptr)
m_editionModeUI->addAttributeContainer(m_eventType);
}
}
@@ -0,0 +1,59 @@
/**
* @RDE_HEADER@
* attribute entry to manage a specific attribute
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AttributeEditingModeUI.h"
#include "AttributeSelectionModeUI.h"
#include <QGroupBox>
class QLabel;
class QVBoxLayout;
namespace Timeline
{
class DetailData;
class FilterTableEntryAttribute : public QGroupBox //QWidget
{
Q_OBJECT
public:
explicit FilterTableEntryAttribute(DetailData *detailData, int eventType, const QString &attributeType, QWidget *parent = nullptr);
~FilterTableEntryAttribute();
const QJsonObject filter(bool typeSafe = false) const;
void updateData(DetailData *detailData);
int m_eventType;
QString m_attributeType;
// ui
//EmptyModeUI* m_emptyModeUI;
SelectionModeUI *m_selectionModeUI;
EditingModeUI *m_editionModeUI;
QVBoxLayout *m_attributesLayout;
QLabel *m_lbSelectText;
DetailData *m_detailData;
signals:
void signalChanges();
void attributeChanged(const bool &hasAttribute);
private:
NGL::INGLLogger &m_logger;
private slots:
void slotAddAttributeContainer();
void slotDeleteAttributeContainer(); // delete attribute (munition, player, etc.)
void slotAddSelectionMode(); // to end Empty Mode
void slotAddEditingMode(bool createBlankContainer = true); // to end Selection Mode
};
}
@@ -0,0 +1,326 @@
/**
* @RDE_HEADER@
* table widget to manage filters
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "FilterWindow.h"
#include "ui_FilterWindowUi.h"
#include "FilterTableEntry.h"
#include "interpreter/FilterWorker.h"
#include "../detail_data.h"
#include "logger.h"
#include <FileManagement/flm_translation.h>
#include <QMessageBox>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonParseError>
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
#include <QScopeGuard>
#endif
#include <QDebug>
namespace Timeline
{
FilterWindow::FilterWindow(DetailData *detailData,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
LtsSession::LtsSession *session,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_flmIfc(flmIfc)
, m_ui(new Ui::FilterWindow)
, updateTimer(new QTimer(this))
, m_detailData(detailData)
//, m_filterWorker(new FilterWorker(detailData))
, m_logger(getLogger("EventFilter.FilterWindow"))
, m_session(session)
{
m_ui->setupUi(this);
setMinimumSize(1500, 800);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
m_buttonBox->connectButton(LtsWidgets::DialogButton::Add,
this, &FilterWindow::slotAddNewFilter);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show,
this, &FilterWindow::slotShowEvents);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Save,
this, &FilterWindow::slotSave);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load,
this, &FilterWindow::slotLoad);
// m_buttonBox->connectButton(LtsWidgets::DialogButton::Hide,
// this, &FilterWindow::slotHide);
// UPDATER
connect(updateTimer, &QTimer::timeout, this, &FilterWindow::slotUpdate);
updateTimer->setInterval(5000);
updateTimer->start();
// // HACK: but works good for FAT
// QTimer *hack = new QTimer(this);
// hack->setInterval(750);
// // NOTE: super difficult to track changes, so make apply always available
// connect(hack, &QTimer::timeout, this, &FilterWindow::slotChangesMade);
// hack->start();
setOnlineHelpFilename(QStringLiteral("Timeline-Filter_Functions"));
}
FilterWindow::~FilterWindow()
{
const QList<FilterTableEntry*> lst = findChildren<FilterTableEntry*>();
for (FilterTableEntry *fte : lst)
fte->disconnect(this);
updateTimer->stop();
delete m_ui;
}
void FilterWindow::retranslate()
{
translate(m_ui, tr("Filters"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Add)->setText(tr("Add Filter"));
m_buttonBox->getButton(LtsWidgets::DialogButton::Show)->setText(tr("Show Events"));
}
void FilterWindow::slotSave()
{
int fileId = m_flmIfc->saveFileContent(this, FileManagement::FileType::Xml, m_session->exerciseId(),
filterToDoc(exportFilter()));
if (fileId > 0)
{
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
__unchange__();
}
else
ERROR_LOG(m_logger) << "Could not save file in library ";
}
void FilterWindow::slotUpdate()
{
for (int i = 0; i < m_ui->filterContainer->count(); i++)
{
FilterTableEntry *fte = qobject_cast<FilterTableEntry *>(m_ui->filterContainer->itemAt(i)->widget()); // try to convert to fte
if (!fte) // not an fte (e.g. ignore add button)
continue;
fte->updateData();
}
}
FilterTableEntry *FilterWindow::addNewFilter(const int eventType, bool active)
{
FilterTableEntry *fte = new FilterTableEntry(m_detailData, eventType, active, this);
connect(fte, &FilterTableEntry::signalChanges, this, &FilterWindow::slotChangesMade);
connect(fte, &FilterTableEntry::destroyed, this, &FilterWindow::slotChangesMade);
connect(fte, &FilterTableEntry::signalShowEvents, this, &FilterWindow::signalShowEvents);
//m_ui->filterContainer->insertWidget(1, fte); // after the ADD button !
m_ui->filterContainer->addWidget(fte);
slotChangesMade();
return fte;
}
void FilterWindow::slotAddNewFilter()
{
addNewFilter();
}
const QJsonArray FilterWindow::exportFilter() const
{
if (!m_ui->filterContainer->count())
return QJsonArray();
QJsonArray result;
for (int i = 0; i < m_ui->filterContainer->count(); i++)
{
FilterTableEntry *fte = qobject_cast<FilterTableEntry *>(m_ui->filterContainer->itemAt(i)->widget()); // try to convert to fte
if (!fte) // not an fte (e.g. ignore add button)
continue;
const QJsonObject filterTile = fte->filter(true /* type safe !! */);
if (filterTile.isEmpty()) // fte has no result, continue!
continue;
result.append(filterTile);
}
//filterTextEdit->setText(filterText);
qWarning() << "filter text:" << result;
return result;
}
const QByteArray FilterWindow::filterToDoc(const QJsonArray &flt)
{
QJsonObject obj;
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::TLNTimelineFilter);
obj[QStringLiteral("Filter")] = flt;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
bool FilterWindow::apply()
{
slotApplyFilter();
return true;
}
void FilterWindow::aboutToClose()
{
//emit signalApplyFilter(QString());
}
void FilterWindow::slotApplyFilter()
{
emit signalApplyFilter(exportFilter());
//emit signalChangesMade(); // => slotChangesMade(); // NOTE: super difficult to track changes, so make apply always available
}
void FilterWindow::slotShowEvents()
{
emit signalShowEvents(exportFilter());
}
void FilterWindow::slotLoad()
{
QByteArray content;
int fileId = m_flmIfc->loadFileContent(this, content, {FileManagement::FileType::Xml}, m_session->exerciseId(), FileManagement::toString(FileManagement::XmlType::TLNTimelineFilter));
if (fileId > 0)
{
// first ask if filters can be replaced by loaded ones
int msgResult = QMessageBox::question(this,
tr("Load filter set"),
tr("Do you really want to discard current filter set to load the requested filter set?\n"
"\n"
"Yes, discard current filter set and load a filter set.\n"
"No, do not make any changes."),
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No);
if (msgResult != QMessageBox::Yes)
return;
if (!m_flmIfc->isXmlType(content, FileManagement::toString(FileManagement::XmlType::TLNTimelineFilter)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
const QJsonArray tlnFilter(obj.value(QStringLiteral("Filter")).toArray());
loadFilters(tlnFilter);
slotChangesMade();
}
}
}
bool FilterWindow::loadFilters(const QJsonArray &filter)
{
setUnchangeable();
// call setUnchangeable(false) before the scope (whenever) of this fn endsq
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
auto unchangableReset = qScopeGuard([this] { setUnchangeable(false); });
#else
std::shared_ptr<int> unchangeableReset(nullptr, [this](int *) { setUnchangeable(false); });
#endif
// get model filter lsit by filter text
FilterWorker::GetModelFilterListResult result = FilterWorker::getModelFilterList(m_detailData, filter, true);
if (!FilterWorker::getInstance()->handleResultValues(result.resultValueList, this, true))
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("The selected filter set is invalid and cannot be loaded."), tr("Cannot load filter"));
return false;
}
// now, after we know that the filter set is valid: first remove all old filters from Filter Set
while (m_ui->filterContainer->count())
{
delete m_ui->filterContainer->itemAt(0)->widget(); // delete (removes them from the layout automatically)
}
const ModelFilterList &modelFilterList = result.result;
// now create all new filters and add to laoyut
int lastEventType = -1;
FilterTableEntry *lastFTW = nullptr;
QString lastAttributeType; // empty = events only
FilterTableEntryAttribute *lastFTEA = nullptr;
for (const FilterWorker::ModelFilter &mf : modelFilterList)
{
if (lastEventType != mf.eventType || lastEventType == -1 || !lastFTW) // create a new FTE when we are not the same event type anymore (or there never was a fte)
{
lastEventType = mf.eventType;
lastFTW = addNewFilter(mf.eventType, mf.active); // create a new event filter
}
if (mf.attributeType == QString()) // attribute type 0 = EVENT_ONLY (create event, but do not add attribute entry !
continue;
if (lastAttributeType != mf.attributeType || lastAttributeType.isEmpty() || !lastFTEA)
{
lastAttributeType = mf.attributeType;
lastFTEA = lastFTW->addAttribute(mf.attributeType); // create a new attribute filter
}
EditingModeUI *emui = lastFTEA->m_editionModeUI;
if (!emui)
continue;
emui->addAttributeContainer(mf.eventType, mf.attributeValue); // create a new attribute container witht he given value
}
return true;
}
} // namespace Timeline
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* table widget to manage filters
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <NGLLogger/INGLLogger.h>
#include <LtsSession/lts_session.h>
#include <QTimer>
#include <QJsonArray>
namespace Ui
{
class FilterWindow;
}
namespace Timeline
{
class DetailData;
class FilterTableEntry;
class FilterWorker;
class FilterWindow : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit FilterWindow(DetailData *detailData,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
LtsSession::LtsSession *session,
QWidget *parent = nullptr);
~FilterWindow();
FilterTableEntry *addNewFilter(const int eventType = -1, bool active = true); // invalid
//void addFilterToTable(const NGLDAO::EventFilter &eventFilter);
void clearSavedFiltersList();
void updateData();
const QJsonArray exportFilter() const;
const QByteArray filterToDoc(const QJsonArray &flt);
bool loadFilters(const QJsonArray &filter);
private:
void retranslate() override;
bool apply() override;
void aboutToClose() override;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmIfc;
Ui::FilterWindow *m_ui;
QTimer *updateTimer;
DetailData *m_detailData;
//FilterWorker *m_filterWorker;
NGL::INGLLogger &m_logger;
LtsSession::LtsSession *m_session;
signals:
void signalConvertFilterText(const QString &);
void signalShowEvents(const QJsonArray &events);
void signalApplyFilter(const QJsonArray &filter);
void signalReloadSavedFiltersList();
void signalDeleteFilter(int);
void signalReplaceFilter(int);
void signalReplaceFilterNow(int, const QString &);
//void signalFilterInTableChanged(const NGLDAO::EventFilter &); // redirection
//void signalNewFilter(const NGLDAO::EventFilter &); // redirection
public slots:
void slotLoad();
void slotSave();
void slotUpdate();
void slotAddNewFilter();
private slots:
void slotApplyFilter();
void slotShowEvents();
};
} // namespace Timeline
@@ -0,0 +1,309 @@
/**
* @RDE_HEADER@
* equations methods for filter/interpreter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "logger.h"
#include "FilterEquationFunctions.h"
#include "FilterValueInterpreter.h"
#include "../../detail_data.h"
#include <QString>
#include <QRect>
#include <QVariant>
namespace Timeline
{
static bool inTime(DetailData *detailData, const QVariant &value, const QString &interpreterText)
{
NGL::INGLLogger &logger(getLogger("FilterEquationFunctions"));
bool ok = false;
unsigned int time = value.toUInt(&ok);
if (!ok)
{
WARNING_LOG(logger) << "inTime: value error!" << value.toString();
return false;
}
InterpreterResult ir = interpretValue(detailData, DetailData::INTERPRETER_TYPE_RANGE, interpreterText);
if (ir.resultType == InterpreterResult::InterpreterResultTypes::RESULT_SUCCESS && ir.resultList.count() == 2)
{
bool ok1 = false;
bool ok2 = false;
unsigned int t1 = ir.resultList.at(0).toUInt(&ok1);
unsigned int t2 = ir.resultList.at(1).toUInt(&ok2);
if (ok1 && ok2 && t1 <= time && t2 >= time)
{
return true;
}
}
return false;
}
// Detail Data Filter Text
static bool inArea(DetailData *detailData, const QVariant &value, const QString &interpreterText)
{
NGL::INGLLogger &logger(getLogger("FilterEquationFunctions"));
(void)detailData;
QStringList areaValuesList = value.toString().split(QLatin1Char('x'));
if (areaValuesList.count() != 2)
{
WARNING_LOG(logger) << "inArea: areaValuesList.count() != 2 (" << areaValuesList.count() << ")";
return false;
}
QStringList interpreterValuesList = interpreterText.split(QLatin1Char('x'));
if (interpreterValuesList.count() != 4)
{
WARNING_LOG(logger) << "inArea: interpreterValuesList.count() != 4 (" << interpreterValuesList.count() << ")";
return false;
}
bool nb = false;
int n = areaValuesList.at(0).toInt(&nb);
bool ob = false;
int o = areaValuesList.at(1).toInt(&ob);
bool n1b = false;
int n1 = interpreterValuesList.at(0).toInt(&n1b);
bool n2b = false;
int n2 = interpreterValuesList.at(1).toInt(&n2b);
bool o1b = false;
int o1 = interpreterValuesList.at(2).toInt(&o1b);
bool o2b = false;
int o2 = interpreterValuesList.at(3).toInt(&o2b);
/*/ Y
* | N1
* O1 |
* ----------- X
* | O2
* N2 |
/*/
if (!nb || !ob || !n1b || !n2b || !o1b || !o2b)
{
WARNING_LOG(logger) << "inArea: !nb || !ob || !n1b || !n2b || !o1b || !o2b";
return false;
}
QRect range;
range.adjust(o1, n1, o2, n2);
if (range.contains(o, n))
{
WARNING_LOG(logger) << "inArea: in area!";
return true;
}
WARNING_LOG(logger) << "inArea: not in area";
return false;
}
static bool inRad(DetailData *detailData, const QVariant &value, const QString &interpreterText)
{
NGL::INGLLogger &logger(getLogger("FilterEquationFunctions"));
(void)detailData;
QStringList radValuesList = value.toString().split(QLatin1Char('x'));
if (radValuesList.count() != 2)
{
WARNING_LOG(logger) << "inRad: radValuesList.count() != 2 (" << radValuesList.count() << ")";
return false;
}
QStringList interpreterValuesList = interpreterText.split(QLatin1Char('x'));
if (interpreterValuesList.count() != 3)
{
WARNING_LOG(logger) << "inArea: interpreterValuesList.count() != 3 (" << interpreterValuesList.count() << ")";
return false;
}
bool nb = false;
int n = radValuesList.at(0).toInt(&nb);
bool ob = false;
int o = radValuesList.at(1).toInt(&ob);
bool n1b = false;
int n1 = interpreterValuesList.at(0).toInt(&n1b);
bool o1b = false;
int o1 = interpreterValuesList.at(1).toInt(&o1b);
bool r1b = false;
int r1 = interpreterValuesList.at(2).toInt(&r1b);
/*/ Y
* | 'C' = center
* = '=' = radius
* ---==C==--- X
* =
* |
/*/
if (!nb || !ob || !n1b || !o1b || !r1b)
{
WARNING_LOG(logger) << "inRad: !nb || !ob || !n1b || !o1b || !r1b";
return false;
}
QRect range;
range.adjust(o1 - r1, n1 - r1, o1 + r1, n1 + r1);
if (range.contains(o, n))
{
WARNING_LOG(logger) << "inRad: in radius!";
return true;
}
WARNING_LOG(logger) << "inRad: not in radius";
return false;
}
static bool isIntEqual(DetailData *detailData, const QVariant &value, const QString &interpreterText) // filter for 0 ?
{
NGL::INGLLogger &logger(getLogger("FilterEquationFunctions"));
(void)detailData;
bool ok1 = false;
bool ok2 = false;
//qWarning() << "isIntEqual: val1 =" << value << " val2 =" << interpreterText;
int i1 = value.toInt(&ok1);
int i2 = interpreterText.toInt(&ok2);
if (!ok1 || !ok2)
{
WARNING_LOG(logger) << "isIntEqual: STR to INT conversion error! ok1 (" << ok1 << ") ok2 (" << ok2 << ") value (" << value.toString() << ") intTxt (" << interpreterText << ")";
}
if (ok1 && ok2 && i1 == i2)
{
return true;
}
return false;
}
bool checkValue(DetailData *detailData, int attributeInterpreterType, const QVariant &value, const QString &interpreterText)
{
switch(attributeInterpreterType)
{
case DetailData::INTERPRETER_TYPE_TIME:
{
return inTime(detailData, value, interpreterText); // inTime is a range checker
}
case DetailData::INTERPRETER_TYPE_AREA:
{
return inArea(detailData, value, interpreterText);
}
case DetailData::INTERPRETER_TYPE_RAD: // TODO: test !!
{
return inRad(detailData, value, interpreterText);
}
case DetailData::INTERPRETER_TYPE_STR_COMPARE: // TODO: rm - unused
{
//WARNING_LOG(logger) << "INTERPRETER_TYPE_STR_COMPARE value " << value.toString() << " text " << interpreterText;
if (value.toString() == interpreterText) // ez
return true;
return false;
}
case DetailData::INTERPRETER_TYPE_ID_COMPARE:
{
bool tryIt = isIntEqual(detailData, value, interpreterText);
// NOTE: everything typesafe, ids only
// if (!tryIt) // means: cannot convert text OR is not equal; reason: is text OR is not equal; solution: probably value string - getIDKeyFromValue!!
// {
// int newTry = detailData->attributeValueLookup.getValueIDKeyFromValueString(attributeType, interpreterText);
// if (newTry == -1) // not even a value string (solution does not work)
// {
// DEBUG_LOG(logger) << "checkValue: no key found";
// tryIt = false;
// }
// else // okay, seems to be a value string
// {
// tryIt = isIntEqual(detailData, value, QString::number(newTry));
// }
// }
return tryIt;
}
default:
{
/* attribute types with ui type for combobox can redirect unproblematically
* => attribute types with special ui type(s) should not be redirected
* so if the attempt to lookup a attribute interpreter type fails, try another lookup with redirection
* side effects can only happen if new attribute types are not completly integrated */
// TODO: endless loops possible in some way?
// QString newAttributeType = detailData->attributeRedirectionLookup.getRedirection(attributeType);
// //qWarning() << "checkValue: newAttributeType" << newAttributeType << " attributeType" << attributeType;
// if (newAttributeType != attributeType) // not equal = redirection
// {
// return checkValue(detailData, newAttributeType, value, interpreterText); // try again with redirected attribute type
// }
//qWarning() << "checkValue: attribute type"<<attributeType<<"not handled";
return false;
}
}
}
bool FilterEquationFunctions::checkValue(DetailData *detailData, const QString &attributeType, const QVariant &value, const QString &interpreterText)
{
int attributeInterpreterType = detailData->attributeInterpreterLookup.getInterpreterType(attributeType); // get interpretertype (e.g. for area_pos for pos)
return checkValue(detailData, attributeInterpreterType, value, interpreterText);
}
} // namespace Timeline
@@ -0,0 +1,26 @@
/**
* @RDE_HEADER@
* equations methods for filter/interpreter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
class QVariant;
class QString;
namespace Timeline
{
class DetailData;
namespace FilterEquationFunctions
{
bool checkValue(DetailData *detailData, const QString &attributeType, const QVariant &value, const QString &interpreterText);
}
}
@@ -0,0 +1,174 @@
/**
* @RDE_HEADER@
* interpreter for filter in excon model
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "FilterValueInterpreter.h"
#include "../../detail_data.h"
#include <QString>
#include <QStringList>
#include <QRect>
namespace Timeline
{
static InterpreterResult interpretAreaText(const QString &areaText)
{
QStringList areaList = areaText.split(QLatin1Char('x'));
if (areaList.count() != 4) // must be 4 values (N1, N2, O1, O2)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_AREATEXT_COUNT_NOT_FOUR);
}
bool ok1 = false;
bool ok2 = false;
bool ok3 = false;
bool ok4 = false;
int n1 = areaList.at(0).toInt(&ok1);
int n2 = areaList.at(1).toInt(&ok2);
int o1 = areaList.at(2).toInt(&ok3);
int o2 = areaList.at(3).toInt(&ok4);
/*/ Y
* | N1
* O1 |
* ----------- X
* | O2
* N2 |
/*/
if (!ok1) // do not set coord if some value could nbot be converted
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_AREATEXT_CANNOT_INTERPRET_N1);
}
if (!ok2)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_AREATEXT_CANNOT_INTERPRET_N2);
}
if (!ok3)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_AREATEXT_CANNOT_INTERPRET_O1);
}
if (!ok4)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_AREATEXT_CANNOT_INTERPRET_O2);
}
InterpreterResult ir(InterpreterResult::InterpreterResultTypes::RESULT_SUCCESS);
ir.resultList.append(QRect(o1, n1, o2, n2));
return ir;
}
static InterpreterResult interpretToInt(const QString &text)
{
bool ok = false;
int result = text.toInt(&ok); // add more informations like: INFO_INT_IS_NEGATIVE [if (result < 0)] if needed
if (!ok)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_TOINT_CANNOT_INTERPRET_VALUE);
}
InterpreterResult ir(InterpreterResult::InterpreterResultTypes::RESULT_SUCCESS);
ir.resultList.append(result);
return ir;
}
static InterpreterResult interpretRangeText(const QString &rangeText)
{
QStringList rangeList = rangeText.split(QLatin1Char('-')); // 123456789-123456900 (why does ('-') not work ???)
if (rangeList.count() != 2)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_RANGETEXT_COUNT_NOT_TWO);
}
bool ok1 = false;
bool ok2 = false;
unsigned int t1 = rangeList.at(0).toUInt(&ok1);
unsigned int t2 = rangeList.at(1).toUInt(&ok2);
if (!ok1)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_RANGETEXT_CANNOT_INTERPRET_T1);
}
if (!ok2)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_RANGETEXT_CANNOT_INTERPRET_T2);
}
if (t1 > t2)
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_RANGETEXT_BEGIN_GREATER_THAN_END);
}
InterpreterResult ir(InterpreterResult::InterpreterResultTypes::RESULT_SUCCESS);
ir.resultList.append(t1);
ir.resultList.append(t2);
return ir;
}
InterpreterResult interpretValue(DetailData *detailData, int attributeInterpreterType, const QString &value)
{
(void)detailData;
switch (attributeInterpreterType)
{
case DetailData::INTERPRETER_TYPE_ID_COMPARE:
{
return interpretToInt(value);
}
case DetailData::INTERPRETER_TYPE_RANGE:
{
return interpretRangeText(value);
}
case DetailData::INTERPRETER_TYPE_AREA:
{
return interpretAreaText(value);
}
// TODO: radius
case DetailData::INTERPRETER_TYPE_STR_COMPARE: // TODO: rm - unused
{
InterpreterResult ir(InterpreterResult::InterpreterResultTypes::RESULT_SUCCESS);
ir.resultList.append(value);
return ir;
}
default:
{
return InterpreterResult(InterpreterResult::InterpreterResultTypes::RESULT_ATTRIBUTE_HAS_NO_INTERPRETER);
}
}
}
InterpreterResult interpretValue(DetailData *detailData, const QString &attributeType, const QString &value)
{
int interpreterType = detailData->attributeInterpreterLookup.getInterpreterType(attributeType); // -1 = invalid
return interpretValue(detailData, interpreterType, value);
}
}
@@ -0,0 +1,64 @@
/**
* @RDE_HEADER@
* interpreter for filter in excon model
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <QString>
#include <QVariant>
namespace Timeline
{
class DetailData;
class InterpreterResult
{
public:
enum class InterpreterResultTypes
{
RESULT_UNKNOWN,
RESULT_SUCCESS,
//RESULT_UNKNOWN_INTERPRETER_TYPE,
RESULT_ATTRIBUTE_HAS_NO_INTERPRETER,
RESULT_RANGETEXT_COUNT_NOT_TWO,
RESULT_RANGETEXT_BEGIN_GREATER_THAN_END,
RESULT_RANGETEXT_CANNOT_INTERPRET_T1,
RESULT_RANGETEXT_CANNOT_INTERPRET_T2,
RESULT_AREATEXT_COUNT_NOT_FOUR,
RESULT_AREATEXT_CANNOT_INTERPRET_N1,
RESULT_AREATEXT_CANNOT_INTERPRET_N2,
RESULT_AREATEXT_CANNOT_INTERPRET_O1,
RESULT_AREATEXT_CANNOT_INTERPRET_O2,
RESULT_TOINT_CANNOT_INTERPRET_VALUE
};
explicit InterpreterResult(const InterpreterResult::InterpreterResultTypes &res)
: resultType(res)
{
}
InterpreterResult()
: resultType(InterpreterResultTypes::RESULT_UNKNOWN)
{
}
InterpreterResultTypes resultType;
QList<QVariant> additionalInformation;
QList<QVariant> resultList;
};
InterpreterResult interpretValue(DetailData *detailData, int attributeInterpreterType, const QString &value);
InterpreterResult interpretValue(DetailData *detailData, const QString &attributeType, const QString &value);
}
@@ -0,0 +1,319 @@
/**
* @RDE_HEADER@
* interpreter to convert text-filter to model filter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "FilterWorker.h"
#include "InfoWindow.h"
#include "../../detail_data.h"
#include <QString>
#include <QStringList>
#include <QApplication>
#include <QJsonArray>
#include <QJsonObject>
#include <QDebug>
namespace Timeline
{
FilterWorker* FilterWorker::s_instance = nullptr;
FilterWorker* FilterWorker::getInstance()
{
if (!s_instance)
{
s_instance = new FilterWorker();
}
return s_instance;
}
FilterWorker::~FilterWorker()
{
//qWarning() << "~FilterWorker";
}
void FilterWorker::deleteInstance()
{
// TODO: rm
// if (s_instance)
// {
// delete s_instance;
// s_instance = 0;
// }
// else
// {
// //qWarning() << "~FilterWorker [no instance to delete!]";
// }
}
FilterWorker::FilterWorker()
{
#define addErrorHandler(x,y) { FilterWorker::ErrorHandler eh; eh.errorText = y; eh.errorParamCount = eh.errorText.split(QLatin1Char('%'), QString::SkipEmptyParts).count(); m_errorHandlerLookupMap.insert(x, eh); }(void)this
// WARNING: addErrorHandler auto discover paramCOunt: .find("%").count ! also, do not use single %'s more than once
addErrorHandler(GetModelFilterListResult::ERR_ATTRIBUTE_BLOCK_NOT_CLOSED, tr("Attribute block not closed: \n\'%1'"));
addErrorHandler(GetModelFilterListResult::ERR_ATTRIBUTE_HAS_NO_VALUES, tr("Attribute '%1' has no values"));
addErrorHandler(GetModelFilterListResult::ERR_COLON_MISUSE, tr("Colon misuse: \n\'%1'"));
addErrorHandler(GetModelFilterListResult::ERR_EVENT_HAS_NOT_ATTRIBUTE, tr("Attribute type '%1' does not belong to event type '%2': \n'%3'"));
addErrorHandler(GetModelFilterListResult::ERR_NO_EVENT_NAME, tr("No event name given for filter '%1'"));
addErrorHandler(GetModelFilterListResult::ERR_UNKNOWN_ATTRIBUTE_TYPE_BY_STRING, tr("Attribute type for '%1' does not exist"));
addErrorHandler(GetModelFilterListResult::ERR_UNKNOWN_EVENT_TYPE, tr("Event type '%1' does not exist"));
//addErrorHandler(GetModelFilterListResult::INFO_EVENT_ONLY_FILTER, tr("%1: Filtering by event type only"));
//addErrorHandler(GetModelFilterListResult::INFO_NO_FILTERS, tr("No filters selected - now resetting Timeline Filters"));
addErrorHandler(GetModelFilterListResult::ERR_VALUE_NOT_FOUND, tr("The attribute value '%1' is not a value of attribute type '%2'"));
addErrorHandler(GetModelFilterListResult::ERR_NO_VALUE_GIVEN, tr("There are no values given inside the brackets: \n'%1'"));
addErrorHandler(GetModelFilterListResult::ERR_TOO_MUCH_BRACKETS, tr("Too much brackets: \n'%1'"));
}
/* PRIVATE */ FilterWorker::Result makeResult(const int resultValue, const QString& additionalInformation = QString())
{
FilterWorker::Result result;
result.resultValue = resultValue;
if (!additionalInformation.isEmpty())
{
result.additionalInformation.append(additionalInformation);
}
return result;
}
/* PRIVATE */ FilterWorker::Result makeResult(const int resultValue, const QStringList& additionalInformationList)
{
FilterWorker::Result result;
result.resultValue = resultValue;
result.additionalInformation.append(additionalInformationList);
return result;
}
FilterWorker::GetModelFilterListResult FilterWorker::getModelFilterList(DetailData *detailData, const QJsonArray &filter, bool fromTypeSafe) // returns a ResultStruct, as the user has to know WHAT failures he did when writing the filter text by himself!
{
GetModelFilterListResult result;
if (filter.isEmpty()) // no text, no filters
{
//result.resultValueList.append(makeResult(GetModelFilterListResult::ResultValues::INFO_NO_FILTERS));
return result;
}
for (auto i1 = filter.constBegin(); i1 != filter.constEnd(); ++i1) // seperate Event Type from Rest & get attribute list per event type >>> "Shot" "Munition{...},Bla{...},..." <<<
{
const QJsonObject &currentFilter = (*i1).toObject();
//#warning TODO: used?
// if (currentFilter.isEmpty())
// {
// result.resultValueList.append(makeResult(GetModelFilterListResult::ResultValues::ERR_NO_EVENT_NAME,
// QString::number(i1)));
// continue;
// }
// if (currentFilter[QStringLiteral("Active")].toBool(false))
// continue;
QString eventTypeString = currentFilter[QStringLiteral("EventType")].toString(); // get event type string & look if event type string exists & get attribute keys for event type 0 = Event Type
int eventType = -1;
if (fromTypeSafe)
{
bool ok = false;
eventType = eventTypeString.toInt(&ok);
if (!ok)
{
eventType = -1;
}
}
else
{
eventType = detailData->eventTypeLookup.getEventType(eventTypeString); // get event type by its string fromt he lookup list
}
QString eventTypeName = detailData->eventTypeLookup.getEventText(eventType);
if (!eventTypeName.isEmpty())
eventTypeString = eventTypeName;
if (eventType == -1)
{
result.resultValueList.append(makeResult(GetModelFilterListResult::ResultValues::ERR_UNKNOWN_EVENT_TYPE,
eventTypeString));
continue;
}
bool active = currentFilter[QStringLiteral("Active")].toBool(false);
const QJsonArray &attributes = currentFilter[QStringLiteral("Attributes")].toArray();
if (attributes.isEmpty()) // if there are NO attributes, then add this a event-only-filter
{
ModelFilter mf; // already done!
mf.eventType = eventType;
mf.attributeType = QString(); // aka 'ATTRIBUTE_TYPE_EVENT_ONLY'
mf.active = active;
//mf.attributeValue = 0; QString is invalid when created
result.result.append(mf);
continue; // next!
}
// otherwise.. is/are attribute(s) then must have one Event Type and the Attributes (as one)
for (auto i2 = attributes.constBegin(); i2 != attributes.constEnd(); i2++) // process every value for that attribute
{
const QJsonObject &attribute = (*i2).toObject();
QString attributeType = attribute[QStringLiteral("AttributeType")].toString(); // type => "GearType"
if (!fromTypeSafe)
{
attributeType = detailData->attributeTypeLookup.getAttributeType(attributeType); // get attribute type by attribute type string
}
// NOTE: fix this in ngldao timeline, too
attributeType = detailData->attributeRedirectionLookup.getRedirection(attributeType);
if (attributeType.isEmpty()) // no attribute type found from attribute type string? next!
{
result.resultValueList.append(makeResult(GetModelFilterListResult::ResultValues::ERR_UNKNOWN_ATTRIBUTE_TYPE_BY_STRING,
attributeType));
continue;
}
// NOTE: fix this in ngldao timeline
// TODO: FIXME: we must trust that the attribute type is in the event *for now*. Because there is no backwards-lookup from *redirected* attributes back to the original !
// if (!detailData->attributeEventLookup.isAttributeInEvent(eventType, attributeType)) // check if event has that attribute
// {
// QStringList vl;
// vl.append(attributeTypeString);
// vl.append(eventTypeString);
// vl.append(currentFilterString);
// result.resultValueList.append(makeResult(GetModelFilterListResult::ResultValues::ERR_EVENT_HAS_NOT_ATTRIBUTE,
// vl));
// continue;
// }
// check if this is an pure string
int interpreterType = detailData->attributeInterpreterLookup.getInterpreterType(attributeType);
if (interpreterType == DetailData::INTERPRETER_TYPE_NONE)
{
continue; // ignore, not filterable
}
const QJsonArray &attributeValues = attribute[QStringLiteral("AttributeValues")].toArray(); // values => "M3,M5, ..."
for (auto i3 = attributeValues.constBegin(); i3 != attributeValues.constEnd(); ++i3)
{
QString attributeValueString = (*i3).toString(); // get single value >>> "M3" <<<
// remember: attribute value id is -2 for attribute types without entries in attribute value lookup <<<< Ist das noch so?
ModelFilter mf; // create a model filter for every value
mf.eventType = eventType;
mf.attributeType = attributeType;
mf.active = active;
mf.attributeValue = attributeValueString;
result.result.append(mf);
// qWarning() << "new model filter: event type " << eventType << " attr type " << attributeType << " attr val " << attributeValueString << " active " << mf.active;
}
}
}
return result;
}
bool FilterWorker::handleResultValues(const QVector<Result> &resultList, QWidget* parentWidget, bool ignoreInfos)
{
bool errorOccured = false;
InfoWindowEntryList iwel;
for (int i = 0; i < resultList.count(); i++)
{
FilterWorker::Result result = resultList.at(i);
int resultValue = result.resultValue;
if (!errorOccured && (resultValue & GetModelFilterListResult::ERR) == GetModelFilterListResult::ERR) // check if ERROR-bit is set (when there are no errors yet)
{
errorOccured = true;
}
if (ignoreInfos && (resultValue & GetModelFilterListResult::INFO) == GetModelFilterListResult::INFO)
{
continue;
}
// lookup error handler
InfoWindowEntry iwe;
const ErrorHandler& eh = m_errorHandlerLookupMap.value(result.resultValue);
if (!eh.isValid())
{
iwe.text = tr("Unhandled error (%1)");
iwe.infoType = InfoWindowEntry::InfoTypes::Error;
iwel.append(iwe);
continue; // cannot handle, handle next info
}
// add info to text
int additionalInformationCount = result.additionalInformation.count();
QString iweText = eh.errorText;
if (eh.errorParamCount) // only fill text if there is some text to fill with
{
for (int i2 = 0; i2 < eh.errorParamCount; i2++) // add every REQUESTED parameter by getting the value from the list (check if count is hogh enough, otherwise parameter will be '???')
{
if (additionalInformationCount >= eh.errorParamCount) // only add from addInfo list if the requested error param iterator < add info count !
{
iweText = iweText.arg(result.additionalInformation.at(i2));
}
else // add info is missing, fill with '???'
{
iweText = iweText.arg(QStringLiteral("???"));
}
}
}
iwe.text = iweText;
iwel.append(iwe);
}
if (iwel.count())
{
InfoWindow* iw = new InfoWindow(iwel, parentWidget);
iw->show();
}
return !errorOccured; // must return true if NO errors occured
}
}
@@ -0,0 +1,119 @@
/**
* @RDE_HEADER@
* interpreter to convert text-filter to model filter
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <QVector>
#include <QString>
#include <QWidget>
#include <QMap>
namespace Timeline
{
class DetailData;
class FilterWorker : public QObject
{
Q_OBJECT
private:
FilterWorker();
~FilterWorker();
static FilterWorker* s_instance;
public:
static FilterWorker* getInstance();
static void deleteInstance(); // TODO: to qt instance
// static Q_CONSTEXPR QLatin1Char filterSeparator() { return QLatin1Char(';'); }
// static Q_CONSTEXPR QLatin1Char filterEventNamespace() { return QLatin1Char(':'); }
// static Q_CONSTEXPR QLatin1Char attributeSeparator() { return QLatin1Char('+'); }
// static Q_CONSTEXPR QLatin1Char attributeValueSeparator() { return QLatin1Char(','); }
// static Q_CONSTEXPR QLatin1Char bracketOpen() { return QLatin1Char('{'); }
// static Q_CONSTEXPR QLatin1Char bracketClose() { return QLatin1Char('}'); }
struct ModelFilter
{
int eventType = -1;
QString attributeType;
bool active = false;
QString attributeValue; // stores IDs or values (position ("xxxx.xxx;xxxx.xxx"), doubles, etc.)
};
struct Result
{
int resultValue = -1;
QStringList additionalInformation;
};
struct GetModelFilterListResult
{
// 4 Bit ResultType => [0000] [0000] <= 4 Bit ResultValues
enum ResultType
{
ERR = 0x80, // bit 8 set - 1000 0000
WARN = 0x40, // bit 7 set - 0100 0000
INFO = 0x20, // bit 6 set - 0010 0000
};
enum ResultValues
{
//INFO_NO_FILTERS = INFO | 0,
//INFO_EVENT_ONLY_FILTER = INFO | 1,
ERR_NO_EVENT_NAME = ERR | 0,
ERR_COLON_MISUSE = ERR | 1,
ERR_UNKNOWN_EVENT_TYPE = ERR | 2,
ERR_ATTRIBUTE_HAS_NO_VALUES = ERR | 3,
ERR_ATTRIBUTE_BLOCK_NOT_CLOSED = ERR | 4,
ERR_UNKNOWN_ATTRIBUTE_TYPE_BY_STRING = ERR | 5,
//ERR_UNKNOWN_ATTRIBUTE_TYPE_BY_TYPE = ERR | 6,
ERR_EVENT_HAS_NOT_ATTRIBUTE = ERR | 7,
ERR_VALUE_NOT_FOUND = ERR | 8,
ERR_NO_VALUE_GIVEN = ERR | 9,
ERR_TOO_MUCH_BRACKETS = ERR | 10,
};
QVector<Result> resultValueList;
QVector<ModelFilter> result;
};
static GetModelFilterListResult getModelFilterList(DetailData *detailData, const QJsonArray &filter, bool fromTypeSafe = false); // returns a ResultStruct, as the user has to know WHAT failures he did when writing the filter text by himself!
bool handleResultValues(const QVector<Result> &resultList, QWidget* parentWidget = nullptr, bool ignoreInfos = false); // returns true if NO error(s) occured (info & warnings are okay) - false = ERRORS !!!
private:
class ErrorHandler
{
public:
ErrorHandler(const QString& errorText = QString(), const int errorParamCount = 0)
: errorText(errorText)
, errorParamCount(errorParamCount)
{
}
bool isValid() const
{
if (errorText.isEmpty() && !errorParamCount)
return false;
return true;
}
QString errorText;
int errorParamCount;
};
using ErrorHandlerLookupMap = QMap<int, ErrorHandler>;
ErrorHandlerLookupMap m_errorHandlerLookupMap;
};
using ModelFilterList = QVector<FilterWorker::ModelFilter>;
}
@@ -0,0 +1,134 @@
/**
* @RDE_HEADER@
* window to provide errors and informations when applying filter to excon model
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "InfoWindow.h"
#include <QHBoxLayout>
#include <QMessageBox>
namespace Timeline
{
InfoWindow::InfoWindow(const InfoWindowEntryList &infoWindowEntryList, QWidget* parent)
: QDialog(parent)
, infoWindowEntryList(infoWindowEntryList)
// ui
, mainLayout(new QVBoxLayout())
, pbPrev(new QPushButton(tr("< Previous")))
, lbIndicator(new QLabel())
, pbNext(new QPushButton(tr("Next >")))
, pbOK(new QPushButton(tr("OK")))
, teText(new QTextEdit(tr("Missing Message")))
, listPosition(-1) // invalid / not positioned
{
if (!infoWindowEntryList.count())
{
return;
}
connect(pbPrev, &QPushButton::clicked, this, &InfoWindow::slotPrev);
connect(pbNext, &QPushButton::clicked, this, &InfoWindow::slotNext);
connect(pbOK , &QPushButton::clicked, this, &InfoWindow::deleteLater);
// top buttons (< & >)
QHBoxLayout* topButtonsLayout = new QHBoxLayout();
pbPrev->setEnabled(false); // set prev disabled by default (as there is no prev)
topButtonsLayout->addWidget(pbPrev);
topButtonsLayout->addWidget(lbIndicator);
topButtonsLayout->addWidget(pbNext);
pbNext->setEnabled(false); // set next disabled by default (as there CAN be no next)
QWidget* topButtonsWidget = new QWidget();
topButtonsWidget->setLayout(topButtonsLayout);
mainLayout->addWidget(topButtonsWidget);
// text field for info / error / warning text
mainLayout->addWidget(teText);
// bottom buttons (ignore, ignore all, cancel)
QHBoxLayout* bottomButtonsLayout = new QHBoxLayout();
bottomButtonsLayout->addWidget(pbOK);
QWidget* bottomButtonsWidget = new QWidget();
bottomButtonsWidget->setLayout(bottomButtonsLayout);
mainLayout->addWidget(bottomButtonsWidget);
// fill first thingy....
slotNext();
this->setLayout(mainLayout);
}
void InfoWindow::checkNavigationButtons(int newPosition)
{
int listCount = infoWindowEntryList.count();
if (newPosition -1 >= 0) // if there is no prev, do not show prev
{
pbPrev->setEnabled(true);
}
else
{
pbPrev->setEnabled(false);
}
if (newPosition +2 > listCount) // if there is no next, do not show next
{
pbNext->setEnabled(false);
}
else
{
pbNext->setEnabled(true);
}
}
void InfoWindow::slotPrev()
{
int listCount = infoWindowEntryList.count();
int prevListPosition = listPosition - 1 /* 0 -1 = -1 */;
checkNavigationButtons(prevListPosition);
if (prevListPosition >= 0 && prevListPosition < listCount) // if there is a prev one
{
teText->setText(infoWindowEntryList.at(prevListPosition).text); // set text
QString indicatorString = QStringLiteral("%1/%2");
lbIndicator->setText(indicatorString.arg(prevListPosition + 1 /* so there is no "Info 0" */).arg(listCount)); // set indicator like: "Info 4/8"
listPosition = prevListPosition; // set new current list position
}
}
void InfoWindow::slotNext()
{
int listCount = infoWindowEntryList.count();
int nextListPosition = listPosition + 1 /* -1 + 1 = 0 */;
checkNavigationButtons(nextListPosition);
if (nextListPosition >= 0 && nextListPosition < listCount) // if there is a next one (some safety checks)
{
teText->setText(infoWindowEntryList.at(nextListPosition).text); // set text
QString indicatorString = QStringLiteral("%1/%2");
lbIndicator->setText(indicatorString.arg(nextListPosition + 1 /* so there is no "Info 0" */).arg(listCount)); // set indicator like: "Info 4/8"
listPosition = nextListPosition; // set new current list position
}
}
}
@@ -0,0 +1,67 @@
/**
* @RDE_HEADER@
* window to provide errors and informations when applying filter to excon model
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <QDialog>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextEdit>
#include <QLabel>
namespace Timeline
{
struct InfoWindowEntry
{
enum class InfoTypes
{
Error,
Info,
Warning
};
QString title;
QString text;
InfoTypes infoType = InfoTypes::Info; // error, info, warning
};
typedef QVector<InfoWindowEntry> InfoWindowEntryList;
class InfoWindow : public QDialog
{
Q_OBJECT
public:
InfoWindow(const InfoWindowEntryList &infoWindowEntryList, QWidget* parent = nullptr);
~InfoWindow() = default;
private:
InfoWindowEntryList infoWindowEntryList;
QVBoxLayout* mainLayout;
QPushButton* pbPrev;
QLabel* lbIndicator;
QPushButton* pbNext;
QPushButton* pbOK;
QTextEdit* teText;
int listPosition; // current position in list which information is currently shown
void checkNavigationButtons(int newPosition);
public slots:
void slotPrev();
void slotNext();
};
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FilterWindow</class>
<widget class="QWidget" name="FilterWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>628</width>
<height>393</height>
</rect>
</property>
<property name="windowTitle">
<string>Filter Table</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Closing this window will reset all filters for the timeline.</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaContent">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>610</width>
<height>358</height>
</rect>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QVBoxLayout" name="filterContainer"/>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,44 @@
/**
* @RDE_HEADER@
* abstract widget with basic/easy implementation of new widget types in Filter Table
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "AbstractAttributeWidget.h"
#include <QHBoxLayout>
#include <QPushButton>
namespace Timeline
{
AbstractAttributeWidget::AbstractAttributeWidget(QWidget *parent)
: QWidget(parent)
, m_abstractMainLayout(new QHBoxLayout(this))
{
QPushButton *pbDelete(new QPushButton(QStringLiteral("-")));
pbDelete->setPalette(QPalette(QColor("red")));
pbDelete->setToolTip(tr("Delete this value"));
m_abstractMainLayout->addWidget(pbDelete);
connect(pbDelete, &QPushButton::clicked, this, &AbstractAttributeWidget::slotDelete);
}
void AbstractAttributeWidget::slotDelete()
{
//delete this; // deletes itself from the parents layout. DONE! <= UGH! Ich als Azubi halt :)
emit deleteClicked();
deleteLater();
}
void AbstractAttributeWidget::addCustomWidget(QWidget* widget)
{
m_abstractMainLayout->insertWidget(0, widget, 1, Qt::AlignLeft); // add widget to the left
}
}
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* abstract widget with basic/easy implementation of new widget types in Filter Table
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include <QWidget>
class QHBoxLayout;
namespace Timeline
{
class DetailData;
class AbstractAttributeWidget : public QWidget
{
Q_OBJECT
public:
explicit AbstractAttributeWidget(QWidget *parent = nullptr);
virtual QString filterText(bool typeSafe = false) const = 0; // only returns the chosen values for this attribute
virtual void updateData(DetailData *detailData) = 0;
protected:
void addCustomWidget(QWidget* widget); // use this method to add the abstracted widget to this base classes ready layout
protected:
QHBoxLayout *m_abstractMainLayout;
private slots:
void slotDelete();
signals:
void signalChanges();
void deleteClicked();
};
}
@@ -0,0 +1,77 @@
/**
* @RDE_HEADER@
* select area with n1, n2, o1, o2
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "AreaWidget.h"
#include <QGridLayout>
#include <QSpinBox>
#include <QWidget>
namespace Timeline
{
AreaWidget::AreaWidget(int n1, int n2, int o1, int o2, QWidget *parent)
: AbstractAttributeWidget(parent)
, sbN1(new QSpinBox())
, sbN2(new QSpinBox())
, sbO1(new QSpinBox())
, sbO2(new QSpinBox())
{
connect(sbN1, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &AreaWidget::signalChanges);
connect(sbN2, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &AreaWidget::signalChanges);
connect(sbO1, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &AreaWidget::signalChanges);
connect(sbO2, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &AreaWidget::signalChanges);
QString sbNPrefix = tr("N ");
QString sbOPrefix = tr("O ");
sbN1->setPrefix(sbNPrefix);
sbN2->setPrefix(sbNPrefix);
sbO1->setPrefix(sbOPrefix);
sbO2->setPrefix(sbOPrefix);
sbN1->setRange(n1, n2);
sbN2->setRange(n1, n2);
sbO1->setRange(o1, o2);
sbO2->setRange(o1, o2);
sbN1->setValue(n1);
sbN2->setValue(n2);
sbO1->setValue(o1);
sbO2->setValue(o2);
QGridLayout* mainLayout = new QGridLayout();
mainLayout->addWidget(sbN1, 0, 1); // top center
mainLayout->addWidget(sbN2, 2, 1); // bottom center
mainLayout->addWidget(sbO1, 1, 0); // left center
mainLayout->addWidget(sbO2, 1, 2); // right center
QWidget* mainLayoutWidget = new QWidget();
mainLayoutWidget->setLayout(mainLayout);
this->addCustomWidget(mainLayoutWidget);
}
QString AreaWidget::filterText(bool typeSafe) const
{
Q_UNUSED(typeSafe);
return QStringLiteral("%1x%2x%3x%4").arg(sbN1->cleanText(), sbN2->cleanText(), sbO1->cleanText(), sbO2->cleanText());
}
void AreaWidget::updateData(DetailData *detailData)
{
// data cannot change
Q_UNUSED(detailData);
}
}
@@ -0,0 +1,36 @@
/**
* @RDE_HEADER@
* select area with n1, n2, o1, o2
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
class QSpinBox;
namespace Timeline
{
class AreaWidget : public AbstractAttributeWidget//QWidget
{
Q_OBJECT
public:
explicit AreaWidget(int n1, int n2, int o1, int o2, QWidget *parent = nullptr);
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *detailData) override;
private:
QSpinBox* sbN1;
QSpinBox* sbN2;
QSpinBox* sbO1;
QSpinBox* sbO2;
};
}
@@ -0,0 +1,73 @@
/**
* @RDE_HEADER@
* select true / false
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "CheckBoxWidget.h"
#include "../../detail_data.h"
#include <QCheckBox>
namespace Timeline
{
CheckBoxWidget::CheckBoxWidget(DetailData *detailData, const QString &attributeType, const QString &initialValue, QWidget* parent)
: AbstractAttributeWidget(parent)
, cbCheck(new QCheckBox(this))
{
connect(cbCheck, &QCheckBox::stateChanged, this, &CheckBoxWidget::signalChanges);
trueValue = detailData->attributeValueLookup.getValueStringFromValueId(attributeType, 1);
falseValue = detailData->attributeValueLookup.getValueStringFromValueId(attributeType, 0);
cbCheck->setText(trueValue);
addCustomWidget(cbCheck);
if (initialValue.isEmpty())
return;
cbCheck->setChecked(initialValue != QStringLiteral("0"));
}
void CheckBoxWidget::setText(const QString &text)
{
cbCheck->setText(text);
}
QString CheckBoxWidget::filterText(bool typeSafe) const
{
QString filterText;
if (cbCheck->isChecked())
{
if (typeSafe)
filterText += QStringLiteral("1");
else
filterText += trueValue;
}
else
{
if (typeSafe)
filterText += QStringLiteral("0");
else
filterText += falseValue;
}
return filterText;
}
void CheckBoxWidget::updateData(DetailData *)
{
// data cannot change
}
}
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* select true / false
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
#include "../../detail_data.h"
class QCheckBox;
namespace Timeline
{
class CheckBoxWidget : public AbstractAttributeWidget//QWidget
{
Q_OBJECT
public:
explicit CheckBoxWidget(DetailData *detailData, const QString &attributeType, const QString &initialValue, QWidget *parent = nullptr);
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *) override;
void setText(const QString &text);
private:
QCheckBox* cbCheck;
QString trueValue;
QString falseValue;
};
}
@@ -0,0 +1,108 @@
/**
* @RDE_HEADER@
* select pre-defined value
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "ComboBox.h"
#include "logger.h"
#include <QComboBox>
namespace Timeline
{
// combobox must be aware of getting his own data, as he needs to update himself (performance and coding reasons)
ComboBox::ComboBox(DetailData *detailData, const QString &attributeType, int eventType, const QString &initialValue, QWidget* parent)
: AbstractAttributeWidget(parent)
, m_cbValues(new QComboBox())
, m_eventType(eventType)
, m_logger(getLogger("EventFilter.ComboBox"))
{
connect(m_cbValues, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ComboBox::signalChanges);
addCustomWidget(m_cbValues);
m_concreteAttributeType = detailData->attributeRedirectionLookup.getRedirection(attributeType); // returns 'attributeType' if there is no redirection
updateData(detailData);
if (initialValue.isEmpty())
return;
bool k = false;
int initialValueKey = initialValue.toInt(&k);// everything is typesafe now, saved as index keys, not values which index have to be looked up anymore
if (initialValueKey < 0 || !k) // invalid!
{
WARNING_LOG(m_logger) << "ComboBox::c_tor: initialValueKey < 0 (" << initialValueKey << ")" << "initialValue: " << initialValue;
return;
}
// select initial value
m_cbValues->setCurrentIndex(m_cbValues->findData(initialValueKey, Qt::UserRole)); // set initial value by id WARNING: does not check for invalid index
}
void ComboBox::updateData(DetailData *detailData)
{
int lastSelectedKey = -1;
if (m_cbValues->currentIndex() > 0) // none selected yet (if m_cbValues has to be built first)
lastSelectedKey = m_cbValues->itemData(m_cbValues->currentIndex()).toInt();
// now reset and load new data
QSignalBlocker blocker(m_cbValues);
m_cbValues->clear();
m_avm = detailData->m_attributeValueLookupDtc.getAttributeValueMap(m_eventType, m_concreteAttributeType);
if (m_avm.isEmpty())
m_avm = detailData->attributeValueLookup.getAttributeValueMap(m_concreteAttributeType); // get new avm
for (auto i = m_avm.constBegin(); i != m_avm.constEnd(); ++i)
{
m_cbValues->addItem(i.value()
#ifdef DEBUG
+ " (" + QString::number(i.key()) + ")"
#endif
, i.key()); // add value and key (storing key as userdata)
}
if (lastSelectedKey < 0) // only set new index if there was a last selected index first
{
m_cbValues->setCurrentIndex(0); // set to the first entry
return; // done!
}
// select old value (if one)
m_cbValues->setCurrentIndex(m_cbValues->findData(lastSelectedKey, Qt::UserRole)); // set initial value by id WARNING: does not check for invalid index
DEBUG_LOG(m_logger) << "ComboBox: no initial item index found for value id" << lastSelectedKey;
}
QString ComboBox::filterText(bool typeSafe) const
{
QString resultFilterText;
bool ok = false;
int key = m_cbValues->itemData(m_cbValues->currentIndex()).toInt(&ok);
if (!ok)
{
return resultFilterText;
}
if (typeSafe)
resultFilterText = QString::number(key); // returns the selected value's ID
else
resultFilterText = m_avm.value(key); // returns the selected value's STRING
return resultFilterText;
}
}
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* select pre-defined value
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
#include "../../detail_data.h"
#include <NGLLogger/INGLLogger.h>
class QComboBox;
namespace Timeline
{
class ComboBox : public AbstractAttributeWidget
{
Q_OBJECT
public:
explicit ComboBox(DetailData *detailData, const QString &attributeType,
int eventType, const QString &initialValue, QWidget *parent = nullptr); // combobox fills itself with values, by getting informations from FilterData
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *detailData) override;
private:
QComboBox *m_cbValues;
int m_eventType;
QString m_concreteAttributeType;
AttributeValueMap m_avm;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* select any range between values
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "DefaultRangeWidget.h"
#include "logger.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QSpinBox>
#include <QWidget>
namespace Timeline
{
DefaultRangeWidget::DefaultRangeWidget(unsigned int min, unsigned int max, const QString &prefix, const QString &suffix, const QString &initialValue, QWidget *parent)
: AbstractAttributeWidget(parent)
, box1(new QSpinBox())
, box2(new QSpinBox())
, m_logger(getLogger("EventFilter.Widgets.DefaultRangeWidget"))
{
connect(box1, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &DefaultRangeWidget::signalChanges);
connect(box2, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &DefaultRangeWidget::signalChanges);
box1->setRange(min, max);
box2->setRange(min, max);
box1->setPrefix(prefix);
box2->setPrefix(prefix);
box1->setSuffix(suffix);
box2->setSuffix(suffix);
box1->setValue(min);
box2->setValue(max);
QHBoxLayout* mainLayout = new QHBoxLayout();
mainLayout->addWidget(new QLabel(tr("between")));
mainLayout->addWidget(box1);
mainLayout->addWidget(new QLabel(tr("and")));
mainLayout->addWidget(box2);
QWidget* mainLayoutWidget = new QWidget();
mainLayoutWidget->setLayout(mainLayout);
this->addCustomWidget(mainLayoutWidget);
box1->setFocus(); // focus first widget
// not necessary, as initialValues are already checked bz checkValue ...
if (initialValue.length()) // is there an initial value ?
{
QStringList valueList = initialValue.split(QLatin1Char('-'), QString::SkipEmptyParts);
if (valueList.count() != 2) // must be exactly 2 (e.g. "99,999")
{
WARNING_LOG(m_logger) << "DefaultRangeWidget: invalid initial value '" << initialValue << "'";
return;
}
bool k1 = false;
bool k2 = false;
unsigned int val1 = valueList.at(0).toUInt(&k1);
unsigned int val2 = valueList.at(1).toUInt(&k2);
if (!k1 || !k2)
{
WARNING_LOG(m_logger) << "DefaultRangeWidget: initial value is not numeric '" << initialValue << "'";
return;
}
box1->setValue(val1);
box2->setValue(val2);
}
}
QString DefaultRangeWidget::filterText(bool typeSafe) const
{
Q_UNUSED(typeSafe);
return QStringLiteral("%1 - %2").arg(box1->value(), box2->value());
}
void DefaultRangeWidget::updateData(DetailData *detailData)
{
// not needed, as min / max is always static (0 - 999 bullets, etc)
Q_UNUSED(detailData);
}
}
@@ -0,0 +1,44 @@
/**
* @RDE_HEADER@
* select any range between values
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
#include <NGLLogger/INGLLogger.h>
class QHBoxLayout;
class QSpinBox;
namespace Timeline
{
class DefaultRangeWidget : public AbstractAttributeWidget
{
Q_OBJECT
public:
explicit DefaultRangeWidget(unsigned int min,
unsigned int max,
const QString &prefix,
const QString &suffix,
const QString &initialValue = QString(),
QWidget *parent = nullptr);
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *detailData) override;
private:
QSpinBox* box1;
QSpinBox* box2;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,70 @@
/**
* @RDE_HEADER@
* select area with n1, n2, o1, o2
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "RadiusWidget.h"
#include <QGridLayout>
#include <QSpinBox>
#include <QWidget>
namespace Timeline
{
RadiusWidget::RadiusWidget(int n1, int n2, int o1, int o2, int radius, QWidget *parent)
: AbstractAttributeWidget(parent)
, sbN(new QSpinBox(this))
, sbO(new QSpinBox(this))
, sbR(new QSpinBox(this))
{
Q_ASSERT(false); // not ready to use!
QString sbNPrefix = tr("N ");
QString sbOPrefix = tr("O ");
QString sbRPrefix = tr("radius of ");
sbN->setPrefix(sbNPrefix);
sbO->setPrefix(sbOPrefix);
sbR->setPrefix(sbRPrefix);
sbN->setRange(n1, n2);
sbO->setRange(o1, o2);
sbR->setRange(0, 9999);
sbN->setValue(n1);
sbO->setValue(o1);
sbR->setValue(radius);
QGridLayout* mainLayout = new QGridLayout();
mainLayout->addWidget(sbN, 0, 0); // left
mainLayout->addWidget(sbR, 0, 1); // middle
mainLayout->addWidget(sbO, 0, 2); // right
QWidget* mainLayoutWidget = new QWidget();
mainLayoutWidget->setLayout(mainLayout);
addCustomWidget(mainLayoutWidget);
}
QString RadiusWidget::filterText(bool typeSafe) const
{
Q_UNUSED(typeSafe);
return QStringLiteral("%1x%2x%3").arg(sbN->cleanText(), sbO->cleanText(), sbR->cleanText());
}
void RadiusWidget::updateData(DetailData *)
{
// TODO: !
}
}
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* select radius with n1, n2, o1, o2
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
class QSpinBox;
namespace Timeline
{
class RadiusWidget : public AbstractAttributeWidget//QWidget
{
Q_OBJECT
public:
explicit RadiusWidget(int n1, int n2, int o1, int o2, int radius, QWidget *parent = nullptr);
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *detailData) override;
private:
QSpinBox* sbN;
QSpinBox* sbO;
QSpinBox* sbR; // radius
};
}
@@ -0,0 +1,109 @@
/**
* @RDE_HEADER@
* select any range between time
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#include "logger.h"
#include "TimeRangeWidget.h"
#include "../../detail_data.h"
#include <LtsWidgets/lts_date_time_edit.h>
#include <QHBoxLayout>
#include <QLabel>
#include <QDateTime>
namespace Timeline
{
TimeRangeWidget::TimeRangeWidget(DetailData *detailData, const QString &initialValue, QWidget *parent)
: AbstractAttributeWidget(parent)
, m_logger(getLogger("EventFilter.Widgets.TimeRangeWidget"))
, m_edit1(new LtsWidgets::LtsDateTimeEdit(this))
, m_edit2(new LtsWidgets::LtsDateTimeEdit(this))
{
connect(m_edit1, &LtsWidgets::LtsDateTimeEdit::dateTimeChanged, this, &TimeRangeWidget::signalChanges);
connect(m_edit2, &LtsWidgets::LtsDateTimeEdit::dateTimeChanged, this, &TimeRangeWidget::signalChanges);
// m_edit1->setCalendarPopup(true);
// m_edit2->setCalendarPopup(true);
// m_edit1->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue);
// m_edit2->setCorrectionMode(QAbstractSpinBox::CorrectToNearestValue);
QHBoxLayout* mainLayout = new QHBoxLayout();
mainLayout->addWidget(new QLabel(tr("between")));
mainLayout->addWidget(m_edit1);
mainLayout->addWidget(new QLabel(tr("and")));
mainLayout->addWidget(m_edit2);
QWidget* mainLayoutWidget = new QWidget();
mainLayoutWidget->setLayout(mainLayout);
addCustomWidget(mainLayoutWidget);
m_edit1->setFocus(); // focus first widget
updateData(detailData);
if (initialValue.isEmpty())
return;
const auto initialTimeRange(initialValue.splitRef(QLatin1Char('-'), QString::SkipEmptyParts));
if (initialTimeRange.count() != 2)
{
WARNING_LOG(m_logger) << "invalid initial time range value: " << initialValue;
return;
}
bool k = false;
qint64 time = initialTimeRange.at(0).toInt(&k);
time *= 1000;
if (!k)
WARNING_LOG(m_logger) << "invalid initial time range start value: " << initialTimeRange.at(0).toString();
else
m_edit1->setMsecsSinceEpoch(time);
time = initialTimeRange.at(1).toInt(&k);
time *= 1000;
if (!k)
WARNING_LOG(m_logger) << "invalid initial time range start value: " << initialTimeRange.at(1).toString();
else
m_edit2->setMsecsSinceEpoch(time);
}
QString TimeRangeWidget::filterText(bool typeSafe) const
{
(void)typeSafe;
QString filterText;
filterText += QString::number(m_edit1->msecsSinceEpoch() / 1000 /* otherwise would not fit into int */); // secs since epoch
filterText += QLatin1Char('-'); // trennzeichen
filterText += QString::number(m_edit2->msecsSinceEpoch() / 1000 /* otherwise would not fit into int */); // s.a.a.
return filterText;
}
void TimeRangeWidget::updateData(DetailData *detailData)
{
LtsSession::LtsSession *session = detailData->session();
m_edit1->setMsecsSinceEpoch(session->partitionTime() - 60 * 1000 /* TODO: minus 1 minute offset ? */);
m_edit2->setMsecsSinceEpoch(session->partitionTime());
}
} // namespace Timeline
@@ -0,0 +1,41 @@
/**
* @RDE_HEADER@
* select any range between time
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2015-09-23
*/
#pragma once
#include "AbstractAttributeWidget.h"
#include <NGLLogger/INGLLogger.h>
namespace LtsWidgets
{
class LtsDateTimeEdit;
}
namespace Timeline
{
class TimeRangeWidget : public AbstractAttributeWidget
{
Q_OBJECT
public:
explicit TimeRangeWidget(DetailData *detailData, const QString &initialValue, QWidget *parent = nullptr);
QString filterText(bool typeSafe = false) const override;
void updateData(DetailData *detailData) override;
private:
NGL::INGLLogger &m_logger;
public:
LtsWidgets::LtsDateTimeEdit *m_edit1;
LtsWidgets::LtsDateTimeEdit *m_edit2;
};
}
@@ -0,0 +1,303 @@
/**
* @RDE_HEADER@
* Helper classes to retrieve attribute values
* $Id$
* $HeadURL$
* $Author$
*
* Created by Christian Ehrlicher, NLE, 2020-02-07
*/
#include "attribute_value_lookup.h"
#include <AudioVideoManagement/avm_dtc.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <OperatorManagement/opm_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <OperatorManagement/opm_translation.h>
#include <ParticipantManagement/ppm_translation.h>
#include <QVector>
namespace Timeline
{
//
// AvmChargeType
//
AttributeValueMap AvmChargeType::getAttributeValueMap(int eventType) const
{
if (!m_dtc)
return {};
using namespace ProjectDataManagement;
QVector<ChargeType> charges;
switch (eventType)
{
case EventType::Hit:
case EventType::WeaponAssignment:
case EventType::FireEvent:
charges = m_dtc->chargesForGearClass(GearClass::DuelSim);
break;
case EventType::HitBuilding:
charges = m_dtc->chargesForGearClass(GearClass::DuelSim);
Q_FALLTHROUGH();
case EventType::HitEffectSimulation:
charges += m_dtc->chargesForGearClass(GearClass::Artillery);
charges += m_dtc->chargesForGearClass(GearClass::Mortar);
charges += m_dtc->chargesForGearClass(GearClass::Geopairing);
charges += m_dtc->chargesForGearClass(GearClass::AirSupport);
charges += m_dtc->chargesForGearClass(GearClass::MineArea);
charges += m_dtc->chargesForGearClass(GearClass::Biological);
charges += m_dtc->chargesForGearClass(GearClass::Chemical);
break;
case EventType::HitB:
charges = m_dtc->chargesForGearClass(GearClass::Biological);
break;
case EventType::HitC:
charges = m_dtc->chargesForGearClass(GearClass::Chemical);
break;
case EventType::BCContamination:
charges = m_dtc->chargesForGearClass(GearClass::Biological);
charges += m_dtc->chargesForGearClass(GearClass::Chemical);
break;
case EventType::HitMine:
case EventType::EnterMineArea:
case EventType::LeaveMineArea:
case EventType::MineArea:
charges = m_dtc->chargesForGearClass(GearClass::MineArea);
//charges =+ dtc->chargesForGearClass(GearClass::MineSingle); // same charges
break;
}
Q_ASSERT(!charges.isEmpty());
AttributeValueMap ret;
for (const ChargeType &data : qAsConst(charges))
ret.insert(data.id(), m_dtc->trChargeTypeShortName(data.id()));
return ret;
}
QString AvmChargeType::getValueStringFromValueId(int attributeValueId) const
{
if (!m_dtc)
return {};
return m_dtc->trChargeTypeShortName(attributeValueId);
}
//
// AvmGearType
//
AttributeValueMap AvmGearType::getAttributeValueMap(int eventType) const
{
using namespace ProjectDataManagement;
if (eventType != EventType_def::WeaponAssignment &&
eventType != EventType_def::FireEvent)
{
Q_ASSERT(false);
return {};
}
if (!m_dtc)
return {};
const auto gearTypes = m_dtc->gearTypes();
AttributeValueMap ret;
for (const auto &data : gearTypes)
{
if (data.gearClassId() == GearClass::DuelSim &&
data.tasCode() != 0)
ret.insert(data.id(), m_dtc->trGearTypeName(data.id()));
}
return ret;
}
QString AvmGearType::getValueStringFromValueId(int attributeValueId) const
{
if (!m_dtc)
return {};
return m_dtc->trGearTypeName(attributeValueId);
}
//
// AvmParty
//
AttributeValueMap AvmParty::getAttributeValueMap(int eventType) const
{
Q_UNUSED(eventType);
using namespace ParticipantManagement;
if (!m_dtc)
return {};
const auto parties = m_dtc->partys();
AttributeValueMap ret;
for (const auto &data : parties)
ret.insert(data.id(), trPartyName(data.name()));
return ret;
}
QString AvmParty::getValueStringFromValueId(int attributeValueId) const
{
using namespace ParticipantManagement;
if (!m_dtc)
return {};
return trPartyName(m_dtc->party(attributeValueId).name());
}
//
// AvmEquipmentNumber
//
AttributeValueMap AvmEquipmentNumber::getAttributeValueMap(int eventType) const
{
Q_UNUSED(eventType);
using namespace ParticipantManagement;
if (!m_dtc)
return {};
AttributeValueMap ret;
auto cb = [&ret](const ParticipantManagement::Participant & p) -> void
{
if (!p.equipmentNumber().is_set())
return;
const auto equipNo = p.equipmentNumber().get();
ret.insert(equipNo, QString::number(equipNo));
};
m_dtc->participants(cb);
return ret;
}
QString AvmEquipmentNumber::getValueStringFromValueId(int attributeValueId) const
{
if (!m_dtc)
return {};
auto ret = m_dtc->participant(attributeValueId).equipmentNumber();
return ret.is_set() ? QString::number(ret.get()) : QString();
}
//
// AvmOperator
//
AttributeValueMap AvmOperator::getAttributeValueMap(int eventType) const
{
Q_UNUSED(eventType);
using namespace OperatorManagement;
if (!m_dtc)
return {};
const auto users = m_dtc->users();
AttributeValueMap ret;
for (const auto &data : users)
ret.insert(data.id(), QString::fromStdString(data.name()));
ret.insert(0, QObject::tr("EXCON"));
return ret;
}
QString AvmOperator::getValueStringFromValueId(int attributeValueId) const
{
if (attributeValueId == 0)
return QObject::tr("EXCON");
if (!m_dtc)
return {};
return QString::fromStdString(m_dtc->user(attributeValueId).name());
}
//
// AvmBuilding
//
AttributeValueMap AvmBuilding::getAttributeValueMap(int eventType) const
{
Q_UNUSED(eventType);
if (!m_dtc)
return {};
AttributeValueMap ret;
auto cb = [this, &ret](const InfrastructureManagement::MainStructureElement &mse) -> void
{
if (mse.structureElementTypeId() != InfrastructureManagement::StructureElementType::Building)
return;
ret.insert(mse.id(), m_dtc->getBuildingDisplayName(mse));
};
m_dtc->mainStructureElements(cb);
return ret;
}
QString AvmBuilding::getValueStringFromValueId(int attributeValueId) const
{
if (!m_dtc)
return {};
return m_dtc->getBuildingDisplayName(attributeValueId);
}
//
// AvmStreamName
//
AvmStreamName::AvmStreamName(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc)
: m_dtc(dtc)
, m_snm(AudioVideoManagement::StreamNameMapping::create(m_dtc.get()))
{}
AvmStreamName::~AvmStreamName() = default;
AttributeValueMap AvmStreamName::getAttributeValueMap(int eventType) const
{
AttributeValueMap ret;
if (!m_dtc)
return ret;
switch (eventType)
{
case ProjectDataManagement::EventType::RadioCnr:
{
auto cb = [&ret](const AudioVideoManagement::CnrConfig &cnrConfig) -> void
{
ret.insert(cnrConfig.id(), QString::fromStdString(cnrConfig.radioCircuit()));
};
m_dtc->cnrConfigs(cb);
break;
}
case ProjectDataManagement::EventType::RadioTvc:
{
auto cb = [&ret](const AudioVideoManagement::TvcConfig &tvcConfig) -> void
{
ret.insert(tvcConfig.id(), QString::fromStdString(tvcConfig.radioCircuit()));
};
m_dtc->tvcConfigs(cb);
break;
}
case ProjectDataManagement::EventType::CameraVideo:
{
auto cb = [this, &ret](const AudioVideoManagement::Stream &stream) -> void
{
if (stream.mountingPointId() == 0 ||
stream.type() != AudioVideoManagement::StreamType::Video)
return;
ret.insert(stream.id(), m_snm->displayName(stream.mountingPointId()));
};
m_dtc->streams(cb);
break;
}
default:
break;
}
return ret;
}
QString AvmStreamName::getValueStringFromValueId(int attributeValueId) const
{
if (!m_dtc)
return {};
return QString::fromStdString(m_dtc->stream(attributeValueId).name());
}
} // namespace Timeline
@@ -0,0 +1,138 @@
/**
* @RDE_HEADER@
* Helper classes to retrieve attribute values
* $Id$
* $HeadURL$
* $Author$
*
* Created by Christian Ehrlicher, NLE, 2020-02-07
*/
#pragma once
#include <QMap>
#include <QString>
#include <memory>
namespace AudioVideoManagement
{
class StreamNameMapping;
namespace Interface
{
class Dtc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace OperatorManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ProjectDataManagement
{
namespace Interface
{
class Dtc;
}
}
namespace Timeline
{
using AttributeValueMap = QMap<int, QString>;
using AtributeValueLookupMap = QMap<QString, AttributeValueMap>;
class AttributeValueLookupProvider
{
public:
virtual ~AttributeValueLookupProvider() = default;
virtual AttributeValueMap getAttributeValueMap(int eventType) const = 0;
virtual QString getValueStringFromValueId(int attributeValueId) const = 0;
};
class AvmChargeType final : public AttributeValueLookupProvider
{
public:
explicit AvmChargeType(const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_dtc;
};
class AvmGearType final : public AttributeValueLookupProvider
{
public:
explicit AvmGearType(const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_dtc;
};
class AvmParty final : public AttributeValueLookupProvider
{
public:
explicit AvmParty(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_dtc;
};
class AvmEquipmentNumber final : public AttributeValueLookupProvider
{
public:
explicit AvmEquipmentNumber(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_dtc;
};
class AvmOperator final : public AttributeValueLookupProvider
{
public:
explicit AvmOperator(const std::shared_ptr<OperatorManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<OperatorManagement::Interface::Dtc> m_dtc;
};
class AvmBuilding final : public AttributeValueLookupProvider
{
public:
explicit AvmBuilding(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &dtc) : m_dtc(dtc) {}
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_dtc;
};
class AvmStreamName final : public AttributeValueLookupProvider
{
public:
explicit AvmStreamName(const std::shared_ptr<AudioVideoManagement::Interface::Dtc> &dtc);
~AvmStreamName();
AttributeValueMap getAttributeValueMap(int eventType) const override;
QString getValueStringFromValueId(int attributeValueId) const override;
private:
std::shared_ptr<AudioVideoManagement::Interface::Dtc> m_dtc;
std::shared_ptr<AudioVideoManagement::StreamNameMapping> m_snm;
};
} // namespace Timeline
@@ -0,0 +1,928 @@
/**
* @RDE_HEADER@
* Model to handle datail data for the different event types
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-09-04
*/
#include "detail_data.h"
#include "logger.h"
#include "tln_legend.h"
#include "attribute_value_lookup.h"
#include <LtsTimelineClassic/Event.h>
#include <LtsWidgets/lts_date_time_edit.h>
#include <LtsSession/lts_session.h>
#include <LtsUtilsSettings/settings.h>
// Dtc lookups: (data ifc libs)
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
// Enums only: (svc libs only)
#include <Services/ExerciseEventManagement/eem_enums.hpp>
#include <Services/ExerciseEventManagement/eem_translation.h>
#include <Services/ProjectDataManagement/pdm_translation.h>
#include <Services/AreaEffectSimulation/aes_translation.h>
#include <Services/MarkerManagement/mrk_translation.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QVariant>
namespace Timeline
{
static inline QString removeSpaces(const QString &str)
{
// no QString::erase(it) available...
QString ret;
ret.reserve(str.size());
for (const QChar c : str)
{
if (!c.isSpace())
ret += c;
}
return ret;
}
DetailData::DetailData(LtsSession::LtsSession *session, QObject *parent)
: QObject(parent)
, m_session(session)
, __already_populated__(false)
, m_logger(getLogger("DetailData"))
{
populate();
}
void DetailData::addRedirectedAttributeType(const QString &newAttributeTypeId, const QString &attributeText, const QString &redirectToAttributeTypeId)
{
attributeTypeLookup.addAttributeText(newAttributeTypeId, attributeText);
attributeRedirectionLookup.addRedirection(newAttributeTypeId, redirectToAttributeTypeId);
}
void DetailData::addAttributeType(const QString &attributeTypeId, const QString &attributeText, const int uiTypeId, const int interpreterTypeId)
{
attributeTypeLookup.addAttributeText(attributeTypeId, attributeText);
attributeUITypeLookup.addUITypeToAttribute(attributeTypeId, uiTypeId);
attributeInterpreterLookup.addInterpreterTypeToAttribute(attributeTypeId, interpreterTypeId);
}
void DetailData::addEventType(int eventTypeId, const QString &eventTypeString) // with default attribute types [time, etc.]
{
eventTypeLookup.addEventType(eventTypeId, eventTypeString);
attributeEventLookup.addAttributesToEvent(eventTypeId, {QStringLiteral("TimeRange")});
}
void DetailData::addEventType(const ProjectDataManagement::EventType &eventType) // with default attribute types [time, etc.]
{
addEventType(eventType.underlying(), ProjectDataManagement::toString(eventType));
}
void DetailData::populate() // call on any retranslate
{
if (__already_populated__) // TODO: repopulate!!
return;
__already_populated__ = true;
// TODO: clear all lookups !
// populate Event Types - see build/Lts/Services/ExerciseEventManagement/eem_enums.hpp
// * retrieved as of 2017-09-01
addEventType(0, tr("General")); // would be type 'unknown' in EventType_def DE: "Allgemein"
addEventType(markerTypeId, tr("Marker"));
attributeEventLookup.addAttributesToEvent(markerTypeId, {QStringLiteral("MarkerName"),
QStringLiteral("MarkerNote"),
QStringLiteral("Operator"),
QStringLiteral("MarkerOffset"),
QStringLiteral("IsSituationType"),
QStringLiteral("IsVideoType"),
QStringLiteral("IsAudioType")});
using namespace ProjectDataManagement;
const auto possibleEventTypes = TimelineLegendDialog::possibleEvents();
for (const auto eventType : possibleEventTypes)
{
switch (eventType.underlying())
{
case EventType::Hit:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("VulnerabilityState"),
QStringLiteral("OriginatorChargeTypeId"),
QStringLiteral("FriendlyFire"),
//QStringLiteral("OriginatorId"), // TODO: kann ich nix mit anfangen..
//QStringLiteral("OriginatorType"), // not really useful - see EEM HandlerDynamicParticipantData::tryCreateHitEvent()
QStringLiteral("OriginatorEquipmentNumber")});
break;
case EventType::BiteMessage:
break;
case EventType::WeaponAssignment:
case EventType::FireEvent:
attributeEventLookup.addAttributesToEvent(eventType, {//<< QStringLiteral("GearId"), // is a unique serial number not known to excon operator
QStringLiteral("GearTypeId"),
QStringLiteral("ChargeTypeId")});
break;
case EventType::HitBuilding:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesType"),
QStringLiteral("ChargeTypeId"),
QStringLiteral("OriginatorEquipmentNumber"),
QStringLiteral("MainStructureElementId"),
QStringLiteral("VulnerabilityState"),
QStringLiteral("damagePercent"), // range
//QStringLiteral("damagePercentOld"),
}); // range
break;
case EventType::Manipulation:
break;
case EventType::Sanitation:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("Vitality"),
QStringLiteral("SanitationProceduresNeeded"),
QStringLiteral("SanitationProceduresTaken")});
break;
case EventType::CameraVideo:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("PartyId"),
QStringLiteral("MainStructureElementId"),
QStringLiteral("duration"),
QStringLiteral("streamName")});
break;
case EventType::RadioCnr:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("PartyId"),
QStringLiteral("duration"),
QStringLiteral("streamName")});
break;
case EventType::RadioTvc:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("duration"),
QStringLiteral("streamName")});
break;
case EventType::HitEffectSimulation:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesType"),
QStringLiteral("AesName"),
QStringLiteral("OriginatorChargeTypeId")});
break;
case EventType::HitC:
case EventType::HitB:
case EventType::HitMine:
case EventType::EnterMineArea:
case EventType::LeaveMineArea:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesName"),
QStringLiteral("OriginatorChargeTypeId")});
break;
case EventType::HitN:
case EventType::HitGasPipe:
case EventType::EnterDangerArea:
case EventType::LeaveDangerArea:
case EventType::CrossLocalBorderline:
case EventType::CrossGlobalBorderline:
case EventType::EnterFunctionalArea:
case EventType::LeaveFunctionalArea:
case EventType::EnterNuclear:
case EventType::LeaveNuclear:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesName")});
break;
case EventType::Borderline:
case EventType::NContamination:
case EventType::DangerArea:
case EventType::FunctionalArea:
case EventType::MineCorridor:
case ProjectDataManagement::EventType::GasPipe:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesName"),
QStringLiteral("duration")});
break;
case EventType::MineArea:
case EventType::BCContamination:
attributeEventLookup.addAttributesToEvent(eventType, {QStringLiteral("AesName"),
QStringLiteral("duration"),
QStringLiteral("ChargeTypeId")});
break;
case EventType::Unknown:
case EventType::FriendlyFire:
case EventType::TechnicalArea:
case EventType::EquipmentErrorReport:
case EventType::EmergencyCall:
Q_ASSERT(false);
continue;
}
addEventType(eventType);
}
// populate Attribute Types - see /home/ref/TDS/src/Lts/Services/ExerciseEventManagement/eem_service.xml:52
/*
* retrieved as of 2017-09-04
*
*
* FireEvent:
* GearId
* GearTypeId
* ChargeTypeId
* Remain
*
* WeaponAssignment:
* GearId
* GearTypeId
* DryFire
*
* Manipulation: => show bite window
* DeviceType
* BiteMessage
* BatteryState
* BiteState
* ConfigState
* JsonArray BiteErrors:
* ErrorNo
* CanDeviceNumber
* SubDeviceNumber
* UniqueId
*
* HitBuilding:
* ChargetTypeId
* damagePercent
* damagePercentOld
* AesId
* VunerabilityState
* OriginatorParticipantId
* MainStructureElementId
*
*
* >>> Ab hier bin ich mir nicht mehr Sicher, was genau im Comment gemeint ist...
* HitIndrect:
* HitMine:
* HitN:
* HitC:
* EnterDangerArea:
* LeaveDangerArea:
* CES:
* AesId
* FriendlyFire
*/
/* Jedes Attribute benoetigt einen Interpreter Typen. Ein Interpreter Typ sagt dem Interpreter, wie er den Wert eines Attributs auf den Filter anwenden kann. Hier als Beispiel:
* "OriginatorChargeTypeId" ist ein Attribut, welches bloß eine Value ID zurueckgibt. D.h. id:9000 (wird dann später einem lesbaren Wert zugeordnet: z.B: "30mm Irgendwas")
* SOll der Wert im Filter also verglichen werden, muessen hier die IDs verglichen werden. Id 9000 => Id 9000.
* Hier ist also folgender Interpreter korrekt: INTERPRETER_TYPE_ID_COMPARE
*
* Anderes Beispiel:
* Zeit. Wird als Filter eine Zeit zwischen X und Y angegeben, muss geschaut werden, ob die Zeit Z sich zwischen X und Y befindet. Mit "INTERPRETER_TYPE_ID_COMPARE" wuerde das
* nicht funktionieren, da sonst nur EINE Zeit (naemlich genau Zeitpunkt Z) beim Interpreter korrekt ist. Fuer diesen Fall gibt es "INTERPRETER_TYPE_RANGE".
* Dieser Interpreter prueft, ob etwas in der Range zwischen X und Y ist.
*/
// AttributeUiTypeLookup: Wie wird das Attribut angezeigt? Bei einer Liste aus Einträgen: COmboBox, oder wenn z.B. es nur 2 Zustände gib: FriendlyFire => Yes / No => CheckBox
addAttributeType(QStringLiteral("TimeRange"), tr("Time range:"), ATTRIBUTE_UI_TYPE_TIME_RANGE_WIDGET /* TODO: none! */, INTERPRETER_TYPE_TIME); // is added in getDetailData as dummy so that the filter has a value to filter with TimeRange
// Vulnerability State:
addAttributeType(QStringLiteral("VulnerabilityState"), tr("Vulnerability State:"));
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::CombatReady);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::LightDamage);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::MediumDamage);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::HeavyDamage);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::MobilityKill);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::VisibleMobilityKill);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::WeaponSystemDamage);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::VisibleWeaponSystemDamage);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::CatastrophicKill);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::TemporarySeverance);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::NearMiss);
addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::HitWithNoEffect);
//addAttributeValue(QStringLiteral("VulnerabilityState"), ProjectDataManagement::VulnerabilityState::NotReady);
// Marker Types:
addAttributeTypeWithYesAndNo(QStringLiteral("IsSituationType"), tr("Is situation type:"));
addAttributeTypeWithYesAndNo(QStringLiteral("IsVideoType"), tr("Is video type:"));
addAttributeTypeWithYesAndNo(QStringLiteral("IsAudioType"), tr("Is audio type:"));
// AES Types:
// addRedirectedAttributeType(QStringLiteral("AesId"), tr("Aes Type:"), QStringLiteral("AesType")); // because the result type is Aes Type (but has to be resolved by looking at the AES behind the AesId
addAttributeType(QStringLiteral("AesType"), tr("Aes Type:"));
//addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtUnknown);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtNContamination);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtBCContamination);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtExplosionArea);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtExplosionSingle);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtMineArea);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtDangerArea);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtFunctionalArea);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtMineCorridor);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtBorderline);
//addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtTechnicalArea);
//addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtExplosionAreaSingleExplosions);
addAttributeValue(QStringLiteral("AesType"), AreaEffectSimulation::AesType::AtMineAreaSingleMines);
// Friendly Fire
addAttributeTypeWithYesAndNo(QStringLiteral("FriendlyFire"), tr("Friendly Fire:"));
// Attribute: Dry Fire
addAttributeTypeWithYesAndNo(QStringLiteral("DryFire"), tr("Dry Fire:"));
// non-filterables:
addAttributeType(QStringLiteral("MarkerName"), tr("Name:"), ATTRIBUTE_UI_TYPE_CHECKBOX /* TODO: FIXME: none! */, INTERPRETER_TYPE_NONE);
addAttributeType(QStringLiteral("MarkerNote"), tr("Note:"), ATTRIBUTE_UI_TYPE_CHECKBOX /* TODO: FIXME: none! */, INTERPRETER_TYPE_NONE);
// TODO:
//(QStringLiteral("Remain"), tr("Remain:")); was ist "remain" ?
// Marker offset:
addAttributeType(QStringLiteral("MarkerOffset"), tr("Offset [msecs]:"), ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET /* 0 - x */, INTERPRETER_TYPE_RANGE);
{
DetailData::AttributeParameterLookup::ParameterMap pm;
pm.insert(DetailData::UI_PARAM_TYPE_MIN, QVariant::fromValue<unsigned int>(0));
pm.insert(DetailData::UI_PARAM_TYPE_MAX, QVariant::fromValue<unsigned int>(9999999));
pm.insert(DetailData::UI_PARAM_TYPE_SUFFIX, QVariant::fromValue<QString>(QString()));
pm.insert(DetailData::UI_PARAM_TYPE_PREFIX, QVariant::fromValue<QString>(QString()));
attributeParameterLookup.addParameter(QStringLiteral("MarkerOffset"), pm);
}
// Duration:
addAttributeType(QStringLiteral("duration"), tr("Duration [msecs]:"), ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET /* 0 - x */, INTERPRETER_TYPE_RANGE);
{
DetailData::AttributeParameterLookup::ParameterMap pm;
pm.insert(DetailData::UI_PARAM_TYPE_MIN, QVariant::fromValue<unsigned int>(0));
pm.insert(DetailData::UI_PARAM_TYPE_MAX, QVariant::fromValue<unsigned int>(99999999));
pm.insert(DetailData::UI_PARAM_TYPE_SUFFIX, QVariant::fromValue<QString>(QString()));
pm.insert(DetailData::UI_PARAM_TYPE_PREFIX, QVariant::fromValue<QString>(QString()));
attributeParameterLookup.addParameter(QStringLiteral("duration"), pm);
}
// Damage:
addAttributeType(QStringLiteral("damagePercent"), tr("Damage [%]:"), ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET /* 0 - 100 */, INTERPRETER_TYPE_RANGE);
addRedirectedAttributeType(QStringLiteral("damagePercentOld"), tr("Damage before [%]:"), QStringLiteral("damagePercent"));
{
DetailData::AttributeParameterLookup::ParameterMap pm;
pm.insert(DetailData::UI_PARAM_TYPE_MIN, QVariant::fromValue<unsigned int>(0));
pm.insert(DetailData::UI_PARAM_TYPE_MAX, QVariant::fromValue<unsigned int>(100));
pm.insert(DetailData::UI_PARAM_TYPE_SUFFIX, QVariant::fromValue<QString>(tr(" %")));
pm.insert(DetailData::UI_PARAM_TYPE_PREFIX, QVariant::fromValue<QString>(QString()));
attributeParameterLookup.addParameter(QStringLiteral("damagePercent"), pm); // only for filters (event filter), so only filtereable attributes like MUNITION_LEFT_BETWEEN (not MUNITION_LEFT!)
}
// Equipment Numbers: (Achtung! Uebersetzt nur direkt eqid zu eqid !)
std::shared_ptr<ParticipantManagement::Interface::Dtc> dtcPpm;
m_session->getDtc<ParticipantManagement::DataInterface>(dtcPpm);
addAttributeType(QStringLiteral("EquipmentNumber"), tr("Equip. No.:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("EquipmentNumber"), new AvmEquipmentNumber(dtcPpm));
addRedirectedAttributeType(QStringLiteral("OriginatorEquipmentNumber"), tr("Equip. No. of Originator:"), QStringLiteral("EquipmentNumber"));
std::shared_ptr<ProjectDataManagement::Interface::Dtc> dtcPdm;
m_session->getDtc<ProjectDataManagement::DataInterface>(dtcPdm);
// Charge Types:
addAttributeType(QStringLiteral("ChargeTypeId"), tr("Charge Type:")); // Achtung: OriginatorChargeTypeId wird auf den Generellen Typ "ChargeTypeId" weitergeleitet, daher muss nur der Interpreter Type des Haupt-Typen gesetzt werden!
addRedirectedAttributeType(QStringLiteral("OriginatorChargeTypeId"), tr("Originator Charge Type:"), QStringLiteral("ChargeTypeId"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("ChargeTypeId"), new AvmChargeType(dtcPdm));
// Gear Types:
addAttributeType(QStringLiteral("GearTypeId"), tr("Gear Type:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("GearTypeId"), new AvmGearType(dtcPdm));
// Originator Types:
addAttributeType(QStringLiteral("OriginatorType"), tr("Originator Type:"));
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::Unknown);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::Participant);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::AreaEffectSimulation);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::ObserverController);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::ExerciseControl);
//addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::WoundTreatmentDevice);
//addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::MaintainanceDevice);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::FunctionalArea);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::Initialization);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::ImprovisedExplosiveDevice);
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::DusMine);
const bool showPvSan = LtsUtils::ProjectSettings::isFunctionEnabled(QStringLiteral("ParticipantInformation"),
QStringLiteral("HasPvSan"));
if (showPvSan)
addAttributeValue(QStringLiteral("OriginatorType"), ProjectDataManagement::StateChangeOriginator::Sanitation);
// (ISM) Structure Elements:
std::shared_ptr<InfrastructureManagement::Interface::Dtc> dtcIsm;
m_session->getDtc<InfrastructureManagement::DataInterface>(dtcIsm);
addAttributeType(QStringLiteral("MainStructureElementId"), tr("Building:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("MainStructureElementId"), new AvmBuilding(dtcIsm));
// (PPM) Parties
addAttributeType(QStringLiteral("PartyId"), tr("Party:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("PartyId"), new AvmParty(dtcPpm));
// (OPM) Operator Management:
std::shared_ptr<OperatorManagement::Interface::Dtc> dtcOpm;
m_session->getDtc<OperatorManagement::DataInterface>(dtcOpm);
addAttributeType(QStringLiteral("Operator"), tr("Operator:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("Operator"), new AvmOperator(dtcOpm));
// (AVM) Streams
std::shared_ptr<AudioVideoManagement::Interface::Dtc> dtcAvm;
m_session->getDtc<AudioVideoManagement::DataInterface>(dtcAvm);
addAttributeType(QStringLiteral("streamName"), tr("Stream Name:"));
m_attributeValueLookupDtc.addHandlerForDtc(QStringLiteral("streamName"), new AvmStreamName(dtcAvm));
}
DetailData::GetDetailDataResultList DetailData::getDetailData(const LtsTimelineClassic::Event *event)
{
GetDetailDataResultList result;
if (event->detailData().isEmpty()) // no detail data so search in
return result;
const QJsonObject &root = event->detailData();
for (auto i = root.constBegin(); i != root.constEnd(); ++i)
{
DEBUG_LOG(m_logger) << "Adding Detail Data for: " << i.key();
result.append(getDetailDataForAttribute(i.key(), *i));
}
return result;
}
DetailData::GetDetailDataForAttributeResult DetailData::getDetailDataForAttribute(const LtsTimelineClassic::Event *event, const QString &attributeType)
{
GetDetailDataForAttributeResult result;
const QJsonObject &root = event->detailData();
root[QStringLiteral("TimeRange")] = event->startTime() / 1000 /* as value would be too big for int => see getDetailDataForAttribute(..) */; // event time as temporary detail data
root[QStringLiteral("PartyId")] = event->partyId();
const QJsonValue attribute = root[attributeType];
if (attribute.isNull() || attribute.isUndefined())
{
WARNING_LOG(m_logger) << "DetailData::getDetailDataForAttribute: no attribute type " << attributeType
<< " in " << QJsonDocument(root).toJson();
result.requestResult = DetailData::RequestResult::InvalidAttributeType;
return result;
}
return getDetailDataForAttribute(attributeType, attribute);
}
DetailData::GetDetailDataForAttributeResult DetailData::getDetailDataForAttribute(const QString &attributeType, const QJsonValue &attributeValueJson)
{
GetDetailDataForAttributeResult result;
result.attributeTypeId = attributeType;
result.attributeValueId = -1;
result.requestResult = DetailData::RequestResult::Error;
QString attributeTypeTranslated = attributeTypeLookup.getAttributeText(attributeType);
if (attributeTypeTranslated.isEmpty())
{
result.requestResult = DetailData::RequestResult::InvalidAttributeType;
//ERROR_LOG(m_logger) << "getDetailDataForAttribute: Invalid Attribute Type for " << attributeType;
return result;
}
result.attributeType = attributeTypeTranslated;
int attributeValueId = -1;
/*
enum Type
{
Null = 0x0,
Bool = 0x1,
Double = 0x2,
String = 0x3,
Array = 0x4,
Object = 0x5,
Undefined = 0x80
};
*/
switch (attributeValueJson.type())
{
// sorted by frequency of occurrence
case QJsonValue::Double:
{
attributeValueId = attributeValueJson.toInt(-1);
break;
}
case QJsonValue::Null:
case QJsonValue::Undefined:
{
WARNING_LOG(m_logger) << "Timeline: DetailData::getDetailDataForAttribute: attribute value is null or undefined! " << attributeValueJson.toString();
result.requestResult = DetailData::RequestResult::InvalidAttributeTypeJson;
return result;
}
case QJsonValue::Bool:
{
attributeValueId = attributeValueJson.toBool() ? 1 : 0;
break;
}
case QJsonValue::String:
{
bool k = false;
QString attributeValueString = attributeValueJson.toString();
if (attributeValueString.isEmpty())
{
attributeValueId = -1;
break;
}
attributeValueId = attributeValueString.toInt(&k);
if (!k) // not a number but treat as text... NOTE: has no id
{
result.attributeValue = attributeValueString;
result.requestResult = DetailData::RequestResult::Okay;
return result;
}
break;
}
default:
{
WARNING_LOG(m_logger) << "Timeline: DetailData::getDetailDataForAttribute: attribute value cannot be handled. Type " << attributeValueJson.type() << attributeValueJson.toString();
result.requestResult = DetailData::RequestResult::InvalidAttributeType;
return result;
}
}
if (attributeValueId == -1)
{
result.requestResult = DetailData::RequestResult::InvalidAttributeType;
return result;
}
result.attributeValueId = attributeValueId;
// there are values that do not have to be translated:
int attributeUiType = attributeUITypeLookup.getUIType(attributeType);
switch (attributeUiType)
{
// do not translate
case ATTRIBUTE_UI_TYPE_TIME_RANGE_WIDGET:
case ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET:
case ATTRIBUTE_UI_TYPE_RAD_RANGE_WIDGET:
case ATTRIBUTE_UI_TYPE_AREA_RANGE_WIDGET:
{
result.attributeValue = QString::number(attributeValueId); // use "id" as "translated" string
result.requestResult = DetailData::RequestResult::Okay;
break;
}
// translate
case ATTRIBUTE_UI_TYPE_COMBO_BOX:
case ATTRIBUTE_UI_TYPE_CHECKBOX:
{
result.attributeValue = attributeValueLookup.getValueStringFromValueId(attributeType, attributeValueId);
if (result.attributeValue.isEmpty())
result.attributeValue = m_attributeValueLookupDtc.getValueStringFromValueId(attributeType, attributeValueId);
result.requestResult = DetailData::RequestResult::Okay;
break;
}
default: // -1
{
// maybe the type was redirected?
QString concreteAttributeType = attributeRedirectionLookup.getRedirection(attributeType);
if (concreteAttributeType == attributeType) // it was not redirected - give up!
{
ERROR_LOG(m_logger) << "getDetailDataForAttribute: attribute type " << attributeType << " does not have an UiType !!!";
result.requestResult = DetailData::RequestResult::NoUiTypeForAttribute;
}
else // there is a redirection, try this method with the redirection
{
//qWarning() << "trying redirection for attribute type" << attributeType;
// save some results, as getDetailDataForAttribute will set ALL the results for the redirection, instead of ONLY the value..
QString attributeType = result.attributeType;
QString attributeTypeId = result.attributeTypeId;
result = getDetailDataForAttribute(concreteAttributeType, attributeValueJson);
result.attributeType = attributeType;
result.attributeTypeId = attributeTypeId;
}
break;
}
}
return result;
}
void DetailData::createRetrieveCurrentTimeCallback(LtsWidgets::LtsDateTimeEdit *dateTimeEdit)
{
dateTimeEdit->setRetrieveCurrentTimeCallback([this]() { return m_session->partitionTime(); });
}
// LOOKUP BASE
DetailData::LookupBase::LookupBase(const QString &lookupClassName)
: m_lookupClassName(lookupClassName)
, m_logger(getLogger(QStringLiteral("DetailData.") + lookupClassName))
{
}
// EVENT TYPE LOOKUP
DetailData::EventTypeLookup::EventTypeLookup()
: LookupBase(QStringLiteral("EventTypeLookup"))
{
}
void DetailData::EventTypeLookup::addEventType(const int key, const QString &value)
{
m_eventTypeMap.insert(key, value);
}
int DetailData::EventTypeLookup::getEventType(const QString &eventText) const
{
// as eventText is simplified, method 'key' would not work
const QString paramText = removeSpaces(eventText);
for (auto i = m_eventTypeMap.constBegin(); i != m_eventTypeMap.constEnd(); ++i)
{
const QString mapText = removeSpaces(i.value());
if (mapText == paramText)
return i.key();
}
return -1; // -1 = invalid !
}
QString DetailData::EventTypeLookup::getEventText(const int eventType) const
{
return m_eventTypeMap.value(eventType);
}
bool DetailData::EventTypeLookup::isEventExists(const int eventType) const
{
return m_eventTypeMap.contains(eventType);
}
QStringList DetailData::EventTypeLookup::getEventTexts() const
{
return m_eventTypeMap.values();
}
// ATTRIBUTE EVENT LOOKUP
DetailData::AttributeEventLookup::AttributeEventLookup()
: LookupBase(QStringLiteral("AttributeEventLookup"))
{
}
void DetailData::AttributeEventLookup::addAttributesToEvent(const int eventType, const AttributeList &attributeTypeList)
{
AttributeList &al = m_attributeEventLookupMap[eventType];
al += attributeTypeList;
}
void DetailData::AttributeEventLookup::addAttributesToEvent(const ProjectDataManagement::EventType &eventType, const AttributeList &attributeTypeList)
{
addAttributesToEvent(eventType.underlying(), attributeTypeList);
}
bool DetailData::AttributeEventLookup::isAttributeInEvent(const int eventType, const QString &attributeType)
{
auto eventTypeIt = m_attributeEventLookupMap.constFind(eventType);
if (eventTypeIt == m_attributeEventLookupMap.constEnd())
return false; // event does not even exist
const AttributeList &attributeList = *eventTypeIt;
if (attributeList.contains(attributeType))
return true;
return false;
}
DetailData::AttributeEventLookup::AttributeList DetailData::AttributeEventLookup::getEventsAttributeList(const int eventType) const // get all attribute types from the event
{
return m_attributeEventLookupMap.value(eventType);
}
// ATTRIBUTE TEXT LOOKUP
DetailData::AttributeTypeLookup::AttributeTypeLookup()
: LookupBase(QStringLiteral("AttributeTypeLookup"))
{
}
void DetailData::AttributeTypeLookup::addAttributeText(const QString &attributeType, const QString &attributeText)
{
m_attributeTypeLookupMap.insert(attributeType, attributeText);
}
QString DetailData::AttributeTypeLookup::getAttributeType(const QString &attributeText) const
{
const QString paramText = removeSpaces(attributeText);
// as eventText is simplified, method 'key' would not work
for (auto i = m_attributeTypeLookupMap.constBegin(); i != m_attributeTypeLookupMap.constEnd(); ++i)
{
const QString mapText = removeSpaces(i.value());
if (mapText == paramText)
return i.key();
}
WARNING_LOG(m_logger) << "getAttributeType: attribute type not found by text '" << attributeText << "'";
return QString(); // invalid
}
DetailData::AttributeTypeLookup::AttributeTypeList DetailData::AttributeTypeLookup::getAttributeTypes() const
{
return m_attributeTypeLookupMap.keys();
}
QString DetailData::AttributeTypeLookup::getAttributeText(const QString &attributeType, const QString &defaultReturnValue) const
{
return m_attributeTypeLookupMap.value(attributeType, defaultReturnValue);
}
DetailData::AttributeTypeLookup::AttributeTextList DetailData::AttributeTypeLookup::getAttributeTexts() const
{
return m_attributeTypeLookupMap.values();
}
// ATTRIBUTE PARAMETER LOOKUP
DetailData::AttributeParameterLookup::AttributeParameterLookup()
: LookupBase(QStringLiteral("AttributeParameterLookup"))
{
}
void DetailData::AttributeParameterLookup::addParameter(const QString &attributeType, const ParameterMap &parameterMap)
{
if (!parameterMap.count())
{
WARNING_LOG(m_logger) << "addParameter: parameter map is empty!";
return;
}
m_attributeParameterMap.insert(attributeType, parameterMap);
}
DetailData::AttributeParameterLookup::ParameterMap DetailData::AttributeParameterLookup::getParameterMap(const QString &attributeType) const
{
return m_attributeParameterMap.value(attributeType);
}
QVariant DetailData::AttributeParameterLookup::getParameterValue(const QString &attributeType, const int parameterType) const
{
const ParameterMap &pm = getParameterMap(attributeType);
auto pmci = pm.constFind(parameterType);
if (pmci == pm.constEnd()) // exists? FIXME: in NGLDAO Timeline: "!=" !!
return QVariant();
return *pmci;
}
// ATTRIBUTE UI TYPE
DetailData::AttributeUITypeLookup::AttributeUITypeLookup()
: LookupBase(QStringLiteral("AttributeUITypeLookup"))
{
}
void DetailData::AttributeUITypeLookup::addUITypeToAttribute(const QString &attributeType, const int uiType)
{
m_attributeUITypeLookupMap.insert(attributeType, uiType);
}
int DetailData::AttributeUITypeLookup::getUIType(const QString &attributeType) const
{
return m_attributeUITypeLookupMap.value(attributeType, -1); // -1 = invalid
}
// INTERPRETER LOOKUP
DetailData::AttributeInterpreterLookup::AttributeInterpreterLookup()
: LookupBase(QStringLiteral("AttributeInterpreterLookup"))
{
}
void DetailData::AttributeInterpreterLookup::addInterpreterTypeToAttribute(const QString &attributeType, const int interpreterType)
{
m_attributeInterpreterLookupMap.insert(attributeType, interpreterType);
}
int DetailData::AttributeInterpreterLookup::getInterpreterType(const QString &attributeType) const
{
return m_attributeInterpreterLookupMap.value(attributeType);
}
// ATTRIBUTE VALUE LOOKUP
DetailData::AttributeValueLookup::AttributeValueLookup()
: LookupBase(QStringLiteral("AttributeValueLookup"))
{
}
void DetailData::AttributeValueLookup::removeValueFromAttributeValueMap(const QString &attributeType, const int valueKey)
{
auto attributeTypeValueMapIt = m_attributeTypeValueMap.find(attributeType);
if (attributeTypeValueMapIt == m_attributeTypeValueMap.end()) // there are currently no values for this attribute type, insert all new values
return; // attribute type not even found!
AttributeValueMap &currentAttributeValueMap = *attributeTypeValueMapIt;
currentAttributeValueMap.remove(valueKey);
}
void DetailData::AttributeValueLookup::addValueToAttributeValueMap(const QString &attributeType, const int valueKey, const QString &valueValue)
{
AttributeValueMap &avm = m_attributeTypeValueMap[attributeType];
avm.insert(valueKey, valueValue);
}
void DetailData::AttributeValueLookup::addValuesToAttributeValueMap(const QString &attributeType, const AttributeValueMap &avm) // add a VALUE (consisting of id & string) to the map coresponding to the attribute type
{
AttributeValueMap &thisAvm = m_attributeTypeValueMap[attributeType];
for (auto i = avm.constBegin(); i != avm.constEnd(); ++i)
{
thisAvm.insert(i.key(), i.value());
}
}
AttributeValueMap DetailData::AttributeValueLookup::getAttributeValueMap(const QString &attributeType) const
{
return m_attributeTypeValueMap.value(attributeType);
}
QString DetailData::AttributeValueLookup::getValueStringFromValueId(const QString &attributeType, const int attributeValueId, const QString& defaultValue) const
{
return getAttributeValueMap(attributeType).value(attributeValueId, defaultValue);
}
void DetailData::AttributeValueLookup::clear()
{
m_attributeTypeValueMap.clear();
}
// ATTRIBUTE REDIRECTION LOOKUP
DetailData::AttributeRedirectionLookup::AttributeRedirectionLookup()
: LookupBase(QStringLiteral("AttributeRedirectionLookup"))
{
}
void DetailData::AttributeRedirectionLookup::addRedirection(const QString &attributeType, const QString &redirectTo)
{
m_attributeRedirectionMap.insert(attributeType, redirectTo);
}
QString DetailData::AttributeRedirectionLookup::getRedirection(const QString &attributeType) const
{
auto it = m_attributeRedirectionMap.find(attributeType);
if (it == m_attributeRedirectionMap.end())
return attributeType;
return it.value();
}
} // namespace Timeline
+343
View File
@@ -0,0 +1,343 @@
/**
* @RDE_HEADER@
* Model to handle datail data for the different event types
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-09-04
*/
#pragma once
#include "logger.h"
#include <LtsUtilsCore/data_modification_enum.h>
#include <LtsSession/lts_session.h>
#include <Services/ProjectDataManagement/pdm_enums.hpp> // needed for this header
#include <QHash>
#include <QMap>
#include <QObject>
#include "attribute_value_lookup.h"
namespace LtsSession
{
class LtsSession;
}
namespace LtsTimelineClassic
{
class Event;
}
namespace LtsWidgets
{
class LtsDateTimeEdit;
}
namespace Timeline
{
constexpr int markerTypeId = 999999; // HACK: there is no marker type
class DetailData : public QObject
{
Q_OBJECT
public:
enum class RequestResult
{
Error = 0,
Okay = 1,
InvalidAttributeType,
NoValueTranslationForAttribute,
NoUiTypeForAttribute,
InvalidAttributeTypeJson,
JsonCorrupted
};
struct GetDetailDataForAttributeResult
{
QString attributeType; // translated! "Friendly Fire" instead of the key "FriendlyFire"
QString attributeValue; // also translated! "No" instead of the key "0"
QString attributeTypeId; // "FriendlyFire" instead of the translated version "Friendly Fire"
int attributeValueId = 0;
RequestResult requestResult = RequestResult::Error;
};
using GetDetailDataResultList = QVector<GetDetailDataForAttributeResult>;
enum AttributeUITypes // attribute ui types
{
ATTRIBUTE_UI_TYPE_TIME_RANGE_WIDGET = 0,
ATTRIBUTE_UI_TYPE_DEFAULT_RANGE_WIDGET,
ATTRIBUTE_UI_TYPE_AREA_RANGE_WIDGET,
ATTRIBUTE_UI_TYPE_COMBO_BOX, // most attributes use this
ATTRIBUTE_UI_TYPE_CHECKBOX,
ATTRIBUTE_UI_TYPE_RAD_RANGE_WIDGET
};
enum UIParameterType
{
UI_PARAM_TYPE_N1,
UI_PARAM_TYPE_N2,
UI_PARAM_TYPE_O1,
UI_PARAM_TYPE_O2,
UI_PARAM_TYPE_RAD,
UI_PARAM_TYPE_MIN,
UI_PARAM_TYPE_MAX,
UI_PARAM_TYPE_PREFIX,
UI_PARAM_TYPE_SUFFIX
};
enum InterpreterTypes
{
INTERPRETER_TYPE_NONE = 0, // ERROR VALUE
//INTERPRETER_TYPE_TOINT,
INTERPRETER_TYPE_RANGE,
INTERPRETER_TYPE_AREA, // for filter interpreter
INTERPRETER_TYPE_ID_COMPARE, // or key compare
INTERPRETER_TYPE_STR_COMPARE, // or value compare [deprecated - do not use]
INTERPRETER_TYPE_POSITION, // for event info windows line edits !
INTERPRETER_TYPE_TIME, // for event info windows line edits !
INTERPRETER_TYPE_DURATION, // for event info windows line edits !
INTERPRETER_TYPE_RAD
};
explicit DetailData(LtsSession::LtsSession *session, QObject *parent = nullptr);
void populate();
GetDetailDataResultList getDetailData(const LtsTimelineClassic::Event *event);
GetDetailDataForAttributeResult getDetailDataForAttribute(const LtsTimelineClassic::Event *event, const QString &attributeType);
void createRetrieveCurrentTimeCallback(LtsWidgets::LtsDateTimeEdit *dateTimeEdit);
LtsSession::LtsSession *session() { return m_session; }
private:
/**
* @brief addRedirectedAttributeType just like "addAttributeType", but automatically redirects to "redirectionAttributeTypeId" (note that redirected attributes do not need to have ui types or interpreter types)
* @param attributeTypeId
* @param attributeText
* @param redirectionAttributeTypeId
*/
void addRedirectedAttributeType(const QString &newAttributeTypeId, const QString &attributeText, const QString &redirectToAttributeTypeId);
void addAttributeType(const QString &attributeTypeId, const QString &attributeText, const int uiTypeId = ATTRIBUTE_UI_TYPE_COMBO_BOX, const int interpreterTypeId = INTERPRETER_TYPE_ID_COMPARE);
void addAttributeTypeWithYesAndNo(const QString &attributeTypeId, const QString &attributeText)
{
addAttributeType(attributeTypeId, attributeText, ATTRIBUTE_UI_TYPE_CHECKBOX);
attributeValueLookup.addValueToAttributeValueMap(attributeTypeId, 0, tr("No"));
attributeValueLookup.addValueToAttributeValueMap(attributeTypeId, 1, tr("Yes"));
}
template <typename AttributeEnumType>
void addAttributeValue(const QString &attributeTypeId, const AttributeEnumType &attributeEnum)
{
//qWarning() << "<<<" << attributeTypeId << ":" << (int)attributeEnum << "=>" << toString(attributeEnum);
attributeValueLookup.addValueToAttributeValueMap(attributeTypeId, (int)attributeEnum, toString(attributeEnum));
}
void addEventType(int eventTypeId, const QString &eventTypeString);
void addEventType(const ProjectDataManagement::EventType &eventType);
GetDetailDataForAttributeResult getDetailDataForAttribute(const QString &attributeType, const QJsonValue &attributeValueJson);
LtsSession::LtsSession *m_session;
bool __already_populated__; // do not allow secondary population, this would take to much time to develop atm
NGL::INGLLogger &m_logger;
public:
class LookupBase
{
public:
explicit LookupBase(const QString &lookupClassName);
protected:
QString m_lookupClassName;
NGL::INGLLogger &m_logger;
};
class EventTypeLookup : public LookupBase
{
public:
using EventTypeMap = QMap<int /* eventTypeId */, QString /* Event Name */>; // example: 3, 'Hit'
EventTypeLookup();
void addEventType(const int key, const QString &value);
int getEventType(const QString &eventText) const;
QString getEventText(const int eventType) const;
QStringList getEventTexts() const; // all texts
bool isEventExists(const int eventType) const;
private:
EventTypeMap m_eventTypeMap;
};
EventTypeLookup eventTypeLookup;
class AttributeTypeLookup : public LookupBase
{
public:
using AttributeTypeLookupMap = QMap<QString /* attributeTypeId */, QString /* name */>;
using AttributeTypeList = QList<QString>;
using AttributeTextList = QStringList;
AttributeTypeLookup();
void addAttributeText(const QString &attributeType, const QString &attributeText);
QString getAttributeType(const QString &attributeText) const;
AttributeTypeList getAttributeTypes() const;
QString getAttributeText(const QString &attributeType, const QString &defaultReturnValue = QString()) const;
AttributeTextList getAttributeTexts() const;
private:
AttributeTypeLookupMap m_attributeTypeLookupMap;
};
AttributeTypeLookup attributeTypeLookup;
class AttributeEventLookup : public LookupBase
{
public:
using AttributeEventLookupMap = QMap<int /* eventTypeId */, QList<QString> /* attributeTypeId List */>;
using AttributeList = QList<QString>;
AttributeEventLookup();
void addAttributesToEvent(const int eventType, const AttributeList &attributeTypeList);
void addAttributesToEvent(const ProjectDataManagement::EventType &eventType, const AttributeList &attributeTypeList);
bool isAttributeInEvent(const int eventType, const QString &attributeType);
AttributeList getEventsAttributeList(const int eventType) const; // get all attribute types from the event
private:
AttributeEventLookupMap m_attributeEventLookupMap;
};
AttributeEventLookup attributeEventLookup;
class AttributeParameterLookup : public LookupBase // as ui type is fixed for attribute type, it is perfectly okay to request parameters (min, max, etc.) by attribute type
{
public:
using ParameterMap = QMap<int, QVariant>; // Parameter Type => Parameter String
using AttributeParameterMap = QHash<QString, ParameterMap>;
AttributeParameterLookup();
void addParameter(const QString &attributeType, const ParameterMap &parameterMap); // add a parameter map to the lookup
ParameterMap getParameterMap(const QString &attributeType) const;
QVariant getParameterValue(const QString &attributeType, const int parameterType) const;
private:
AttributeParameterMap m_attributeParameterMap;
};
AttributeParameterLookup attributeParameterLookup;
class AttributeUITypeLookup : public LookupBase
{
public:
using AttributeUITypeLookupMap = QHash<QString, int>;
AttributeUITypeLookup();
void addUITypeToAttribute(const QString &attributeType, const int uiType);
int getUIType(const QString &attributeType) const;
private:
AttributeUITypeLookupMap m_attributeUITypeLookupMap;
};
AttributeUITypeLookup attributeUITypeLookup;
class AttributeInterpreterLookup : public LookupBase
{
public:
using AttributeInterpreterLookupMap = QHash<QString, int>;
AttributeInterpreterLookup();
void addInterpreterTypeToAttribute(const QString &attributeType, const int interpreterType);
int getInterpreterType(const QString &attributeType) const;
private:
AttributeInterpreterLookupMap m_attributeInterpreterLookupMap;
};
AttributeInterpreterLookup attributeInterpreterLookup;
class AttributeValueLookup : public LookupBase
{
public:
AttributeValueLookup();
void addValueToAttributeValueMap(const QString &attributeType, const int valueKey, const QString &valueValue /* ^^ */);
void removeValueFromAttributeValueMap(const QString &attributeType, const int valueKey);
void addValuesToAttributeValueMap(const QString &attributeType, const AttributeValueMap &avm); // add a VALUE (consisting of id & string) to the map coresponding to the attribute type
void clear(); // clears map for updating map and sets to uninitialized
AttributeValueMap getAttributeValueMap(const QString &attributeType) const;
QString getValueStringFromValueId(const QString &attributeType, const int attributeValueId, const QString &defaultValue = QString()) const;
private:
AtributeValueLookupMap m_attributeTypeValueMap; // contains a map for each attribute type, that contain a map for their keys & values
};
AttributeValueLookup attributeValueLookup;
class AttributeValueLookupDtc : public LookupBase
{
public:
AttributeValueLookupDtc() : LookupBase(QStringLiteral("AttributeValueLookupDtc")) {}
~AttributeValueLookupDtc() { qDeleteAll(m_provider); }
// takes ownership on provider
void addHandlerForDtc(const QString &attributeType, AttributeValueLookupProvider *provider)
{
auto &p = m_provider[attributeType];
delete p;
p = provider;
}
AttributeValueMap getAttributeValueMap(int eventType, const QString &attributeType) const
{
const auto provider = m_provider.value(attributeType);
if (!provider)
return {};
return provider->getAttributeValueMap(eventType);
}
QString getValueStringFromValueId(const QString &attributeType, int attributeValueId) const
{
const auto provider = m_provider.value(attributeType);
if (!provider)
return {};
return provider->getValueStringFromValueId(attributeValueId);
}
private:
QHash<QString, AttributeValueLookupProvider*> m_provider;
};
AttributeValueLookupDtc m_attributeValueLookupDtc;
AttributeValueMap operatorsForEventType(int eventType) const;
QString operatorNameFromId(int id) const;
class AttributeRedirectionLookup : public LookupBase
{
public:
using AttributeRedirectionMap = QHash<QString, QString>;
explicit AttributeRedirectionLookup();
void addRedirection(const QString &attributeType, const QString &redirectTo);
QString getRedirection(const QString &attributeType) const; // returns redirection, or if no redirection for attribute type, then returns original attribute type
private:
AttributeRedirectionMap m_attributeRedirectionMap;
};
AttributeRedirectionLookup attributeRedirectionLookup;
};
} // namespace Timeline
@@ -0,0 +1,218 @@
#include "excon_filter.h"
#include "detail_data.h"
#include "EventFilter/interpreter/FilterWorker.h"
#include "EventFilter/interpreter/FilterEquationFunctions.h"
#include <LtsTimelineClassic/EventList.h>
#include <LtsTimelineClassic/Event.h>
#include <ProjectDataManagement/pdm_enums.hpp>
#include "logger.h"
#include <QVariant>
namespace Timeline
{
//m_rootItem->getFullEventList(true, true);
static LtsTimelineClassic::EventList applyFilter(DetailData *detailData,
const LtsTimelineClassic::EventList &eventList,
const ModelFilterList &modelFilterList,
bool simulation) // returns remaining/visible events
{
NGL::INGLLogger &logger(getLogger("ExconFilter"));
/*
*
* general event can be filtered by event's default base data (time, duration, etc.)
* .. and can be filtered by e.g. position (if the inherit event contains a position - if not, ignore error)
*
*/
//const QSet<int> eventOnlyEventList = getEventOnlyEventList(modelFilterList);
//EventList eventList = eventListToFilter; //m_rootItem->getFullEventList(true, true); // get all events from model's root item, recursive and all (even hidden, logically) events
LtsTimelineClassic::EventList visibleEvents;
bool resetFilterOnly = false;
if (!modelFilterList.count()) // no filters set = no events to filter => show all events
{
DEBUG_LOG(logger) << "applyFilter: Model Filter List is EMPTY";
if (simulation) // do not return any visible event if there is NO filter (or every event will be shown which is NOT a nice feature)
return visibleEvents;
resetFilterOnly = true;
}
visibleEvents = eventList; // copy list of pointers (!)
QSet<int> mentionedEventTypes;
mentionedEventTypes.insert(markerTypeId); // markers are always visible!
for (const FilterWorker::ModelFilter &mf : modelFilterList)
{
if (mf.active)
mentionedEventTypes.insert(mf.eventType);
}
/* the filter must consider one of these 2 cases
*
* case 1: filter all events - if there is "General Event" only
* case 2: case 1 + hide all events that are not mentioned - if there are other events than "General Event" AND the "General Event"
*/
bool showAllEvents = false;
if (mentionedEventTypes.count() == 1 && mentionedEventTypes.contains(0 /* GENERAL_EVENT */))
showAllEvents = true;
// PER ATTRIBUTE THERE MUST BE AT LEAST ONE FITTING VALUE ! (or)
for (LtsTimelineClassic::Event *event : eventList) // go through all events in model
{
if (!event)
{
WARNING_LOG(logger) << "applyFilter: Erroneous event!";
continue; // next event, we can't handle this event
}
int eventType = event->typeId();
//int eventID = event->getID();
if (resetFilterOnly /*|| eventOnlyEventList.contains(eventType)*/) // reset filter or show entire event type when event type is event-type-filter (& search eoe list for current events event type)
{
if (!simulation)
event->show();
// no inc / dec needed for vec here !
continue; // do not filter further - done!
}
bool eventVisible = true;
// check if event must be filtered (mentioned in model filter list)
if (!showAllEvents && !mentionedEventTypes.contains(eventType))
eventVisible = false;
if (!eventVisible /*&& m_showOnlyThese*/) // TODO: settings for "SHOW ALL BUT FILTER ONLY THESE" and "SHOW ONLY THESE"
{
if (!simulation)
event->hide();
visibleEvents.removeAll(event);
continue;
}
const auto eventsAttributeList = detailData->attributeEventLookup.getEventsAttributeList(eventType); // get attribute types from event
// at least one value has to fit per attribute !! (or)
for (const QString &attributeType : eventsAttributeList) // attribute of event [[ATTR]]
{
QString concreteAttributeType = detailData->attributeRedirectionLookup.getRedirection(attributeType); // why did I not "get the concrete type in any case" back then?
bool visibleByAttribute = false; // false from the beginning, because at least one value HAS TO FIT to an attribute
bool attributeTypeMentioned = false; // if not filtered by an attribute, then do not filter by this attribute
// no fitting value? then hide event
for (const FilterWorker::ModelFilter &mf : modelFilterList) // values from filter list [[VAL]]
{
if (mf.eventType == eventType || mf.eventType == 0 /* GENERAL_EVENT */) // if true, there are only ModelFilters for General Event (which filters all events) [General Event's attributes get applied on all other Events]
{
if (mf.attributeType == concreteAttributeType && mf.active) // the attribute from the event looking for?
{
attributeTypeMentioned = true; // actually filtering by this attribute !
DetailData::GetDetailDataForAttributeResult result = detailData->getDetailDataForAttribute(event, attributeType); // get attribute from event detail data (zwmsdd)
if (result.requestResult != DetailData::RequestResult::Okay) // detail data not found for attr type? must be a redirection
result = detailData->getDetailDataForAttribute(event, concreteAttributeType); // TRY REDIRECTION
/* filter says: look for detail data with attr type "area_pos" => must be redirected before doing this to "position"
* filter says: look for "attribute verursacher" => do NOT redirect as this is an existing detail data! */
/* attribute types with ui type for combobox can redirect unproblematically (same as in filter equation functions)
* => attribute types with special ui type(s) should not be redirected
* so if the attempt to lookup a attribute interpreter type fails, try another lookup with redirection
* side effects: when new attribute types are not completly integrated
* guess: ????
*/
if (result.requestResult != DetailData::RequestResult::Okay) // detail data not found for attr type? must be a redirection
{
WARNING_LOG(logger) << "applyFilter: result.requestResult != DetailData::OKAY -" << (int)result.requestResult;
continue; // // TODO: show error and continue
}
//qWarning() << "checkValue: type" << concreteAttributeType << "value id" << result.attributeValueId << "filter value" << mf.attributeValue << "(value as string:"<<result.attributeValue<<")";
visibleByAttribute = FilterEquationFunctions::checkValue(detailData, concreteAttributeType, result.attributeValueId, mf.attributeValue);
if (visibleByAttribute) // the event is visible if minimum ONE attribute fits
break;
#ifdef DEBUG // as it is time consuming
DEBUG_LOG(logger)
<< "\r\napplyFilter:\r\n"
<< "Evnt Type (" << eventType << ")\r\n"
<< "Attr Type (" << attributeType << ")\r\n"
<< "Evnt Val (" << result.resultValue << ")\r\n"
<< "Fltr Val (" << mf.attributeValue << ")\r\n"
<< "Evnt SHOW (" << eventVisible << ")\r\n"
<< "Attr SHOW (" << visibleByAttribute << ")\r\n";
#endif
}
}
}
// at the end, after looping through all values - no value fitting: hide | at least one value fitting: show
if (attributeTypeMentioned && !visibleByAttribute) // if attribute is mentioned, but NOT marked as visible, then hide event - because one WHOLE attribute does not fit !
{
eventVisible = false;
break;
}
}
if (!simulation)
event->setVisible(eventVisible);
if (!eventVisible)
visibleEvents.removeAll(event);
}
DEBUG_LOG(logger) << "applyFilter: Visible Events after applying filter:" << visibleEvents.count();
return visibleEvents;
}
LtsTimelineClassic::EventList setFilter(DetailData *detailData,
const LtsTimelineClassic::EventList &eventListToFilter,
const QJsonArray &filter,
QWidget *parentWidget,
bool simulation,
bool handleResult)
{
/*/
*
* Filter Text:
*
* Syntax : [Filter];[Filter]; ...
* Filter : [Event Type]:[Attribute]+[Attribute]+ ...
* Attribute : [Attr Type]{[Attr Value],[Attr Value], ...}
*
/*/
LtsTimelineClassic::EventList visibleEvents;
FilterWorker::GetModelFilterListResult result = FilterWorker::getModelFilterList(detailData, filter, true /* type safe !! */); // get model filter list by the filter text
ModelFilterList modelFilterList = result.result;
if (handleResult)
FilterWorker::getInstance()->handleResultValues(result.resultValueList, parentWidget);
visibleEvents = applyFilter(detailData, eventListToFilter, modelFilterList, simulation); // apply the model filter list to the timeline
return visibleEvents;
}
} // namespace Timeline
@@ -0,0 +1,22 @@
#pragma once
#include <LtsTimelineClassic/EventList.h>
#include <QJsonArray>
class QWidget;
namespace Timeline
{
class DetailData;
LtsTimelineClassic::EventList setFilter(DetailData *detailData,
const LtsTimelineClassic::EventList &eventListToFilter,
const QJsonArray &filter,
QWidget *parentWidget,
bool simulation = false,
bool handleResult = true);
}
+37
View File
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace Timeline
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "Plugins.Timeline.Gui.";
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(sizeof(ns) + len);
loggerName.append(ns, sizeof(ns) - 1);
loggerName.append(name, len);
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
}
+23
View File
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for BuildingViewer
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-08-17
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace Timeline
{
using LOGGER_T = NGL::INGLLogger &;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
}
@@ -0,0 +1,111 @@
/**
* @RDE_HEADER@
* ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#include "tln_cache_dialog.h"
#include "ui_tln_cache_dialog.h"
#include <QDateTime>
#include <QDebug>
namespace Timeline
{
TimelineCacheDialog::TimelineCacheDialog(QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::TimelineCacheDialog)
{
m_ui->setupUi(this);
connect(m_ui->pbUncache, &QPushButton::clicked,
this, [this]()
{
bool k = false;
int index = m_ui->twCaches->item(m_ui->twCaches->selectionModel()->currentIndex().row(), 0)->text().toInt(&k);
if (!k)
return;
emit signalUncache(index);
close();
});
connect(m_ui->pbDelete, &QPushButton::clicked,
this, [this]()
{
bool k = false;
int index = m_ui->twCaches->item(m_ui->twCaches->selectionModel()->currentIndex().row(), 0)->text().toInt(&k);
if (!k)
return;
emit signalDelete(index);
close();
});
}
TimelineCacheDialog::~TimelineCacheDialog()
{
delete m_ui;
}
void TimelineCacheDialog::addCache(const CacheRange &cr, const QString &indexString)
{
int rowCount = m_ui->twCaches->rowCount();
m_ui->twCaches->insertRow(rowCount);
int row = m_ui->twCaches->rowCount() - 1;
int c = 0;
m_ui->twCaches->setItem(row, c++ /* pun not intended */, new QTableWidgetItem(indexString));
QString hash(QStringLiteral("<%1:%2:%3>").arg(cr.start).arg(cr.end).arg(cr.events.count()));
m_ui->twCaches->setItem(row, c++, new QTableWidgetItem(QString::number(qHash(hash), 16)));
m_ui->twCaches->setItem(row, c++, new QTableWidgetItem(QStringLiteral("%1 (%2)").arg(QDateTime::fromMSecsSinceEpoch(cr.start).toString()).arg(cr.start)));
m_ui->twCaches->setItem(row, c++, new QTableWidgetItem(QStringLiteral("%1 (%2)").arg(QDateTime::fromMSecsSinceEpoch(cr.end).toString()).arg(cr.end)));
m_ui->twCaches->setItem(row, c++, new QTableWidgetItem(QString::number(cr.events.count())));
}
bool TimelineCacheDialog::init(const QVector<QPair<qint64, CacheRange>> &cachedRanges, const CacheRange &liveCache)
{
m_ui->twCaches->setRowCount(0);
int eventCount = 0;
{
int i = 0;
for (auto it = cachedRanges.constBegin(); it != cachedRanges.constEnd(); ++it)
{
const CacheRange &cr = it->second;
eventCount += cr.events.count();
addCache(cr, QString::number(i));
i++;
}
}
addCache(liveCache, QStringLiteral("live")); // add live events from its own cache
m_ui->lblEventCount->setText(QString::number(eventCount));
for (int i = 0; i < m_ui->twCaches->columnCount(); i++)
{
m_ui->twCaches->resizeColumnToContents(i);
}
return true;
}
} // namespace Timeline
@@ -0,0 +1,48 @@
/**
* @RDE_HEADER@
* ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#pragma once
#include "tln_dialog_model.h"
#include <QDialog>
#include <QVector>
namespace Ui
{
class TimelineCacheDialog;
}
namespace Timeline
{
class TimelineCacheDialog : public QDialog
{
Q_OBJECT
public:
explicit TimelineCacheDialog(QWidget *parent = nullptr);
~TimelineCacheDialog();
bool init(const QVector<QPair<qint64, CacheRange> > &cachedRanges, const CacheRange &liveCache);
private:
void addCache(const CacheRange &cache, const QString &indexString);
Ui::TimelineCacheDialog *m_ui;
signals:
void signalUncache(int index);
void signalDelete(int index);
};
} // namespace Timeline
@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TimelineCacheDialog</class>
<widget class="QWidget" name="TimelineCacheDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>846</width>
<height>500</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Cache View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Event count:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblEventCount">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Never uncache events from a stale cache vector !</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTableWidget" name="twCaches">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderDefaultSectionSize">
<number>100</number>
</attribute>
<column>
<property name="text">
<string>idx</string>
</property>
</column>
<column>
<property name="text">
<string>hash</string>
</property>
</column>
<column>
<property name="text">
<string>start</string>
</property>
</column>
<column>
<property name="text">
<string>end</string>
</property>
</column>
<column>
<property name="text">
<string>event count</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbUncache">
<property name="text">
<string>uncache selected</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbDelete">
<property name="text">
<string>delete selected</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
File diff suppressed because it is too large. Load diff
+174
View File
@@ -0,0 +1,174 @@
/**
* @RDE_HEADER@
* ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#pragma once
#include "tln_view_gui_plugin.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsTimelineClassic/EventList.h>
#include <Plugins/Timeline/tln_data_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
#include <Plugins/SituationReplayManagement/srm_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/AreaEffectSimulation/aes_data_plugin_interface.h>
#include <Plugins/FilterManagement/flt_participant_plugin_interface.h>
#include <Plugins/FilterManagement/flt_building_cmpnt_plugin_interface.h>
// Replay
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
#include <QJsonArray>
namespace AudioVideoManagement
{
class StreamNameMapping;
}
namespace LtsTimelineClassic
{
class EventGraphic;
class Event;
}
namespace Ui
{
class TimelineViewDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace LtsPluginManager
{
class LtsPluginManager;
}
namespace Timeline
{
class FilterWindow;
class DetailData;
class DDSTimelineModel;
class EventListDialog;
class TimelineLegendDialog;
class TimelineViewDialog : public LtsWidgets::LtsDialog
, protected SituationReplayManagement::Interface::Ftc::ReplayPartitionCreateReplyHandler
{
Q_OBJECT
public:
explicit TimelineViewDialog(const QString &menuGroup, const LtsWidgets::ButtonCombination &buttonCombo, bool isMain, QWidget *parent = nullptr);
~TimelineViewDialog();
bool init(const std::shared_ptr<TimeLineManagement::ForeignDataInterface> &eemForeign,
const std::shared_ptr<MarkerManagement::Interface::Dtc> &mrkDtc,
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ParticipantManagement::ForeignParticipantInfoGuiInterface> &ppmInfoIfc,
const std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> &avmIfc,
const std::shared_ptr<MarkerManagement::ForeignGuiInterface> &mrkIfc,
const std::shared_ptr<SituationReplayManagement::ForeignDataInterface> &srmDataIfc,
const std::shared_ptr<Core::Interface::Dtc> &coreDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> &fltParticipantIfc,
const std::shared_ptr<FilterManagement::ForeignAesGuiInterface> &fltAesIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> &fltBuildingComponentIfc,
LtsSession::LtsSession *session,
AudioVideoManagement::StreamNameMapping *streamNameMapping);
private slots:
void showDetailsAction(LtsTimelineClassic::EventGraphic *eg, int action);
void showDetails(const LtsTimelineClassic::EventList &eventList);
void slotApplyFilter(const QJsonArray &filter);
void slotShowEventDetails(const LtsTimelineClassic::Event *event);
void slotShowEvents(const QJsonArray &filter);
void slotWaitForReady();
void slotShowFiltersDialog();
void slotShowLegend();
void slotStartReplaySituation();
void updateRange(bool initial = false);
void slotLoading();
void slotLoadingDone();
void slotSpanChanged();
void slotEventTypesChanged(QSet<int> activeEventTypeIds); // no const ref because markerTypeId might be added
void loadAllEvents();
void slotDebugCreateFakeEvent();
signals:
void signalWaitForReady();
void signalInitEventList();
private:
void retranslate() override;
void onReplayPartitionCreateReply(const Core::FunctionReplyObject &reply) override { m_requestHelper->requestReply(reply); }
qint64 currentSpanTime() const; // in msecs
// dtc's
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
// foreign gui's
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmIfc;
std::shared_ptr<ParticipantManagement::ForeignParticipantInfoGuiInterface> m_ppmInfoIfc;
std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> m_avmIfc;
std::shared_ptr<MarkerManagement::ForeignGuiInterface> m_mrkIfc;
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_srmDataIfc;
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> m_fltParticipantIfc;
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> m_fltAesIfc;
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> m_fltBuildingComponentIfc;
Ui::TimelineViewDialog *m_ui;
QString m_menuGroup;
DDSTimelineModel *m_model;
DetailData *m_detailData;
FilterWindow *m_filterWindow;
QPointer<EventListDialog> m_eventListDialog;
QPointer<EventListDialog> m_allEventsListDialog;
LtsSession::LtsSession *m_session;
LtsUtils::RequestHelper *m_requestHelper;
QPointer<LtsPluginManager::LtsPluginManager> m_replayApp;
QPointer<LtsSession::LtsSession> m_replayAppSession;
AudioVideoManagement::StreamNameMapping *m_streamNameMapping;
bool m_rangeChanged;
QJsonArray m_filter;
int m_loadingCount;
bool m_armed;
qint64 m_exerciseStart;
TimelineLegendDialog *m_legendDialog;
QTimer *m_armTimer;
NGL::INGLLogger &m_logger;
void setCustomSpan();
void calculateAndSetRange();
void setRange(qint64 min, qint64 max);
void updateActiveEventTypesByFilters();
void updateFiltersByActiveEventTypes();
void applyFilter(const QJsonArray &filter, bool doUpdateActiveEventTypes = true, bool reloadFilterWindow = false);
void rangeChanged();
void setTimelineUsable(bool usable = true);
void aboutToClose() override;
};
} // namespace Timeline
+215
View File
@@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TimelineViewDialog</class>
<widget class="QWidget" name="TimelineViewDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1401</width>
<height>519</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Timeline View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Time span:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbLive">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Live</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblStartTime">
<property name="text">
<string>Start:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbStartTime">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollBar" name="sbSpan">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="singleStep">
<number>3600</number>
</property>
<property name="pageStep">
<number>3600</number>
</property>
<property name="tracking">
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblEndTime">
<property name="text">
<string>End:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbEndTime">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblNoEventsSelected">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#ff5656;&quot;&gt;Please create at least one filter of any event type to see events.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="LtsTimelineClassic::TimelineWidget" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="debugWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">Cursor at </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCursorTime">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="cbFake">
<property name="text">
<string notr="true">[fake]</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbUpdate">
<property name="text">
<string notr="true">[update]</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbShowCache">
<property name="text">
<string notr="true">[show cache]</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbDebugCreateFakeEvent">
<property name="text">
<string notr="true">[create fake event]</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsTimelineClassic::TimelineWidget</class>
<extends>QWidget</extends>
<header location="global">LtsTimelineClassic/TimelineWidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,209 @@
/**
* @RDE_HEADER@
* Timeline Model for DDS
* $Id$
* $HeadURL$
* $Author$
* Created by Mike Wille, SLSE, 2017-03-31
*/
#pragma once
#include "detail_data.h"
#include <LtsTimelineClassic/AbstractTimelineModel.h>
#include <Plugins/Timeline/tln_data_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_data_plugin_interface.h>
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <QJsonArray>
#include <QAbstractSocket>
namespace LtsUtils
{
class RequestHelper;
}
namespace LtsSession
{
class LtsSession;
}
namespace AudioVideoManagement
{
class StreamNameMapping;
}
namespace Timeline
{
struct CacheRange : public LtsTimelineClassic::Range
{
QVector<LtsTimelineClassic::Event *> events;
bool incomplete = true;
bool visible = false; // events currently visible/set on timeline?
};
class DDSTimelineModel : public LtsTimelineClassic::AbstractTimelineModel
{
Q_OBJECT
public:
explicit DDSTimelineModel(QObject *parent);
~DDSTimelineModel();
bool init(const std::shared_ptr<TimeLineManagement::ForeignDataInterface> &eemForeign,
const std::shared_ptr<MarkerManagement::Interface::Dtc> &mrkDtc,
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
LtsSession::LtsSession *session,
Timeline::DetailData *detailData,
AudioVideoManagement::StreamNameMapping *streamNameMapping);
void applyFilter(const QJsonArray &filter);
void setDetailData(DetailData *detailData) { m_detailData = detailData; }
void setColorPaintFun(LtsTimelineClassic::Event::EventPaintFunc &paintFunc, QColor &color, ProjectDataManagement::EventType eventType) const;
void setTimeRange(qint64 min, qint64 max);
void getEvents(qint64 rangeMin, qint64 rangeMax);
QVector<LtsTimelineClassic::Range> getKnownRanges() const;
const QVector<QPair<qint64, CacheRange>> &cachedEvents() const { return m_cachedEvents; } // for debug only !
const CacheRange &liveCache() const { return m_liveCache; } // for debug only !
void forceUncache();
void setLive(bool isLive = true);
void setCreateFakeEvents(bool createFakeEvents = true);
void setActiveEventTypeIds(const QSet<int> &activeEventTypeIds);
const QSet<int> &activeEventTypeIds() const { return m_activeEventTypeIds; }
bool hasActiveEventTypes() const { return !m_activeEventTypeIds.isEmpty(); }
void getAllEventsOfType(const QSet<int> &eventTypeIds);
private:
qint64 currentTime() const override;
//void addExerciseEventManagementEvent(const ExerciseEventManagement::Event &data);
QStringList getPathForAvsEvents(const QString &type, const QJsonObject &obj) const;
QStringList getPathForBuildingEvents(const QString &type, const QJsonObject &obj) const;
QStringList getPathForAesEvents(const QString &type, const QJsonObject &obj) const;
void addMarkerManagementMarker(const MarkerManagement::Marker &data);
void addEventsAndFilter(const QVector<LtsTimelineClassic::Event *> &eventList);
void addEventAndFilter(LtsTimelineClassic::Event *event); // returns false if NOT added (so delete the event if false)
void deleteMarker(int uniqueId);
void modifyMarker(const MarkerManagement::Marker &data);
QJsonObject createMarkerDetailData(const MarkerManagement::Marker &data) const;
LtsTimelineClassic::Event *getMarker(int uniqueId);
void checkCache();
void removeEvent(int eventId);
void addCacheRange(const LtsTimelineClassic::Range &range, const QVector<LtsTimelineClassic::Event *> &cachedEvents, qint64 incompleteEnd, bool addToTimeline = true);
bool isLive() const { return m_isLive; }
LtsTimelineClassic::Event *createEvent(const ExerciseEventManagement::Event &data) const;
//const QVector<LtsTimelineClassic::Event *> createEvents(const QVector<ExerciseEventManagement::Event> &eventList) const;
bool checkDetailData(const LtsTimelineClassic::Event *event) const;
void processEventList(qint64 &highest, bool &incomplete, QVector<LtsTimelineClassic::Event *> &cachedEvents, QVector<LtsTimelineClassic::Event *> &eventList, const int initialCount = 0);
void receivedEvents(const LtsTimelineClassic::Range &range, QVector<LtsTimelineClassic::Event *> &receivedEventsList);
void requestEvents(const LtsTimelineClassic::Range &range);
QVector<LtsTimelineClassic::Event *> createTestEvents(qint64 time, int count, qint64 distance, qint64 duration = 0);
static bool isInsideCachedRange(const CacheRange &cacheRange, qint64 start, qint64 end);
bool isCached(int eventId) const;
void showCached(CacheRange &cacheRange);
bool isCacheFull() const;
//void forceUncache();
void uncache(CacheRange &cr);
const CacheRange *getCache(int eventId) const;
void addCacheRangeToTimeline(const CacheRange &cr);
bool splitLowest(qint64 &rangeMin, qint64 rangeMax, qint64 &requestMin, qint64 &requestMax, const CacheRange &lowestCache) const;
void rebuildTimelineKnownRanges();
void _uncache(const CacheRange &cr);
void saveAndClearLiveCache();
const QJsonObject getDetailData(const ExerciseEventManagement::Event &event) const;
const QJsonObject getDetailData(const QByteArray &data, int id, const ProjectDataManagement::EventType& type, qint64 startTime) const;
std::shared_ptr<TimeLineManagement::ForeignDataInterface> m_eemForeign;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_dtcExm;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
QJsonArray m_filter;
LtsSession::LtsSession *m_session;
int64_t m_startTime;
int64_t m_endTime;
DetailData *m_detailData;
AudioVideoManagement::StreamNameMapping *m_streamNameMapping;
bool m_isLive;
int m_cacheSize; // per range in m_cachedEvents
int m_maxCacheRanges; // max entries in m_cachedEvents
int m_eventCount;
qint64 m_lastCachedEventsId;
QVector<QPair<qint64, CacheRange>> m_cachedEvents;
CacheRange m_liveCache;
bool m_createFakeEvents;
QSet<int> m_activeEventTypeIds;
//bool m_dtorEmergencyMode;
QVector<int> m_knownMarkerIds;
NGL::INGLLogger &m_logger;
public slots:
void slotAddLiveEvent(const ExerciseEventManagement::Event &event);
private slots:
//void slotEventChanged(const ExerciseEventManagement::Event &data, DataModification modType);
void slotMarkerChanged(const MarkerManagement::Marker &data, DataModification modType);
void slotAddLiveEventsFake();
void slotRequestMessageReceived(const QString &message);
signals:
void signalTooMuchEvents();
void signalRebuildTimelineKnownRanges(); // sadly, the model cannot do this..
void signalLoading();
void signalLoadingDone();
};
class EventRequest : public QObject
{
Q_OBJECT
public:
EventRequest(const QUrl &url, qint64 start, qint64 end, const QSet<int> &eventTypes, int limit);
~EventRequest() = default;
void append(LtsTimelineClassic::Event *newEvent) { m_eventList.push_back(newEvent); }
const QVector<LtsTimelineClassic::Event *> &eventList() const { return m_eventList; }
qint64 start() const { return m_start; }
qint64 end() const { return m_end; }
private:
void requestFailed(const QString &errorString);
qint64 m_start;
qint64 m_end;
QVector<LtsTimelineClassic::Event *> m_eventList;
QTimer *m_timeoutTimer;
private slots:
void slotRequestFailed();
void slotMessageReceived(const QString &message);
signals:
void signalRequestFailed(const QString &errorString);
void signalMessageReceived(const QString &message);
void signalDone();
void signalTimeout();
};
} // namespace Timeline
@@ -0,0 +1,274 @@
#include "tln_event_list.h"
#include "tln_event_list_columns.h"
#include "ui_tln_event_list.h"
#include "ui_tln_event_list_columns.h"
#include "tln_event_list_model.h"
#include "tln_show_details.h"
#include <Viewer/LtsDataView/lts_data_view.h>
#include <LtsTimelineClassic/EventGraphic.h>
#include <LtsPrinting/printing.h>
#include <QHeaderView>
#include <QTreeView>
const int MAX_EVENTS = 30; //TODO: Project Settings ?
namespace Timeline
{
EventListDialog::EventListDialog(const QString &menuGroup,
LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
bool showMarkers,
QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ui(new Ui::EventListDialog)
, m_columns(nullptr)
, m_model(new EventListDialogModel(ppmDtc, pdmDtc, ismDtc, this))
, m_detailData(nullptr)
, m_menuGroup(menuGroup)
, m_listEvents(false)
, m_flmIfc(flmIfc)
, m_session(session)
, m_logger(getLogger("EventList"))
{
m_ui->setupUi(this);
if (showMarkers)
{
m_ui->ltsDataView->setModel(m_model);
}
else // don't show markers - the proxy model filters all markers out .... and the sort methods don't seem to be different from default
{
m_proxyModel = new EventListDialogProxyModel(this);
m_proxyModel->setSourceModel(m_model);
m_ui->ltsDataView->setModel(m_proxyModel);
}
connect(m_ui->ltsDataView, &LtsDataView::LtsDataView::signalDoubleClicked,
this, &EventListDialog::slotShowSelected);
connect(m_ui->ltsDataView, &LtsDataView::LtsDataView::signalSelectionChanged,
this, &EventListDialog::slotEnaDisa);
connect(this, &EventListDialog::signalAddNewEvents,
m_model, &EventListDialogModel::appendEventList);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Show, this, &EventListDialog::slotShowSelected);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &EventListDialog::onPrint);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Load, this, &EventListDialog::onConfigureColumns);
#ifndef LTS_DEBUG
addConcealedColumns({ EventListDialogModel::Id, EventListDialogModel::AesId, EventListDialogModel::ParticipantId,
EventListDialogModel::DamagePercentOld });
#endif
setUnchangeable();
}
EventListDialog::~EventListDialog()
{
DEBUG_LOG(m_logger) << "~EventListDialog";
if (m_proxyModel != nullptr)
m_proxyModel->setInvalid(); // just in case
m_model->setInvalid(); // just in case
delete m_ui;
delete m_columns;
}
void EventListDialog::retranslate()
{
m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Configuration"));
if (m_listEvents)
translate(m_ui, QString(), false, tr("All Events"));
else
translate(m_ui, QString(), false, tr("%1 Events").arg(m_model->rowCount()));
}
bool EventListDialog::init(DetailData *detailData, const LtsTimelineClassic::EventList &eventList, bool listEvents)
{
m_detailData = detailData;
m_listEvents = listEvents;
if (m_listEvents)
m_ui->label->setVisible(false);
if (eventList.isEmpty())
return false;
m_model->setData(eventList);
m_headerState = m_ui->ltsDataView->saveState();
#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
m_ui->ltsDataView->treeView()->header()->setFirstSectionMovable(true);
m_ui->ltsDataView->searchView()->header()->setFirstSectionMovable(true);
#endif
initColumns();
return true;
}
void EventListDialog::addConcealedColumns(const QVector<int> &concealedColumns)
{
for (const int id : concealedColumns)
{
m_ui->ltsDataView->hideColumn(id);
if (!m_concealedColumns.contains(id))
m_concealedColumns += id;
}
}
void EventListDialog::initColumns()
{
const int colCount = m_model->columnCount();
if (colCount > 0)
{
m_headerColumns.clear();
QVector<ColumnInfo> columnInfos; // must be sorted by visualIndex
columnInfos.reserve(colCount);
const auto header = m_ui->ltsDataView->searchView()->header();
DEBUG_LOG(m_logger) << "concealed count: " << m_concealedColumns.length();
for (int visualIndex = 0; visualIndex < colCount; ++visualIndex)
{
DEBUG_LOG(m_logger) << visualIndex << " concealed? " << m_concealedColumns.contains(visualIndex);
if (m_concealedColumns.contains(visualIndex)) // don't offer concealed columns to be shown
continue;
ColumnInfo ci;
ci.logicalIndex = header->logicalIndex(visualIndex);
ci.visualIndex = visualIndex;
ci.size = header->sectionSize(ci.logicalIndex);
ci.visible = !header->isSectionHidden(ci.logicalIndex);
ci.name = m_model->headerData(ci.logicalIndex, Qt::Orientation::Horizontal, Qt::DisplayRole).toString();
DEBUG_LOG(m_logger) << "InitColumns: Adding column " << ci.name <<
" with visual index = " << ci.visualIndex <<
", logical index = " << ci.logicalIndex;
m_headerColumns.insert(ci.name, ci.logicalIndex);
columnInfos += ci;
}
m_columns->deleteLater();
m_columns = new Timeline::EventListColumns(columnInfos, m_flmIfc, this);
connect(m_columns, &EventListColumns::accepted,
this, &EventListDialog::showColumns);
showColumns();
}
}
void EventListDialog::clearModel()
{
m_model->clear();
}
bool EventListDialog::appendEventList(const LtsTimelineClassic::EventList &eventList)
{
m_model->appendEventList(eventList);
retranslate();
return true;
}
void EventListDialog::slotEnaDisa()
{
m_buttonBox->setButtonEnabled(LtsWidgets::DialogButton::Show, (m_ui->ltsDataView->selectedRows().size() > 0));
}
void EventListDialog::slotShowSelected()
{
const QModelIndexList indexList = m_ui->ltsDataView->selectedRows();
if (!m_listEvents && indexList.count() > MAX_EVENTS)
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("You selected too many events to show (> %1). Please deselect some.").arg(MAX_EVENTS));
return;
}
for (const QModelIndex &idx : indexList)
{
const LtsTimelineClassic::Event *event = m_model->eventList().at(idx.row());
if (event->uniqueId() > 0)
emit signalShowEventDetails(event);
}
}
void EventListDialog::onPrint()
{
LtsPrinting::printView(m_session, this, m_ui->ltsDataView->treeView());
}
void EventListDialog::onConfigureColumns()
{
initColumns();
m_columns->show();
}
void EventListDialog::showColumns()
{
if (!m_columns)
return;
// sorted by visual index
const auto columnInfo = m_columns->currentColumnsState();
m_ui->ltsDataView->restoreState(m_headerState);
auto header = m_ui->ltsDataView->searchView()->header();
for (const auto &ci : columnInfo)
{
int oldVisualIndex = header->visualIndex(ci.logicalIndex);
DEBUG_LOG(m_logger) << "ShowColumns: Column " << ci.name
<< ", colVisible: " << ci.visible
<< ", logical index: " << ci.logicalIndex
<< ", old index: " << oldVisualIndex
<< ", new index: " << ci.visualIndex;
if (oldVisualIndex != ci.visualIndex)
header->moveSection(oldVisualIndex, ci.visualIndex);
header->setSectionHidden(ci.logicalIndex, !ci.visible);
if (ci.visible && ci.size > 0)
header->resizeSection(ci.logicalIndex, ci.size);
}
}
void EventListDialog::reInitEventList(const LtsTimelineClassic::EventList &eventList, qint64 startTime, qint64 endTime)
{
LtsTimelineClassic::EventList newEventList;
for (auto i = eventList.constBegin(); i != eventList.constEnd(); ++i)
{
LtsTimelineClassic::Event *e = (*i);
if (e->startTime() >= startTime && (e->startTime() + e->duration()) <= endTime)
newEventList += e;
}
DEBUG_LOG(m_logger) << "reInitEventList size = " << eventList.size();
DEBUG_LOG(m_logger) << "reInitEventList new size = " << newEventList.size();
m_model->clear();
m_model->setData(newEventList);
}
} // namespace Timeline
@@ -0,0 +1,83 @@
#pragma once
#include "logger.h"
#include <LtsTimelineClassic/EventList.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <ParticipantManagement/Data/ppm_data_plugin.h>
#include <ProjectDataManagement/Data/pdm_data_plugin.h>
#include <InfrastructureManagement/Data/ism_data_plugin.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
namespace Ui
{
class EventListDialog;
}
namespace LtsTimelineClassic
{
class EventGraphic;
class Event;
}
namespace Timeline
{
class EventListDialogProxyModel;
class EventListDialogModel;
class DetailData;
class EventListColumns;
class EventListDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
EventListDialog(const QString &menuGroup, LtsSession::LtsSession *session,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
bool showMarkers = false,
QWidget *parent = nullptr);
~EventListDialog();
bool init(DetailData *detailData, const LtsTimelineClassic::EventList &eventList, bool listEvents);
bool appendEventList(const LtsTimelineClassic::EventList &eventList);
void clearModel();
void reInitEventList(const LtsTimelineClassic::EventList &eventList, qint64 startTime, qint64 endTime);
private:
void retranslate() override;
void initColumns();
void addConcealedColumns(const QVector<int> &concealedColumns);
Ui::EventListDialog *m_ui;
EventListColumns *m_columns;
EventListDialogModel *m_model;
DetailData *m_detailData;
QHash<QString, int> m_headerColumns; // original
QByteArray m_headerState;
QString m_menuGroup;
bool m_listEvents;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmIfc;
LtsSession::LtsSession *m_session;
EventListDialogProxyModel *m_proxyModel { nullptr };
NGL::INGLLogger &m_logger;
QVector<int> m_concealedColumns;
signals:
void signalShowEventDetails(const LtsTimelineClassic::Event *event);
void signalAddNewEvents(const QVector<LtsTimelineClassic::Event *> &eventList);
private slots:
void showColumns();
void slotShowSelected();
void slotEnaDisa();
void onPrint();
void onConfigureColumns();
};
} // namespace Timeline
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EventListDialog</class>
<widget class="QWidget" name="EventListDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>824</width>
<height>445</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Exercise Management</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Selected event contained more than one event:</string>
</property>
</widget>
</item>
<item>
<widget class="LtsDataView::LtsDataView" name="ltsDataView">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</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,213 @@
/**
* @RDE_HEADER@
* ComboBox's popup, optimized for tablets (bigger dropdown/popup)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#include "tln_event_list_columns.h"
#include "ui_tln_event_list_columns.h"
#include "tln_event_list_model.h"
#include <FileManagement/flm_translation.h>
#include <LtsWidgets/lts_dialog.h>
#include <QSortFilterProxyModel>
#include <QAbstractItemModel>
#include <QColor>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonParseError>
namespace Timeline
{
static inline QString colObjectName(int idx)
{
return QStringLiteral("Column_") + QString::number(idx);
}
EventListColumns::EventListColumns(const QVector<ColumnInfo> &columns,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
QWidget *parent)
: LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal )
, m_ui(new Ui::EventListColumns)
, m_flmIfc(flmIfc)
, m_columns(columns)
, m_logger(getLogger("EventListColumns"))
{
m_ui->setupUi(this);
connect(m_ui->pbOk, &QPushButton::clicked,
this, &EventListColumns::accept);
connect(m_ui->pbClose, &QPushButton::clicked,
this, &EventListColumns::reject);
connect(m_ui->pbLoad, &QPushButton::clicked,
this, &EventListColumns::load);
connect(m_ui->pbSave, &QPushButton::clicked,
this, &EventListColumns::save);
init();
}
EventListColumns::~EventListColumns()
{
delete m_ui;
}
QVector<ColumnInfo> EventListColumns::currentColumnsState() const
{
const auto children = m_ui->itemLayoutWidget->findChildren<EventListColumn*>();
QVector<ColumnInfo> result;
result.reserve(children.size());
for (const EventListColumn *elc : children)
result.push_back(elc->columnInfo());
std::sort(result.begin(), result.end(),
[](const ColumnInfo &a, const ColumnInfo &b)
{
return a.visualIndex < b.visualIndex;
});
return result;
}
void EventListColumns::init()
{
qDeleteAll(m_ui->itemLayoutWidget->findChildren<EventListColumn*>());
// assumption: m_columns is sorted by visual index
for (const auto &ci : qAsConst(m_columns))
{
EventListColumn *rtcItem = new EventListColumn(ci);
rtcItem->setObjectName(colObjectName(ci.logicalIndex));
m_ui->itemLayoutWidget->layout()->addWidget(rtcItem);
}
m_ui->lblConfiguration->setText(tr("No configuration file loaded"));
}
bool EventListColumns::load()
{
bool ret = false;
QByteArray content;
int fileId = m_flmIfc->loadFileContent(this, content, {FileManagement::FileType::Xml}, 0, FileManagement::toString(FileManagement::XmlType::TLNEventListConfiguration));
if (fileId > 0)
{
if (!m_flmIfc->isXmlType(content, FileManagement::toString(FileManagement::XmlType::TLNEventListConfiguration)))
{
QHash<LtsWidgets::Button, QString> texts({{LtsWidgets::Button::Yes, tr("Ok")}});
LtsWidgets::LtsMessageDialog::showMessage(this,
tr("Invalid xml file content"),
windowTitle(),
LtsWidgets::Behaviour::Modal,
LtsWidgets::Button::Yes,
texts);
}
else
{
const QJsonDocument doc(QJsonDocument::fromJson(content));
const QJsonObject obj(doc.object());
const QJsonArray elcCols(obj.value(QStringLiteral("Columns")).toArray());
loadColumns(elcCols);
ret = true;
}
}
if (ret)
{
QString fileName = m_flmIfc->getFileName(fileId);
m_ui->lblConfiguration->setText(tr("Configuration file: %1").arg(fileName));
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Successfully loaded configuration file"));
}
else
{
m_ui->lblConfiguration->setText(tr("No configuration file loaded"));
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Could not load configuration file"));
}
return ret;
}
void EventListColumns::loadColumns(const QJsonArray &cols)
{
qDeleteAll(m_ui->itemLayoutWidget->findChildren<EventListColumn*>());
Timeline::ColumnInfo ci;
for (const auto &val : cols)
{
const QJsonObject obj = val.toObject();
ci.logicalIndex = obj.value(QStringLiteral("logicalIndex")).toInt();
ci.visualIndex = obj.value(QStringLiteral("visualIndex")).toInt();
ci.name = obj.value(QStringLiteral("name")).toString();
ci.size = obj.value(QStringLiteral("size")).toInt();
ci.visible = obj.value(QStringLiteral("visible")).toBool();
const QString objName = colObjectName(ci.logicalIndex);
EventListColumn *elc = m_ui->itemLayoutWidget->findChild<EventListColumn*>(objName);
if (elc)
{
// should never happens since we write the json file...
ERROR_LOG(m_logger) << "Modifed Column: " << ci.visualIndex
<< ", " << ci.logicalIndex << ", " << ci.name
<< ", " << ci.visible << ", " << objName;
elc->setChecked(ci.visible);
}
else
{
ERROR_LOG(m_logger) << "New Column: " << ci.visualIndex
<< ", " << ci.logicalIndex << ", " << ci.name
<< ", " << ci.visible << ", " << objName;
elc = new EventListColumn(ci);
elc->setObjectName(objName);
m_ui->itemLayoutWidget->layout()->addWidget(elc);
}
}
}
bool EventListColumns::save()
{
int fileId = m_flmIfc->saveFileContent(this, FileManagement::FileType::Xml, 0, columnsToDoc(exportColumns()));
if (fileId > 0)
DEBUG_LOG(m_logger) << "Saved file with id = " << fileId;
else
ERROR_LOG(m_logger) << "Could not save file in library ";
return (fileId > 0);
}
const QByteArray EventListColumns::columnsToDoc(const QJsonArray &cols)
{
QJsonObject obj;
obj[QStringLiteral("XmlType")] = FileManagement::toString(FileManagement::XmlType::TLNEventListConfiguration);
obj[QStringLiteral("Columns")] = cols;
QJsonDocument doc(obj);
return doc.toJson(QJsonDocument::Compact);
}
const QJsonArray EventListColumns::exportColumns() const
{
QJsonArray result = QJsonArray();
const auto children = m_ui->itemLayoutWidget->findChildren<EventListColumn*>();
for (const EventListColumn *elc : children)
{
const auto &ci = elc->columnInfo();
DEBUG_LOG(m_logger) << "Saving column: " << ci.visualIndex << ", " << elc->text() << ", " << elc->isChecked();
QJsonObject elcObj;
elcObj[QStringLiteral("visualIndex")] = ci.visualIndex;
elcObj[QStringLiteral("logicalIndex")] = ci.logicalIndex;
elcObj[QStringLiteral("name")] = ci.name;
elcObj[QStringLiteral("size")] = ci.size;
elcObj[QStringLiteral("visible")] = ci.visible;
result.append(elcObj);
}
return result;
}
} // namespace
@@ -0,0 +1,96 @@
/**
* @RDE_HEADER@
* ComboBox's popup, optimized for tablets (bigger dropdown/popup)
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2016-03-09
*/
#pragma once
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <LtsWidgets/lts_frameless_dialog.h>
#include "logger.h"
#include <QCheckBox>
namespace Ui
{
class EventListColumns;
}
class QAbstractItemModel;
namespace Timeline
{
struct ColumnInfo
{
QString name;
int logicalIndex = 0;
int visualIndex = 0;
int size = 0;
bool visible = false;
};
class EventListColumn : public QCheckBox
{
Q_OBJECT
public:
explicit EventListColumn(const ColumnInfo &ci)
: QCheckBox(ci.name)
, m_ci(ci)
{
setChecked(ci.visible);
connect(this, &EventListColumn::clicked,
this, [this]() { m_ci.visible = isChecked(); });
}
const ColumnInfo &columnInfo() const { return m_ci; }
private:
ColumnInfo m_ci;
};
/**
* @brief The EventListColumns is a popup window (LtsFramelessDialog) with a list of QCheckboxes.
* This is a custom Popup which is touch-friendly and screen-centered.
*/
class EventListColumns : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
/**
* @brief EventListColumns creates a popup with all its items (from model) in it
* @param model the model which contains all items
* @param parent parent of this widget
*/
EventListColumns(const QVector<ColumnInfo> &columns,
const std::shared_ptr<FileManagement::ForeignGuiInterface> &flmIfc,
QWidget *parent = nullptr);
~EventListColumns();
QVector<ColumnInfo> currentColumnsState() const;
public slots:
bool save();
bool load();
private:
const QJsonArray exportColumns() const;
void init();
const QByteArray columnsToDoc(const QJsonArray &cols);
void loadColumns(const QJsonArray &cols);
private:
QString m_fileLoaded;
Ui::EventListColumns *m_ui;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmIfc;
QVector<ColumnInfo> m_columns;
NGL::INGLLogger &m_logger;
};
} // namespace
@@ -0,0 +1,217 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EventListColumns</class>
<widget class="QDialog" name="EventListColumns">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>368</width>
<height>234</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lblShowColumns">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>Columns:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblConfiguration">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true"/>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<widget class="QWidget" name="itemLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>356</width>
<height>96</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>800</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>100</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>1</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>3</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbSave">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbLoad">
<property name="text">
<string>Load</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>pbSave</tabstop>
<tabstop>pbClose</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,268 @@
#include "tln_event_list_model.h"
#include "detail_data.h" // marker type
#include <ngltools/ngltools_dtg.h>
#include <Services/ProjectDataManagement/pdm_translation.h>
#include <Services/ParticipantManagement/ppm_translation.h>
#include <Services/AreaEffectSimulation/aes_translation.h>
#include <QJsonObject>
#include <QDateTime>
namespace Timeline
{
EventListDialogModel::EventListDialogModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
QObject *parent)
: LtsWidgets::LtsAbstractTableModel<LtsTimelineClassic::Event *>({tr("Id"),
tr("Type"),
tr("Time"),
tr("Duration [s]", "Dauer in Sekunden"),
tr("Party"),
tr("Equipment Number"),
tr("Hierarchy"),
tr("Participant Id"),
tr("Aes Name"),
tr("Aes Type"),
// tr("Battery State"),
// tr("Bite State"),
tr("Building"),
tr("Charge Type"),
tr("Charge Remain"),
// tr("Config State"),
tr("Damage Percent"),
tr("Damage Percent Old"),
// tr("Device Type"),
tr("Friendly Fire"),
tr("Gear Type"),
tr("Stream Name"),
tr("Vulnerability State")},
parent)
, m_ppmDtc(ppmDtc)
, m_pdmDtc(pdmDtc)
, m_ismDtc(ismDtc)
, m_logger(getLogger("EventListModel"))
{}
QVariant EventListDialogModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index) || m_invalid)
return QVariant();
LtsTimelineClassic::Event *event = m_vecData.at(index.row());
const QJsonObject &obj = event->detailData();
// ERROR_LOG(m_logger) << "Event details #: " << obj.count();
// for (auto i = obj.constBegin(); i != obj.constEnd(); ++i)
// ERROR_LOG(m_logger) << "Key = " << i.key() << " Value = " << i.value().toString();
switch (index.column())
{
case EventListDialogModel::Columns::Id:
return event->uniqueId();
case EventListDialogModel::Columns::Type:
{
if (event->typeId() == markerTypeId /* marker */)
return tr("Marker");
return ProjectDataManagement::toString(static_cast/*not the best idea*/<ProjectDataManagement::EventType::type>(event->typeId()));
}
case EventListDialogModel::Columns::Time:
return QDateTime::fromMSecsSinceEpoch(event->startTime()); //, DTG::Short
case EventListDialogModel::Columns::Duration:
return (event->duration() / 1000);
case EventListDialogModel::Columns::Party:
{
if (event->typeId() == ProjectDataManagement::EventType::HitBuilding) // don't show any text on Hit Building as a building can't have a party
break;
return ParticipantManagement::trPartyName(m_ppmDtc->party(event->partyId()).name());
}
case EventListDialogModel::Columns::EquipNo:
{
if (event->typeId() == ProjectDataManagement::EventType::HitBuilding) // don't show any text on Hit Building as a building can't have a party
break;
return event->equipmentId();
}
case EventListDialogModel::Columns::Hierarchy:
return m_ppmDtc->hierarchyFromParticipant(event->playerId());
case EventListDialogModel::Columns::ParticipantId:
return event->playerId();
// TODO: fill data
case EventListDialogModel::AesType:
{
const auto ele = obj.value(QLatin1String("AesType"));
if (!ele.isDouble())
return QVariant();
const int aesTypeId = ele.toInt();
return AreaEffectSimulation::toString(static_cast<AreaEffectSimulation::AesType_def::type>(aesTypeId));
}
case EventListDialogModel::AesId:
{
const auto ele = obj.value(QLatin1String("AesName"));
if (ele.isString())
return ele.toString();
const auto ele2 = obj.value(QLatin1String("AesId"));
return ele2.isDouble() ? QVariant(ele.toInt()) : QVariant();
}
/*
case EventListDialogModel::BatteryState:
{
if (!obj.contains(QStringLiteral("BatteryState")))
return QVariant();
return obj[QStringLiteral("BatteryState")].toInt();
}
case EventListDialogModel::BiteState:
{
if (!obj.contains(QStringLiteral("BiteState")))
return QVariant();
return obj[QStringLiteral("BiteState")].toInt();
}
case EventListDialogModel::ConfigState:
{
if (!obj.contains(QStringLiteral("ConfigState")))
return QVariant();
return obj[QStringLiteral("ConfigState")].toInt();
}
case EventListDialogModel::DeviceType:
{
if (!obj.contains(QStringLiteral("DeviceType")))
return QVariant();
return obj[QStringLiteral("DeviceType")].toVariant();
}
*/
case EventListDialogModel::Building:
{
const auto ele = obj.value(QLatin1String("MainStructureElementId"));
if (!ele.isDouble())
return QVariant();
int buildingId = ele.toInt();
if (buildingId > 0)
return m_ismDtc->getBuildingDisplayName(buildingId);
return QVariant();
}
case EventListDialogModel::ChargeType:
{
int chargeTypeId = obj.value(QLatin1String("OriginatorChargeTypeId")).toInt();
if (chargeTypeId <= 0)
chargeTypeId = obj.value(QLatin1String("ChargeTypeId")).toInt();
if (chargeTypeId <= 0)
return QVariant();
return m_pdmDtc->trChargeTypeShortName(chargeTypeId);
}
case EventListDialogModel::ChargeRemain:
{
const auto ele = obj.value(QLatin1String("Remain"));
if (!ele.isDouble())
return QVariant();
return ele.toInt();
}
case EventListDialogModel::DamagePercent:
{
const auto ele = obj.value(QLatin1String("damagePercent"));
if (!ele.isDouble())
return QVariant();
return ele.toInt();
}
case EventListDialogModel::DamagePercentOld:
{
const auto ele = obj.value(QLatin1String("damagePercentOld"));
if (!ele.isDouble())
return QVariant();
return ele.toInt();
}
case EventListDialogModel::FriendlyFire:
{
const auto ele = obj.value(QLatin1String("FriendlyFire"));
if (!ele.isBool())
return QVariant();
return ele.toBool();
}
case EventListDialogModel::GearType:
{
int gearTypeId = obj.value(QLatin1String("GearTypeId")).toInt();
if (gearTypeId <= 0)
return QVariant();
return m_pdmDtc->trGearTypeName(gearTypeId);
}
case EventListDialogModel::StreamName:
{
const auto ele = obj.value(QLatin1String("streamName"));
if (!ele.isString())
return QVariant();
return ele.toString();
}
case EventListDialogModel::VulnerabilityState:
{
const auto ele = obj.value(QLatin1String("VulnerabilityState"));
if (!ele.isDouble())
return QVariant();
const int vId = ele.toInt();
return ProjectDataManagement::toString(static_cast<ProjectDataManagement::VulnerabilityState_def::type>(vId));
}
default:
break;
}
return QVariant();
}
void EventListDialogModel::appendEventList(const QVector<LtsTimelineClassic::Event *> &eventList)
{
for (const auto &event : eventList)
{
if (!m_vecData.contains(event))
insertRow(event);
}
}
//
// EventListProxyModel
//
void EventListDialogProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<EventListDialogModel*>(sourceModel);
LtsWidgets::LtsColumnFilterProxyModel::setSourceModel(sourceModel);
}
bool EventListDialogProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel ||
source_left.column() != source_right.column() ||
!source_left.isValid() ||
!source_right.isValid() ||
m_invalid)
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case EventListDialogModel::Time:
return dataLeft->startTime() < dataRight->startTime();
case EventListDialogModel::EquipNo:
return dataLeft->equipmentId() < dataRight->equipmentId();
default:
break;
}
return LtsWidgets::LtsColumnFilterProxyModel::lessThan(source_left, source_right);
}
bool EventListDialogProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if (!m_sourceModel || source_parent.isValid() || source_row < 0 || source_row >= m_sourceModel->rowCount() || m_invalid)
return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent);
LtsTimelineClassic::Event *event = m_sourceModel->m_vecData.at(source_row);
if (event->typeId() == markerTypeId) // not showing Markers in list model
return false;
return LtsWidgets::LtsColumnFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
} // namespace Timeline
@@ -0,0 +1,86 @@
#pragma once
#include <LtsTimelineClassic/Event.h>
#include <ParticipantManagement/Data/ppm_data_plugin.h>
#include <ProjectDataManagement/Data/pdm_data_plugin.h>
#include <InfrastructureManagement/Data/ism_data_plugin.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <LtsWidgets/lts_column_filter_proxy_model.h>
namespace Timeline
{
class EventListDialogModel final : public LtsWidgets::LtsAbstractTableModel<LtsTimelineClassic::Event *>
{
Q_OBJECT
public:
enum Columns
{
Id,
Type,
Time,
Duration,
Party,
EquipNo,
Hierarchy,
ParticipantId,
AesId,
AesType,
// BatteryState, //opt
// BiteState, //opt
Building,
ChargeType,
ChargeRemain,
// ConfigState, //opt
DamagePercent,
DamagePercentOld,
// DeviceType, //opt
FriendlyFire,
GearType,
// OriginatorType, //opt
StreamName,
VulnerabilityState,
Count
};
explicit EventListDialogModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<ProjectDataManagement::Interface::Dtc> &pdmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
QObject *parent = nullptr);
const QVector<LtsTimelineClassic::Event *> &eventList() const { return m_vecData; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void setInvalid() { m_invalid = true; }
public slots:
void appendEventList(const QVector<LtsTimelineClassic::Event *> &eventList);
private:
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
bool m_invalid = false;
NGL::INGLLogger &m_logger;
friend class EventListDialogProxyModel;
};
class EventListDialogProxyModel final : public LtsWidgets::LtsColumnFilterProxyModel
{
Q_OBJECT
public:
using LtsWidgets::LtsColumnFilterProxyModel::LtsColumnFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
void setInvalid() { m_invalid = true; }
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
EventListDialogModel *m_sourceModel = nullptr;
bool m_invalid = false;
};
} // namespace Timeline
@@ -0,0 +1,327 @@
/**
* @RDE_HEADER@
* ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#include "tln_legend.h"
#include "ui_tln_legend.h"
#include "detail_data.h"
#include "tln_dialog_model.h"
#include <LtsTimelineClassic/Event.h>
#include <LtsTimelineClassic/EventGraphic.h>
#include <LtsUtilsSettings/settings.h>
#include <ProjectDataManagement/pdm_enums.hpp>
#include <ProjectDataManagement/pdm_translation.h>
#include <QLabel>
#include <QImage>
#include <QPixmap>
#include <QPainter>
namespace Timeline
{
TimelineLegendDialog::TimelineLegendDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::TimelineLegendDialog)
, m_logger(getLogger("TimelineLegendDialog"))
{
m_ui->setupUi(this);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->hide(); // apply() always returns false (because it can't be executed with async ops), resulting in the window not closing on OK - only use apply button until apply() can have async ops
connect(m_ui->cbCheckAll, &QCheckBox::toggled,
this, [this](bool toggled)
{
const auto cbList = findChildren<QCheckBox *>();
for (QCheckBox *cb : cbList)
{
if (cb == m_ui->cbCheckAll) // needed? maybe recursion?
continue;
cb->setChecked(toggled);
}
});
}
TimelineLegendDialog::~TimelineLegendDialog()
{
delete m_ui;
}
void TimelineLegendDialog::retranslate()
{
translate(m_ui, tr("Timeline Legend"));
}
void TimelineLegendDialog::setActiveEventTypeIds(const QSet<int> &activeEventTypeIds)
{
setUnchangeable();
// first, deactivate all checkboxes, as we can't keep track on WHICH event types are now inactive
const QList<QCheckBox *> cbs(findChildren<QCheckBox *>()); // yes, even the "check all" cb
for (QCheckBox *cb : cbs)
cb->setChecked(false);
for (int activeEventType : activeEventTypeIds)
{
QCheckBox *cb = checkBox(activeEventType);
if (cb != nullptr)
cb->setChecked(true);
else
WARNING_LOG(m_logger) << "cb for event type " << activeEventType << " not found!";
}
setUnchangeable(false);
}
QVector<ProjectDataManagement::EventType> TimelineLegendDialog::possibleEvents()
{
QVector<ProjectDataManagement::EventType> ret
({
// ProjectDataManagement::EventType::Unknown,
ProjectDataManagement::EventType::Hit,
ProjectDataManagement::EventType::WeaponAssignment,
ProjectDataManagement::EventType::BiteMessage,
ProjectDataManagement::EventType::FireEvent,
ProjectDataManagement::EventType::HitBuilding,
ProjectDataManagement::EventType::Manipulation,
ProjectDataManagement::EventType::CameraVideo,
ProjectDataManagement::EventType::RadioCnr,
ProjectDataManagement::EventType::RadioTvc,
ProjectDataManagement::EventType::HitEffectSimulation,
// ProjectDataManagement::EventType::HitMine, // HitEffectSimulation
// ProjectDataManagement::EventType::HitN, // HitEffectSimulation
// ProjectDataManagement::EventType::HitC, // HitEffectSimulation
// ProjectDataManagement::EventType::EnterDangerArea, // showEnterLeaveEvents
// ProjectDataManagement::EventType::LeaveDangerArea, // showEnterLeaveEvents
// ProjectDataManagement::EventType::EnterMineArea, // showEnterLeaveEvents
// ProjectDataManagement::EventType::LeaveMineArea, // showEnterLeaveEvents
ProjectDataManagement::EventType::CrossLocalBorderline,
// ProjectDataManagement::EventType::EnterFunctionalArea, // showEnterLeaveEvents
// ProjectDataManagement::EventType::LeaveFunctionalArea, // showEnterLeaveEvents
// ProjectDataManagement::EventType::EnterNuclear, // showEnterLeaveEvents
// ProjectDataManagement::EventType::LeaveNuclear, // showEnterLeaveEvents
ProjectDataManagement::EventType::CrossGlobalBorderline,
// ProjectDataManagement::EventType::HitB, // HitEffectSimulation
// ProjectDataManagement::EventType::FriendlyFire, // currently not displayed
// ProjectDataManagement::EventType::HitGasPipe, // HitEffectSimulation
// ProjectDataManagement::EventType::MineArea, // showEffectSimulationEvents
// ProjectDataManagement::EventType::NContamination, // showEffectSimulationEvents
// ProjectDataManagement::EventType::BCContamination, // showEffectSimulationEvents
// ProjectDataManagement::EventType::DangerArea, // showEffectSimulationEvents
// ProjectDataManagement::EventType::FunctionalArea, // showEffectSimulationEvents
// ProjectDataManagement::EventType::MineCorridor, // showEffectSimulationEvents
// ProjectDataManagement::EventType::Borderline, // showEffectSimulationEvents
// ProjectDataManagement::EventType::TechnicalArea,
// ProjectDataManagement::EventType::GasPipe, // separate
// ProjectDataManagement::EventType::EquipmentErrorReport,
// ProjectDataManagement::EventType::EmergencyCall,
});
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
if (ps->value(QStringLiteral("tln"),
QStringLiteral("showEffectSimulationEvents")).toBool())
{
ret += ProjectDataManagement::EventType::MineArea;
ret += ProjectDataManagement::EventType::DangerArea;
ret += ProjectDataManagement::EventType::FunctionalArea;
ret += ProjectDataManagement::EventType::MineCorridor;
ret += ProjectDataManagement::EventType::Borderline;
if (ps->value(QStringLiteral("aes"),
QStringLiteral("enableNuclearContaminations")).toBool())
ret += ProjectDataManagement::EventType::NContamination;
if (ps->value(QStringLiteral("aes"),
QStringLiteral("enableNuclearContaminations")).toBool() ||
ps->value(QStringLiteral("aes"),
QStringLiteral("enableNuclearContaminations")).toBool())
ret += ProjectDataManagement::EventType::BCContamination;
}
if (ps->value(QStringLiteral("tln"),
QStringLiteral("showEnterLeaveEvents")).toBool())
{
ret += ProjectDataManagement::EventType::EnterDangerArea;
ret += ProjectDataManagement::EventType::LeaveDangerArea;
ret += ProjectDataManagement::EventType::EnterMineArea;
ret += ProjectDataManagement::EventType::LeaveMineArea;
ret += ProjectDataManagement::EventType::EnterFunctionalArea;
ret += ProjectDataManagement::EventType::LeaveFunctionalArea;
if (ps->value(QStringLiteral("aes"),
QStringLiteral("enableNuclearContaminations")).toBool())
{
ret += ProjectDataManagement::EventType::EnterNuclear;
ret += ProjectDataManagement::EventType::LeaveNuclear;
}
}
if (ps->value(QStringLiteral("aes"),
QStringLiteral("enableGasPipes")).toBool())
{
ret += ProjectDataManagement::EventType::GasPipe;
//ret += ProjectDataManagement::EventType::HitGasPipe; covered by HitEffectSimulation yes or no ??
}
const bool showPvSan = LtsUtils::ProjectSettings::isFunctionEnabled(QStringLiteral("ParticipantInformation"),
QStringLiteral("HasPvSan"));
if (showPvSan)
ret += ProjectDataManagement::EventType::Sanitation;
return ret;
}
// helper for grouped sorting
static int categoryFromEventType(ProjectDataManagement::EventType et)
{
switch (et.underlying())
{
case ProjectDataManagement::EventType::Unknown:
return 0;
case ProjectDataManagement::EventType::Hit:
case ProjectDataManagement::EventType::WeaponAssignment:
case ProjectDataManagement::EventType::BiteMessage:
case ProjectDataManagement::EventType::FireEvent:
case ProjectDataManagement::EventType::FriendlyFire:
case ProjectDataManagement::EventType::Manipulation:
return 1;
case ProjectDataManagement::EventType::CameraVideo:
case ProjectDataManagement::EventType::RadioCnr:
case ProjectDataManagement::EventType::RadioTvc:
return 5;
case ProjectDataManagement::EventType::HitBuilding:
case ProjectDataManagement::EventType::HitEffectSimulation:
case ProjectDataManagement::EventType::HitMine:
case ProjectDataManagement::EventType::HitN:
case ProjectDataManagement::EventType::HitB:
case ProjectDataManagement::EventType::HitC:
case ProjectDataManagement::EventType::HitGasPipe:
return 2;
case ProjectDataManagement::EventType::EnterDangerArea:
case ProjectDataManagement::EventType::LeaveDangerArea:
case ProjectDataManagement::EventType::EnterMineArea:
case ProjectDataManagement::EventType::LeaveMineArea:
case ProjectDataManagement::EventType::CrossLocalBorderline:
case ProjectDataManagement::EventType::EnterFunctionalArea:
case ProjectDataManagement::EventType::LeaveFunctionalArea:
case ProjectDataManagement::EventType::EnterNuclear:
case ProjectDataManagement::EventType::LeaveNuclear:
case ProjectDataManagement::EventType::CrossGlobalBorderline:
case ProjectDataManagement::EventType::Sanitation:
return 3;
case ProjectDataManagement::EventType::MineArea:
case ProjectDataManagement::EventType::NContamination:
case ProjectDataManagement::EventType::BCContamination:
case ProjectDataManagement::EventType::DangerArea:
case ProjectDataManagement::EventType::FunctionalArea:
case ProjectDataManagement::EventType::MineCorridor:
case ProjectDataManagement::EventType::Borderline:
case ProjectDataManagement::EventType::GasPipe:
return 6;
case ProjectDataManagement::EventType::TechnicalArea:
case ProjectDataManagement::EventType::EquipmentErrorReport:
case ProjectDataManagement::EventType::EmergencyCall:
return 0;
}
Q_ASSERT(false);
return 0;
}
bool TimelineLegendDialog::init(DDSTimelineModel *model, const QSet<int> &activeEventTypeIds)
{
struct WidgetSortHelper
{
QLabel *lbl = nullptr;
QCheckBox *cbx = nullptr;
ProjectDataManagement::EventType et = ProjectDataManagement::EventType::Unknown;
};
QImage p(16, 24, QImage::Format_ARGB32);
QColor c; // dummy
LtsTimelineClassic::Event::EventPaintFunc paintFunc;
WidgetSortHelper wsh;
const auto events = possibleEvents();
std::vector<WidgetSortHelper> helperCont;
helperCont.reserve(events.size());
for (const ProjectDataManagement::EventType eventType : events)
{
model->setColorPaintFun(paintFunc, c, eventType.underlying());
if (paintFunc == nullptr)
{
WARNING_LOG(m_logger) << "No draw function for event " << eventType;
Q_ASSERT(false);
continue;
}
p.fill(Qt::transparent);
QPainter pp(&p);
pp.setRenderHint(QPainter::Antialiasing);
pp.setBrush(QBrush(c));
paintFunc(&pp, p.rect().adjusted(0, 0, -1, -1));
wsh.lbl = new QLabel(this);
wsh.lbl->setPixmap(QPixmap::fromImage(p));
wsh.et = eventType;
wsh.cbx = new QCheckBox(ProjectDataManagement::toString(eventType), this);
wsh.cbx->setObjectName(checkBoxName(eventType));
wsh.cbx->setChecked(activeEventTypeIds.contains(eventType.underlying()));
wsh.cbx->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
helperCont.push_back(wsh);
}
std::sort(helperCont.begin(), helperCont.end(), [](const WidgetSortHelper &a, const WidgetSortHelper &b)
{
const auto et1 = categoryFromEventType(a.et);
const auto et2 = categoryFromEventType(b.et);
if (et1 != et2)
return et1 < et2;
return a.cbx->text() < b.cbx->text();
});
int curGridRow = 1; // 0 is 'Check all'
for (const auto &ele : helperCont)
{
m_ui->gridLayout->addWidget(ele.lbl, curGridRow, 0);
m_ui->gridLayout->addWidget(ele.cbx, curGridRow, 1);
m_ui->gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding), curGridRow, 2);
++curGridRow;
}
m_ui->gridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), curGridRow, 0);
return true;
}
bool TimelineLegendDialog::apply()
{
QSet<int> activeEventTypeIds;
const auto events = possibleEvents();
for (const ProjectDataManagement::EventType eventType : events)
{
if (checkBox(eventType)->isChecked())
activeEventTypeIds.insert(eventType.underlying());
}
emit signalEventTypesChanged(activeEventTypeIds);
return false; // because this has to happen async.. (reset changes after async ops are (successfully) done)
}
} // namespace Timeline
//
+58
View File
@@ -0,0 +1,58 @@
/**
* @RDE_HEADER@
* ExerciseManangement - Exercise Overview Dialog
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <ProjectDataManagement/pdm_enums.hpp>
#include "logger.h"
namespace Ui
{
class TimelineLegendDialog;
}
namespace Timeline
{
class DDSTimelineModel;
class TimelineLegendDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit TimelineLegendDialog(QWidget *parent = nullptr);
~TimelineLegendDialog();
bool apply() override;
bool init(DDSTimelineModel *model, const QSet<int> &activeEventTypeIds);
void setActiveEventTypeIds(const QSet<int> &activeEventTypeIds);
static QVector<ProjectDataManagement::EventType> possibleEvents();
signals:
void signalEventTypesChanged(const QSet<int> &activeEventTypeIds);
private:
void retranslate() override;
static QString checkBoxName(int eventTypeId) { return QStringLiteral("CheckBoxEvent%1").arg(eventTypeId); }
static QString checkBoxName(ProjectDataManagement::EventType eventType) { return checkBoxName(eventType.underlying()); }
QCheckBox *checkBox(int eventTypeId) { return findChild<QCheckBox *>(checkBoxName(eventTypeId)); }
QCheckBox *checkBox(ProjectDataManagement::EventType eventType) { return checkBox(eventType.underlying()); }
private:
Ui::TimelineLegendDialog *m_ui;
NGL::INGLLogger &m_logger;
};
} // namespace Timeline
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TimelineLegendDialog</class>
<widget class="QWidget" name="TimelineLegendDialog">
<property name="windowTitle">
<string notr="true">Timeline View</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QCheckBox" name="cbCheckAll">
<property name="text">
<string>Check all</string>
</property>
</widget>
</item>
<item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,521 @@
/**
* @RDE_HEADER@
* Show Details Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Iuliana Ojog, SLSE, 2017-05-22
*/
#include "tln_show_details.h"
#include "ui_tln_show_details.h"
#include "tln_view_gui_plugin.h"
#include "detail_data.h"
#include "logger.h"
#include <LtsWidgets/lts_base.h>
#include <ngltools/ngltools_dtg.h>
#include <LtsTimelineClassic/Event.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <Services/ProjectDataManagement/pdm_translation.h>
#include <AudioVideoManagement/stream_name_mapping.h>
#include <Plugins/MarkerManagement/Gui/Common/mrk_apply_filter.h>
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
/*
* NOTE:
*
* This Dialog uses foreign gui ifcs from a Session that is not ours!
* If we have the last shared_ptr to a foreign Session's foreign Ifc,
* and our Dialog gets deleted AFTER the foreign Session was killed,
* then we trigger the deletion of the dead foreign Ifc of a DEAD SESSION.
* So ifc shared_ptrs are fetched when needed and removed directly after use.
*
* Shared Pointers.
*
*/
namespace Timeline
{
ShowDetailsDialog::ShowDetailsDialog(QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ui(new Ui::ShowDetailsDialog)
, m_logger(getLogger("ShowDetailsDialog"))
{
m_ui->setupUi(this);
setUnchangeable();
m_ui->debugWidget->hide();
connect(m_ui->pbStartReplay, &QPushButton::clicked,
this, &ShowDetailsDialog::slotStartReplay);
connect(m_ui->pbShowMarker, &QPushButton::clicked,
this, &ShowDetailsDialog::slotShowMarker);
connect(m_ui->pbPlayback, &QPushButton::clicked,
this, &ShowDetailsDialog::slotStartPlayback);
connect(m_ui->pbFocus, &QPushButton::clicked,
this, &ShowDetailsDialog::slotFocusOnMap);
m_ui->tbFocus->hide();
m_ui->tbMarker->hide();
m_ui->tbPlayback->hide();
m_ui->tbReplay->hide();
#ifndef LTS_DEBUG
if (LtsSessionData::LtsSessionData::getInstance()->debugFlagValue(QStringLiteral("Timeline.Debug")) == QVariant(true))
#endif
{
m_ui->debugWidget->show();
}
}
//------------------------------------------------------------------------------------------------------------------------
ShowDetailsDialog::~ShowDetailsDialog()
{
m_session = nullptr;
m_mrkDtc = nullptr;
delete m_ui;
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::retranslate()
{
translate(m_ui, m_eventType, false, tr("Event"));
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::slotFocusOnMap()
{
if (m_replayAppSession == nullptr)
{
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no replay app, no focus";
return;
}
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
if (!m_replayAppSession->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(sitMapIfc))
{
WARNING_LOG(m_logger) << "no sitMapIfc, no focusing on replay in Show Details Dialog 1";
return;
}
if (!sitMapIfc)
{
WARNING_LOG(m_logger) << "no sitMapIfc, no focusing on replay in Show Details Dialog 2";
return;
}
if (m_event->typeId() != markerTypeId) // NOTE: marker events have their position stored in the map settings
{
if (!m_event->positionValid())
{
WARNING_LOG(m_logger) << "event has no valid position, skip focusOnMap";
return;
}
// x == lon ; y == lat
sitMapIfc->setCenterPoint(m_event->pos2d());
}
const QJsonObject root(m_event->detailData());
const QJsonValue mapSettings(root[QStringLiteral("MapSettings")]);
if (!mapSettings.isUndefined())
sitMapIfc->setOperatorMapSettings(mapSettings);
const QJsonValue filterSettings(root[QStringLiteral("Filter")]);
if (!filterSettings.isUndefined())
{
MarkerManagement::MarkerManagementApplyFilter applyFilter(m_replayAppSession, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc);
if (!applyFilter.applyFilter(filterSettings.toObject()))
{
WARNING_LOG(m_logger) << "filters from event #"<<m_event->uniqueId()<<" could not be applied";
return;
}
}
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::slotShowMarker()
{
m_mrkIfc->showMarker(m_event->uniqueId());
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::hasNameAndEndTime() const
{
QString dummy1;
qint64 dummy2;
return getStreamNameAndEndTime(dummy1, dummy2, true);
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::getStreamNameAndEndTime(QString &streamName, qint64 &endTime, bool silent) const
{
const QJsonObject root(m_event->detailData());
const QString _streamName = root[QStringLiteral("streamName")].toString(); // Lts/Services/ExerciseEventManagement/web_socket_avs.cpp:160
if (_streamName.isEmpty())
{
if (!silent)
ERROR_LOG(m_logger) << "ShowDetailsDialog::slotStartVideo(): stream name is empty " << QJsonDocument(m_event->detailData()).toJson();
return false;
}
qint64 _endTime = root[QStringLiteral("endTime")].toDouble(0);
if (_endTime == 0 && !silent)
WARNING_LOG(m_logger) << "ShowDetailsDialog::slotStartPlayback: event " << m_event->uniqueId() << " has no end time!";
streamName = _streamName;
endTime = _endTime;
return true;
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::slotStartPlayback()
{
QString streamName;
qint64 endTime;
if (!getStreamNameAndEndTime(streamName, endTime))
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Cannot start playback as this event's informations are missing."));
return;
}
const bool isAudio = isAudioEventType(m_event->typeId());
if (!m_avmIfc->startAudioVideoPlayback(streamName, m_event->startTime(), isAudio))
new LtsWidgets::LtsMessageDialogNew(this, tr("Playback cannot be started because of an error. Please try again later."));
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::slotStartReplay()
{
if (m_replayAppSession == nullptr)
{
ERROR_LOG(m_logger) << "ShowDetailsDialog::slotStartReplay: no replay app, no replay";
return;
}
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
if (!m_replayAppSession->getForeignGuiInterface<SituationReplay::ForeignGuiInterface>(srpIfc))
{
ERROR_LOG(m_logger) << "ShowDetailsDialog::slotStartReplay: no srp, no replay in Show Details Dialog";
return;
}
srpIfc->startReplay(m_event->startTime() - (10 * 1000) /* TODO: offset? */); // TODO: endTime..
slotFocusOnMap();
}
//------------------------------------------------------------------------------------------------------------------------
void ShowDetailsDialog::setButtonDisabled(QPushButton *button, QToolButton *warningButton, const QString &reasonMessage, bool hide)
{
if (hide)
button->setVisible(false);
button->setEnabled(false);
button->setToolTip(reasonMessage);
warningButton->setToolTip(reasonMessage);
warningButton->show();
connect(warningButton, &QToolButton::clicked,
warningButton, [this, warningButton]()
{
LtsWidgets::LtsMessageDialog::showMessage(this, warningButton->toolTip());
});
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::isMarkerAudioVideo() const
{
const QJsonObject root(m_event->detailData());
return (root[QStringLiteral("IsAudioType")].toBool(false) || root[QStringLiteral("IsVideoType")].toBool(false));
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::isAvsCapable(const LtsTimelineClassic::Event *event)
{
const int eventType = event->typeId();
return isAvEventType(eventType) || eventType == Timeline::markerTypeId;
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::isHitEventType(int eventTypeId)
{
return (eventTypeId == ProjectDataManagement::EventType_def::Hit ||
eventTypeId == ProjectDataManagement::EventType_def::HitB ||
eventTypeId == ProjectDataManagement::EventType_def::HitBuilding ||
eventTypeId == ProjectDataManagement::EventType_def::HitC ||
eventTypeId == ProjectDataManagement::EventType_def::HitGasPipe ||
eventTypeId == ProjectDataManagement::EventType_def::HitEffectSimulation ||
eventTypeId == ProjectDataManagement::EventType_def::HitMine ||
eventTypeId == ProjectDataManagement::EventType_def::HitN);
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::isAvEventType(int eventTypeId)
{
return (isAudioEventType(eventTypeId) ||
eventTypeId == ProjectDataManagement::EventType_def::CameraVideo);
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::isAudioEventType(int eventTypeId)
{
return (eventTypeId == ProjectDataManagement::EventType_def::RadioCnr ||
eventTypeId == ProjectDataManagement::EventType_def::RadioTvc);
}
//------------------------------------------------------------------------------------------------------------------------
bool ShowDetailsDialog::init(DetailData *detailData, const LtsTimelineClassic::Event *event,
const QPointer<LtsSession::LtsSession> &session,
const QPointer<LtsSession::LtsSession> &replayAppSession,
const std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> &avmIfc,
const std::shared_ptr<MarkerManagement::ForeignGuiInterface> &mrkIfc,
const std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> &fltParticipantIfc,
const std::shared_ptr<FilterManagement::ForeignAesGuiInterface> &fltAesIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> &fltBuildingComponentIfc,
bool isTimelineApp,
QWidget *pseudoParent,
AudioVideoManagement::StreamNameMapping *streamNameMapping)
{
m_session = session;
//Q_ASSERT(streamNameMapping);
// m_srpIfc = srpIfc; << from replay app
// m_sitMapIfc = sitMapIfc; << from replay app
std::shared_ptr<SituationReplay::ForeignGuiInterface> srpIfc;
std::shared_ptr<SituationMap::ForeignGuiInterface> sitMapIfc;
if (!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc))
WARNING_LOG(m_logger) << "!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc)";
m_mrkDtc->enableMarker();
connect(m_mrkDtc.get(), &MarkerManagement::Interface::Dtc::markerChanged,
this, &ShowDetailsDialog::updateMarker);
if (replayAppSession != nullptr)
{
m_replayAppSession = replayAppSession;
connect(replayAppSession.data(), &LtsSession::LtsSession::destroyed,
this, [this]()
{
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("No map available."));
setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("Situation Replay App not started."));
});
if (!replayAppSession->getForeignGuiInterface<SituationReplay::ForeignGuiInterface>(srpIfc))
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no srp, no replay in Show Details Dialog";
if (!replayAppSession->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(sitMapIfc))
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no sitMapIfc, no focusing on replay in Show Details Dialog";
}
m_avmIfc = avmIfc;
m_mrkIfc = mrkIfc;
m_fltParticipantIfc = fltParticipantIfc;
m_fltAesIfc = fltAesIfc;
m_fltBuildingComponentIfc = fltBuildingComponentIfc;
m_streamNameMapping = streamNameMapping;
if (m_avmIfc == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no avm ifc given";
if (m_mrkIfc == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no mrk ifc given";
if (m_fltParticipantIfc == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no flt Part ifc given";
if (m_fltAesIfc == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no flt Aes ifc given";
if (m_fltBuildingComponentIfc == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no flt buildingComponent ifc given";
if (m_streamNameMapping == nullptr)
WARNING_LOG(m_logger) << "ShowDetailsDialog::init: no stream name mapping given";
m_event = event;
int eventTypeId = event->typeId();
// hide equipment id labels if event has no eq id
if (event->equipmentId() <= 0)
{
m_ui->lblEquipNo->hide();
m_ui->lblValueEquipNo->hide();
}
else
m_ui->lblValueEquipNo->setText(QString::number(event->equipmentId()));
connect(pseudoParent, &QWidget::destroyed, this, &ShowDetailsDialog::deleteLater);
QString eventTime;
if (m_logger.isDebugEnabled())
eventTime = QStringLiteral("%1 <font color='#aebac6'>(%2)</font>").arg(DTG::toStringMSec(event->startTime())).arg(event->startTime());
else
eventTime = DTG::toStringMSec(event->startTime());
m_ui->lblValueEventTime->setText(eventTime);
m_eventType = detailData->eventTypeLookup.getEventText(eventTypeId);
if (m_eventType.isEmpty())
m_eventType = tr("<unknown> (%1)").arg(eventTypeId);
if (sitMapIfc == nullptr || isTimelineApp)
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("No map available"), true);
else if (eventTypeId != markerTypeId && !m_event->positionValid()) // NOTE: markers get their position from the map settings !
setButtonDisabled(m_ui->pbFocus, m_ui->tbFocus, tr("Event has an invalid position and cannot be focused"));
if (srpIfc == nullptr)
setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("Situation Replay App not started."));
else if (sitMapIfc == nullptr)
setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("This app has no map to focus this event on."));
else if (eventTypeId != markerTypeId && !m_event->positionValid()) // NOTE: markers get their position from the map settings !
setButtonDisabled(m_ui->pbStartReplay, m_ui->tbReplay, tr("Event has an invalid position, cannot be focused and therefore has no replay capability."));
if (eventTypeId != markerTypeId)
setButtonDisabled(m_ui->pbShowMarker, m_ui->tbMarker, tr("This event is not a marker."), true);
else if (m_mrkIfc == nullptr)
setButtonDisabled(m_ui->pbShowMarker, m_ui->tbMarker, tr("This app has no marker interface available."), true);
if (!isAvsCapable(event))
setButtonDisabled(m_ui->pbPlayback, m_ui->tbPlayback, tr("This event is not an audio/video type."), true);
else if (m_avmIfc == nullptr)
setButtonDisabled(m_ui->pbPlayback, m_ui->tbPlayback, tr("This app has no audio/video interface available."), true);
else if (eventTypeId == Timeline::markerTypeId && !isMarkerAudioVideo())
setButtonDisabled(m_ui->pbPlayback, m_ui->tbPlayback, tr("This marker event is not an audio / video type."), true);
else if (!hasNameAndEndTime())
setButtonDisabled(m_ui->pbPlayback, m_ui->tbPlayback, tr("This event has missing informations about the playback."));
// Button im Ereignisfenster abhaengig vom Eventtyp ausblenden
if (isAvsCapable(event))
{
m_ui->pbFocus->hide();
m_ui->tbFocus->hide();
m_ui->pbStartReplay->hide();
m_ui->tbReplay->hide();
}
const DetailData::GetDetailDataResultList result = detailData->getDetailData(event);
QLocale locale; // uses system locale
for (const DetailData::GetDetailDataForAttributeResult &attributeDetail : result)
{
// do not add time again
if (detailData->attributeInterpreterLookup.getInterpreterType(attributeDetail.attributeTypeId) == DetailData::INTERPRETER_TYPE_TIME)
continue;
QString attributeTypeText;
QString attributeValueText;
if (attributeDetail.requestResult != DetailData::RequestResult::Okay)
{
if (attributeDetail.requestResult == DetailData::RequestResult::NoValueTranslationForAttribute) // attribute type valid, but no attribute value, but a attribute value id is given - show the id!
{
attributeTypeText = attributeDetail.attributeType;
attributeValueText = QStringLiteral("%1 (%2)").arg(tr("<unknown>")).arg(attributeDetail.attributeValueId);
}
else if (attributeDetail.requestResult == DetailData::RequestResult::InvalidAttributeType)
{
// an attribute which we don't handle, it's fine
DEBUG_LOG(m_logger) << "Unhandled attribute " << attributeDetail.attributeTypeId << " - skipping";
continue;
}
else // everything went wrong, only show unknown ids for both key and value
{
attributeTypeText = QStringLiteral("%1 (%2)").arg(tr("<unknown>"), attributeDetail.attributeTypeId);
attributeValueText = tr("<type unknown>");
WARNING_LOG(m_logger) << "getDetailData failed with message id " << (int)attributeDetail.requestResult << " for attribute " << attributeDetail.attributeTypeId;
continue; // do not add unknown types
}
}
else // everything worked fine:
{
attributeTypeText = attributeDetail.attributeType;
// user readability
bool isNumber = false;
qint64 attributeValueAsNumber = attributeValueText.toLongLong(&isNumber);
if (isNumber)
attributeValueText = locale.toString(attributeValueAsNumber);
if (m_logger.isDebugEnabled())
attributeValueText = QStringLiteral("%1 <font color='#aebac6'>(%2)</font>").arg(attributeDetail.attributeValue).arg(attributeDetail.attributeValueId);
else
attributeValueText = attributeDetail.attributeValue;
}
QString attributeType(attributeDetail.attributeTypeId);
// leave some attributes out ...
int eventType = event->typeId();
if (isAudioEventType(eventType))
{
if (attributeType == QStringLiteral("MainStructureElementId"))
continue;
}
else if (isHitEventType(eventType))
{
if (attributeType == QStringLiteral("OriginatorType"))
continue;
}
if (attributeType == QStringLiteral("GearId")) // never show gear id
continue;
QLabel *attributeValueTextLabel = new QLabel(attributeValueText);
DEBUG_LOG(m_logger) << "attributeValueText=" << attributeValueText;
attributeValueTextLabel->setTextInteractionFlags(attributeValueTextLabel->textInteractionFlags() | Qt::TextInteractionFlag::TextSelectableByMouse);
DEBUG_LOG(m_logger) << "attributeTypeText=" << attributeTypeText
<< " attributeValueTextLabel=" << attributeValueTextLabel;
m_ui->formLayout->addRow(new QLabel(attributeTypeText), attributeValueTextLabel);
}
if (m_eventType.isEmpty())
m_eventType = QStringLiteral("%1 (%2)").arg(tr("<unknown>"), ProjectDataManagement::toString(static_cast<ProjectDataManagement::EventType::type>(event->typeId())));
m_ui->lblEventType->setText(QStringLiteral("<b>%1</b>").arg(m_eventType));
m_ui->pteDetailDataTest->setPlainText(QString::fromUtf8(QJsonDocument(event->detailData()).toJson()));
QString pos(QStringLiteral("x: %1 - y: %2 - z: %3").arg(m_event->position().x()).arg(m_event->position().y()).arg(m_event->position().z()));
m_ui->lblPos->setText(pos);
return true;
}
void ShowDetailsDialog::updateMarker()
{
DEBUG_LOG(m_logger) << "ShowDetailsDialog::updateMarker";
}
//------------------------------------------------------------------------------------------------------------------------
} // namespace Timeline
@@ -0,0 +1,96 @@
/**
* @RDE_HEADER@
* FileManagement - Rename Folder Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Iuliana Ojog, SLSE, 2017-05-22
*/
#pragma once
#include "tln_view_gui_plugin.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
#include <LtsTimelineClassic/Event.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
#include <Plugins/FilterManagement/flt_participant_plugin_interface.h>
#include <Plugins/FilterManagement/flt_aes_plugin_interface.h>
#include <Plugins/FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <MarkerManagement/mrk_dtc.h>
namespace Ui
{
class ShowDetailsDialog;
}
namespace AudioVideoManagement
{
class StreamNameMapping;
}
namespace Timeline
{
class DetailData;
class ShowDetailsDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit ShowDetailsDialog(QWidget *parent = nullptr);
~ShowDetailsDialog();
bool init(DetailData *detailData, const LtsTimelineClassic::Event *event,
const QPointer<LtsSession::LtsSession> &session,
const QPointer<LtsSession::LtsSession> &replayAppSession,
const std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> &avmIfc,
const std::shared_ptr<MarkerManagement::ForeignGuiInterface> &mrkIfc,
const std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> &fltParticipantIfc,
const std::shared_ptr<FilterManagement::ForeignAesGuiInterface> &fltAesIfc,
const std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> &fltBuildingComponentIfc,
bool isTimelineApp,
QWidget *pseudoParent,
AudioVideoManagement::StreamNameMapping *streamNameMapping);
void retranslate() override;
private slots:
void slotStartReplay();
void slotStartPlayback();
void slotShowMarker();
void slotFocusOnMap();
void updateMarker();
private:
bool hasNameAndEndTime() const;
bool getStreamNameAndEndTime(QString &streamName, qint64 &endTime, bool silent = false) const;
void setButtonDisabled(QPushButton *button, QToolButton *warningButton, const QString &reasonMessage, bool hide = false);
bool isMarkerAudioVideo() const;
static bool isAvsCapable(const LtsTimelineClassic::Event *event);
static bool isHitEventType(int eventTypeId);
static bool isAvEventType(int eventTypeId);
static bool isAudioEventType(int eventTypeId);
QString m_eventType;
Ui::ShowDetailsDialog *m_ui{nullptr};
const LtsTimelineClassic::Event *m_event{nullptr};
// everything from replay app:
QPointer<LtsSession::LtsSession> m_replayAppSession;
// end
std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> m_avmIfc;
std::shared_ptr<MarkerManagement::ForeignGuiInterface> m_mrkIfc;
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> m_fltParticipantIfc;
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> m_fltAesIfc;
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> m_fltBuildingComponentIfc;
AudioVideoManagement::StreamNameMapping *m_streamNameMapping{nullptr};
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
LtsSession::LtsSessionDataDds *m_session{nullptr};
NGL::INGLLogger &m_logger;
};
} // namespace Timeline
@@ -0,0 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ShowDetailsDialog</class>
<widget class="QWidget" name="ShowDetailsDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>366</width>
<height>451</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Exercise Management</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblEventType">
<property name="text">
<string notr="true">&lt;Event Type&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lblEquipNo">
<property name="toolTip">
<string>Folder Name</string>
</property>
<property name="text">
<string>Equip. No.:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="lblTime">
<property name="text">
<string>Event Time:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblValueEquipNo">
<property name="text">
<string notr="true">...</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblValueEventTime">
<property name="text">
<string notr="true">...</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="tbPlayback">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Styles/Dark/resources/rme_dark_style.qrc">
<normaloff>:/style/warning.svg</normaloff>:/style/warning.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPlayback">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Plays the audio and/or video clip assoociated to the event in the replay application, in a audio/video dialog.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Start Playback</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QToolButton" name="tbFocus">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Styles/Dark/resources/rme_dark_style.qrc">
<normaloff>:/style/warning.svg</normaloff>:/style/warning.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbFocus">
<property name="text">
<string>Focus on map</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QToolButton" name="tbReplay">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Styles/Dark/resources/rme_dark_style.qrc">
<normaloff>:/style/warning.svg</normaloff>:/style/warning.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbStartReplay">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sets the replay application back to the time the event occured and focuses the event on the map. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Start Replay</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QToolButton" name="tbMarker">
<property name="minimumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Styles/Dark/resources/rme_dark_style.qrc">
<normaloff>:/style/warning.svg</normaloff>:/style/warning.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbShowMarker">
<property name="text">
<string>Show Marker</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QWidget" name="debugWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="lblDebug">
<property name="text">
<string notr="true">&lt;font color='#aebac6'&gt;Debug:&lt;/font&gt;</string>
</property>
</widget>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">Position:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblPos">
<property name="text">
<string notr="true">-</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPlainTextEdit" name="pteDetailDataTest">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../../../Styles/Dark/resources/rme_dark_style.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,167 @@
/**
* @RDE_HEADER@
* GUI Plugin for ExerciseManangement
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#include "EventFilter/FilterWindow.h"
#include "tln_view_gui_plugin.h"
#include "tln_dialog.h"
#include "logger.h"
#include <stdlib.h> // for abort
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsSession/lts_session_data.h>
#include <QTimer>
namespace Timeline
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
{
return std::make_shared<InterfaceImpl>(session);
}
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, getLogger("View"))
{}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getForeignDataInterface<TimeLineManagement::DataInterface>(m_eemForeign))
return false;
if (!m_session->getDtc<MarkerManagement::DataInterface>(m_mrkDtc))
return false;
if (!m_session->getDtc<ExerciseManagement::DataInterface>(m_exmDtc))
return false;
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
return false;
if (!m_session->getForeignGuiInterface<FileManagement::ForeignGuiInterface>(m_flmIfc))
return false;
if (!m_session->getForeignGuiInterface<ParticipantManagement::ForeignParticipantInfoGuiInterface>(m_ppmInfoIfc))
return false;
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
return false;
// if (!m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc))
// return false;
if (!m_session->getForeignDataInterface_NEW(m_avmDataIfc))
WARNING_LOG(m_logger) << "no avm data ifc, no stream name mapping";
if (!m_session->getForeignDataInterface<SituationReplayManagement::DataInterface>(m_srmDataIfc)) // to start the replay situation app
WARNING_LOG(m_logger) << "no srm data ifc, no situation replay app";
if (!m_session->getDtc<SituationReplayManagement::DataInterface>(m_coreDtc))
WARNING_LOG(m_logger) << "no core dtc, no situation replay app";
if (!m_session->getForeignGuiInterface<AudioVideoManagement::ForeignMenuEditGuiInterface>(m_avmIfc))
WARNING_LOG(m_logger) << "Timeline Gui Plugin: no avm ifc";
if (!m_session->getForeignGuiInterface<MarkerManagement::ForeignGuiInterface>(m_mrkIfc))
WARNING_LOG(m_logger) << "Timeline Gui Plugin: no mrk ifc";
if (!m_session->getForeignGuiInterface<FilterManagement::ForeignFltParticipantGuiInterface>(m_fltParticipantIfc))
WARNING_LOG(m_logger) << "Timeline Gui Plugin: no flt Part ifc";
if (!m_session->getForeignGuiInterface<FilterManagement::ForeignAesGuiInterface>(m_fltAesIfc))
WARNING_LOG(m_logger) << "Timeline Gui Plugin: no flt Aes ifc";
if (!m_session->getForeignGuiInterface<FilterManagement::ForeignFltBuildingComponentGuiInterface>(m_fltBuildingComponentIfc))
WARNING_LOG(m_logger) << "Timeline Gui Plugin: no flt BuildingComponent ifc";
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_eemForeign = nullptr;
m_mrkDtc = nullptr;
m_exmDtc = nullptr;
m_ppmDtc = nullptr;
m_flmIfc = nullptr;
m_ppmInfoIfc = nullptr;
m_srmDataIfc = nullptr;
m_coreDtc = nullptr;
m_avmIfc = nullptr;
m_mrkIfc = nullptr;
m_avmDataIfc = nullptr;
m_pdmDtc = nullptr;
//m_ismDtc = nullptr;
m_fltParticipantIfc = nullptr;
m_fltAesIfc = nullptr;
m_fltBuildingComponentIfc = nullptr;
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Timeline");
plData.identifier = QLatin1String("tlnView");
plData.exercisePart = LtsWidgets::ExercisePart::eExecution;
plData.menuIcon = QIcon(QLatin1String("icon:timeline.svg"));
if (getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN))
{
plData.layoutArea = LtsWidgets::LayoutArea::eMainTab;
plData.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndDiscardAction;
plData.createWidgetFunc = [this](QWidget *)
{
return onShowTimelineViewDialog(true);
};
}
else
{
plData.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
plData.createWidgetFunc = [this](QWidget *)
{
return onShowTimelineViewDialog(false);
};
}
ret.append(plData);
}
return ret;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Timeline"); // TODO: oder Events?
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowTimelineViewDialog(bool isMain)
{
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(ismDtc))
{
new LtsWidgets::LtsMessageDialogNew(m_session->baseLayoutWidget(), tr("The Infrastructure Management is not available!"), tr("Cannot open Timeline"));
return nullptr;
}
LtsWidgets::ButtonCombination buttonCombo = /*isMain ? LtsWidgets::ButtonCombination::SeamlessDialog : */LtsWidgets::ButtonCombination::CloseOnly;
AudioVideoManagement::StreamNameMapping *streamNameMapping = nullptr;
if (m_avmDataIfc)
streamNameMapping = m_avmDataIfc->streamNameMapping();
return LtsWidgets::initDialog(new TimelineViewDialog(menuGroup(), buttonCombo, isMain, nullptr),
m_eemForeign, m_mrkDtc, m_exmDtc, m_flmIfc, m_ppmDtc,
m_ppmInfoIfc, m_avmIfc, m_mrkIfc,m_srmDataIfc, m_coreDtc,
m_pdmDtc, ismDtc, m_fltParticipantIfc, m_fltAesIfc, m_fltBuildingComponentIfc, m_session,
streamNameMapping);
}
} // namespace Timeline
@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* Gui Plugin for ExerciseManangement
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-02-10
*/
#pragma once
#include <Plugins/Timeline/tln_view_gui_plugin_interface.h>
#include <Plugins/Timeline/tln_data_plugin_interface.h> // forward decls for dtc/ftc
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Common/ProjectConfig/project_defines.h>
#include <Plugins/MarkerManagement/mrk_data_plugin_interface.h>
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_data_plugin_interface.h>
#include <Plugins/ParticipantManagement/ppm_participant_info_gui_plugin_interface.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
#include <Plugins/SituationReplayManagement/srm_data_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_data_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_data_plugin_interface.h>
#include <Plugins/InfrastructureManagement/ism_data_plugin_interface.h>
#include <Plugins/FilterManagement/flt_participant_plugin_interface.h>
#include <Plugins/FilterManagement/flt_aes_plugin_interface.h>
#include <Plugins/FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <Plugins/AreaEffectSimulation/aes_data_plugin_interface.h>
namespace Timeline
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:tln.view")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("TimelineGuiPlugin"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay;
}
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
QVector<LtsWidgets::PluginData> getActions() override;
QString menuGroup() const override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise || partition == IDL::PartitionType::Replay; }
bool fetchInterfaces() override;
void clearInterfaces() override;
private:
LtsWidgets::LtsDialog *onShowTimelineViewDialog(bool isMain);
// dtc's
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
std::shared_ptr<ExerciseManagement::Interface::Dtc> m_exmDtc;
std::shared_ptr<MarkerManagement::Interface::Dtc> m_mrkDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
//std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<AudioVideoManagement::DataInterface> m_avmDataIfc;
// foreign gui's
std::shared_ptr<TimeLineManagement::ForeignDataInterface> m_eemForeign;
std::shared_ptr<FileManagement::ForeignGuiInterface> m_flmIfc;
std::shared_ptr<ParticipantManagement::ForeignParticipantInfoGuiInterface> m_ppmInfoIfc;
std::shared_ptr<AudioVideoManagement::ForeignMenuEditGuiInterface> m_avmIfc;
std::shared_ptr<MarkerManagement::ForeignGuiInterface> m_mrkIfc;
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_srmDataIfc;
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> m_fltParticipantIfc;
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> m_fltAesIfc;
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> m_fltBuildingComponentIfc;
};
} // namespace Timeline