Qt6 migration: build fixes for Groups 10/11 + missing plugin modules

Continue the Qt5->Qt6 migration so the LTS Projects (Group 10) and LTS
Application (Group 11) compile, link, and ctest runs.

Qt5->Qt6 API migration (resolves removed/deprecated Qt6 APIs) across Lts
Tools and Apps, e.g. QAction->QActionGroup, QAbstractSocket::errorOccurred,
Qt::SkipEmptyParts, QRandomGenerator, QInputDevice::DeviceType,
QRegularExpression, QLatin1Literal->QStringLiteral, and the SampleModel
m_description->description rename.

CMake build fixes:
- CMakeLists.txt: add Lts/Common to CMAKE_AUTOMOC_INCLUDE_DIRECTORIES so moc
  resolves Q_INTERFACES(ProjectPlugin::Plugin) for the migrated Projects.
- cmake/NGLMacros.cmake: NGL_COMPILE_RESOURCES - replace return() (which aborts
  the calling CMakeLists.txt) with an if/else/endif.
- OpApp/THP: guard the ArcGISQt5Hack link with $<IF:$<TARGET_EXISTS:...>> (the
  target is absent when the SituationMap viewer is stubbed); matches
  CentralEffectSimulation.
- LtsSession: prefer the in-tree svcCoreClient target over a FIND_LIBRARY in
  /usr/local/lib.
- CnrProxyTester/DotMatrixDisplay: set AUTOUIC_SEARCH_PATHS for the ui/ subdir.
- TopicDiscover: link NGLLogger.

Export-header visibility stubs (expand the empty *_EXPORT macro to
__attribute__((visibility("default"))); the build sets -fvisibility=hidden and
these stubs shadow the CMake-generated header):
- include/LtsProjectionWorkstation/ltsprojectionworkstation_export.h (fixes
  undefined reference to LtsProjectionWorkstation::getInstance /
  setProjectionOperatorPreset)
- include/LtsSession/ltssession_export.h, include/LtsUtilsGuiDds/ltsutilsguidds_export.h (same pattern)

New plugin modules (copied from legacy) so LtsAppDescriptorChecker finds the
gui:3 IIDs referenced by the Projects' apps.json:
- TakeHomePackageManager/OldTHP              gui:3:tpm.man, gui:3:tpm.usb
- PresentationManagement/.../ScriptEditor   gui:3:pre.script.edit
- Timeline/Gui/EventTimelineClassic         gui:3:tln.view

new_docker_build.sh: correct group targets to the actual CMake target names,
guard LtsReportDisplay on BUILD_CODIFICATION, and skip modules unavailable in
the prebuilt image (AsmWorkstationCertifier/botan2, TrajectoryWidget,
VideoImport, thp_viewer/VLC).
This commit is contained in:
Berrux Kana committed 2026-10-05 10:49:34 +02:00
1 parent 040bdd558f
commit 3f7beb3c7e
154 files changed
+24958 -167

No files matched your search

@@ -676,6 +676,16 @@ IF(BUILD_RTI)
# the AutogenInfo MOC_INCLUDES.
SET(CMAKE_AUTOMOC_INCLUDE_DIRECTORIES ${CMAKE_AUTOMOC_INCLUDE_DIRECTORIES} ${CMAKE_SOURCE_DIR}/include/Viewer/SituationMap/ElevationMap)
# The migrated Projects declare Q_INTERFACES(ProjectPlugin::Plugin) and include
# <PluginInterfaces/project_plugin_interface.h>. That include dir
# (Lts/Common/PluginInterfaces/..) is only a target-level PUBLIC include of
# ProjectPluginInterface and is NOT part of the global include_directories that
# reach the AutogenInfo MOC_INCLUDES, so moc fails with "Undefined interface".
# Add the Lts/Common root explicitly (mirrors the Lts append above) so moc can
# resolve the interface. Must be set at root scope - subdir appends do not
# reach the AutogenInfo MOC_INCLUDES.
SET(CMAKE_AUTOMOC_INCLUDE_DIRECTORIES ${CMAKE_AUTOMOC_INCLUDE_DIRECTORIES} ${CMAKE_SOURCE_DIR}/Lts/Common)
# IMPORTED Targets für RTI Libraries erstellen
# Debug und Release Varianten korrekt konfigurieren
FOREACH(lib nddsc nddscore nddscpp nddscpp2 nddsctesthelpers)
@@ -56,13 +56,13 @@ bool DatabasesModel::initModel(const QSqlDatabase &db)
}
Exercise exercise;
exercise.m_id = q.value(0).toInt();
exercise.id = q.value(0).toInt();
exercise.name = q.value(1).toString();
exercise.startTime = q.value(2).toLongLong();
exercise.endTime = q.value(3).toLongLong();
exercise.dbName = QStringLiteral("lts_db_") + QString::number(exercise.m_id);
exercise.dbName = QStringLiteral("lts_db_") + QString::number(exercise.id);
exercise.stateId = q.value(4).toInt();
TRACE_LOG(m_logger) << "Found exercise with id=" << exercise.m_id << ", name=" << exercise.name << ", startTime="
TRACE_LOG(m_logger) << "Found exercise with id=" << exercise.id << ", name=" << exercise.name << ", startTime="
<< exercise.startTime << ", endTime=" << exercise.endTime << ", dbName=" << exercise.dbName
<< ", stateId=" << exercise.stateId;
if (exercise.stateId != 6)
@@ -86,7 +86,7 @@ QVariant DatabasesModel::data(const QModelIndex &index, int role) const
switch (index.column())
{
case Id:
return e.m_id;
return e.id;
case Name:
return QString(e.name);
case StartTime:
@@ -176,7 +176,7 @@ void DatabasesModel::onDataChanged(const Exercise &data, DataModification modTyp
row = m_vecData.indexOf(data);
if (row != -1)
if (removeRow(row))
emit dataChanged(index(std::min(row, m_vecData.size()-1), Columns::Id), index(m_vecData.size()-1, Columns::EndTime));
emit dataChanged(index(std::min<qsizetype>(row, m_vecData.size() - 1), Columns::Id), index(m_vecData.size()-1, Columns::EndTime));
return;
} // switch
}
@@ -32,8 +32,8 @@ struct Exercise
qint64 endTime = 0;
qint32 stateId = 0;
bool operator == (const Exercise &other) { return id == other.m_id; }
bool operator != (const Exercise &other) { return id != other.m_id; }
bool operator == (const Exercise &other) const { return id == other.id; }
bool operator != (const Exercise &other) const { return id != other.id; }
};
@@ -38,7 +38,7 @@ ENDIF()
if(QT6_GUI_AVAILABLE)
NGL_TARGET_LINK_LIBRARIES(OperatorApplication
ArcGISQt5Hack # make sure it's loaded *before* libEsriCommonQt.so
$<IF:$<TARGET_EXISTS:ArcGISQt5Hack>,ArcGISQt5Hack,> # make sure it's loaded *before* libEsriCommonQt.so (guard: only linked when the SituationMap viewer is built, matching CentralEffectSimulation)
${Qt6_Widgets}
Qt6::Network
nglapplication
@@ -36,6 +36,9 @@
#include <QInputDialog>
#include <QNetworkProxy>
#include <QtCore/QTimer>
#include <QRandomGenerator>
#include <QInputDevice>
#include <algorithm>
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
@@ -157,7 +160,7 @@ public:
bool isForcedTabletMode() const { return isSet(QStringLiteral("x")); }
bool isForcedWSMode() const { return isSet(QStringLiteral("w")); }
QString langIsoCode() const { return value(QStringLiteral("l")); }
QStringList ignoredPlugins() const { return value(QStringLiteral("ignore-plugins")).split(QLatin1Char(','), QString::SkipEmptyParts); }
QStringList ignoredPlugins() const { return value(QStringLiteral("ignore-plugins")).split(QLatin1Char(','), Qt::SkipEmptyParts); }
bool checkRequirements() const { return isSet(QStringLiteral("check-requirements")); }
QString skipScreen() const { return value(QStringLiteral("k")); }
QString qosFile() const { return value(QStringLiteral("q")).size() > 0 ? value(QStringLiteral("q")) : QString(); }
@@ -172,7 +175,7 @@ public:
// ------------------------------------------------------------------
int main(int argc, char **argv) // TODO: json strings to lower
{
qsrand(time(nullptr));
QRandomGenerator::global()->seed(time(nullptr));
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
@@ -380,7 +383,7 @@ int main(int argc, char **argv) // TODO: json strings to lower
if (!skipScreen.isEmpty())
{
QStringList skipScreenList(skipScreen.split(QLatin1Char('.'), QString::SkipEmptyParts));
QStringList skipScreenList(skipScreen.split(QLatin1Char('.'), Qt::SkipEmptyParts));
int skipScreenListCount = skipScreenList.count();
@@ -431,7 +434,8 @@ int main(int argc, char **argv) // TODO: json strings to lower
sessionData->setAsTablet();
else if (options.isForcedWSMode())
sessionData->setAsTablet(false);
else if (!QTouchDevice::devices().isEmpty())
else if (std::any_of(QInputDevice::devices().cbegin(), QInputDevice::devices().cend(),
[](const QInputDevice *dev) { return dev && dev->type() == QInputDevice::DeviceType::TouchScreen; }))
sessionData->setAsTablet();
LtsWidgets::LtsComboBox::setUseSelectionDialog(sessionData->isTablet());
@@ -72,7 +72,7 @@ NGL_TARGET_COMPILE_DEFINITIONS(thp_viewer
${VLC_DEFINES}
)
NGL_TARGET_LINK_LIBRARIES(thp_viewer
ArcGISQt5Hack # make sure it's loaded *before* libEsriCommonQt.so
$<IF:$<TARGET_EXISTS:ArcGISQt5Hack>,ArcGISQt5Hack,> # make sure it's loaded *before* libEsriCommonQt.so (guard: only linked when the SituationMap viewer is built, matching CentralEffectSimulation)
${VLC_LIBRARIES}
Qt6::Core
@@ -258,8 +258,9 @@ void ThpViewer::onLinkClicked(const QUrl &url)
const QString path = url.path();
const QString lpath = path.toLower();
const QStringRef ext = lpath.splitRef(QLatin1Char('.')).last();
const QStringRef ext2Chars = ext.left(2);
// Qt6: QStringRef/QString::splitRef were removed; QStringView::split provides zero-copy access
const QStringView ext = QStringView{lpath}.split(QLatin1Char('.')).last();
const QStringView ext2Chars = ext.left(2);
ui->audioWidget->stop();
ui->videoWidget->stop();
@@ -370,7 +371,7 @@ void ThpViewer::onLinkClicked(const QUrl &url)
ui->backButton->setEnabled(true);
}
else if (ext == QLatin1String("html") &&
lpath.midRef(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
QStringView{lpath}.mid(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
{
// Kombiniert
QHash<QString, QUrl> urls = getCombinedPlaybackDetails(url);
@@ -32,10 +32,11 @@ public:
bool needsToBeOpenedExternally(const QUrl &url)
{
const QString lpath = url.toString();
const QStringRef ext = lpath.splitRef(QLatin1Char('.')).last();
const QStringRef ext2Chars = ext.left(2);
// Qt6: QStringRef/QString::splitRef and QString::midRef were removed; use QStringView
const QStringView ext = QStringView{lpath}.split(QLatin1Char('.')).last();
const QStringView ext2Chars = ext.left(2);
if (ext == QLatin1String("html") &&
lpath.midRef(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
QStringView{lpath}.mid(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
return true;
// PDF
if (ext == QLatin1String("pdf"))
@@ -140,7 +140,7 @@ void LtsAppManager::slotStartDebugServer()
QString commandStr(
QString::fromUtf8(client->readLine(100)).remove(QStringLiteral("\r")).remove(QStringLiteral("\n")));
QStringList argList = commandStr.split(QLatin1Char(' '), QString::SplitBehavior::SkipEmptyParts);
QStringList argList = commandStr.split(QLatin1Char(' '), Qt::SkipEmptyParts);
if (argList.count() < 1)
return;
@@ -16,7 +16,7 @@ LogoutInformationDialog::LogoutInformationDialog(int timeOut, AssetManagement::S
{
m_ui->setupUi(this);
QString actionText;
switch (shutdownAction.underlying())
switch (shutdownAction)
{
case AssetManagement::ShutdownAction::NoAction:
case AssetManagement::ShutdownAction::Logout:
@@ -154,13 +154,13 @@ void LtsLogin::slotLogoutShutdown(const AssetManagement::WorkstationLogout& data
if (!m_asmDtc->isAssetEnabled())
m_asmDtc->enableAsset();
for (int id : data.ids())
for (int id : data.ids)
{
if (QString::fromStdString(m_asmDtc->asset(id).name())
if (QString::fromStdString(m_asmDtc->asset(id).name)
.compare(LtsSession::LtsSession::workstationName(), Qt::CaseInsensitive) == 0)
{
m_isInLogoutDialog = true;
showLogoutInformationDialog(data.shutdownAction());
showLogoutInformationDialog(data.shutdownAction);
break;
}
}
@@ -250,7 +250,7 @@ void LtsLogin::logoff()
m_loginApp = nullptr;
QString cmd;
switch (m_shutdownAction.underlying())
switch (m_shutdownAction)
{
case AssetManagement::ShutdownAction::Shutdown:
#ifdef LTS_DEBUG
@@ -21,7 +21,7 @@ namespace LtsPluginManager
LtsPluginManager::LtsPluginManager()
: m_sessionManager(new LtsSession::LtsSessionManager)
.getNGLLogger("LtsPluginManager"))
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("LtsPluginManager"))
, m_markedForDeletion(false)
{
connect(m_sessionManager, &LtsSession::LtsSessionManager::signalQuit, this, &LtsPluginManager::signalQuit);
@@ -79,7 +79,14 @@ NGL_TARGET_INCLUDE_DIRECTORIES(LtsSession
)
# Optional external dependencies
FIND_LIBRARY(SVCCORECLIENT_LIBRARY NAMES svcCoreClient PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH)
# Prefer the in-tree target: a fresh build tree does not have svcCoreClient
# installed in /usr/local/lib, and the Core::Interface::Ftc::create symbols
# referenced at link time live in libsvcCoreClient.so
IF(TARGET svcCoreClient)
SET(SVCCORECLIENT_LIBRARY svcCoreClient)
ELSE()
FIND_LIBRARY(SVCCORECLIENT_LIBRARY NAMES svcCoreClient PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH)
ENDIF()
FIND_LIBRARY(OHPONLINEHELP_LIBRARY NAMES OhpOnlineHelpViewerGuiPluginInterface PATHS /usr/local/lib /usr/lib NO_DEFAULT_PATH)
SET(LtsSession_PRIVATE_LIBS
@@ -0,0 +1,81 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLSE, 2016-03-17
#
SET(pre_script_gui_SRCS
pre_script_gui_plugin.cpp
pre_script_gui_plugin.h
pre_script_dialog.h
pre_script_dialog.cpp
pre_storyboard.cpp
pre_storyboard.h
pre_journal_types.h
pre_journal_types.cpp
pre_journal_dialog.h
pre_journal_dialog.cpp
pre_screen.h
pre_screen.cpp
filter_topic.h
filter_topic.cpp
)
QT5_WRAP_UI(pre_script_gui_SRCS
pre_storyboard.ui
pre_script_dialog.ui
pre_journal_dialog.ui
)
NGL_ADD_LIBRARY(WS PreScriptGuiPlugin MODULE ${pre_script_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(PreScriptGuiPlugin
NGLLogger
LtsSession
PreDataPluginInterface
PrePresentationGuiPluginInterface
PreGuiCommon
PreGuiScriptCommon
svcPreCommon
Qt5::Widgets
LtsWidgets
LtsDataView
LtsUtilsGuiDds
LtsSessionData
ProjectConfig
LtsScriptTimeline
LtsMultiMonitor
LtsAppManager # start replay app
LtsSessionData # set partition preset for replay app
TopicEditorInterface
TopicEditorBase
LtsPluginManager # manipulate apps
# for Topics
EvlGuiPluginInterface
SrmDataPluginInterface # start playback
LtsPrinting
SituationMapGuiPluginInterface
# Script Player Controls:
SrpGuiPluginInterface
# file topic
FlmExplorerGuiPluginInterface
# building topic
BdvViewerGuiPluginInterface
# report topic
RepGuiPluginInterface
LtsReportDisplay
MrkCommon # for ApplyFilter @ SitmapFilter
LtsUtilsSettings
ngltools # DTG
situationMap # set time string on map
ExportBdvTopicSetting
)
NGL_INSTALL_TARGET(PreScriptGuiPlugin)
@@ -0,0 +1,17 @@
#include "filter_topic.h"
namespace PresentationManagement
{
FilterTopic::~FilterTopic()
{
//delete filterTopic;
}
FilterTopic::FilterTopic(const FilterTopic &other)
{
mainTopicId = other.mainTopicId;
filterTopic = other.filterTopic;//new LtsScriptTimeline::TrackEntry(other.filterTopic);
}
} // namespace PresentationManagement
@@ -0,0 +1,28 @@
#pragma once
#include <LtsScriptTimeline/track_entry.h>
namespace PresentationManagement
{
class FilterTopic
{
public:
FilterTopic()
: mainTopicId(-1)
, filterTopic(nullptr)
{}
FilterTopic(int _mainTopicId, LtsScriptTimeline::TrackEntry *_filterTopic)
: mainTopicId(_mainTopicId)
, filterTopic(_filterTopic)
{}
FilterTopic(const FilterTopic &other);
~FilterTopic();
int mainTopicId;
LtsScriptTimeline::TrackEntry *filterTopic;
};
} // namespace PresentationManagement
@@ -0,0 +1,81 @@
#include "pre_journal_dialog.h"
#include "ui_pre_journal_dialog.h"
#include <LtsPrinting/printing.h>
namespace PresentationManagement
{
JournalDialog::JournalDialog(LtsSession::LtsSession *session, QWidget *parent)
: LtsWidgets::LtsDialog(parent)
, m_ui(new Ui::JournalDialog)
, m_journal(nullptr)
, m_session(session)
{
m_ui->setupUi(this);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &JournalDialog::slotPrint);
}
JournalDialog::~JournalDialog()
{
delete m_ui;
}
void JournalDialog::slotPrint()
{
LtsPrinting::printView(m_session, this, m_ui->listWidget);
}
bool JournalDialog::init(MainJournal *journal)
{
m_journal = journal;
return true;
}
void JournalDialog::retranslate()
{
translate(m_ui, tr("Script Journal"));
fillJournal();
}
void JournalDialog::fillJournal()
{
m_ui->listWidget->clear();
if (!m_journal)
return;
m_ui->listWidget->addItem(m_journal->getText());
for (const Journal *j : qAsConst(m_journal->journalList))
m_ui->listWidget->addItem(j->getText());
}
} // namespace PresentationManagement
@@ -0,0 +1,37 @@
#pragma once
#include "pre_journal_types.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsSession/lts_session.h>
namespace Ui
{
class JournalDialog;
}
namespace PresentationManagement
{
class JournalDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit JournalDialog(LtsSession::LtsSession *session, QWidget *parent);
~JournalDialog();
bool init(MainJournal *journal);
void retranslate() override final;
private slots:
void slotPrint();
private:
void fillJournal();
Ui::JournalDialog *m_ui;
MainJournal *m_journal;
LtsSession::LtsSession *m_session;
};
} // namespace PresentationManagement
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JournalDialog</class>
<widget class="QWidget" name="JournalDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>707</width>
<height>464</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Script Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QListWidget" name="listWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,90 @@
#include "pre_journal_types.h"
#include <QDateTime>
#include <ngltools/ngltools_dtg.h>
namespace PresentationManagement
{
QString ttt(qint64 msecsSinceEpoch)
{
return DTG::toStringMSec(msecsSinceEpoch);
}
QString pttt(int presentationTime)
{
return QTime::fromMSecsSinceStartOfDay(presentationTime * 1000).toString();
}
Journal::Journal()
: m_started(QDateTime::currentMSecsSinceEpoch())
{}
QString Journal::started() const
{
return ttt(m_started);
}
MainJournal::~MainJournal()
{
qDeleteAll(journalList);
}
QString MainJournal::getText() const
{
return Journal::tr("Script Player started on %1").arg(ttt(started));
}
NextTopicJournal::NextTopicJournal(int presentationTime, bool isFilter, const QString &topicTypeName, int length)
: Journal()
, m_isFilter(isFilter)
, m_topicTypeName(topicTypeName)
, m_length(length)
, m_presentationTime(presentationTime)
{}
QString NextTopicJournal::getText() const
{
return Journal::tr("%1 '%2' started at %3 for %4 seconds on %5").arg(m_isFilter ? tr("FilterTopic") : tr("Topic"),
m_topicTypeName, pttt(m_presentationTime)).arg(m_length).arg(started());
}
QString PlayJournal::getText() const
{
return Journal::tr("Started / resumed script at %1 on %2").arg(pttt(m_presentationTime), started());
}
QString PauseJournal::getText() const
{
return Journal::tr("Paused script at %1 on %2").arg(pttt(m_presentationTime), started());
}
QString EndedJournal::getText() const
{
return Journal::tr("Script ended at %1 on %2").arg(pttt(m_presentationTime), started());
}
QString StopJournal::getText() const
{
return Journal::tr("Script stopped at %1 on %2").arg(pttt(m_presentationTime), started());
}
QString SetTimeJournal::getText() const
{
return Journal::tr("Position time set to %1 on %2").arg(pttt(m_toPresentationTime), started());
}
} // namespace PresentationManagement
@@ -0,0 +1,131 @@
#pragma once
#include <QCoreApplication>
#include <QVector>
namespace PresentationManagement
{
class Journal
{
Q_DECLARE_TR_FUNCTIONS(Journal)
public:
Journal();
virtual ~Journal() = default;
QString started() const;
virtual QString getText() const = 0;
qint64 m_started;
};
class MainJournal
{
public:
~MainJournal();
QString getText() const;
qint64 started = 0;
QVector<Journal *> journalList;
};
class NextTopicJournal : public Journal
{
public:
NextTopicJournal(int presentationTime, bool isFilter, /*int topicId, */const QString &topicTypeName, int length);
QString getText() const override final;
bool m_isFilter;
//int m_topicId;
QString m_topicTypeName;
int m_length;
int m_presentationTime;
};
class PlayJournal : public Journal
{
public:
explicit PlayJournal(int presentationTime)
: Journal()
, m_presentationTime(presentationTime)
{}
QString getText() const override final;
int m_presentationTime;
};
class PauseJournal : public Journal
{
public:
explicit PauseJournal(int presentationTime)
: Journal()
, m_presentationTime(presentationTime)
{}
QString getText() const override final;
int m_presentationTime;
};
class EndedJournal : public Journal
{
public:
explicit EndedJournal(int presentationTime)
: Journal()
, m_presentationTime(presentationTime)
{}
QString getText() const override final;
int m_presentationTime;
};
class StopJournal : public Journal
{
public:
explicit StopJournal(int presentationTime)
: Journal()
, m_presentationTime(presentationTime)
{}
QString getText() const override final;
int m_presentationTime;
};
class SetTimeJournal : public Journal
{
public:
explicit SetTimeJournal(int toPresentationTime)
: Journal()
, m_toPresentationTime(toPresentationTime)
{}
QString getText() const override final;
int m_toPresentationTime;
};
} // namespace PresentationManagement
@@ -0,0 +1,35 @@
#include "pre_screen.h"
#include <QStyleOption>
#include <QPainter>
Screen::Screen()
: QWidget()
{
setBgColor(QColor("black"));
setContentsMargins(0, 0, 0, 0);
}
Screen::~Screen()
{
const auto p1Widgets = findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly);
for (QWidget *w : p1Widgets)
w->setParent(nullptr);
}
void Screen::setBgColor(const QColor &color)
{
setStyleSheet(QStringLiteral("Screen { background: %1; background-color: %1; }").arg(color.name()));
}
void Screen::paintEvent(QPaintEvent *) // needed to apply the style sheet...........
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
@@ -0,0 +1,18 @@
#pragma once
#include <QWidget>
class Screen : public QWidget
{
Q_OBJECT
public:
Screen();
virtual ~Screen();
void setBgColor(const QColor &color);
private:
void paintEvent(QPaintEvent *) override;
};
@@ -0,0 +1,402 @@
/**
* @RDE_HEADER@
* PresentationManagement - Script Editor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-17
*/
#pragma once
#include "pre_screen.h"
#include "pre_journal_types.h"
#include "pre_storyboard.h" // QPointer
#include "filter_topic.h"
#include <DDS/Core/core_dtc.h>
#include <PluginInterfaces/topic_replay_interface.h>
#include <LtsPluginManager/lts_plugin_manager.h>
#include <LtsWidgets/lts_dialog.h>
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsScriptTimeline/track_entry_painters.h> // TET
#include <LtsScriptTimeline/temporary_pointer_vector.h>
#include <LtsSession/lts_session.h>
#include <Plugins/PresentationManagement/Gui/Common/topic_assignment.h>
#include <Services/PresentationManagement/pre_dtc.h>
#include <Services/PresentationManagement/pre_ftc.h>
#include <Services/PresentationManagement/logger.h>
#include <Services/SituationReplayManagement/srm_ftc.h>
#include <Services/OperatorManagement/opm_dtc.h>
// foreign gui plugins for the topic playback
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <QString>
#include <QStringList>
#include <QMap>
#include <functional>
#include <memory>
namespace TakeHomePackageGenerator
{
class ExportProcess;
}
namespace Ui
{
class ScriptDialog;
}
namespace LtsScriptTimeline
{
class TrackEntry;
}
namespace SituationReplayManagement
{
class ForeignDataInterface;
}
namespace LtsPluginManager
{
class LtsPluginManager;
}
namespace Ui
{
class ScriptPlayerDialog;
}
namespace LtsScriptTimeline
{
class TrackEntry;
}
namespace LtsUtils
{
class Promise;
}
namespace PresentationManagement
{
class TopicDesigner;
namespace Interface
{
class Ftc;
}
class ScriptDialog : public LtsWidgets::LtsDialog
, protected PresentationManagement::Interface::Ftc::PresentationModifyReplyHandler
, protected PresentationManagement::Interface::Ftc::GetPresentationContentReplyHandler
, protected SituationReplayManagement::Interface::Ftc::ReplayPartitionCreateReplyHandler
, protected SituationReplayManagement::Interface::Ftc::ReplayPartitionRemoveReplyHandler
{
public:
Q_OBJECT
public:
explicit ScriptDialog(const QString &menuGroup, QWidget *parent = nullptr);
~ScriptDialog();
LtsSession::LtsSession *eVSession() { return m_essentialsViewSession; }
bool init(LtsSession::LtsSession *session,
const int scriptId,
const std::shared_ptr<Interface::Dtc> &dtc,
const std::shared_ptr<Interface::Ftc> &ftc,
const std::shared_ptr<SituationReplayManagement::ForeignDataInterface> &srmForeign,
const std::shared_ptr<Core::Interface::Dtc> &coreDtc,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc);
void loadScript();
TopicEditorBase::TopicEditor *createTopic(const TopicAssignment &ta);
TopicEditorBase::TopicEditor *createTopic(const LtsScriptTimeline::TrackEntry &data);
void createTopicFromTrackEntry(LtsScriptTimeline::TrackEntry *data);
bool topicStringToType(const QString &typeName, LtsScriptTimeline::TrackEntryType &tet) const;
int getCurrentScriptEndTime() const;
bool isEssentialsViewOpened() const;
static LtsScriptTimeline::TrackEntry *trackEntryFromContent(const PresentationManagement::PresentationContent &content,
int topicId,
LtsScriptTimeline::TrackEntryType trackType);
bool importTrackEntries(const QVector<LtsScriptTimeline::TrackEntry *> &tes);
std::shared_ptr<SituationReplay::ForeignGuiInterface> getReplayGuiIfc() const
{
return m_essentialsViewsSrpGuiIfc;
}
private:
enum class ProjectionWidget
{
Unknown = 0,
Screen1,
Screen2,
};
enum class SpecialTopicType
{
NonSpecial = 0,
ReplayTopic,
FilterTopic,
};
class PlayingTopic
{
public:
PlayingTopic() = default;
~PlayingTopic()
{
if (topicReplayIfc != nullptr)
{
topicReplayIfc->playbackStop();
delete topicReplayIfc;
}
// if (replayWidget) HACK: FIXME: trading memory for security BUT dont delete if it's the situation map app's base layout !
// delete replayWidget;
}
QString topicTypeName;
LtsScriptTimeline::TrackEntry *topic = nullptr;
TopicReplay::Interface *topicReplayIfc = nullptr; // 1 instance per 1 currently playing topic
QWidget *replayWidget = nullptr;
LtsScriptTimeline::TrackEntryType trackEntryType = LtsScriptTimeline::TrackEntryType::INVALID;
bool isFilter = false;
};
void onPresentationModifyReply(const Core::FunctionReplyObject &replyObject) override { if (m_requestHelper != nullptr) m_requestHelper->requestReply(replyObject); }
void onGetPresentationContentReply(const GetPresentationContentReply &reply) override { if (m_requestHelper != nullptr) m_requestHelper->requestReply(reply); }
void onReplayPartitionCreateReply(const Core::FunctionReplyObject &reply) override { if (m_requestHelper != nullptr) m_requestHelper->requestReply(reply); }
void onReplayPartitionRemoveReply(const Core::FunctionReplyObject &) override { /* ignore */ }
void retranslate() override;
bool apply() override;
void aboutToClose() override;
void getPresentationContent(); // tryAddTopics has to be called first to populate m_trackEntryAssignments
TopicEditorBase::TopicEditor *createTopicEditor(LtsScriptTimeline::TrackEntry *te);
bool topicTypeOnEssentialsView(LtsScriptTimeline::TrackEntryType tet) const;
void startEssentialsViewStage2(const Core::ReplayPartition &replay);
bool tryAddTopics(QStringList &resultList);
void fillTopicVector();
void createSitMapApp();
void startReplaySession();
void exportPresentationContent(const Core::FunctionReply &baseReply);
void disableSessionDialog(LtsSession::LtsSession *pSession, bool disable);
void deletePlayingTopic(PlayingTopic *playingTopic); // WARNING: use a mutable iterator while iterating over m_currentlyPlayingTopics and remove the entry before calling this method!
static QString toReadableTime(qint64 seconds);
ProjectionWidget identifyProjectionWidget(LtsScriptTimeline::TrackEntry *topic) const;
bool usesAvsWindow(LtsScriptTimeline::TrackEntryType trackEntryType) const;
void playTopic(LtsScriptTimeline::TrackEntry *topic);
bool enableFireEventFlash(bool enable);
void pausePlayingTopics();
static bool isFilter(LtsScriptTimeline::TrackEntryType tet);
void initProjectionWidget(Screen **pointerToWidgetPointer);
void deleteProjectionWidget(Screen **pointerToWidgetPointer);
bool _tryAddTopic(LtsScriptTimeline::TrackEntryType trackEntryType,
const std::shared_ptr<TopicEditorBase::Interface> &topicEditorIfc,
const std::shared_ptr<TopicReplay::Factory> &topicReplayFactory,
const std::shared_ptr<TopicReplay::Factory> &topicExportFactory,
QStringList &resultList,
const SpecialTopicType &specialTopicType = SpecialTopicType::NonSpecial);
void initFailed(int reason);
void setFreestanding(bool freestanding, const ProjectionWidget &pw);
void setReplayWidgetFreestanding(PlayingTopic *playingTopic, bool freestanding);
void printCurrentlyPlayingTopics();
void addReplayWidget(PlayingTopic *playingTopic, QWidget *replayWidget);
Screen *getProjectionWidget(const ScriptDialog::ProjectionWidget &projectionWidget);
bool isScreenFreestanding(const ProjectionWidget &pw) const;
void previewSecond(int time, int ignoreTopicForReplay = -1);
void setReplayWidgetPosition(PlayingTopic *playingTopic);
void avsFreestandingHack(bool freestanding, QWidget *projectionWidget, QWidget **parentForScreen);
void avsVisibilityHack(QWidget *parentForScreen, bool visible);
void stop(bool ended = false, bool ignoreFireEventFlashing = false);
void changelogCheck();
int previousTopicTime() const;
bool hasPreviousTopic() const { return previousTopicTime() > -1; }
int nextTopicTime() const;
bool hasNextTopic() const { return nextTopicTime() > -1; }
void setPositionTime(int positionTime, bool setByUser = false);
void setPositionTimeActive(int positionTime, bool setByUser = false); // setPositionTime, but, if playing, also ticks and resets timeout
template <typename TopicEditorInterface>
std::shared_ptr<TopicEditorBase::Interface> getTopicEditorIfc(LtsScriptTimeline::TrackEntryType trackEntryType)
{
std::shared_ptr<TopicEditorBase::Interface> topicEditorIfc;
if (!m_exerciseSession->interfaceFromGui<TopicEditorInterface>(topicEditorIfc))
{
ERROR_LOG(m_logger) << "getTopicEditorIfc failed: Cannot add topic type "
<< LtsScriptTimeline::trackEntryTypeName(trackEntryType)
<< " because topic editor ifc is missing.";
return nullptr;
}
return topicEditorIfc;
}
template <typename TopicReplayInterface>
std::shared_ptr<TopicReplay::Factory> getTopicReplayFactory(LtsScriptTimeline::TrackEntryType trackEntryType)
{
std::shared_ptr<TopicReplay::Factory> topicReplayFactory;
if (!m_replaySession->interfaceFromGui<TopicReplayInterface>(topicReplayFactory))
{
ERROR_LOG(m_logger) << "getTopicIfcFactory failed: Cannot add topic type "
<< LtsScriptTimeline::trackEntryTypeName(trackEntryType)
<< " because topic ifc factory is missing.";
return nullptr;
}
return topicReplayFactory;
}
template <typename TopicEditorGuiInterface,
typename TopicReplayFactory = TopicReplay::Factory,
typename TopicExportFactory = TopicReplayFactory>
bool tryAddTopic(LtsScriptTimeline::TrackEntryType trackEntryType,
QStringList &resultList,
const SpecialTopicType &specialTopicType = SpecialTopicType::NonSpecial)
{
std::shared_ptr<TopicEditorBase::Interface> topicEditorIfc;
std::shared_ptr<TopicReplay::Factory> topicReplayFactory;
std::shared_ptr<TopicReplay::Factory> topicExportFactory;
if (!std::is_same<TopicEditorBase::Interface, TopicEditorGuiInterface>::value)
{
topicEditorIfc = getTopicEditorIfc<TopicEditorGuiInterface>(trackEntryType);
if (topicEditorIfc == nullptr)
{
resultList.append(LtsScriptTimeline::trackEntryTypeName(trackEntryType));
return false;
}
}
if (!std::is_same<TopicReplay::Factory, TopicReplayFactory>::value)
{
topicReplayFactory = getTopicReplayFactory<TopicReplayFactory>(trackEntryType);
if (topicReplayFactory == nullptr)
{
resultList.append(LtsScriptTimeline::trackEntryTypeName(trackEntryType));
return false;
}
}
if (!std::is_same<TopicReplay::Factory, TopicExportFactory>::value)
{
topicExportFactory = getTopicReplayFactory<TopicExportFactory>(trackEntryType);
if (topicExportFactory == nullptr)
{
resultList.append(LtsScriptTimeline::trackEntryTypeName(trackEntryType));
return false;
}
}
else
topicExportFactory = topicReplayFactory;
return _tryAddTopic(trackEntryType, topicEditorIfc, topicReplayFactory, topicExportFactory, resultList, specialTopicType);
}
Ui::ScriptDialog *m_ui;
LtsSession::LtsSession *m_essentialsViewSession;
std::shared_ptr<SituationReplay::ForeignGuiInterface> m_essentialsViewsSrpGuiIfc;
std::shared_ptr<PresentationManagement::Interface::Dtc> m_dtc;
std::shared_ptr<PresentationManagement::Interface::Ftc> m_ftc;
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_srmForeign;
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
QString m_menuGroup;
LtsUtils::RequestHelper *m_requestHelper;
int m_selectedTeId;
TopicEditorBase::TopicEditor *m_currentTopicEditor;
int m_scriptId;
QMap<QString, TopicAssignment> m_trackEntryAssignments;
QMap<LtsScriptTimeline::TrackEntryType, TopicAssignment> m_trackEntryToTopicAssignment;
LtsPluginManager::LtsPluginManager *m_essentialsViewApp;
QString m_scriptName;
QString m_scriptOwner;
qint64 m_lastModified = 0;
QVector<LtsScriptTimeline::TrackEntry *> m_topicVector;
QMultiMap<int, LtsScriptTimeline::TrackEntry *> m_topicMap; // <seconds, trackEntry>
QVector<FilterTopic> m_filterTopicVector;
TakeHomePackageGenerator::ExportProcess *m_exportProcess;
LtsSession::LtsSession *m_exerciseSession; // aka. script editor app
LtsSession::LtsSession *m_replaySession;
QTimer *m_presentationTimer;
int m_lowestTime;
int m_highestTime;
int m_positionTime;
int m_duration;
int m_nextPauseTime;
QVector<PlayingTopic *> m_currentlyPlayingTopics; // also indicates that the script is either playing or in paused state if NOT empty
Screen *m_projectionWidget1;
Screen *m_projectionWidget2;
MainJournal m_journal;
int m_scriptsExerciseId;
QString m_exerciseName;
LtsPluginManager::LtsPluginManager *m_sitMapApp;
LtsSession::LtsSession *m_sitMapSession;
bool m_currentlyFreestandingTopicsScreen1;
bool m_currentlyFreestandingTopicsScreen2;
TopicReplay::Interface *m_sitMapTopicIfc;
bool m_firstPlayAfterStop;
bool m_enableApply;
bool m_stopAfterPlayingFilterTopic;
QWidget *m_previousParentForScreen1;
QWidget *m_previousParentForScreen2;
int m_ignoreTopicForReplay;
QPointer<TopicDesigner> m_currentTopicDesigner;
bool m_avsVisibilityHackActive;
QPointer<StoryboardDialog> m_storyboardDialog;
bool m_ignoreChanges;
bool m_scriptRunning;
bool m_bTimerTickRecursionGuard;
bool m_isPlayTopicMethodStillRunning;
LOGGER_T m_logger;
private slots:
void slotEditEntry(const int topicId);
void slotTrackEntrySelectionChanged(LtsScriptTimeline::TrackEntry *);
void slotChangesWereMade();
void slotTrackEntryModified();
void slotNull(QObject *obj);
void slotDiscard();
void slotStartEssentialsView();
void slotLoadScript();
void slotExportScript();
void slotExportSuccessful(const QString &path, const QString &failedTopics);
void slotBurnDvD(const QString &strExportPath);
void slotBurningFinished(bool success);
void slotPlay();
void slotPause();
void slotStop();
void slotTimerTick();
void slotShowStoryboard();
void slotShowJournal();
void slotToNextTopic();
void slotToPreviousTopic();
void slotPlayUntilNextTopic();
void slotSetTopicPosition(QWidget *previewWidget, int ignoreTopicForReplay);
void slotTrackEntryDeleted(const int topicId);
void slotEditSelectedEntry();
void slotCheckNewTopicLength(int newLength, LtsUtils::Promise *promise);
void slotAddFakeLegacyPlanTopic();
void slotChangesMade();
};
} // namespace PresentationManagement
@@ -0,0 +1,320 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptDialog</class>
<widget class="QWidget" name="ScriptDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1010</width>
<height>587</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Script Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="999,0">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="LtsScriptTimeline::ScriptTimeline" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="replayControls">
<property name="title">
<string>Replay Controls</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QWidget" name="replayControlsWidget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,999">
<item>
<layout class="QHBoxLayout" name="replayControlsLayout">
<item>
<layout class="QVBoxLayout" name="replayControlsMainLayout">
<item>
<layout class="QHBoxLayout" name="replayControlsScreensLayout">
<item>
<widget class="QPushButton" name="pbInitScr1">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Set Screen 1</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbInitScr2">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Set Screen 2</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="pbStop">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Stop</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPause">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Pause</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPlay">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="replayControlsJumpLayout">
<item>
<widget class="QPushButton" name="pbUntilNextTopic">
<property name="text">
<string>Play Until Next Topic</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPreviousTopic">
<property name="text">
<string>Previous Topic</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbNextTopic">
<property name="text">
<string>Next Topic</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="replayInfoLayout">
<property name="horizontalSpacing">
<number>9</number>
</property>
<property name="verticalSpacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="durationLabel">
<property name="text">
<string>Presentation duration:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblDuration">
<property name="text">
<string notr="true"> - </string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="posLabel">
<property name="text">
<string>Pos:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSlider" name="posSlider">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="const" stdset="0">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="currentPosLabel">
<property name="text">
<string>Current position:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="currentositionLayout">
<item>
<widget class="QLabel" name="lblPos">
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="slashLabel">
<property name="text">
<string>/</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblMax">
<property name="text">
<string notr="true">0</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>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="statusLayout">
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>Status:</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="lwStatus">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContentsOnFirstShow</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="movement">
<enum>QListView::Free</enum>
</property>
<property name="isWrapping" stdset="0">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsScriptTimeline::ScriptTimeline</class>
<extends>QWidget</extends>
<header location="global">LtsScriptTimeline/script_timeline.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,376 @@
/**
* @RDE_HEADER@
* GUI Plugin for Script Editor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-17
*/
#include "pre_script_gui_plugin.h"
#include "pre_script_dialog.h"
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
#include <Plugins/PresentationManagement/Gui/Script/Common/ScriptChooser/script_chooser.h>
#include <Plugins/PresentationManagement/Gui/Common/pre_edit_dialog.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsSession/lts_session_data.h>
#include <LtsWidgets/lts_background_widget.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <LtsScriptTimeline/track_entry.h>
#include <LtsScriptTimeline/temporary_pointer_vector.h>
#include <Common/TopicEditorBase/topic_editor_base.h>
// Control- & Widget-Ifcs:
#include <Plugins/SituationReplay/srp_gui_plugin_interface.h>
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <Plugins/FileManagement/flm_explorer_gui_plugin_interface.h>
#include <Plugins/BuildingViewer/bdv_viewer_gui_plugin_interface.h>
#include <Plugins/AudioVideoManagement/avm_menu_edit_gui_plugin_interface.h>
#include <Plugins/SituationMapPlugin/situation_map_script_filter_gui_plugin_interface.h>
#include <Plugins/MarkerManagement/mrk_gui_plugin_interface.h>
#include <Plugins/Evaluations/evl_gui_plugin_interface.h>
#include <Plugins/ReportManagement/rep_gui_plugin_interface.h>
#include <Plugins/PlanOverlays/plan_overlays_plugin_interface.h>
#include <Services/PresentationManagement/pre_operator_rights.h>
#include <Services/PresentationManagement/pre_dtc.h>
#include <QDir>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QProgressDialog>
#include <QTimer>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QDateTime>
#include <stdlib.h> // for abort
namespace PresentationManagement
{
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("Plugins.Gui.Pre.Script"))
, m_dtc(nullptr)
, m_opmDtc(nullptr)
, m_ftc(nullptr)
, m_repForeign(nullptr)
, m_scriptDialog(nullptr)
, m_scriptId(0) // invalid
, m_requestHelper(new LtsUtils::RequestHelper(this))
{
connect(this, &InterfaceImpl::signalSuccess, m_session, &LtsSession::LtsSession::slotExecuteNextPayload, Qt::QueuedConnection);
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
{
LtsWidgets::PluginData data;
data.menuGroup = menuGroup();
data.identifier = QStringLiteral("PresentationManagement.ScriptDialog");
data.menuText = tr("Show Script Editor");
if (getFlags().testFlag(LtsAppDescriptor::PluginInfo::Flag::MAIN))
{
data.layoutArea = LtsWidgets::LayoutArea::eMainTab;
data.createInstant = LtsWidgets::CreateInstantType::CreateInstantAndDiscardAction;
data.closable = false;
}
else // (as dialog) = rather unlikely..
{
data.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
}
data.operatorRight = OperatorRights::ScriptEditorShow;
data.createWidgetFunc = [this](QWidget *parent)
{
return onShowScriptDialog(parent);
};
ret.append(data);
}
{
LtsWidgets::PluginData data;
data.menuGroup = menuGroup();
data.identifier = QLatin1String("PresentationManagement.ScriptDialog.ImportScript");
data.menuIcon = QIcon(QLatin1String("icon:import_documents.svg"));
data.menuText = tr("Insert Script");
data.operatorRight = OperatorRights::ScriptEditorEdit;
data.directFunc = [this](const LtsWidgets::PluginData &)
{
return onImportScript();
};
data.createWidgetFunc = nullptr;
ret.append(data);
}
return ret;
}
bool InterfaceImpl::fetchInterfaces()
{
if (!m_session->getDtc<PresentationManagement::DataInterface>(m_dtc))
{
DEBUG_LOG(m_logger) << "getDtc for PresentationManagement::DataInterface failed.";
return false;
}
if (!m_session->getFtc<PresentationManagement::DataInterface>(m_ftc))
{
DEBUG_LOG(m_logger) << "getFtc for PresentationManagement::DataInterface failed.";
return false;
}
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_opmDtc))
{
DEBUG_LOG(m_logger) << "getDtc for OperatorManagement::DataInterface failed.";
return false;
}
if (!m_session->getDtc<SituationReplayManagement::DataInterface>(m_coreDtc))
{
DEBUG_LOG(m_logger) << "getDtc for SituationReplayManagement::DataInterface failed.";
return false;
}
if (!m_session->getForeignDataInterface<SituationReplayManagement::DataInterface>(m_repForeign))
{
WARNING_LOG(m_logger) << "getForeignDataInterface for SituationReplayManagement::DataInterface failed.";
return false;
}
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_repForeign = nullptr;
m_coreDtc = nullptr;
m_opmDtc = nullptr;
m_ftc = nullptr;
m_dtc = nullptr;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Presentation");
}
LtsWidgets::LtsDialog *InterfaceImpl::onShowScriptDialog(QWidget *parent)
{
m_scriptDialog = new ScriptDialog(menuGroup(), parent);
if (!m_scriptDialog->init(m_session, m_scriptId, m_dtc, m_ftc, m_repForeign, m_coreDtc, m_opmDtc))
{
m_scriptDialog->deleteLater();
m_scriptDialog = nullptr;
// dialog is app's main dialog, close app if dialog cannot initialize
emit m_session->signalClose();
}
return m_scriptDialog.data();
}
void InterfaceImpl::onImportScript()
{
ScriptChooser *scriptChooser = new ScriptChooser(m_dtc, m_opmDtc, false, true);
scriptChooser->setAttribute(Qt::WA_DeleteOnClose);
connect(scriptChooser, &ScriptChooser::signalSelectScript,
this, &InterfaceImpl::slotImportScript, Qt::QueuedConnection);
connect(scriptChooser, &ScriptChooser::signalQuit,
m_session, &LtsSession::LtsSession::signalQuit);
if (!LtsSessionData::LtsSessionData::getInstance()->isTablet())
scriptChooser->setModal(true);
scriptChooser->show();
}
void InterfaceImpl::slotNewScript()
{
// prepare request
PresentationModifyRequest req;
req.name(tr("New Script").toStdString());
req.description(tr("No description available.").toStdString());
req.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
req.lastModified(QDateTime::currentMSecsSinceEpoch());
req.fileStatus(FileStatus::Valid);
if (m_requestHelper->startSingleRequest([this, req]()
{
return m_ftc->presentationModifyRequest(req, this);
}))
{
//slotSelectScript(m_requestHelper->getRepliedId());
}
}
void InterfaceImpl::slotAddScript()
{
PresentationEditDialog *dialog = new PresentationEditDialog(nullptr);
if (!dialog->init(-1 /* new presentation */, m_dtc, m_ftc, m_opmDtc))
{
delete dialog;
return;
}
QObject::connect(dialog, &PresentationEditDialog::rejected, dialog, &PresentationEditDialog::deleteLater);
dialog->show();
}
void InterfaceImpl::executePayload(LtsWidgets::LtsBackgroundWidget *mainBackground)
{
Q_UNUSED(mainBackground);
if (m_session->partitionType() != IDL::PartitionType::Exercise) // only on valid exercise
{
emit signalSuccess();
return;
}
int scriptId = m_session->getSavegame()[QStringLiteral("ScriptId")].toInt();
if (scriptId > 0)
{
slotSelectScript(scriptId);
return;
}
ScriptChooser *scriptChooser = new ScriptChooser(m_dtc, m_opmDtc, false, false, mainBackground);
scriptChooser->setAttribute(Qt::WA_DeleteOnClose);
connect(scriptChooser, &ScriptChooser::signalSelectScript,
this, &InterfaceImpl::slotSelectScript, Qt::QueuedConnection);
connect(scriptChooser, &ScriptChooser::signalQuit,
m_session, &LtsSession::LtsSession::signalQuit);
connect(scriptChooser, &ScriptChooser::signalNewScript,
this, &InterfaceImpl::slotAddScript);
if (!LtsSessionData::LtsSessionData::getInstance()->isTablet())
scriptChooser->setModal(true);
scriptChooser->show();
}
void InterfaceImpl::slotSelectScript(int scriptId)
{
m_scriptId = scriptId;
// std::shared_ptr<ForeignGuiInterface> preGuiIfc;
// if (!m_session->getForeignGuiInterface<ForeignGuiInterface>(preGuiIfc))
// {
// // TODO: MSG !!1
// return;
// }
// preGuiIfc->setScriptId(scriptId);
emit signalSuccess();
}
void InterfaceImpl::slotImportScript(int scriptId)
{
if (m_scriptId == scriptId)
{
ERROR_LOG(m_logger) << "Not possible to import same script!";
LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), tr("Not possible to import same script!"));
return;
}
if (!importScript(scriptId))
{
ERROR_LOG(m_logger) << "Failed to import script Id " << scriptId;
LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), tr("Failed to import Script Id %1").arg(scriptId));
return;
}
DEBUG_LOG(m_logger) << "Succesfully imported script Id " << scriptId ;
//LtsWidgets::LtsMessageDialog::showMessage(nullptr, tr("Successfully imported Script Id %1").arg(scriptId));
}
bool InterfaceImpl::importScript(int idSource)
{
if (!m_scriptDialog)
{
ERROR_LOG(m_logger) << "no script dialog open for import script";
return false;
}
PresentationManagement::Presentation preSource = m_dtc->presentation(idSource);
if (preSource.id() < 1)
return false;
// import topics
std::function<void(const Core::FunctionReply &baseReply)> importPresentationContent = [&](const Core::FunctionReply &baseReply)
{
const GetPresentationContentReply &reply = static_cast<const GetPresentationContentReply &>(baseReply);
const auto &contentList = reply.content();
if (contentList.size() < 1)
{
ERROR_LOG(m_logger) << "No Presentation Content for Script Id " << reply.id();
return;
}
LtsScriptTimeline::TemporaryPointerVector<LtsScriptTimeline::TrackEntry *> importedTes;
for (const PresentationManagement::PresentationContent &content : contentList)
{
LtsScriptTimeline::TrackEntryType tet = LtsScriptTimeline::TrackEntryType::INVALID;
if (!m_scriptDialog->topicStringToType(QString::fromStdString(content.presentationTopicType()), tet))
{
ERROR_LOG(m_logger) << "Topic ignored - invalid topic type for presentation's content id " << content.id();
continue;
}
importedTes.append(ScriptDialog::trackEntryFromContent(content, 0, tet));
}
if (m_scriptDialog->importTrackEntries(importedTes))
importedTes.setDiscardable(false); // don't delete tes if they could be imported and finally be managed by the script timeline
};
GetPresentationContentRequest req(idSource);
req.presentationId(idSource);
if (!m_requestHelper->startSingleRequest([req, this]()
{
return m_ftc->getPresentationContentRequest(req, this);
}, importPresentationContent))
{
LtsWidgets::LtsMessageDialog::showMessage(m_session->baseLayout(), tr("There is no content to import for this script."));
return false;
}
return true;
}
} // namespace PresentationManagement
@@ -0,0 +1,100 @@
/**
* @RDE_HEADER@
* GUI Plugin for Script Editor
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-17
*/
#pragma once
#include <PresentationManagement/logger.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/PresentationManagement/pre_data_plugin_interface.h>
#include <Plugins/PresentationManagement/pre_script_gui_plugin_interface.h>
#include <Services/PresentationManagement/pre_ftc.h> // reply handler
#include <Services/OperatorManagement/opm_dtc.h>
#include <Plugins/SituationReplayManagement/srm_data_plugin_interface.h>
#include <Common/ProjectConfig/project_defines.h>
#include <LtsUtilsGuiDds/request_helper.h>
// test:
#include <LtsScriptTimeline/track_entry_painters.h>
#include "pre_script_dialog.h"
#include <LtsSession/lts_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <PluginInterfaces/topic_replay_interface.h>
#include <Plugins/PresentationManagement/Gui/Common/topic_assignment.h>
namespace PresentationManagement
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "gui:3:pre.script.edit")
Q_INTERFACES(GuiPluginBase::Plugin)
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return ForeignScriptDialogGuiInterface::pluginName(); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{ return (partition == IDL::PartitionType::Exercise); }
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
, protected PresentationManagement::Interface::Ftc::PresentationModifyReplyHandler
, protected PresentationManagement::Interface::Ftc::GetPresentationContentReplyHandler
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
QVector<LtsWidgets::PluginData> getActions() override;
QString menuGroup() const override;
bool fetchInterfaces() override;
void clearInterfaces() override;
bool availableInPartition(IDL::PartitionType partition) const override
{ return partition == IDL::PartitionType::Exercise; }
// be able to show script chooser
int executionPriority() const override { return 9999; }
void executePayload(LtsWidgets::LtsBackgroundWidget *mainBackground) override;
protected:
void onPresentationModifyReply(const Core::FunctionReplyObject &replyObject) override final { m_requestHelper->requestReply(replyObject); }
void onGetPresentationContentReply(const GetPresentationContentReply &reply) override { m_requestHelper->requestReply(reply); }
private:
LtsWidgets::LtsDialog *onShowScriptDialog(QWidget *parent);
void onImportScript();
bool importScript(/*int idDest, */int idSource);
std::shared_ptr<PresentationManagement::Interface::Dtc> m_dtc;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
std::shared_ptr<PresentationManagement::Interface::Ftc> m_ftc;
std::shared_ptr<SituationReplayManagement::ForeignDataInterface> m_repForeign;
std::shared_ptr<Core::Interface::Dtc> m_coreDtc;
QPointer<PresentationManagement::ScriptDialog> m_scriptDialog;
int m_scriptId;
LtsUtils::RequestHelper *m_requestHelper;
signals:
void signalSuccess();
private slots:
void slotSelectScript(int scriptId);
void slotNewScript();
void slotImportScript(int scriptId);
void slotAddScript();
};
} // namespace PresentationManagement
@@ -0,0 +1,152 @@
#include "pre_storyboard.h"
#include "ui_pre_storyboard.h"
#include <LtsPrinting/printing.h>
#include <LtsScriptTimeline/track_entry.h>
#include <ngltools/ngltools_dtg.h>
using namespace LtsScriptTimeline;
namespace PresentationManagement
{
StoryboardDialog::StoryboardDialog(QWidget *parent, LtsSession::LtsSession *session)
: LtsWidgets::LtsDialog(parent)
, m_ui(new Ui::StoryboardDialog)
, m_session(session)
, m_alreadyTranslated(false)
{
m_ui->setupUi(this);
m_buttonBox->connectButton(LtsWidgets::DialogButton::Print, this, &StoryboardDialog::slotPrint);
}
StoryboardDialog::~StoryboardDialog()
{
delete m_ui;
}
void StoryboardDialog::slotPrint()
{
QString headingHtml = (QStringLiteral("<H1>"));
headingHtml.append (tr("Scriptname:"));
headingHtml.append(m_scriptName);
headingHtml.append(QStringLiteral("</H1>"));
headingHtml.append(QStringLiteral("<H3>"));
headingHtml.append(tr("Script Owner: "));
headingHtml.append(m_scriptOwner);
headingHtml.append(QStringLiteral("</H3>"));
headingHtml.append(QStringLiteral("<H3>"));
headingHtml.append(tr("Last modified: "));
headingHtml.append(DTG::toStringMSec(m_lastModified));
headingHtml.append(QStringLiteral("<br>"));
headingHtml.append(tr("Duration: "));
QString durationStr = m_duration;
headingHtml.append(durationStr);
headingHtml.append(QStringLiteral("</H3><br>"));
LtsPrinting::printView(m_session, this, m_ui->listWidget, headingHtml);
}
bool StoryboardDialog::init(const QVector<LtsScriptTimeline::TrackEntry *> &topicVector,
const QVector<FilterTopic> &filterTopicVector,
const QString &scriptName,
const QString &scriptOwner,
const qint64 lastModified,
const QString duration)
{
m_topicVector = topicVector; // cpy
m_filterTopicVector = filterTopicVector; // cpy
m_scriptName = scriptName; // cpy
m_scriptOwner = scriptOwner;
m_lastModified = lastModified;
m_duration = duration;
// sort storyboard by time
std::sort(m_topicVector.begin(), m_topicVector.end(), [](const TrackEntry *a, const TrackEntry *b) -> bool
{
return a->m_startTime < b->m_startTime;
});
return true;
}
void StoryboardDialog::retranslate()
{
translate(m_ui, tr("Script Storyboard"));
if (m_alreadyTranslated)
return;
fillStoryboard();
m_alreadyTranslated = true;
}
void StoryboardDialog::fillStoryboard()
{
m_ui->listWidget->clear();
QVector<FilterTopic> correspondingFilterTopics;
int ii1 = 1;
for (const LtsScriptTimeline::TrackEntry *te : qAsConst(m_topicVector))
{
QString itemText = tr("Topic %1: %2 - %3 (%4 seconds)\n Type: %5").arg(
ii1).arg(QTime::fromMSecsSinceStartOfDay(te->m_startTime * 1000).toString(),
QTime::fromMSecsSinceStartOfDay((te->m_startTime + te->m_length) * 1000).toString()).arg(
te->m_length).arg(LtsScriptTimeline::translateTrackEntryType(te->m_type));
if (!te->m_description.isEmpty())
itemText += QStringLiteral("\n ") + tr("Description: %1").arg(te->m_description);
m_ui->listWidget->addItem(itemText);
// look for filters for this topic:
correspondingFilterTopics.clear();
for (const FilterTopic &_ft : qAsConst(m_filterTopicVector))
{
if (_ft.mainTopicId != te->m_topicId)
continue;
correspondingFilterTopics.append(_ft);
}
// sort filter topics by start time
std::sort(correspondingFilterTopics.begin(), correspondingFilterTopics.end(), [](const FilterTopic &a, const FilterTopic &b) -> bool
{
return a.filterTopic->m_startTime < b.filterTopic->m_startTime;
});
int ii2 = 1;
for (const FilterTopic &_ft : qAsConst(correspondingFilterTopics))
{
if (_ft.mainTopicId != te->m_topicId)
continue;
const LtsScriptTimeline::TrackEntry *fte = _ft.filterTopic;
QString filterItemText = tr(" > Filter %1: %2 - %3 (%4 seconds)\n Type: %5").arg(
ii2).arg(QTime::fromMSecsSinceStartOfDay(fte->m_startTime * 1000).toString(),
QTime::fromMSecsSinceStartOfDay((fte->m_startTime + te->m_length) * 1000).toString()).arg(
fte->m_length).arg(LtsScriptTimeline::translateTrackEntryType(te->m_type));
if (!fte->m_description.isEmpty())
filterItemText += QStringLiteral("\n ") + tr("Description: %1").arg(fte->m_description);
m_ui->listWidget->addItem(filterItemText);
ii2++;
}
ii1++;
}
}
} // namespace PresentationManagement
@@ -0,0 +1,50 @@
#pragma once
#include "filter_topic.h"
#include <LtsWidgets/lts_dialog.h>
#include <LtsScriptTimeline/track_entry.h>
#include <LtsSession/lts_session.h>
#include <QVector>
namespace Ui
{
class StoryboardDialog;
}
namespace PresentationManagement
{
class StoryboardDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
StoryboardDialog(QWidget *parent, LtsSession::LtsSession *session);
~StoryboardDialog();
bool init(const QVector<LtsScriptTimeline::TrackEntry *> &topicVector,
const QVector<FilterTopic> &filterTopicVector,
const QString &scriptName,
const QString &scriptOwner,
const qint64 lastModified,
const QString duration);
void fillStoryboard();
void retranslate() override final;
private:
Ui::StoryboardDialog *m_ui;
QVector<LtsScriptTimeline::TrackEntry *> m_topicVector;
QVector<FilterTopic> m_filterTopicVector;
LtsSession::LtsSession *m_session;
QString m_scriptName = QString();
QString m_scriptOwner = QString();
qint64 m_lastModified = 0;
QString m_duration = QString();
bool m_alreadyTranslated;
public slots:
void slotPrint();
};
} // namespace PresentationManagement
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StoryboardDialog</class>
<widget class="QWidget" name="StoryboardDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>707</width>
<height>464</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Script Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QListWidget" name="listWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* Dialog for the Topic Designer
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-06-09
*/
#include "pre_topic_designer_dialog.h"
#include "ui_pre_topic_designer_dialog.h"
namespace PresentationManagement
{
TopicDesignerDialog::TopicDesignerDialog(QWidget *parent)
: LtsDialog(parent)
, m_ui(new Ui::TopicDesignerDialog)
{
m_ui->setupUi(this);
setFocusPolicy(Qt::StrongFocus);
connect(m_ui->widget, &TopicDesigner::signalTopicSelected,
this, &TopicDesignerDialog::signalTopicSelected);
connect(m_ui->widget, &TopicDesigner::signalDeleteTrackEntry,
this, &TopicDesignerDialog::signalDeleteTrackEntry);
connect(m_ui->widget, &TopicDesigner::signalUnselect,
this, &TopicDesignerDialog::signalUnselect);
connect(m_ui->widget, &TopicDesigner::signalChangesMade,
this, &LtsWidgets::LtsDialog::signalChangesMade);
// connect(m_ui->pbToFront, &QPushButton::clicked,
// m_ui->widget, &TopicDesigner::to)
}
void TopicDesignerDialog::retranslate()
{
translate(m_ui, tr("Topic Designer"));
}
TopicDesignerDialog::~TopicDesignerDialog()
{
delete m_ui;
}
QVector<LtsScriptTimeline::TrackEntry *> TopicDesignerDialog::exportTopicList() const
{
return m_ui->widget->exportTopicList();
}
void TopicDesignerDialog::selectTopic(int topicId)
{
m_ui->widget->selectTopic(topicId);
}
void TopicDesignerDialog::loadTopicList(const QVector<LtsScriptTimeline::TrackEntry *> &topicsList)
{
m_ui->widget->loadTopicList(topicsList);
}
} // namespace PresentationManagement
@@ -0,0 +1,57 @@
/**
* @RDE_HEADER@
* Dialog for the Topic Designer
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-06-09
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <QVector>
namespace Ui
{
class TopicDesignerDialog;
}
namespace LtsScriptTimeline
{
class TrackEntry;
}
namespace PresentationManagement
{
class TopicDesignerDialog : public LtsWidgets::LtsDialog
{
public:
Q_OBJECT
public:
explicit TopicDesignerDialog(QWidget *parent = nullptr);
~TopicDesignerDialog();
void loadTopicList(const QVector<LtsScriptTimeline::TrackEntry *> &topicsList);
QVector<LtsScriptTimeline::TrackEntry *> exportTopicList() const;
void selectTopic(int topicId);
signals:
void signalTopicSelected(int topicId);
void signalDeleteTrackEntry(int topicId);
void signalUnselect();
private:
void retranslate() override;
Ui::TopicDesignerDialog *m_ui;
};
} // namespace PresentationManagement
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TopicDesignerDialog</class>
<widget class="QWidget" name="TopicDesignerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>338</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Script Editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string notr="true">Some text</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="pbToFront">
<property name="text">
<string>To Front</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbIncIdx">
<property name="text">
<string>Inc Idx</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbDecIdx">
<property name="text">
<string>Dec Idx</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbToBack">
<property name="text">
<string>To Back</string>
</property>
</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>
</item>
<item>
<widget class="PresentationManagement::TopicDesigner" name="widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>300</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PresentationManagement::TopicDesigner</class>
<extends>QWidget</extends>
<header>topic_designer.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,17 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Created by Mike Wille, SLTE, 2020-03-23
#
ADD_SUBDIRECTORY(Common)
ADD_SUBDIRECTORY(ThpManagement)
ADD_SUBDIRECTORY(ThpUsbManagement)
ADD_INTERFACE_LIBRARY(TpmGuiPluginInterface
${CMAKE_CURRENT_SOURCE_DIR}/tpm_gui_plugin_interface.h
${CMAKE_CURRENT_SOURCE_DIR}/..
GuiPluginInterface
)
@@ -0,0 +1,43 @@
#
# $Id: CMakeLists.txt 51804 2020-07-31 04:47:28Z fritsche_h $
# $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/CMakeLists.txt $
# $Author: fritsche_h $
#
# Created by Mike Wille, SLTE, 2020-03-23
#
SET(src
logger.cpp
logger.h
tpm_exported_scripts_model.cpp
tpm_exported_scripts_model.h
tpm_process_dialog.cpp
tpm_process_dialog.h
)
QT5_WRAP_UI(src
tpm_process_dialog.ui
)
NGL_ADD_LIBRARY2(WS ThpCommon SHARED ${src})
NGL_TARGET_INCLUDE_DIRECTORIES(ThpCommon
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../ # export file
)
NGL_TARGET_LINK_LIBRARIES(ThpCommon
Qt6::Core
Qt6::Concurrent
NGLLogger
LtsSession
AsmDataPluginInterface
ExmDataPluginInterface
#nglcompression
#nglsettings
#ngltools
#LtsSessionData
#httq
)
NGL_INSTALL_TARGET(ThpCommon)
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id: logger.cpp 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.cpp $
* $Author: rudolphus_g $
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace TakeHomePackageManager
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "TPM.Common.";
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();
}
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,22 @@
/**
* @RDE_HEADER@
* Logging for CentralEffectSimulation
* $Id: logger.h 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.h $
* $Author: rudolphus_g $
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace TakeHomePackageManager
{
typedef NGL::INGLLogger &LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,632 @@
/**
* @RDE_HEADER@
* Show the exported scripts and allow them to upload them to the server
* $Id: tpm_exported_scripts_model.cpp 53111 2021-03-22 08:21:29Z wille_m $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_model.cpp $
* $Author: wille_m $
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
*/
#include "tpm_exported_scripts_model.h"
#include <ngltools/ngltools_dtg.h>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
namespace TakeHomePackageManager
{
ExportedScriptsModel::ExportedScriptsModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>(
{ tr("id"), tr("Exercise"), tr("Name"), tr("Created"), tr("Last downloaded"), tr("Location"), tr("Last uploaded"), tr("Server")}, parent)
, m_logger(getLogger("ExportedScriptsModel"))
{}
bool ExportedScriptsModel::init(const QString &rootDirectoryToScan)
{
std::shared_ptr<int> _(nullptr, [this](int *) { endResetModel(); }); // TODO: QScopeGuard
m_rootDirectoryToScan = rootDirectoryToScan;
beginResetModel();
m_vecData.clear();
QVector <int> localExercises;
DEBUG_LOG(m_logger) << "ExportedScriptsModel::init rootDirectoryToScan=" << rootDirectoryToScan
/*<< " directoryToScan=" << directoryToScan*/;
//clear(); multiple inits needed, don't clear
// die Skripte aller Uebungen auf der lokalen Workstation anzeigen.
// Die Uebungen sind in der Datei /tmp/THP-xxxxxxx/data.json im Array Exercises enthalten.
{
DEBUG_LOG(m_logger) << "Get all local exercises rootDirectoryToScan=" << rootDirectoryToScan ;
QFile file(rootDirectoryToScan + QDir::separator() + QStringLiteral("exercises.json"));
if (!file.open(QFile::ReadOnly)) // file only exists after a first download, but that's okay
WARNING_LOG(m_logger) << "Cannot open " << file.fileName() << " for read/write: " << file.errorString();
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
const QJsonObject obj = doc.object(); // okay to be empty
const QJsonArray arr = obj[QStringLiteral("Exercises")].toArray();
localExercises.reserve(arr.size());
for (const auto &ele : arr)
localExercises.append(ele.toInt());
}
WARNING_LOG(m_logger) << "local exericses: " << localExercises.count();
// Alle lokalen Übungen:
for (const int exId : qAsConst(localExercises))
{
const QString baseDir = exercisePath(rootDirectoryToScan, exId);
QFile file(baseDir + QDir::separator() + QStringLiteral("data.json"));
if (!file.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Cannot open " << file.fileName() << " for reading: " << file.errorString() << " - not critical";
// There are no local scripts but maybe some are on the server
return true;
}
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
const QJsonObject obj = doc.object();
const QJsonArray presentations = obj[QStringLiteral("presentations")].toArray();
DEBUG_LOG(m_logger) << "exercise: " << exId << ", presentations: " << presentations.size();
for (const QJsonValue &val : presentations)
{
ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val);
DEBUG_LOG(m_logger) << "obj=" << QJsonDocument(obj).toJson(QJsonDocument::Compact);
const QJsonObject presentationObj = val.toObject();
DEBUG_LOG(m_logger) << "presentationObj=" << QJsonDocument(presentationObj).toJson(QJsonDocument::Compact);
if (obj.contains(QStringLiteral("exerciseId")))
{
esi.exerciseId = obj[QStringLiteral("exerciseId")].toInt();
esi.exerciseName = obj[QStringLiteral("exerciseName")].toString();
if (presentationObj.contains(QStringLiteral("name")) )
esi.scriptName = presentationObj[QStringLiteral("name")].toString();
if (presentationObj.contains(QStringLiteral("displayName")) )
esi.scriptName = presentationObj[QStringLiteral("displayName")].toString();
}
esi.local = true;
DEBUG_LOG(m_logger) << "push_back "
<< " exportTime=" << esi.exportTime
<< " scriptId=" << esi.scriptId
<< " scriptName=" << esi.scriptName
<< " exerciseId=" << esi.exerciseId
<< " exerciseName=" << esi.exerciseName
<< " local=" << esi.local;
esi.uploaded = ThpUnknownUploadState;
esi.rootDirectory = rootDirectoryToScan; // to determine if this script is from THP-Download or THP-Export directory (for USB Management, which can copy from both dirs)
m_vecData.push_back(esi);
}
}
return true;
}
/**
* @brief ExportedScriptsModel::setServerContent fills the table with local and
* server scripts and deletes rows of deleted scripts.
* @param jsonArray of scripts from the server
* @return true if jsonArray could be parsed and was processed, false if
* jsonArray could not be parsed
*/
void ExportedScriptsModel::setServerContent(const QString &jsonArray)
{
if (jsonArray.isEmpty()) // no content received yet? no problem, just return
return;
/* these vectors are needed to compare which scripts are already
* on the server or are local or are both on the server and local
* e.g. if the script was downloaded after it was once uploaded
* to the server */
QMap<int, QVector<int>> exerciseContentScriptIds; // scripts from server - Key = ExerciseId, value = script ids of exercise
QMap<int, QVector<int>> exerciseScriptIds; // scripts from local data.json file - Key = ExerciseId, value = script ids of exercise
DEBUG_LOG(m_logger) << "ExportedScriptsModel::setServerContent jsonArray =" << jsonArray;
QJsonParseError err;
const QJsonArray presentations = QJsonDocument::fromJson(jsonArray.toUtf8(), &err).array();
DEBUG_LOG(m_logger) << "presentations.size() =" << presentations.size();
if (err.error != QJsonParseError::NoError)
{
ERROR_LOG(m_logger) << "json array invalid at offset " << err.offset << ": " << err.errorString();
return;
}
// put local scripts into local scripts list
for (const auto &data : qAsConst(m_vecData))
{
exerciseScriptIds[data.exerciseId].append(data.scriptId);
DEBUG_LOG(m_logger) << "exerciseId=" << data.exerciseId
<< " modelScriptIds.append: " << data.scriptId;
}
// put server scripts into server scripts list
for (const QJsonValue &val : presentations)
{
const ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val, false, ThpAlreadyUploaded);
exerciseContentScriptIds[esi.exerciseId].append(esi.scriptId);
}
/* look for scripts that are on the server but also on the local side to
* either remove the corresponding row from the table if the script is not on
* the server and not set as local (that makes it non-local and non-existant
* on the server, which means it was at one point on the server, but was
* deleted from it after the server script list was received here some
* minutes ago) -
* or to add the not-uploaded state to the table row if it's local only
* (but what about local scripts that were uploaded? => This is handeled at
* the next main for-loop below)
*/
for (int i = m_vecData.size() - 1; i >= 0; --i) // scriptId ist nur im Modell aber nicht im content
{
const auto &rowData = m_vecData[i];
const QVector<int> contentScriptIds = exerciseContentScriptIds.value(rowData.exerciseId);
DEBUG_LOG(m_logger) << "m_vecData.at(" << i << ").scriptId=" << rowData.scriptId
<< " exerciseId=" << rowData.exerciseId
<< " contentScriptIds.contains=" << contentScriptIds.contains(rowData.scriptId)
<< " rowData.local=" << rowData.local
<< " contentScriptIds.size()=" << contentScriptIds.size();
if (!contentScriptIds.contains(rowData.scriptId))
{
// the script already in the table is not available on the server side
if (rowData.local == false)
{
/* script already in table is marked as non-loal, which means - in
* combination with the script not being on the server (anymore) - that
* the script was removed from the server after the server script list
* was received from the server and added to the table. So remove it
* from the table */
DEBUG_LOG(m_logger) << "removeRow(" << i << ")"
<< " ScriptId=" << rowData.scriptId
<< " ExerciseId=" << rowData.exerciseId;
removeRow(i);
}
else
{
/* otherwise, if it's marked as local, that means - in combination
* with he script not being on the server (anymore) - that the script
* is only locally available */
setUploadState(i, ThpUploadNotActive, 0, 0);
}
}
}
/* get all server scripts and add them to the table if they're not already in
* the table.LtsUtils::ProjectSettings *ps = L
* if the corresponding row already exist then set the uploaded-state to
* uploaded, because they came from the server list, which means they were
* obviously uploaded at some point */
for (const QJsonValue &val : presentations)
{
ExportedScriptInformation esi = ExportedScriptInformation::fromJson(val, false, ThpAlreadyUploaded);
QVector<int> modelScriptIds = exerciseScriptIds.value(esi.exerciseId);
DEBUG_LOG(m_logger) << " exportTime=" << esi.exportTime
<< " scriptId=" << esi.scriptId
<< " exerciseId=" << esi.exerciseId
<< " exerciseName=" << esi.exerciseName
<< " scriptName=" << esi.scriptName
<< " fileSize=" << esi.fileSize
<< " modelScriptIds.size=" << modelScriptIds.size();
// if a new script was added on the server and not available in this model already, then add it to the model
if (!modelScriptIds.contains(esi.scriptId))
{
DEBUG_LOG(m_logger) << "insertRow()";
insertRow(esi);
continue;
}
// update model
for (int i = 0; i < m_vecData.size(); i++)
{
const auto &rowData = m_vecData[i];
if (rowData.scriptId == esi.scriptId &&
rowData.exerciseId == esi.exerciseId)
{
DEBUG_LOG(m_logger) << "setUploadState i=" << i
<< " esi.scriptId=" << esi.scriptId
<< " esi.exerciseId=" << esi.exerciseId
<< " esi.fileSize=" << esi.fileSize
<< " esi.uploadTime=" << esi.uploadTime;
setUploadState(i, ThpAlreadyUploaded, esi.uploadTime, esi.fileSize);
break; // can only match 1 time
}
}
}
update();
return;
}
qint64 ExportedScriptsModel::downloadTime(const ExportedScriptInformation &val) const
{
if (!val.local) // can't be downloaded if it's not local
return 0;
QString filename(QStringLiteral("%1/THP-Download/Exercise_%2/%3/index.html").arg(QDir::tempPath()).arg(val.exerciseId).arg(val.scriptId));
DEBUG_LOG(m_logger) << "downloadTime: filename " << filename;
QFileInfo fi(filename);
if (!fi.exists())
return 0;
return fi.lastModified().toMSecsSinceEpoch();
}
QVariant ExportedScriptsModel::data(const QModelIndex &index, int role) const
{
if (!indexIsValid(index))
return QVariant();
const ExportedScriptInformation &val = m_vecData[index.row()];
switch (role)
{
case Qt::FontRole:
{
if (!m_isOC
/* because non-oc app has no "downloaded" column, telling the
* user he has a newer version to download (by making the uploaded
* cell fat) makes no sense */
|| !val.local
/* non-downloaded scripts can't be older than the script on the server
* because there obviously is no downloaded (local) script */
|| index.column() != ColUploadedTime
|| val.uploadTime <= (downloadTime(val) + (10 * 1000) /* 10 second grace */))
/* if the script was downloaded from the server before the script was
* updated / uploaded to the server again, mark the uploaded time cell
* as fat to indicate the local, downloaded script is outdated */
break;
QFont font;
font.setBold(true);
return font;
}
case Qt::DisplayRole:
{
switch (index.column())
{
case ColId:
return val.scriptId;
case ColExercise:
return val.exerciseName;
case ColName:
return val.scriptName;
case ColCreated:
return QDateTime::fromMSecsSinceEpoch(val.exportTime);
case ColDownloaded:
{
qint64 dlTime = downloadTime(val);
if (dlTime == 0)
return QVariant();
return QDateTime::fromMSecsSinceEpoch(dlTime);
}
case ColUploadedTime:
return QDateTime::fromMSecsSinceEpoch(val.uploadTime);
case ColLocation:
return val.local ? tr("Local") : tr("Server");
case ColUploaded:
switch (val.uploaded)
{
case ThpConnectToHost:
return tr("Connect to host");
case ThpUploadActive:
return tr("Upload Active");
case ThpUploadError:
return tr("Upload Error");
case ThpUploadNotActive:
return tr("Not uploaded");
case ThpUnknownUploadState:
return tr("Unknown upload status");
case ThpAlreadyUploaded:
return tr("Already uploaded");
}
}
case Qt::UserRole:
{
return val.scriptId;
break;
}
break;
}
}
return QVariant();
}
bool ExportedScriptsModel::createDataJson(const QString &rootDirectory,
const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc,
int exerciseId) const // TODO: static, static logger
{
const QString baseDir = exercisePath(rootDirectory, exerciseId);
if (!QDir().mkpath(baseDir)) // NOTE: returns true if already exists
{
ERROR_LOG(m_logger) << "Failed to create path " << baseDir;
return false;
}
QFile file(baseDir + QStringLiteral("data.json"));
DEBUG_LOG(m_logger) << "createDataJson=" << file.fileName();
if (!file.open(QFile::ReadWrite))
{
QString msg(tr("Could not open data file in script directory: %1").arg(file.errorString()));
ERROR_LOG(m_logger) << msg << " - " << file.fileName();
return false;
}
QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
QJsonObject obj = doc.object();
if (obj[QStringLiteral("exerciseId")].toInt() == exerciseId)
{
DEBUG_LOG(m_logger) << baseDir << " exists for exerciseId " << QString::number(exerciseId);
return true;
}
const ExerciseManagement::Exercise exercise = exmDtc->exercise(exerciseId);
const AssetManagement::TrainingArea ta = asmDtc->trainingArea(exercise.trainingAreaId());
obj[QStringLiteral("title")] = QString::fromStdString(exercise.name());
obj[QStringLiteral("exerciseId")] = exercise.id();
obj[QStringLiteral("exerciseName")] = QString::fromStdString(exercise.name());
obj[QStringLiteral("exerciseArea")] = QString::fromStdString(ta.name());
obj[QStringLiteral("cycleTime")] = QString(QDateTime::fromMSecsSinceEpoch(exercise.beginTime()).toString() +
QStringLiteral(" - ") + QDateTime::fromMSecsSinceEpoch(exercise.endTime()).toString());
obj[QStringLiteral("exerciseStart")] = exercise.beginTime();
obj[QStringLiteral("exerciseEnd")] = exercise.endTime();
obj[QStringLiteral("exercisingForces")] = QString::fromStdString(exercise.forces());
obj[QStringLiteral("exercisingPurpose")] = QString::fromStdString(exercise.description());
file.resize(0);
file.write(QJsonDocument(obj).toJson());
return true;
}
void ExportedScriptsModel::addDownloadData(const ExportedScriptInformation &esi)
{
// Add exercise to ExportRootPath
{
// modify exercises.json
DEBUG_LOG(m_logger) << "Add exercise to ExportRootPath";
if (!QDir(esi.rootDirectory).exists())
{
ERROR_LOG(m_logger) << "Cannot modify exercises.json as the info's root directory '"<<esi.rootDirectory<<"' does not exist!";
return;
}
QFile file(esi.rootDirectory + QDir::separator() + QStringLiteral("exercises.json"));
if (!file.open(QFile::ReadWrite))
{
WARNING_LOG(m_logger) << "Can not open " << file.fileName() << " for read/write: " << file.errorString();
return;
}
const QString key = QStringLiteral("Exercises");
QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
QJsonObject obj = doc.object();
QJsonArray arr = obj.value(key).toArray();
if (!arr.contains(esi.exerciseId))
{
DEBUG_LOG(m_logger) << "arr.append Exercise=" << esi.exerciseId;
arr.append(esi.exerciseId);
}
obj.insert(key, arr);
file.resize(0);
file.write(QJsonDocument(obj).toJson());
INFO_LOG(m_logger) << " file.write " << QJsonDocument(obj).toJson();
file.close();
}
// NOTE: mkdir actually not necessary because the decompressed bzip file of the script already created the exercise directory
const QString baseDir = exercisePath(esi.rootDirectory, esi.exerciseId);
if (!QDir().mkpath(baseDir)) // NOTE: returns true if dir already exists
{
ERROR_LOG(m_logger) << "Cannot create destination path " << baseDir;
return;
}
// modify data.json
QFile file(baseDir + QDir::separator() + QStringLiteral("data.json"));
DEBUG_LOG(m_logger) << "File= " << file.fileName();
if (!file.open(QFile::ReadWrite))
{
WARNING_LOG(m_logger) << "Can not open " << file.fileName() << " for read/write: " << file.errorString();
return;
}
const QString keyArray = QStringLiteral("presentations");
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
QJsonObject obj = doc.object();
QJsonArray arr = obj.value(keyArray).toArray();
bool exported = false;
DEBUG_LOG(m_logger) << "arr.size=" << arr.size();
for (int i = 0; i < arr.size(); ++i)
{
auto arresi = ExportedScriptInformation::fromJson(arr.at(i));
if (arresi.scriptId == esi.scriptId &&
arresi.exerciseId == esi.exerciseId)
{
arresi.jsonPath = esi.jsonPath;
arr[i] = esi.toJson();
exported = true;
}
}
if (!exported)
{
DEBUG_LOG(m_logger) << "info scriptId=" << esi.scriptId
<< " Name=" << esi.scriptName
<< " Exercise=" << esi.exerciseName;
arr.append(esi.toJson());
}
obj.insert(keyArray, arr);
auto content = QJsonDocument(obj).toJson();
DEBUG_LOG(m_logger) << "content =" << content;
file.resize(0);
file.write(content);
file.close();
}
bool ExportedScriptsModel::setLocalState(const int row, const bool localHost)
{
ExportedScriptInformation data = getRowData(row);
data.local = localHost;
//data.exportTime = exportTime;
DEBUG_LOG(m_logger) << "setLocalState row=" << row
<< " scriptId=" << data.scriptId
<< " data.scriptName=" << data.scriptName
<< " Exercise=" << data.exerciseName;
bool result = setRowData(row, data);
if (result && localHost && m_rootDirectoryToScan.contains(QStringLiteral("Download")))
{
data.rootDirectory = m_rootDirectoryToScan;
setRowData(row, data); //again for new download path mabe not needed
addDownloadData(data);
}
return result;
}
bool ExportedScriptsModel::setUploadState(const int row,
const UploadState uploadState,
const qint64 uploadTime,
const qint64 fileSize)
{
ExportedScriptInformation data = getRowData(row);
data.uploaded = uploadState;
data.uploadTime = uploadTime;
data.fileSize = fileSize;
return setRowData(row, data);
}
bool ExportedScriptsModel::removeRow(int row)
{
ExportedScriptInformation data = getRowData(row);
if (!LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>::removeRow(row))
return false;
QString baseDir = exercisePath(data.rootDirectory, data.exerciseId);
QString filename = baseDir + QStringLiteral("data.json");
DEBUG_LOG(m_logger) << "filename= '" << filename << "'";
QFile file(baseDir + QStringLiteral("data.json"));
if (!file.open(QIODevice::ReadWrite))
{
DEBUG_LOG(m_logger) << "!file.open '" << filename << "'"
<< " no local scripts";
return true;
}
const QJsonDocument doc(QJsonDocument::fromJson(file.readAll()));
QJsonObject obj = doc.object();
const int exId = obj.value(QStringLiteral("exerciseId")).toInt();
QJsonArray arr;
for (const auto &esi : qAsConst(m_vecData))
{
if (esi.exerciseId == exId)
arr += esi.toJson();
}
obj[QStringLiteral("presentations")] = arr;
file.resize(0);
auto content = QJsonDocument(obj).toJson();
DEBUG_LOG(m_logger) << "content =" << content;
return file.write(QJsonDocument(obj).toJson());
}
QString ExportedScriptsModel::exercisePath(const QString &rootDirectory, const int exerciseId) const
{
QString path = QStringLiteral("%1/Exercise_%2/").arg(rootDirectory).arg(exerciseId);
#ifndef WIN32
path.prepend(QStringLiteral("/"));
#endif
return path;
}
//
// ExportedScriptInformation
//
QJsonValue ExportedScriptInformation::toJson() const
{
return QJsonObject({
{ QStringLiteral("displayName"), scriptName },
{ QStringLiteral("path"), jsonPath },
{ QStringLiteral("scriptId"), scriptId },
{ QStringLiteral("exportTime"), exportTime },
{ QStringLiteral("fileSize"), fileSize },
{ QStringLiteral("uploadTime"), uploadTime },
{ QStringLiteral("exerciseName"), exerciseName },
{ QStringLiteral("exerciseId"), exerciseId }
});
}
ExportedScriptInformation ExportedScriptInformation::fromJson(const QJsonValue &val,
const bool local,
const UploadState uploadState)
{
const QJsonObject obj = val.toObject();
ExportedScriptInformation esi;
esi.local = local;
esi.scriptName = obj[QStringLiteral("displayName")].toString();
if (esi.scriptName.isEmpty())
esi.scriptName = obj[QStringLiteral("name")].toString();
esi.jsonPath = obj[QStringLiteral("path")].toString();
esi.scriptId = obj[QStringLiteral("scriptId")].toInt();
esi.exportTime = qRound64(obj[QStringLiteral("exportTime")].toDouble());
esi.fileSize = obj[QStringLiteral("fileSize")].toInt();
esi.uploaded = uploadState;
esi.uploadTime = qRound64(obj[QStringLiteral("uploadTime")].toDouble());
esi.exerciseName = obj[QStringLiteral("exerciseName")].toString();
esi.exerciseId = obj[QStringLiteral("exerciseId")].toInt();
Q_ASSERT(esi.scriptId != -1);
Q_ASSERT(esi.exerciseId != -1);
if (esi.scriptId != -1 && esi.exerciseId != -1)
esi.valid = true;
return esi;
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* Show the exported scripts and allow them to upload them to the server
* $Id: tpm_exported_scripts_model.h 53111 2021-03-22 08:21:29Z wille_m $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_model.h $
* $Author: wille_m $
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
*/
#pragma once
#include <logger.h>
#include <LtsWidgets/lts_abstract_table_model.h>
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Plugins/AssetManagement/asm_data_plugin_interface.h>
#include <Common/thpcommon_export.h>
namespace TakeHomePackageManager
{
enum UploadState
{
ThpUnknownUploadState,
ThpUploadNotActive,
ThpConnectToHost,
ThpUploadActive,
ThpAlreadyUploaded,
ThpUploadError,
};
struct ExportedScriptInformation
{
bool valid { false };
QString exerciseName;
QString scriptName;
QString jsonPath;
QString rootDirectory; // temp
qint64 exerciseId = -1;
qint64 exportTime = 0;
qint64 fileSize = 0;
bool local = true; // On local Host
UploadState uploaded = ThpUnknownUploadState;
qint64 uploadTime = 0;
int scriptId = -1;
int id() const { return scriptId; }
QJsonValue toJson() const;
static ExportedScriptInformation fromJson(const QJsonValue &val,
const bool local = true,
const UploadState uploadStates = ThpUnknownUploadState);
};
class THPCOMMON_EXPORT ExportedScriptsModel : public LtsWidgets::LtsAbstractTableModel<ExportedScriptInformation>
{
Q_OBJECT
public:
enum Columns
{
ColId,
ColExercise,
ColName,
ColCreated,
ColDownloaded,
ColLocation,
ColUploadedTime,
ColUploaded,
};
explicit ExportedScriptsModel(QObject *parent = nullptr);
bool init(const QString &rootDirectoryToScan);
void setServerContent(const QString &jsonArray);
void setOC(bool isOC) { m_isOC = isOC; }
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool removeRow(int row) override;
bool setLocalState(const int row, const bool localHost);
bool setUploadState(const int row, const UploadState uploadState, const qint64 uploadTime, const qint64 fileSize);
bool createDataJson(const QString &rootDirectory, const std::shared_ptr<ExerciseManagement::Interface::Dtc> &exmDtc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc,
int exerciseId) const;
void addDownloadData(const ExportedScriptInformation &esi);
private:
QString exercisePath(const QString &rootDirectory, const int exerciseId) const;
qint64 downloadTime(const ExportedScriptInformation &val) const;
private:
bool m_isOC { false };
QString m_rootDirectoryToScan;
NGL::INGLLogger &m_logger;
};
}
@@ -0,0 +1,66 @@
#include "tpm_process_dialog.h"
#include "ui_tpm_process_dialog.h"
namespace TakeHomePackageManager
{
ProcessOverlay::ProcessOverlay(QWidget *parent)
: QWidget(parent)
, m_ui(new Ui::ProcessOverlay)
{
m_ui->setupUi(this);
connect(m_ui->pbCancel, &QPushButton::clicked,
this, [this]()
{
m_ui->pbCancel->setEnabled(false);
emit signalCancel();
});
}
ProcessOverlay::~ProcessOverlay()
{
delete m_ui;
}
void ProcessOverlay::setCancelEnabled(bool enabled)
{
m_ui->pbCancel->setEnabled(enabled);
}
void ProcessOverlay::setStatus(const QString &text, qint64 totalFiles, qint64 curFile, qint64 totalSize, qint64 curPos, const QString &curName)
{
if (!text.isEmpty()) // only set new text if not empty (we dont want to send the text on every update..
m_ui->lblStatus->setText(text);
if (!curName.isEmpty()) // only set new text if not empty (we dont want to send the text on every update..
m_ui->lblFilename->setText(curName);
double progressBytes = (totalSize == 0) ? (double)0 : ((double)curPos / (double)totalSize);
double progressFiles = (((double)curFile - 1) / (double)totalFiles);
double progressTotal = progressFiles + (progressBytes / (double)totalFiles);
progressBytes = qRound(progressBytes * 100);
progressFiles = qRound(progressFiles * 100);
progressTotal = qRound(progressTotal * 100);
m_ui->pbarFiles->setValue(progressFiles);
m_ui->pbarBytes->setValue(progressBytes);
m_ui->pbarProgress->setValue(progressTotal);
QLocale l;
if (curFile == 0)
m_ui->lblFiles->setText(QStringLiteral(" - "));
else
m_ui->lblFiles->setText(QStringLiteral("%1 / %2").arg(l.toString(curFile)).arg(l.toString(totalFiles)));
if (curPos == 0)
m_ui->lblBytes->setText(QStringLiteral(" - "));
else
m_ui->lblBytes->setText(QStringLiteral("%1 / %2").arg(l.toString(curPos)).arg(l.toString(totalSize)));
}
} // namespace TakehomePackageManager
@@ -0,0 +1,32 @@
#pragma once
#include <Common/thpcommon_export.h>
#include <QWidget>
namespace Ui
{
class ProcessOverlay;
}
namespace TakeHomePackageManager
{
class THPCOMMON_EXPORT ProcessOverlay : public QWidget
{
Q_OBJECT
public:
ProcessOverlay(QWidget *parent = nullptr);
~ProcessOverlay();
void setStatus(const QString &text = QString(), qint64 totalFiles = 1, qint64 curFile = 1, qint64 totalSize = 0, qint64 curPos = 0, const QString &curName = QString());
void setCancelEnabled(bool enabled = true);
signals:
void signalCancel();
private:
Ui::ProcessOverlay *m_ui;
bool m_decompressing { false };
};
} // namespace TakehomePackageManager
@@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProcessOverlay</class>
<widget class="QWidget" name="ProcessOverlay">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>220</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>700</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QLabel" name="lblBytes">
<property name="text">
<string notr="true"> - </string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Progress</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QProgressBar" name="pbarFiles">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="lblFiles">
<property name="text">
<string notr="true"> - </string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblStatus">
<property name="text">
<string notr="true">Preparing ...</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Files</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QProgressBar" name="pbarBytes">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="7" column="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pbCancel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0" colspan="3">
<widget class="Line" name="line">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblFilename">
<property name="text">
<string notr="true"> - </string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Bytes</string>
</property>
</widget>
</item>
<item row="5" column="1" colspan="2">
<widget class="QProgressBar" name="pbarProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Filename</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="Line" name="line_2">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,48 @@
#
# $Id: CMakeLists.txt 51804 2020-07-31 04:47:28Z fritsche_h $
# $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/CMakeLists.txt $
# $Author: fritsche_h $
#
# Created by Gerlinde Rudolphus, NLE, 2019-03-25
#
#
# Common
#
SET(thp_SRCS
tpm_gui_plugin.cpp
tpm_gui_plugin.h
tpm_exported_scripts_dialog.cpp
tpm_exported_scripts_dialog.h
logger.cpp
logger.h
)
QT5_WRAP_UI(thp_SRCS
tpm_exported_scripts_dialog.ui
)
#TODO: auto-remove "libTakeHomePackageManagerGuiPlugin.so" from lib as this lib replaces it
NGL_ADD_LIBRARY2(WS ThpManagementGuiPlugin MODULE ${thp_SRCS})
NGL_TARGET_LINK_LIBRARIES(ThpManagementGuiPlugin
ThpCommon
TpmGuiPluginInterface
Qt6::Widgets
Qt6::Network
Qt6::WebSockets
NGLLogger
LtsWidgets
LtsSession
LtsDataView
nglcompression
nglsettings
ngltools
AsmDataPluginInterface
ExmDataPluginInterface
LtsSessionData
httq
)
NGL_INSTALL_TARGET(ThpManagementGuiPlugin)
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id: logger.cpp 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.cpp $
* $Author: rudolphus_g $
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace TakeHomePackageManager
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "TPM.";
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();
}
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,22 @@
/**
* @RDE_HEADER@
* Logging for CentralEffectSimulation
* $Id: logger.h 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.h $
* $Author: rudolphus_g $
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace TakeHomePackageManager
{
typedef NGL::INGLLogger &LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,214 @@
/**
* @RDE_HEADER@
* Show the exported scripts and allow them to upload them to the server
* $Id: tpm_exported_scripts_dialog.h 51804 2020-07-31 04:47:28Z fritsche_h $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.h $
* $Author: fritsche_h $
*
* Created by Gerlinde Rudolphus, NLE, 2019-03-25
* Rebuilt to use httq by Mike Wille, NLE, 2020-07-07
*/
#pragma once
#include "logger.h"
#include <Common/tpm_exported_scripts_model.h>
#include <LtsWidgets/lts_dialog.h>
#include <QUrlQuery>
namespace Ui
{
class ExportedScriptsDialog;
}
namespace NGL
{
class Compress;
}
namespace LtsSession
{
class LtsSession;
}
class QTemporaryFile;
class QNetworkAccessManager;
class QNetworkReply;
class QNetworkRequest;
class QFileSystemWatcher;
namespace TakeHomePackageManager
{
class ProcessOverlay;
class HttpProcess : public QObject
{
Q_OBJECT
public:
HttpProcess(ExportedScriptInformation &&esi, QObject *parent);
virtual ~HttpProcess();
ProcessOverlay *process() const { return m_process; }
signals:
void signalReadyRead();
//void signalFailed(int status, const QString &msg);
void signalSuccess();
void signalTimeout();
void signalCancel();
void signalError();
private slots:
void slotReadyRead();
void slotError();
void slotSuccess();
protected slots:
void stopTimeout();
void resetTimeout();
private:
void doConnections(QNetworkReply *r);
protected:
// do not connect to QNetworkReply's readyRead, connect to this classe's signalReadyRead
QNetworkReply *post(const QString &path, QIODevice *data, const QUrlQuery &q = QUrlQuery(), const QString &contentType = QStringLiteral("application/octet-stream"));
QNetworkReply *get(const QString &path, const QUrlQuery &q = QUrlQuery());
QNetworkReply *deleteResource(const QString &path, const QUrlQuery &q = QUrlQuery());
ExportedScriptInformation m_esi;
bool m_isError { false };
NGL::INGLLogger &m_logger;
ProcessOverlay *m_process;
bool m_wasCanceled { false };
bool m_isTimeout { false };
private:
static QNetworkRequest forgeRequest(const QString &path, const QUrlQuery &q, const QString &contentType = QString());
QNetworkAccessManager *m_nam;
QTimer *m_timeout;
};
class UploadProcess : public HttpProcess
{
Q_OBJECT
public:
UploadProcess(const QString &rootFolder, ExportedScriptInformation &&esi, QObject *parent);
signals:
//void signalDone();
private slots:
void compressionDone();
private:
void abortCompress();
QTemporaryFile *m_tempFile;
NGL::Compress *m_compress;
QString m_rootDir;
};
class DownloadProcess : public HttpProcess
{
Q_OBJECT
public:
DownloadProcess(const QString &rootFolder, int modelRowId, ExportedScriptInformation &&esi, QObject *parent);
int modelRowId() const { return m_modelRowId; }
private slots:
void downloadFinished();
signals:
void signalDone();
private:
QTemporaryFile *m_tempFile;
NGL::Compress *m_compress { nullptr };
int m_modelRowId { -1 };
QString m_rootFolder;
};
class RemoveProcess : public HttpProcess
{
Q_OBJECT
public:
RemoveProcess(int modelRowId, ExportedScriptInformation &&esi, QObject *parent);
int modelRowId() const { return m_modelRowId; }
signals:
void signalDone();
private:
int m_modelRowId { -1 };
};
class ExportedScriptsDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit ExportedScriptsDialog(QWidget *parent = nullptr);
~ExportedScriptsDialog();
void retranslate() override;
bool init(LtsSession::LtsSession *session);
static QString getContentFolder(const QString &rootFolder, int exerciseId);
private slots:
void uploadScript();
void downloadScript();
void removeScriptFromServer();
void removeScriptLocal();
void openScript();
void onSelectionChanged();
private:
enum class WsConnectionStatus
{
NotConnected,
Connecting,
Disconnected,
Error,
Connected,
};
void startUpload();
QString getContentFolder(int exerciseId) const;
QString getRootFolder() const;
bool initModel();
void setWsConnectionStatus(WsConnectionStatus status);
void setWsConnectionStatusLabel();
private:
ExportedScriptsModel *m_model;
Ui::ExportedScriptsDialog *m_ui;
LtsSession::LtsSession *m_session;
int m_exerciseId;
QFileSystemWatcher *m_fsWatcher;
QString m_lastServerContent;
WsConnectionStatus m_wsStatus { WsConnectionStatus::NotConnected };
NGL::INGLLogger &m_logger;
};
} // namespace TakeHomePackageManager
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ExportedScriptsDialog</class>
<widget class="QWidget" name="ExportedScriptsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>834</width>
<height>491</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>The following Scripts are already exported. You can remove them from your local hard disk or upload them to the server here.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Server connection status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblWsStatus">
<property name="text">
<string notr="true">-</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>
<item>
<widget class="LtsDataView::LtsDataView" name="ltsDataView">
<property name="showColumnCount" stdset="0">
<bool>false</bool>
</property>
<property name="toolButtonsVisible">
<bool>false</bool>
</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,60 @@
/**
* @RDE_HEADER@
* GUI Plugin for ExerciseManangement
* $Id: tpm_gui_plugin.cpp 48572 2019-10-02 11:47:04Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.cpp $
* $Author: rudolphus_g $
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "tpm_gui_plugin.h"
#include "tpm_exported_scripts_dialog.h"
#include "logger.h"
namespace TakeHomePackageManager
{
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("Plugins.Gui.Tpm"))
{}
bool InterfaceImpl::fetchInterfaces()
{
return true;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Presentation");
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
if (m_session->app() == LtsAppDescriptor::App::Scripts)
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
else
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.identifier = QLatin1String("PresentationManagement.ScriptDialog.ManageExportedScripts");
plData.menuIcon = QIcon(QLatin1String("icon:export_documents.svg"));
plData.menuText = tr("Manage Exported Scripts");
// plData.operatorRight = OperatorRights::ScriptEditorEdit;
plData.createWidgetFunc = [this](QWidget *)
{
return LtsWidgets::initDialog(new ExportedScriptsDialog(), m_session);
};
ret.append(plData);
return ret;
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* Gui Plugin for MarkerManagement
* $Id: tpm_gui_plugin.h 49807 2020-02-06 09:53:16Z ehrlicher $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.h $
* $Author: ehrlicher $
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <OldTHP/tpm_gui_plugin_interface.h>
#include "logger.h"
#include <LtsSession/lts_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
namespace TakeHomePackageManager
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:tpm.man")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("TakeHomePackageManager"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return partition == IDL::PartitionType::Exercise;
}
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface, public ForeignTpmGuiInterface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
bool fetchInterfaces() override;
QString menuGroup() const override;
QVector<LtsWidgets::PluginData> getActions() override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise; }
private:
};
} // namespace TakeHomePackageManager
@@ -0,0 +1,44 @@
#
# $Id: CMakeLists.txt 51804 2020-07-31 04:47:28Z fritsche_h $
# $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/CMakeLists.txt $
# $Author: fritsche_h $
#
# Created by Mike Wille, SLTE, 2020-03-23
#
SET(src
tpm_usb_gui_plugin.cpp
tpm_usb_gui_plugin.h
tpm_usb_dialog.cpp
tpm_usb_dialog.h
logger.cpp
logger.h
)
QT5_WRAP_UI(src
tpm_usb_dialog.ui
)
NGL_ADD_LIBRARY2(WS ThpUsbManagementGuiPlugin MODULE ${src})
NGL_TARGET_LINK_LIBRARIES(ThpUsbManagementGuiPlugin
ThpCommon
TpmGuiPluginInterface
Qt6::Widgets
Qt6::Network
Qt6::WebSockets
NGLLogger
LtsWidgets
LtsSession
LtsDataView
nglcompression
nglsettings
ngltools
LtsSessionData
# for creating data.json
AsmDataPluginInterface
ExmDataPluginInterface
)
NGL_INSTALL_TARGET(ThpUsbManagementGuiPlugin)
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id: logger.cpp 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.cpp $
* $Author: rudolphus_g $
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace TakeHomePackageManager
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "TPM.USB.";
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();
}
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,22 @@
/**
* @RDE_HEADER@
* Logging for CentralEffectSimulation
* $Id: logger.h 46225 2019-03-25 13:59:56Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/logger.h $
* $Author: rudolphus_g $
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace TakeHomePackageManager
{
typedef NGL::INGLLogger &LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace TakeHomePackageManager
@@ -0,0 +1,138 @@
/**
* @RDE_HEADER@
* Show the exported scripts and allow them to upload them to the server
* $Id: tpm_exported_scripts_dialog.h 51804 2020-07-31 04:47:28Z fritsche_h $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_exported_scripts_dialog.h $
* $Author: fritsche_h $
*
* Created by Mike Wille, SLTE, 2020-03-23
*/
#pragma once
#include "logger.h"
#include <LtsWidgets/lts_dialog.h>
#include <QFileInfoList>
namespace Ui
{
class USBDialog;
}
namespace LtsSession
{
class LtsSession;
}
namespace TakeHomePackageManager
{
class ExportedScriptsModel;
enum eLayerType
{
elevation,
map,
sceneObject,
scene
};
struct layerMappinEntry
{
eLayerType type;
QString description;
int maxScale;
int minScale;
QString name;
QString url;
public:
layerMappinEntry()
: type(eLayerType::map), description(QStringLiteral("NOTSET")), maxScale(125), minScale(-1), name(QStringLiteral("_name")), url(QStringLiteral("_url"))
{
}
layerMappinEntry(eLayerType _type, QString _description, int _maxScale, int _minScale, QString _name, QString _url)
: type(_type), description(_description), maxScale(_maxScale), minScale(_minScale), name(_name), url(_url)
{
}
};
class USBDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
/**
* @brief USBDialog => WARNING: throws! catch exception!
* @param session
* @param parent
*/
explicit USBDialog(LtsSession::LtsSession *session, QWidget *parent = nullptr);
~USBDialog();
void retranslate() override;
static QString getContentFolder(const QString &rootFolder, int exerciseId);
QString getContentFolder(const QString &rootFolder) const;
static QString exerciseDirectoryName(int exerciseId);
static QString getScriptFolder(const QString &rootFolder, int exerciseId, int scriptId);
signals:
void nextMap();
private slots:
void slotSelectionChanged();
void slotCopy();
void slotCopyMaps();
void slotCopyWinBin();
void slotCopyWinBinP2();
void slotCopyWinBinP3();
void slotRemove();
void slotRemoveMaps();
void slotChangePath();
void onNextMap();
void generateLayerMappingXML();
private:
enum class What
{
ScriptWasDeletedFromExercise,
ExerciseDirectoryWasDeleted,
NewExerciseDirectoryWasCreated,
ScriptWasCopiedToExercise,
};
void adjustJsonFiles(What what);
void initModel();
void initUsbModel();
void scriptDeleted(int scriptId);
void scriptCopied(int scriptId);
QFileInfoList prepareFileInfoListForFolder(QString src, QFileInfoList fil);
QString getContentFolder(int exerciseId) const;
QString getContentFolder() const;
QString getRootFolder(bool forceDownloadDir = false) const;
QString exerciseDirectoryName() const;
QString getScriptFolder(const QString &rootFolder, int scriptId) const;
QString getScriptFolder(int scriptId) const;
ExportedScriptsModel *m_localThpModel;
ExportedScriptsModel *m_usbThpModel;
Ui::USBDialog *m_ui;
LtsSession::LtsSession *m_session;
int m_exerciseId;
int m_paths;
QMap<QString, QFileInfoList> m_files;
QString m_usbRoot;
NGL::INGLLogger &m_logger;
int m_finished_mapCopys;
};
} // namespace TakeHomePackageManager
@@ -0,0 +1,274 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>USBDialog</class>
<widget class="QWidget" name="USBDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1081</width>
<height>541</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QGroupBox" name="verticalGroupBox">
<property name="title">
<string>Local</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget_3">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>THPs</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="tvLocal">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCopy">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Copy selected THPs to USB</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_4">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Maps</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="tvLocalMaps">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Type</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Path</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCopyMaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Copy selected Maps to USB</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QGroupBox" name="verticalGroupBox_2">
<property name="title">
<string>On USB</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Path:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblPath">
<property name="text">
<string>(none)</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbCHange">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Change path</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>
<item>
<widget class="QSplitter" name="splitter_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>THPs</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tvUSB">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbRemove">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove selected THPs from USB</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="verticalLayoutWidget_5">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Maps</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="tvUSBMaps">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbRemoveMaps">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove selected Maps from USB</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,102 @@
/**
* @RDE_HEADER@
* GUI Plugin for ExerciseManangement
* $Id: tpm_gui_plugin.cpp 48572 2019-10-02 11:47:04Z rudolphus_g $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.cpp $
* $Author: rudolphus_g $
*
* Created by Mike Wille, SLTE, 2020-03-23
*/
#include "tpm_usb_gui_plugin.h"
#include "tpm_usb_dialog.h"
#include "logger.h"
#include <QDebug>
namespace TakeHomePackageManager
{
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("Plugin"))
{
}
bool InterfaceImpl::fetchInterfaces()
{
return true;
}
QString InterfaceImpl::menuGroup() const
{
return tr("Presentation");
}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
if (m_session->app() == LtsAppDescriptor::App::Scripts)
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
else
plData.exercisePart = LtsWidgets::ExercisePart::eAfterAction;
plData.identifier = QLatin1String("THP.USB.Dialog");
plData.menuIcon = QIcon(QLatin1String("icon:export_documents.svg"));
plData.menuText = tr("USB Management");
// plData.operatorRight = OperatorRights::ScriptEditorEdit;
plData.createWidgetFunc = [this](QWidget *)
{
return LtsWidgets::createThrowableDialog<USBDialog>(m_session);
};
ret.append(plData);
return ret;
}
} // namespace TakeHomePackageManager
//
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* Gui Plugin for MarkerManagement
* $Id: tpm_gui_plugin.h 49807 2020-02-06 09:53:16Z ehrlicher $
* $HeadURL: svn://slssvn/TDS/branches/product/iterations/21_03_salted_caramel/Lts/Plugins/TakeHomePackageManager/tpm_gui_plugin.h $
* $Author: ehrlicher $
*
* Created by Mike Wille, SLTE, 2020-03-23
*/
#pragma once
#include <OldTHP/tpm_gui_plugin_interface.h>
#include "logger.h"
#include <LtsSession/lts_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
namespace TakeHomePackageManager
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:tpm.usb")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("ThpUsbManager"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return partition == IDL::PartitionType::Exercise;
}
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface, public ForeignTpmGuiInterface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
bool fetchInterfaces() override;
QString menuGroup() const override;
QVector<LtsWidgets::PluginData> getActions() override;
bool availableInPartition(IDL::PartitionType partition) const override { return partition == IDL::PartitionType::Exercise; }
private:
};
} // namespace TakeHomePackageManager
@@ -0,0 +1,25 @@
/**
* @RDE_HEADER@
* HMI Plugin for Exercisemanagemt (Interface for access from other Plugins)
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <QString>
namespace TakeHomePackageManager
{
class ForeignTpmGuiInterface
{
public:
ForeignTpmGuiInterface() = default;
virtual ~ForeignTpmGuiInterface() = default;
static QString pluginName() { return QStringLiteral("TpmGuiPlugin"); }
};
} // namespace TakeHomePackageManager
@@ -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();
};
}
@@ -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
@@ -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
Loaded 100 of 154 files, more files were not shown because too many files have changed in this diff. Show more