Add Lts/Application module with Qt6 CMakeLists updates

- Copy Lts/Application from qt5-before (AarApp, OpApp, THP submodules)
- Update all CMakeLists.txt to use Qt6::Core, Qt6::Widgets, Qt6::Xml, Qt6::Network, Qt6::PrintSupport, Qt6::Svg, Qt6::WebEngineCore, Qt6::WebEngineWidgets
- No SIGNAL/SLOT, QRegExp, QDesktopWidget, or QTextCodec issues found in Application source code
- Module ready for Qt6 build validation
This commit is contained in:
torsten committed 2026-08-25 13:28:58 +02:00
1 parent 079175a055
commit 45eb8ee689
68 files changed
+9120

No files matched your search

@@ -0,0 +1,37 @@
#
# $Id$
# $HeadURL$
# $Author$
# Created by Hanna Rathje, SLSE, 2017-12-14
#
SET(SRCS
aar_application.cpp
aar_application.h
databases_model.cpp
databases_model.h
logger.cpp
logger.h
main.cpp
)
NGL_WRAP_UI(SRCS aarApp.ui)
NGL_ADD_EXECUTABLE(AAR aarApp ${SRCS})
NGL_TARGET_LINK_LIBRARIES(aarApp
Qt6::Widgets
Qt6::Core
nglapplication
ngldb
nglcoreapplication
ngltools
ngltranslator
nglwidgets
NGLLogger
LtsCommandLineParser
)
NGL_INSTALL_TARGET(aarApp)
NGL_INSTALL(AAR FILES log4cxx_aarApp.props DESTINATION bin)
@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AarApplication</class>
<widget class="QMainWindow" name="AarApplication">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>846</width>
<height>382</height>
</rect>
</property>
<property name="windowTitle">
<string>Database administration</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Status:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblStatus">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>No active database</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="lblDatabases">
<property name="text">
<string>Databases:</string>
</property>
</widget>
</item>
<item>
<widget class="QTableView" name="tableViewDatabases">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<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>
<item>
<widget class="QPushButton" name="pbActivateNetwork">
<property name="text">
<string>Activate network database</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbActivate">
<property name="text">
<string>Activate local database</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbRemove">
<property name="text">
<string>Remove database</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbClose">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<action name="actionShow">
<property name="text">
<string>&amp;Show</string>
</property>
</action>
<action name="actionStart">
<property name="text">
<string>&amp;Start</string>
</property>
</action>
<action name="actionStop">
<property name="text">
<string>St&amp;op</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
<slots>
<slot>oeffnenClicked()</slot>
</slots>
</ui>
@@ -0,0 +1,501 @@
/*
* @RDE_HEADER@
* <short description>
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Schröder, SLSE, 2016-06-08
*/
#include "aar_application.h"
#include "ui_aarApp.h"
#include "databases_model.h"
#include <ngldb/dbconnection.h>
#include <ngldb/nsqlquery.h>
#include <nglsettings/nglsettings_settings.h>
#include <ngltools/ngltools_filesystem.h>
#include <QMessageBox>
#include <QtCore/QTimer>
#include <QMenu>
#include <QSettings>
#include <QDir>
#include <QThread>
#include <QScreen>
#include <QSqlError>
#define DEFAULT_DB "lts_ets_pro"
namespace AARApplication
{
AarApplication::AarApplication(QWidget *parent)
: QMainWindow(parent)
, m_ui(new Ui::AarApplication)
, m_stopping(false)
, m_proc(nullptr)
, m_progressDialog(nullptr)
, m_logger(getLogger("AarApplication"))
{
setAttribute(Qt::WA_DeleteOnClose);
m_ui->setupUi(this);
const QList<QScreen*> &screenList = QGuiApplication::screens();
m_widgets.reserve(screenList.size());
for (QScreen *s : qAsConst(screenList))
{
QWidget *w = new QWidget(); // no parent
w->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
w->setGeometry(s->geometry());
w->activateWindow();
w->show();
m_widgets.append(w);
}
m_databasesModel = new DatabasesModel(this);
m_proxyModel = new DatabasesProxyModel(this);
m_proxyModel->setSourceModel(m_databasesModel);
m_ui->tableViewDatabases->setModel(m_proxyModel);
connect(m_ui->pbClose, &QPushButton::clicked, this, &AarApplication::onClosedClicked);
connect(m_ui->pbRemove, &QPushButton::clicked, this, &AarApplication::onRemoveClicked);
connect(m_ui->pbActivate, &QPushButton::clicked, this, &AarApplication::onActivateClicked);
connect(m_ui->pbActivateNetwork, &QPushButton::clicked, this, &AarApplication::onActivateNetworkClicked);
connect(m_ui->tableViewDatabases->selectionModel(), &QItemSelectionModel::selectionChanged, this, &AarApplication::enaDisa);
setWindowTitle(QLatin1String("AarApplication"));
enaDisa();
m_progressDialog = new QProgressDialog(this);
m_progressDialog->setLabelText(tr("Stopping server software ..."));
m_progressDialog->setMinimum(0);
m_progressDialog->setMaximum(0);
m_progressDialog->show();
onStopApp();
}
AarApplication::~AarApplication()
{
delete m_ui;
delete m_databasesModel;
delete m_progressDialog;
for (QSqlDatabase db : qAsConst(m_dbMap))
db.close();
m_dbPostgres.close();
if (m_proc)
m_proc->deleteLater();
qDeleteAll(m_widgets);
}
bool AarApplication::init()
{
m_ui->lblStatus->setText(tr("No active database"));
m_dbPostgres = DBConnection::open(m_logger,
QStringLiteral("postgres"),
QStringLiteral("postgres"),
QStringLiteral("QPSQL"),
QStringLiteral("localhost"),
QStringLiteral("postgres"),
QString(),
QStringLiteral("localhost_postgres"));
if (!m_dbPostgres.open())
{
ERROR_LOG(m_logger) << "Error opening postgres database" << m_dbPostgres.lastError().databaseText();
return false;
}
NSqlQuery q(m_dbPostgres);
QString stmt = QStringLiteral("SELECT datname FROM pg_database WHERE datname = '") + QStringLiteral(DEFAULT_DB) + QStringLiteral("'");
if (!q.execAndFetch(stmt))
{
NOTICE_LOG(m_logger) << "Database does not exist: " << DEFAULT_DB;
}
else
{
QSqlDatabase db_lts = DBConnection::open(m_logger,
QStringLiteral("ctc_sv"),
QStringLiteral("ctc11de"),
QStringLiteral("QPSQL"),
QStringLiteral("localhost"),
QStringLiteral(DEFAULT_DB),
QString(),
QStringLiteral("localhost_")+QStringLiteral(DEFAULT_DB));
if (!db_lts.open())
{
ERROR_LOG(m_logger) << "Error opening database " << DEFAULT_DB << " : "<< db_lts.lastError().databaseText();
m_dbPostgres.close();
return false;
}
NSqlQuery ql(db_lts);
stmt = QStringLiteral("SELECT id FROM exm.exercise");
if (!ql.execAndFetch(stmt))
{
ERROR_LOG(m_logger) << "No entry for exercise found in database " << DEFAULT_DB;
}
else
{
int dbId = ql.value(0).toInt();
db_lts.close();
QString dbName = QStringLiteral("lts_db_") + QString::number(dbId);
INFO_LOG(m_logger) << "New dbName for " << DEFAULT_DB << ": " << dbName;
stmt = QStringLiteral("ALTER DATABASE ") + QStringLiteral(DEFAULT_DB) + QStringLiteral(" RENAME TO ") + dbName;
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error renaming database " << DEFAULT_DB << " : " << db_lts.lastError().databaseText();
m_dbPostgres.close();
return false;
}
}
}
stmt = QStringLiteral("SELECT datname FROM pg_database WHERE datname ilike 'lts_db_%';");
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Error while reading dbnames from database";
return false;
}
while (q.next())
{
QString dbName = q.value(0).toString();
QSqlDatabase db = DBConnection::open(m_logger,
QStringLiteral("ctc_sv"),
QStringLiteral("ctc11de"),
QStringLiteral("QPSQL"),
QStringLiteral("localhost"),
dbName,
QString(),
QStringLiteral("localhost_") + dbName);
if (!db.open())
{
ERROR_LOG(m_logger) << "Error while opening database " << dbName << ", error: " << db.lastError().databaseText();
continue;
}
m_databasesModel->initModel(db);
m_dbMap.insert(dbName, db);
}
m_ui->tableViewDatabases->resizeColumnsToContents();
return true;
}
void AarApplication::onClosedClicked()
{
handleProcInit(QStringLiteral("/usr/bin/openbox"), {QStringLiteral("--exit")});
m_proc->start();
m_proc->waitForFinished();
QThread::sleep(2);
close();
}
void AarApplication::onRemoveClicked()
{
disableButtons();
m_databaseToRemove = m_databasesModel->getRowData(m_ui->tableViewDatabases->currentIndex().row());
QString strMsg = QStringLiteral("Do you really want to remove the database '%1'?").arg (m_databaseToRemove.dbName);
int result = QMessageBox::question(this, tr("Remove Database"), strMsg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (result == QMessageBox::No)
{
enableButtons();
return;
}
DEBUG_LOG(m_logger) << "onRemoveClicked: dbName = " << m_databaseToRemove.dbName;
QSqlDatabase db = m_dbMap.value(m_databaseToRemove.dbName);
db.close();
const QStringList args({QStringLiteral("-U"),
QStringLiteral("postgres"),
QStringLiteral("-h"),
QStringLiteral("localhost"),
m_databaseToRemove.dbName
});
QString errStr;
const QString exePath = NGL::postgresExecutablePath(QStringLiteral("dropdb"), errStr);
if (!errStr.isEmpty())
WARNING_LOG(m_logger) << errStr;
handleProcInit(exePath, args);
connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &AarApplication::onDropDbFinished);
// start process
m_proc->start();
}
void AarApplication::onActivateClicked()
{
const QString fileName = QStringLiteral("/shd/CTC/WS/lts-cc.conf.org");
WARNING_LOG(m_logger) << "FileName: " << fileName;
QFile ltsConfig(fileName);
if (ltsConfig.exists())
ltsConfig.rename(QStringLiteral("/shd/CTC/WS/lts-cc.conf.org"), QStringLiteral("/shd/CTC/WS/lts-cc.conf"));
Exercise ex = m_databasesModel->getRowData(m_ui->tableViewDatabases->currentIndex().row());
QString strMsg = tr("Do you really want to activate database '%1'?").arg(ex.name);
int result = QMessageBox::question(this, tr("Activate local database"), strMsg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (result == QMessageBox::No)
return;
INFO_LOG(m_logger) << "onActivateClicked: dbName = " << ex.dbName;
QSqlDatabase db = m_dbMap.value(ex.dbName);
db.close();
NSqlQuery q(m_dbPostgres);
QString stmt = QStringLiteral("ALTER DATABASE ") + ex.dbName + QStringLiteral(" RENAME TO ") + QStringLiteral(DEFAULT_DB);
if (!q.exec(stmt))
ERROR_LOG(m_logger) << "Error renaming " << ex.dbName << " to " << DEFAULT_DB << " "<< m_dbPostgres.lastError().databaseText();
else
INFO_LOG(m_logger) << "Database " << ex.dbName << " renamed to " << DEFAULT_DB;
QString status = ex.name + QStringLiteral(" (") + ex.dbName + QStringLiteral(") ") + tr("active");
m_ui->lblStatus->setText(status);
onStartApp();
}
void AarApplication::onActivateNetworkClicked()
{
onStopApp();
//const QString fileName = QDir::homePath()+ QStringLiteral("/lts-cc.conf");
const QString fileName = QStringLiteral("/shd/CTC/WS") + QStringLiteral("/lts-cc.conf");
WARNING_LOG(m_logger) << "FileName: " << fileName;
QFile ltsConfig(fileName);
if (!ltsConfig.exists())
{
QMessageBox::critical(this, tr ("Activate network database"), tr("Network database active"), QMessageBox::Ok);
m_ui->lblStatus->setText(tr("Network database active"));
return;
}
ltsConfig.rename(QStringLiteral("/shd/CTC/WS/lts-cc.conf"), QStringLiteral("/shd/CTC/WS/lts-cc.conf.org"));
m_ui->lblStatus->setText(tr("Network database active"));
}
void AarApplication::enaDisa()
{
if (!m_ui)
return;
int selected = m_ui->tableViewDatabases->selectionModel()->selectedRows().size();
m_ui->pbRemove->setEnabled(selected > 0);
m_ui->pbActivate->setEnabled(selected > 0);
}
void AarApplication::onDropDbFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
INFO_LOG(m_logger) << "onDropDbFinished: exitCode: " << exitCode << ", exitStatus: " << exitStatus;
static const QString s_title = tr("Remove Database");
static const QString s_msgFailure = tr("Removing database failed.");
static const QString s_msgSuccess = tr("Removing database successful.");
if (exitCode > 0)
{
const QString str = QString::fromLocal8Bit(m_proc->readAllStandardError()) + QLatin1Char('\n') +
QString::fromLocal8Bit(m_proc->readAllStandardOutput());
ERROR_LOG(m_logger) << "Cannot drop database: " << str;
QString msg = s_msgFailure;
QString db0 = QLatin1String("ERROR: database \"") + m_databaseToRemove.dbName + QLatin1String("\" does not exist");
if (!str.contains(db0))
msg += tr(" Database cannot be removed:\n%1.").arg(str);
QMessageBox::critical(this, s_title, msg, QMessageBox::Ok);
}
else
{
m_databasesModel->onDataChanged(m_databaseToRemove, DataModification::eRemoved);
QMessageBox::information(this, s_title, s_msgSuccess);
}
enableButtons();
}
void AarApplication::onErrorOccured(QProcess::ProcessError error)
{
ERROR_LOG(m_logger) << "error: " << error;
}
void AarApplication::onStartApp()
{
handleProcInit(QStringLiteral("ps"), {QStringLiteral("aux")});
connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &AarApplication::onPsauxFinished);
m_proc->start();
}
void AarApplication::onPsauxFinished()
{
enableButtons();
QByteArray output = m_proc->readAllStandardOutput();
if (output.contains("svcServiceManagement"))
{
ERROR_LOG(m_logger) << "ServiceManagement already started";
}
else
{
ERROR_LOG(m_logger) << "Start svcServiceManagement";
handleProcInit(QCoreApplication::applicationDirPath() + QStringLiteral("/svcServiceManagement"), {QStringLiteral("-z")});
connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &AarApplication::onStartAppFinished);
m_proc->start();
m_proc->waitForFinished();
}
}
void AarApplication::onStartAppFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
handleProcFinished(exitCode, exitStatus);
}
void AarApplication::onStopApp()
{
disableButtons();
handleProcInit(QStringLiteral("ps"), {QStringLiteral("aux")});
m_proc->start();
if (m_proc->waitForFinished())
onPsauxStopFinished();
}
void AarApplication::onPsauxStopFinished()
{
DEBUG_LOG(m_logger) << "IN onPsauxStopFinished";
enableButtons();
QByteArray output = m_proc->readAllStandardOutput();
if (output.contains("svcServiceManagement"))
{
if (m_stopping)
{
QThread::sleep(3);
onStopApp();
}
else
{
ERROR_LOG(m_logger) << "ServiceManagement started - stopping";
handleProcInit(QCoreApplication::applicationDirPath() + QStringLiteral("/svcServiceManagement"), {QStringLiteral("-s")});
connect(m_proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
this, &AarApplication::onStopAppFinished);
m_proc->start();
m_stopping = true;
}
}
else
{
INFO_LOG(m_logger) << "ServiceManagement already stopped";
m_stopping = false;
m_progressDialog->hide();
}
m_proc->waitForFinished();
}
void AarApplication::onStopAppFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
if (m_stopping)
onPsauxStopFinished();
handleProcFinished(exitCode, exitStatus);
}
void AarApplication::handleProcInit(const QString &program, const QStringList &args)
{
if (m_proc)
{
m_proc->disconnect(this);
m_proc->deleteLater();
}
m_proc = new QProcess(this);
NOTICE_LOG(m_logger) << "Starting process '" << program << "' with args '"
<< args.join(QLatin1Char(' ')) << "'";
m_proc->setProgram(program);
m_proc->setArguments(args);
connect(m_proc, &QProcess::errorOccurred, this, &AarApplication::onErrorOccured);
disableButtons();
}
void AarApplication::handleProcFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
enableButtons();
if (!m_proc)
return;
if (exitCode != 0 || exitStatus != 0)
{
QByteArray out = m_proc->readAllStandardError();
if (!out.isEmpty())
out += '\n';
out += m_proc->readAllStandardOutput();
ERROR_LOG(m_logger) << "Process finished - exitCode: " << exitCode << ", exitStatus = " << exitStatus << ", " << out;
QMessageBox::critical(this,
tr ("Execute process"), tr("Error executing process, exitCode: %1, exitStatus: %2, output: %3")
.arg(exitCode).arg(exitStatus).arg(QString::fromLocal8Bit(out)),
QMessageBox::Ok);
}
m_proc->disconnect(this);
m_proc->deleteLater();
m_proc = nullptr;
if (m_progressDialog)
m_progressDialog->hide();
}
void AarApplication::enaDisaButtons(bool bEnable)
{
{
const QList<QPushButton*> l = findChildren<QPushButton*>();
for (QPushButton *pb : l)
pb->setEnabled(bEnable);
}
{
const QList<QAction*> l = findChildren<QAction*>();
for (QAction *a : l)
a->setEnabled(bEnable);
}
if (bEnable)
enaDisa();
}
} // namespace AarApplication
@@ -0,0 +1,78 @@
/*
* @RDE_HEADER@
* <short description>
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Schröder, SLSE, 2016-06-08
*/
#pragma once
#include <QMainWindow>
#include <QtCore/QMap>
#include <QStandardItemModel>
#include <QtCore/QVector>
#include <QCloseEvent>
#include <QProcess>
#include <QProgressDialog>
#include <QMenuBar>
#include <QSqlDatabase>
#include "logger.h"
#include "databases_model.h"
namespace Ui {
class AarApplication;
}
namespace AARApplication
{
class AarApplication : public QMainWindow
{
Q_OBJECT
public:
explicit AarApplication(QWidget *parent = nullptr);
~AarApplication();
bool init();
private slots:
void onClosedClicked();
void onRemoveClicked();
void onActivateClicked();
void onActivateNetworkClicked();
void onDropDbFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onPsauxFinished();
void onStartAppFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onPsauxStopFinished();
void onStopAppFinished(int exitCode, QProcess::ExitStatus exitStatus);
void onErrorOccured(QProcess::ProcessError error);
void onStartApp();
void onStopApp();
private:
void enaDisa();
void handleProcInit(const QString &program, const QStringList &args);
void handleProcFinished(int exitCode, QProcess::ExitStatus exitStatus);
void disableButtons() { enaDisaButtons(false); }
void enableButtons() { enaDisaButtons(true); }
void enaDisaButtons(bool bEnable);
private:
Ui::AarApplication *m_ui;
DatabasesModel *m_databasesModel;
DatabasesProxyModel *m_proxyModel;
QMap<QString, QSqlDatabase> m_dbMap;
bool m_stopping;
Exercise m_databaseToRemove;
QProcess *m_proc;
QSqlDatabase m_dbPostgres;
QProgressDialog *m_progressDialog;
QVector<QWidget*> m_widgets;
LOGGER_T m_logger;
};
} // namespace AARApplication
@@ -0,0 +1,214 @@
/**
* @RDE_HEADER@
* PresentationManangement - Script Overview Dialog Model
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-15
*/
#include "databases_model.h"
#include <ngltools/ngltools_dtg.h>
#include <ngldb/nsqlquery.h>
#include <QtCore/QDateTime>
#include <QPalette>
namespace AARApplication
{
DatabasesModel::DatabasesModel(QObject *parent)
: QAbstractTableModel(parent)
, m_logger(getLogger("DatabasesModel"))
{
beginResetModel();
m_vecData.clear();
endResetModel();
}
bool DatabasesModel::initModel(const QSqlDatabase &db)
{
NSqlQuery q(db);
QString stmt = QStringLiteral("SELECT id, name, begin_time, end_time, exercise_state_id FROM exm.exercise");
if (!q.prepare(stmt))
{
ERROR_LOG(m_logger) << "Preparing statement '" << stmt << "' failed.";
return false;
}
if (!q.exec(stmt))
{
ERROR_LOG(m_logger) << "Executing statement '" << stmt << "' failed.";
return false;
}
int count = 0;
while (q.next())
{
if (count > 0)
{
WARNING_LOG(m_logger) << "More than one exercise detected. There should be only one exercise per database.";
break;
}
Exercise exercise;
exercise.m_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.stateId = q.value(4).toInt();
TRACE_LOG(m_logger) << "Found exercise with id=" << exercise.m_id << ", name=" << exercise.name << ", startTime="
<< exercise.startTime << ", endTime=" << exercise.endTime << ", dbName=" << exercise.dbName
<< ", stateId=" << exercise.stateId;
if (exercise.stateId != 6)
onDataChanged(exercise, DataModification::eAdded);
++count;
}
INFO_LOG(m_logger) << "initModel - #exercises: " << m_vecData.size();
return true;
}
QVariant DatabasesModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !index.isValid() || index.row() >= m_vecData.size())
return QVariant();
const Exercise &e = m_vecData.at(index.row());
switch (index.column())
{
case Id:
return e.m_id;
case Name:
return QString(e.name);
case StartTime:
return DTG::toStringMSec(e.startTime);
case EndTime:
return DTG::toStringMSec(e.endTime);
}
return QVariant();
}
int DatabasesModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_vecData.size();
}
int DatabasesModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : EndTime + 1;
}
QVariant DatabasesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal
&& role == Qt::DisplayRole)
{
Columns col = static_cast<Columns>(section);
switch (col)
{
case Id:
return tr("Id");
case Name:
return tr("Name");
case StartTime:
return tr("Start Time");
case EndTime:
return tr("End Time");
}
}
return QAbstractTableModel::headerData(section, orientation, role);
}
Exercise DatabasesModel::getRowData(int row) const
{
if (row < 0 || row >= m_vecData.size())
return Exercise();
return m_vecData.at(row);
}
bool DatabasesModel::insertRow(const Exercise &item)
{
beginInsertRows(QModelIndex(), m_vecData.size(), m_vecData.size());
m_vecData.append(item);
endInsertRows();
return true;
}
bool DatabasesModel::removeRow(const int &row)
{
beginRemoveRows(QModelIndex(), row, row);
m_vecData.remove(row);
endRemoveRows();
return true;
}
void DatabasesModel::onDataChanged(const Exercise &data, DataModification modType)
{
int row = -1;
switch (modType)
{
case DataModification::eAdded:
row = m_vecData.size();
if (insertRow(data))
emit dataChanged(index(row, Columns::Id), index(row, Columns::EndTime));
return;
case DataModification::eModified:
row = m_vecData.indexOf(data);
if (row != -1)
{
m_vecData[row] = data;
emit dataChanged(index(row, Columns::Id), index(row, Columns::EndTime));
}
return;
case DataModification::eRemoved:
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));
return;
} // switch
}
void DatabasesProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
m_sourceModel = qobject_cast<DatabasesModel*>(sourceModel);
QSortFilterProxyModel::setSourceModel(sourceModel);
}
bool DatabasesProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (!m_sourceModel ||
source_left.column() != source_right.column() ||
!source_left.isValid() ||
!source_right.isValid())
return QSortFilterProxyModel::lessThan(source_left, source_right);
const auto &dataLeft = m_sourceModel->m_vecData.at(source_left.row());
const auto dataRight = m_sourceModel->m_vecData.at(source_right.row());
switch (source_left.column())
{
case DatabasesModel::StartTime:
return dataLeft.startTime < dataRight.startTime;
case DatabasesModel::EndTime:
return dataLeft.endTime < dataRight.endTime;
case DatabasesModel::Id:
case DatabasesModel::Name:
break;
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
} // namespace AARApplication
@@ -0,0 +1,100 @@
/**
* @RDE_HEADER@
* PresentationManangement - Script Overview Dialog Model
* $Id$
* $HeadURL$
* $Author$
*
* Created by Mike Wille, SLSE, 2017-03-15
*/
#pragma once
#include "logger.h"
#include <LtsUtils/LtsUtilsCore/data_modification_enum.h>
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
class QSqlDatabase;
namespace AARApplication
{
struct Exercise
{
qint32 id = 0;
QString name;
QString dbName;
qint64 startTime = 0;
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; }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// DatabasesModel
//
class DatabasesModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Columns
{
Id = 0,
Name,
StartTime,
EndTime
};
explicit DatabasesModel(QObject *parent = nullptr);
~DatabasesModel() = default;
bool initModel(const QSqlDatabase &db);
QVariant data(const QModelIndex &, int) const override final;
int rowCount(const QModelIndex &parent) const override final;
int columnCount(const QModelIndex &parent) const override final;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override final;
Exercise getRowData(int row) const;
bool insertRow(const Exercise &item);
bool removeRow(const int &row);
friend class DatabasesProxyModel;
protected:
public slots:
void onDataChanged(const Exercise &data, DataModification modType);
private:
QVector<Exercise> m_vecData;
LOGGER_T m_logger;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// DatabasesProxyModel
//
class DatabasesProxyModel final : public QSortFilterProxyModel
{
Q_OBJECT
public:
using QSortFilterProxyModel::QSortFilterProxyModel;
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private:
DatabasesModel *m_sourceModel = nullptr;
};
}
@@ -0,0 +1,77 @@
log4j.reset=true
log4j.rootLogger=WARN, file
# DbAdministration
###########################################################
#log4j.logger.DbAdministration=DEBUG
# File
###########################################################
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p %c %c{1} - %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
# Examples for files:
@@ -0,0 +1,37 @@
/**
* @RDE_HEADER@
* Building Config - logging facility
* $Id$
* $HeadURL$
* $Author$
*/
#include "logger.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace AARApplication
{
LOGGER_T getLogger(const char *name)
{
static const char ns[] = "DBA.";
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 AARApplication
@@ -0,0 +1,23 @@
/**
* @RDE_HEADER@
* Logging for Evaluations
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
namespace AARApplication
{
typedef NGL::INGLLogger &LOGGER_T;
LOGGER_T getLogger(const char *name);
inline LOGGER_T getLogger(const QString &loggerName)
{
return getLogger(loggerName.toUtf8().constData());
}
} // namespace AARApplication
@@ -0,0 +1,80 @@
/*
* @RDE_HEADER@
* <short description>
* $Id$
* $HeadURL$
* $Author$
* Created by Malte Schröder, SLSE, 2016-06-08
*/
#include "aar_application.h"
#include <NGLLogger/INGLLogger.h>
#include <nglapplication/nglapplication.h>
#include <nglapplication/nglcoreapplication.h>
#include <ngltranslator/ngltranslator.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions::None)
{
addOption({{QStringLiteral("l"), QStringLiteral("lang")},
QStringLiteral("Language iso code to use (de_DE, en_GB, en_US)"),
QStringLiteral("<locale iso code>")
});
}
QString langIsoCode() const { return value(QStringLiteral("l")); }
};
int main(int argc, char *argv[])
{
int retval = -1;
bool bHeadless = false;
for (int i = 1; i < argc; ++i)
{
if (strcmp(argv[i], "-e") == 0)
{
bHeadless = true;
break;
}
}
QScopedPointer<QCoreApplication> app;
app.reset(bHeadless ? dynamic_cast<QCoreApplication*>(new NGLCoreApplication(argc, argv))
: dynamic_cast<QCoreApplication*>(new NGLApplication(argc, argv)));
QCoreApplication::setApplicationName(QLatin1String("AarApplication"));
QCoreApplication::setOrganizationName(QLatin1String("RME"));
if (!bHeadless)
app->setAttribute(Qt::AA_UseHighDpiPixmaps);
NGL::LoggerHandler *lh = dynamic_cast<NGL::LoggerHandler*>(app.data());
lh->createLogger("AarApp", "log4cxx_aarApp.props");
NGL::INGLLogger &logger = lh->baseLogger();
// Parse Options
CmdLineParser options;
if (!options.init())
return retval;
// Set Translation
QString langIsoCode = options.langIsoCode();
if (langIsoCode.isEmpty())
langIsoCode = QLocale::system().name();
NGL::setTranslationLocale(langIsoCode.toLatin1());
AARApplication::AarApplication * w = new AARApplication::AarApplication;
if (!w->init())
return -2;
STARTUP_LOG(logger);
w->show();
QGuiApplication::setQuitOnLastWindowClosed(true);
retval = QCoreApplication::exec();
SHUTDOWN_LOG(logger);
return retval;
}
@@ -0,0 +1,9 @@
#
# $Id$
# $HeadURL$
# $Author$
#
ADD_SUBDIRECTORY(AarApp)
ADD_SUBDIRECTORY(OpApp)
ADD_SUBDIRECTORY(THP)
@@ -0,0 +1,53 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(LOG4CXX_PROPS_FILE log4cxx_opapp.props)
SET(mwt_SRCS
main.cpp
${LOG4CXX_PROPS_FILE}
)
IF(WIN32)
SET(JBN ${JENKINS_BUILD_NUMBER})
IF (NOT JENKINS_BUILD_NUMBER)
SET(JBN "0")
ENDIF()
SET(OPAPP_VERSION_INFO "${LTS_VERSION_MAJOR}, ${LTS_VERSION_MINOR}, ${JBN}, ${LTS_VERSION_PATCH}")
CONFIGURE_FILE(opapp.rc.cmake ${CMAKE_CURRENT_BINARY_DIR}/opapp.rc)
LIST(APPEND mwt_SRCS ${CMAKE_CURRENT_BINARY_DIR}/opapp.rc)
ENDIF()
NGL_ADD_APP_ICON(mwt_SRCS)
IF(BUILD_RPM)
NGL_ADD_EXECUTABLE(WS OperatorApplication WIN32 ${mwt_SRCS})
ELSE()
NGL_ADD_EXECUTABLE(WS OperatorApplication ${mwt_SRCS})
ENDIF()
NGL_TARGET_LINK_LIBRARIES(OperatorApplication
ArcGISQt5Hack # make sure it's loaded *before* libEsriCommonQt.so
Qt6::Widgets
Qt6::Network
nglapplication
LtsCommandLineParser
NGLLogger
LtsSession
LtsPluginLoader
LtsAppDescriptor
LtsAppManager
LtsSessionData
LtsUtilsSettings
ngltranslator
nglsettings
LtsProjectionWorkstation
LtsMultiMonitor # TODO: rm if -m is out
LtsUtilsGuiConnectDialog # to set timeout if -v is set
LtsUtilsGuiStyleManager
)
NGL_INSTALL_TARGET(OperatorApplication)
NGL_INSTALL(FILES WS ${LOG4CXX_PROPS_FILE} DESTINATION "bin")
@@ -0,0 +1,537 @@
# Created by Mike Wille, SLSE, 2016-08-08
log4j.reset=true
log4j.rootLogger=WARN, file
# LtsSession
###########################################################
#log4j.logger.LtsSession.TopicLockHandler=INFO
#log4j.logger.LtsWidget.LtsDialog=INFO
# SituationReplayManagement SRM
###########################################################
#log4j.logger.SRM=DEBUG
# Schnittstelle Adpter Adler SAA
###########################################################
#log4j.logger.SAA.SAAInterfaceGuiPlugin=DEBUG
#log4j.logger.SAA.SaaDialogModel=DEBUG
#log4j.logger.SAA.CustomViewSaa=DEBUG
#log4j.logger.SAA.SaaChooser=DEBUG
# FileManagement FLM
###########################################################
#log4j.logger.AVM=DEBUG
#log4j.logger.FLM.Plugins.Data=INFO
#log4j.logger.FLM.Plugins.Gui.Explorer=INFO
#log4j.logger.FLM.Plugins.Gui.FlmCombobox=INFO
#log4j.logger.FLM.FileManagementDialog=DEBUG
#log4j.logger.FLM.DialogModelFolder=INFO
#log4j.logger.FLM.DialogEditFolder=INFO
#log4j.logger.FLM.DialogModelFile=INFO
#log4j.logger.FLM.FileContentEditDialog=DEBUG
#log4j.logger.FLM.FileContentPreviewDialog=DEBUG
#log4j.logger.FLM.SetAttributesDialog=INFO
#log4j.logger.FLM.HQReportModel=DEBUG
# FilterManagement FLT
###########################################################
#log4j.logger.FLT.AesDisplayFilterImpl=DEBUG
#log4j.logger.FLT.BuildingComponentSettingsImpl=TRACE
#log4j.logger.FLT.BuildingComponentFilterDialog=TRACE
#log4j.logger.FLT.BuildingComponentSettingsWidget=TRACE
#log4j.logger.FLT.SymbolAppearanceSettings=DEBUG
#log4j.logger.FLT.SymbolAppearanceDialog=INFO
#log4j.logger.FLT.SymbolAppearanceWidget=DEBUG
#log4j.logger.FLT.FilterChangeDialog=INFO
# CodificationManagement COD
###########################################################
#log4j.logger.COD.Plugins.Gui.CodificationDataImpl=DEBUG
#log4j.logger.COD.CodificationDataOverviewDialog=DEBUG
#log4j.logger.COD.CodificationDataOverviewModel=DEBUG
#log4j.logger.COD.CodificationDataTableView=DEBUG
#log4j.logger.COD.CodificationDataTableEditView=DEBUG
#log4j.logger.COD.CodificationDataTableView=DEBUG
#log4j.logger.COD.CodificationCSVExport=DEBUG
#log4j.logger.COD.CodificationCSVImport=DEBUG
#log4j.logger.COD.NumeralCodeTableModel=DEBUG
#log4j.logger.COD.AuthenticationCodeTableModel=DEBUG
#log4j.logger.COD.LogisticalTermsModel=DEBUG
#log4j.logger.COD.PhraseTableModel= DEBUG
#log4j.logger.COD.CodeTableEditDelegate=DEBUG
#log4j.logger.COD.AssignCodificationDataTable=DEBUG
# AlarmManagement Info
###########################################################
#log4j.logger.ALM.Config=DEBUG
#log4j.logger.ALM.Model=DEBUG
#log4j.logger.ALM.Dialog=DEBUG
# InfrastructureManagement ISM
###########################################################
#log4j.logger.ISM.ApproachingPointsDialog=DEBUG
#log4j.logger.ISM.ApproachingPointsDialogModel=DEBUG
#log4j.logger.ISM.AssetFilterDialog=DEBUG
#log4j.logger.ISM.AssetListDialog=DEBUG
#log4j.logger.ISM.AssetListDialogModel=DEBUG
#log4j.logger.ISM.AudioVideoConfigPlugin=DEBUG
#log4j.logger.ISM.BuildingMountPointDialog=DEBUG
#log4j.logger.ISM.BuildingMountPointModel=DEBUG
#log4j.logger.ISM.CustomViewSchedule=DEBUG
#log4j.logger.ISM.DdsSelector=DEBUG
#log4j.logger.ISM.EditApproachingPointDialog=DEBUG
#log4j.logger.ISM.EditAssetDialog=DEBUG
#log4j.logger.ISM.ExerciseAssignmentDialog=DEBUG
#log4j.logger.ISM.ExerciseAssignmentDialogModel=DEBUG
#log4j.logger.ISM.ForeignDataInterface=DEBUG
#log4j.logger.ISM.InfrastructureManagement=DEBUG
#log4j.logger.ISM.MountingPointInfoDialog=DEBUG
#log4j.logger.ISM.MountingPointListDialog=DEBUG
#log4j.logger.ISM.MountingPointListDialogModel=DEBUG
#log4j.logger.ISM.NewPlanDialog=DEBUG
#log4j.logger.ISM.OverviewDialog=DEBUG
#log4j.logger.ISM.OverviewDialogModel=DEBUG
#log4j.logger.ISM.OverviewMountingPointListDialog=DEBUG
#log4j.logger.ISM.ViewMountingPointListModel=DEBUG
#log4j.logger.ISM.PlanChooser=DEBUG
#log4j.logger.ISM.PlanDialog=DEBUG
#log4j.logger.ISM.PlanDialogModel=DEBUG
#log4j.logger.ISM.PlanEditDialog=DEBUG
#log4j.logger.ISM.PlanTimeLineDialog=DEBUG
#log4j.logger.ISM.PlanTimeLineModel=DEBUG
#log4j.logger.ISM.Plugins.Gui.IsmCombobox=DEBUG
#log4j.logger.ISM.ResourceManagement=DEBUG
#log4j.logger.ISM.SetPropertiesDialog=DEBUG
#log4j.logger.ISM.SetRecordingDialog=DEBUG
# AreaEffectSimulation Info
###########################################################
#log4j.logger.AES.AESInfo=DEBUG
#log4j.logger.AES.AESInfoDialog=DEBUG
# generated using ctc_find_getlogger.sh
#log4j.logger.AES.AesGuiBase=DEBUG
#log4j.logger.AES.AESInfo=DEBUG
#log4j.logger.AES.AESInfoDialog=DEBUG
#log4j.logger.AES.AesItemList=DEBUG
#log4j.logger.AES.ChargeAdministration=DEBUG
#log4j.logger.AES.ChargeAdministrationList=DEBUG
#log4j.logger.AES.DangerArea.AesItemInfo=DEBUG
#log4j.logger.AES.Plugins.Gui.Aes.Borderline=DEBUG
#log4j.logger.AES.Plugins.Gui.AirSupport=DEBUG
#log4j.logger.AES.Plugins.Gui.BcContamination=DEBUG
#log4j.logger.AES.Plugins.Gui.BorderlineImpl=DEBUG
#log4j.logger.AES.Plugins.Gui.ChargeAdministration=DEBUG
#log4j.logger.AES.Plugins.Gui.DangerArea=DEBUG
#log4j.logger.AES.Plugins.Gui.Explosion=DEBUG
#log4j.logger.AES.Plugins.Gui.FunctionalArea=DEBUG
#log4j.logger.AES.Plugins.Gui.GasPipe=DEBUG
#log4j.logger.AES.Plugins.Gui.MineArea=DEBUG
#log4j.logger.AES.Plugins.Gui.MineCorridor=DEBUG
#log4j.logger.AES.Plugins.Gui.MineSingleVirtual=DEBUG
#log4j.logger.AES.Plugins.Gui.NContamination=DEBUG
#log4j.logger.AES.Plugins.Gui.TechnicalArea=DEBUG
#log4j.logger.AES.BCContaminationEdit=DEBUG
#log4j.logger.AES.BoobyTrapEdit=DEBUG
#log4j.logger.AES.BorderlineEdit=DEBUG
#log4j.logger.AES.DangerAreaEdit=DEBUG
#log4j.logger.AES.ExplosionEdit=DEBUG
### setExplosionsEdit to ERROR (WARN "zValue is very small, set to 100")
log4j.logger.AES.ExplosionsEdit=ERROR
#log4j.logger.AES.FunctionalAreaEdit=DEBUG
#log4j.logger.AES.GasPipeEdit=DEBUG
#log4j.logger.AES.MineAreaEdit=DEBUG
#log4j.logger.AES.MineCorridorEdit=DEBUG
#log4j.logger.AES.NContaminationEdit=DEBUG
#log4j.logger.AES.OffRouteMineEdit=DEBUG
#log4j.logger.AES.TechnicalAreaEdit=DEBUG
# in context ExplosionCluster / Artillery suppress warnings of TrajectoryGenerator, TrajectoryGeneratorVulcano
log4j.logger.AES.TrajectoryGenerator=ERROR
log4j.logger.AES.TrajectoryGeneratorVulcano=ERROR
# AdvancedSymbolgy
###########################################################
#log4j.logger.AdvSymbology=INFO
#log4j.logger.AdvSymbology.FlmSubscriber=DEBUG
# FlmSubscriber.ReFetch should be on INFO to be able check the occurance of ReFetching
#log4j.logger.AdvSymbology.FlmSubscriber.ReFetch=INFO
#log4j.logger.AdvSymbology.FlmSymbolProxy=DEBUG
#log4j.logger.AdvSymbology.EsriDictionaryProcessor=INFO
#log4j.logger.AdvSymbology.SidcBuilder=INFO
#log4j.logger.AdvSymbology.PropertyResult=INFO
#log4j.logger.AdvSymbology.PropertyResultWatcher=INFO
#log4j.logger.AdvSymbology.SymbolResult=INFO
#log4j.logger.AdvSymbology.SymbolResultWatcher=INFO
#log4j.logger.AdvSymbology.SymbolDictionary=INFO
#log4j.logger.AdvSymbology.SymbolDictionaryImpl=INFO
# AdvancedSymbolgy - Plugin
#log4j.logger.AdvSymbology.FlmSubscriber=INFO
#log4j.logger.AdvSymbology.FlmSymbolProxy=INFO
# Building-Viewer
###########################################################
#log4j.logger.BDV=DEBUG
#Common
#log4j.logger.BDV.BuildingManager=DEBUG
#log4j.logger.BDV.BuildingViewerDdsController=DEBUG
#log4j.logger.BDV.BgViewerControllerSingleton=DEBUG
#log4j.logger.BDV.BuildingViewerDdsController=DEBUG
#log4j.logger.BDV.CommunicationInterface=DEBUG
#GUI
#log4j.logger.BDV.BuildingViewer::BuildingEdit=DEBUG
#log4j.logger.BDV.BgViewerDataView=DEBUG
#log4j.logger.BDV.ThreadVideoExporter=DEBUG
#log4j.logger.BDV.ReplayHandler=DEBUG
#log4j.logger.BDV.BdvPluginProvider=DEBUG
#log4j.logger.BDV.BgViewerDataViewDialog=DEBUG
#log4j.logger.BDV.TopicReplayInterface=DEBUG
#Plugin
#log4j.logger.BDV.InterfaceImpl=DEBUG
# Building
###########################################################
#log4j.logger.BLD.BldInfo=DEBUG
#log4j.logger.BLD.BldInfoDialog=DEBUG
#log4j.logger.BLD.BuildingConfigDialog=DEBUG
# TakeHomePackageManager
###########################################################
#log4j.logger.TPM.ExportedScriptsModel=DEBUG
# Script Editor
###########################################################
#log4j.logger.ScriptTimelineDialog.JournalDialog=DEBUG
#log4j.logger.ScriptTimelineDialog.ScriptTimelineDialog=DEBUG
#log4j.logger.ScriptTimelineDialog.ScriptModel=DEBUG
#log4j.logger.ScriptTimelineDialog.Plugins.Gui.Pre.ScriptTimeline=DEBUG
#log4j.logger.ScriptTimelineDialog.StoryboardDialog=DEBUG
#log4j.logger.PRE.ScreenRecorder=DEBUG
#log4j.logger.PRE.TopicRecorder=DEBUG
# SituationMap
###########################################################
#log4j.logger.SituationMap=INFO
### rethink SituationMap.Plugins.Gui.SitMap
#log4j.logger.SituationMap.Plugins.Gui.SitMap=INFO
#log4j.logger.SituationMap.SituationMapInit=INFO
#log4j.logger.SituationMap.MapInitializer=INFO
#log4j.logger.SituationMap.ThatPartyIdToColorFunctor=INFO
#log4j.logger.SituationMap.RegisterAppHandler=INFO
# SituationMap-AdapterDds:
#log4j.logger.SituationMap.AdapterDds=INFO
#log4j.logger.SituationMap.EmergencyCallToMap=INFO
#log4j.logger.SituationMap.MovementTrack=INFO
#log4j.logger.SituationMap.MovementTrackManager=INFO
#log4j.logger.SituationMap.Building2SituationMap=INFO
#log4j.logger.SituationMap.BuildingOwnership=INFO
#log4j.logger.SituationMap.BCContamination2SituationMap=INFO
#log4j.logger.SituationMap.ExplosionSingle2SituationMap=INFO
#log4j.logger.SituationMap.ExplosionArea2SituationMap=INFO
#log4j.logger.SituationMap.ExplosionCluster2SituationMap=INFO
#log4j.logger.SituationMap.ExplosionCluster2SituationMap.update=INFO
#log4j.logger.SituationMap.GuezIed2SituationMap=INFO
#log4j.logger.SituationMap.Participant2SituationMap=INFO
#log4j.logger.SituationMap.HitlineInfo2SituationMap=INFO
#log4j.logger.SituationMap.MineArea2SituationMap=INFO
#log4j.logger.SituationMap.MsePointToMap=INFO
#log4j.logger.SituationMap.MseBridgeToMap=INFO
#log4j.logger.SituationMap.MountingPointToMap=INFO
#log4j.logger.SituationMap.SoundAreaToMap=INFO
#log4j.logger.SituationMap.AerialOrdnance2SituationMap=INFO
#log4j.logger.SituationMap.BoobyTrap2SituationMap=INFO
#log4j.logger.SituationMap.OffRoute2SituationMap=INFO
#log4j.logger.SituationMap.ChargeEffectToMap=INFO
# SituationMap-Core
#log4j.logger.SituationMap=INFO
#log4j.logger.SituationMap.MapImpl=INFO
#log4j.logger.SituationMap.MapSettings=INFO
#log4j.logger.SituationMap.MapInitializer=INFO
#log4j.logger.SituationMap.JumpPoints=INFO
#log4j.logger.SituationMap.JumpPointWidget=TRACE
#log4j.logger.SituationMap.LayerFactoryImpl=INFO
#log4j.logger.SituationMap.LayerControl=INFO
#log4j.logger.SituationMap.LogicalLayer=INFO
#log4j.logger.SituationMap.CoordTranslationFunctorImpl=INFO
#log4j.logger.SituationMap.CoordTranslationThread=INFO
#log4j.logger.SituationMap.PositionsTranslated=INFO
#log4j.logger.SituationMap.GridImpl=INFO
#log4j.logger.SituationMap.ObjectFactory=INFO
#log4j.logger.SituationMap.IdGenerator=INFO
#log4j.logger.SituationMap.ObjectContainer=INFO
#log4j.logger.SituationMap.ObjectContainerCTC=INFO
#log4j.logger.SituationMap.OverlayHandlerImpl=INFO
#log4j.logger.SituationMap.OverviewMapHandlerImpl=INFO
#log4j.logger.SituationMap.OverviewMap=INFO
#log4j.logger.SituationMap.PixelBasedTextBoxHandlerImpl=INFO
#log4j.logger.SituationMap.ViewshedImpl=INFO
#log4j.logger.SituationMap.ViewshedBase=INFO
# SituationMap-AbstractMapObject's
#log4j.logger.SituationMap.AbstractMapObjectImpl=INFO
#log4j.logger.SituationMap.AbstractEffectSimulationImpl=INFO
#log4j.logger.SituationMap.OriginTranslated=INFO
#log4j.logger.SituationMap.PositionsTranslated=INFO
#log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=INFO
#log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=INFO
#log4j.logger.SituationMap.BasicPathObjectImpl=INFO
#log4j.logger.SituationMap.PathContainer=INFO
#log4j.logger.SituationMap.IndicationContainer=INFO
#log4j.logger.SituationMap.PolygonImpl=INFO
#log4j.logger.SituationMap.PolylineImpl=INFO
#log4j.logger.SituationMap.TextImpl=INFO
#log4j.logger.SituationMap.AreaFireImpl=INFO
#log4j.logger.SituationMap.BiologicalAgentImpl=INFO
#log4j.logger.SituationMap.BuildingOutlineImpl=INFO
#log4j.logger.SituationMap.ChemicalAgentImpl=INFO
#log4j.logger.SituationMap.DangerAreaImpl=INFO
#log4j.logger.SituationMap.FunctionalAreaImpl=INFO
#log4j.logger.SituationMap.GeoImageImpl=INFO
#log4j.logger.SituationMap.HitLineImpl=INFO
#log4j.logger.SituationMap.ObjectIndicatorImpl=INFO
#log4j.logger.SituationMap.MineImpl=INFO
#log4j.logger.SituationMap.MineFieldImpl=INFO
#log4j.logger.SituationMap.NineLinerImpl=TRACE
#log4j.logger.SituationMap.NuclearAgentImpl=INFO
#log4j.logger.SituationMap.ParticipantImpl=INFO
#log4j.logger.SituationMap.SingleFireImpl=INFO
#log4j.logger.SituationMap.CubeImpl=INFO
#log4j.logger.SituationMap.VirtualMineImpl=INFO
#log4j.logger.SituationMap.AbstractGraphicalObject=INFO
#log4j.logger.SituationMap.GraphicalObjectAttributes=INFO
#log4j.logger.SituationMap.GraphicalObjectEllipse=INFO
#log4j.logger.SituationMap.GraphicalObjectPath=INFO
#log4j.logger.SituationMap.GraphicalObjectMultiPath=INFO
#log4j.logger.SituationMap.GraphicalObjectPictureMarker=INFO
#log4j.logger.SituationMap.GraphicalObjectRange=INFO
#log4j.logger.SituationMap.GraphicalObjectRendererAttributes=INFO
#log4j.logger.SituationMap.GraphicalObjectSceneMarker=INFO
#log4j.logger.SituationMap.GraphicalObjectText=INFO
#log4j.logger.SituationMap.TacticalAppearance=INFO
#log4j.logger.SituationMap.ElevationMapManager=DEBUG
#log4j.logger.SituationMap.ElevationMap=DEBUG
# SituationMap-Widgets
#log4j.logger.SituationMap.MapWidgetControl=INFO
#log4j.logger.SituationMap.LayerControlDialog=INFO
#log4j.logger.SituationMap.ConfigureMapUrls=INFO
# TargetManagement TGT
###########################################################
# SVC
#log4j.logger.TGT.DdsHandler=INFO
#log4j.logger.TGT.HandlerTargetConfig=INFO
# SVC-L400
#log4j.logger.TGT.HandlerShootbackArea=INFO
#log4j.logger.TGT.HandlerShootbackTarget=INFO
#log4j.logger.TGT.HandlerTargetAreaAssignment=INFO
#log4j.logger.TGT.ObjectShootbackArea=INFO
#log4j.logger.TGT.ObjectShootbackTarget=INFO
#log4j.logger.TGT.ObjectTargetAreaAssignment=INFO
#log4j.logger.TGT.ShootbackController=INFO
# PLUGIN-L400
#log4j.logger.Plugins.Gui.Tgt.Target=INFO
#log4j.logger.TGT.ParticipantHandler=INFO
#log4j.logger.TGT.AreaDialog=INFO
#log4j.logger.TGT.AreaEdit=INFO
#log4j.logger.TGT.AreaModel=INFO
#log4j.logger.TGT.AssignmentParticipantModel=INFO
#log4j.logger.TGT.AssignmentDialog=INFO
#log4j.logger.TGT.AssignmentEditDialog=INFO
#log4j.logger.TGT.ShootbackTargetEdit=INFO
#log4j.logger.TGT.TargetPartDialog=INFO
#log4j.logger.TGT.TargetParticipantModel=INFO
# MeteoDataManagement MDM
###########################################################
#log4j.logger.MDM.MeteoDataManagementDialog=INFO
#log4j.logger.MDM.MeteoDataManagementEdit=INFO
#log4j.logger.MDM.MeteoDataModel=INFO
#log4j.logger.MDM.InterfaceImpl.MeteoData=INFO
# Messaging MSG
###########################################################
#log4j.logger.MSG.MessagingDialog=INFO
#log4j.logger.MSG.MessagingModel=INFO
# AudioVideoManagement AVM
###########################################################
# AVM AudioSettings
#log4j.logger.AVM.AudioSettingsDialog=DEBUG
#log4j.logger.AVM.AudioSettingsAvailStreamsModel=DEBUG
#log4j.logger.AVM.AudioSettingsUsedStreamsModel=DEBUG
# AVM TopicEditor
#log4j.logger.AVM.AvmSequenceModel=DEBUG
# AVM AVMenuEdit
#log4j.logger.AVM.Plugins.Gui.Avm.MenuEdit=DEBUG
#log4j.logger.AVM.AvailableStreamsModel=DEBUG
#log4j.logger.AVM.UsedStreamsModel=DEBUG
#log4j.logger.AVM.ChooseMenu=DEBUG
#log4j.logger.AVM.AudioVideoManagementEdit=DEBUG
#log4j.logger.AVM.AudioVideoManagementDialog=DEBUG
#log4j.logger.AVM.MenuViewModel=DEBUG
#log4j.logger.AVM.NewMenu=DEBUG
# AVM CNR
#log4j.logger.AVM.Plugins.Gui.Avm.CombatNetRadio=DEBUG
#log4j.logger.AVM.CombatNetRadioDialog=DEBUG
#log4j.logger.AVM.CombatNetRadioDialogModel=DEBUG
#log4j.logger.AVM.EditCombatNetRadioDialog=DEBUG
# AVM CNR FrequencyPlanning
#log4j.logger.AVM.Plugins.Gui.Avm.CombatNetRadio=DEBUG
#log4j.logger.AVM.FrequencyPlanningDialog=DEBUG
#log4j.logger.AVM.FrequencyPlanningDialogModel=DEBUG
#log4j.logger.AVM.InitFrequencyPlanningDialog=DEBUG
#log4j.logger.AVM.EditFrequencyPlanningDialog=DEBUG
#log4j.logger.AVM.EditFrequencyPlanningDataModel=DEBUG
#log4j.logger.AVM.EditFrequencyPlanningResourceDialog=DEBUG
#log4j.logger.AVM.EditFrequencyPlanningResourceDataModel=DEBUG
# AVM Eloka
#log4j.logger.AVM.Plugins.Gui.avm.Eloka=DEBUG
#log4j.logger.AVM.ElokaDialog=DEBUG
#log4j.logger.AVM.ElokaEditDialog=DEBUG
#log4j.logger.AVM.ElokaModel=DEBUG
# AVM MobileVideoSystem
#log4j.logger.AVM.Plugins.Gui.Avm.MobileVideoSystem=DEBUG
#log4j.logger.AVM.MvsDialog=DEBUG
#log4j.logger.AVM.EditVideoTeamDialog=DEBUG
#log4j.logger.AVM.MvsModel=DEBUG
#log4j.logger.AVM.MvsVideoTeamDialog=DEBUG
# AVM TVC
#log4j.logger.AVM.Plugins.Gui.Avm.CombatNetRadio=DEBUG
#log4j.logger.AVM.TrainingVoiceCommunicationDialog=DEBUG
#log4j.logger.AVM.EditTrainingVoiceCommunicationDialog=DEBUG
#log4j.logger.AVM.TrainingVoiceCommunicationDialogModel=DEBUG
# OPM
###########################################################
#log4j.logger.OPM.Plugins.Gui.Opm=INFO
#log4j.logger.OPM.Plugins.Gui.Opm.Edit=INFO
#log4j.logger.OPM.Plugins.Gui.Opm.Overview=INFO
#log4j.logger.OPM.Plugins.Gui.Opm.OpmCombobox=INFO
#log4j.logger.OPM.Plugins.Gui.Opm.Settings=INFO
#log4j.logger.OPM.Plugins.Gui.Opm.SettingsDialog=INFO
#log4j.logger.OPM.LoginDialog=INFO
#log4j.logger.OPM.SettingsGroup=DEBUG
# PWS
###########################################################
#log4j.logger.PWS.LtsProjetionWorkstation=INFO
#log4j.logger.PWS.ControlDialog=INFO
# Online Help
#log4j.logger.OLH.onlineHelp=INFO
# ServiceMonitor
###########################################################
#log4j.logger.ServiceMonitorDialog=INFO
#log4j.logger.ServiceMonitorBiteModel=INFO
#log4j.logger.ServiceMonitorManipulationModel=INFO
#log4j.logger.BasePlayerModel=DEBUG
#log4j.logger.PartyEquipmentCheckDialog=DEBUG
#log4j.logger.SingleServiceEquipmentCheckDialog=DEBUG
# Appnd_File
###########################################################
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=100MB
log4j.appender.file.MaxBackupIndex=9
#log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_OpApp.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n
#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n
# Appnd_Syslog
###########################################################
log4j.appender.syslog=org.apache.log4j.SyslogAppender
log4j.appender.syslog.Threshold=ERROR
log4j.appender.syslog.SysLogHost=localhost
log4j.appender.syslog.Facility=LOCAL7
log4j.appender.syslog.layout=org.apache.log4j.PatternLayout
log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m
log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true
log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO
log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL
###########################################################
###########################################################
###########################################################
# 1) Available Levels in LOG4CXX Version 0.10.0:
# TRACE,
# DEBUG,
# INFO,
# WARN,
# ERROR
# FATAL
#
# d = date/time
# p = priority (Level)
# c = category/channel
# m = message
# F = file
# L = line
# n = platform dependent line separator character or characters.
# ....
#
# Examples for console:
# a)
# %d %p %c #%m# (%.50F:%L)%n
# date/time
# priority
# category/channel
# #message#
# (Maximal 50Character des Filename von rechts:Line)
# line separator
#
# b)
# %d (%.15F:%L) %-5p %c |#| %m%n
# date/time
# (Maximal 15Character des Filename von rechts:Line)
# priority, Right pad with spaces if the priority name is less than 5 characters long.
# category/channel
# message
# line separator
#
@@ -0,0 +1,523 @@
/**
* @RDE_HEADER@
* main for OperatorApplication
* $Id$
* $HeadURL$
* $Author$
*/
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_manager.h>
#include <LtsAppManager/lts_app_manager.h>
#include <LtsPluginManager/lts_plugin_loader.h>
#include <LtsSession/lts_session_data.h>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include <LtsMultiMonitor/lts_multi_monitor.h>
#include <StyleManager/style_manager.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
#include <LtsWidgets/lts_splash.h>
#include <LtsWidgets/lts_combo_box.h>
#include <LtsProjectionWorkstation/lts_projection_workstation.h>
#include <LtsUtilsSettings/settings.h>
#include <nglapplication/nglapplication.h>
#include <nglapplication/nglloggerhandler.h>
#include <nglsettings/nglsettings_settings.h>
#include <ngltranslator/ngltranslator.h>
#include <NGLLogger/INGLLogger.h>
#include <rti/config/Logger.hpp>
#include <QPixmap>
#include <QProcess>
#include <QDir>
#include <QtCore/QString>
#include <QMessageBox>
#include <QSurfaceFormat>
#include <QScrollerProperties>
#include <QInputDialog>
#include <QNetworkProxy>
#include <QtCore/QTimer>
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions(CreateDomainIdFromIp | DomainId | ExerciseId | TcpConnectionUrl))
{
addOption({{QStringLiteral("a"), QStringLiteral("application")},
QStringLiteral("Specify the initial application to start (Default: Situation [Demo / Dev - otherwise NOT SPECIFIED => starts AppChooser]"), QStringLiteral("Application"),
QStringLiteral("Situation")
}); // TODO: remove default on release
addOption({{QStringLiteral("b"), QStringLiteral("basePlugin")},
QStringLiteral("Base Project Plugin Iid to start the application with (Default: <not specified>)"), QStringLiteral("Base Project Plugin Iid"),
QString()
}); // TODO: remove default on release
addOption({{QStringLiteral("s"), QStringLiteral("stylePlugin")},
QStringLiteral("Style Plugin to start the application with (Default: rme.light)"), QStringLiteral("Style Plugin Iid"),
QStringLiteral("rme.light")
});
addOption({{QStringLiteral("u"), QStringLiteral("username")},
QStringLiteral("Preset Username to skip login (also specify -p !)"),
QStringLiteral("username")
});
addOption({{QStringLiteral("x"), QStringLiteral("tabletmode")},
QStringLiteral("Enforce Tablet Mode"),
QStringLiteral("false")
});
addOption({{QStringLiteral("w"), QStringLiteral("workstationmode")},
QStringLiteral("Enforce Workstation Mode"),
QStringLiteral("false")
});
addOption({{QStringLiteral("q"), QStringLiteral("qos_file")},
QStringLiteral("Define QoS Profile "),
QStringLiteral("qos_file")
});
addOption({{QStringLiteral("p"), QStringLiteral("password")},
QStringLiteral("Preset Password to skip login (also specify -u !)"),
QStringLiteral("password")
});
addOption({{QStringLiteral("k"), QStringLiteral("skipScreen")},
QStringLiteral("Tell the Multi Monitor mode to not put anything on the screen with id n (example: 1 (skips entire screen 1)) - or on sub screen 0 or 1 (left or right) for ultra wide screens (example: 1.1 (skips the right side of screen 1)) (Default: (none))"),
QStringLiteral("screen id"),
});
addOption({{QStringLiteral("m"), QStringLiteral("multi-monitor-mode")},
QStringLiteral("Use the Multi Monitor mode to place Base Layouts on Screens in Fullscreen")
});
#ifdef LTS_DEBUG
addOption({QStringLiteral("dbgmmm"),
QStringLiteral("Start the multi-monitor-mode with reduced size for use on development maschines")
});
#endif
addOption({{QStringLiteral("r"), QStringLiteral("replayId")},
QStringLiteral("Default: -1"),
QStringLiteral("Replay ID"),
QStringLiteral("-1")
});
addOption({{QStringLiteral("l"), QStringLiteral("lang")},
QStringLiteral("Language iso code to use (de_DE, en_GB, en_US)"),
QStringLiteral("<locale iso code>")
});
addOption({QStringLiteral("ignore-plugins"),
QStringLiteral("Ignore these plugins, which otherwise would be loaded, "
"specified by apps.json of the selected Base Plugin. "
"Example: \"--ignore-plugins data:avm,data:bdv,gui:avm\""),
QStringLiteral("ignore-plugins") // <= value
});
addOption({QStringLiteral("projection"),
QStringLiteral("Application is started on an Projection Workstation."),
});
addOption({QStringLiteral("check-requirements"),
QStringLiteral("Lets the Plugin Loader check if every App's requirements are met before starting"),
});
addOption({QStringLiteral("enable-experimental-web-platform-features"),
QString(),
});
addOption({QStringLiteral("script"),
QStringLiteral("Id of a script. The script has to exist in the given exercise."),
QStringLiteral("ScriptId"),
QStringLiteral("0")
});
addOption({QStringLiteral("place-script-screens"),
QStringLiteral("Let the screen 1 and screen 2 of the script-editor be placed automatically on startup.")
});
m_additionalHelpStr =
QStringLiteral("\n\n\e[33m example OWS : %1 -m -a0 -d1 -b legatus -t EXCTDS01:7400 -l de_DE \n\n\e[0m").arg(QCoreApplication::arguments().at(0));
// fprintf(stdout, "\n\n\e[33m example OWS : %s -m -a0 -d1 -b legatus -t EXCTDS01:7400 -l de_DE \n\n\e[0m", QCoreApplication::arguments().at(0).toLatin1().data());
}
QString operatorName() const { return value(QStringLiteral("u")); }
QString password() const { return value(QStringLiteral("p")); }
int replayId() const { return value(QStringLiteral("r")).toInt(); }
QString projectPluginIid() const { return value(QStringLiteral("b")); }
QString stylePluginIid() const { return value(QStringLiteral("s")); }
int applicationId() const { bool idk = false; int id = value(QStringLiteral("a")).toInt(&idk); if (!idk) { return -1; } return id; }
bool multiMonitorMode() const { return isSet(QStringLiteral("m")); }
bool isProjectionWorkstation() const { return isSet(QStringLiteral("projection")); }
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); }
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(); }
int scriptId() const { return value(QStringLiteral("script")).toInt(); }
bool placeScriptScreens() const { return isSet(QStringLiteral("place-script-screens")); }
#ifdef LTS_DEBUG
bool debugMultiMonitorMode() const { return isSet(QStringLiteral("dbgmmm")); }
#endif
};
// ------------------------------------------------------------------
int main(int argc, char **argv) // TODO: json strings to lower
{
qsrand(time(nullptr));
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
QScrollerProperties props;
props.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
props.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
QScrollerProperties::setDefaultScrollerProperties(props);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
//LtsApp app(argc, argv);
NGLApplication app(argc, argv);
app.createLogger("opApp");
QApplication::setQuitOnLastWindowClosed(false); // we are cool enough to handle it ourselves 8)
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QNetworkProxy::setApplicationProxy(QNetworkProxy()); // do NOT use proxies anywhere!
NGL::LoggerHandler *lh = NGL::LoggerHandler::getLoggerHandler();
if (lh)
{
const QByteArray postfix = lh->loggerNamePostfix().toLocal8Bit() + "_dds_";
const QByteArray prefix = lh->loggerNamePrefix().toLocal8Bit();
rti::config::Logger &ddsLogger = rti::config::Logger::instance();
//ddsLogger.output_file(fdopen(STDOUT_FILENO, "w"));
ddsLogger.output_file_set(postfix.data(), prefix.data(), 10 * 1024 * 1024, 10);
ddsLogger.verbosity(rti::config::Verbosity_def::WARNING);
//ddsLogger.verbosity_by_category(rti::config::LogCategory_def::COMMUNICATION, rti::config::Verbosity_def::STATUS_ALL);
}
// TODO: replace all exit(x) with qApp.quit() and an error msg
LtsWidgets::LtsGlobalWaitDialog::getInstance()->setVisible(false); // avoid wait screen, we have the splash for this reason!
#ifdef __linux__
// svcSituationRep - pgrep/pkill length limitation
QProcess::startDetached(QLatin1String("pkill -9 svcSituationRep")); // kill it!
QProcess::startDetached(QLatin1String("pkill -9 ffmpeg")); // otherwise a stray ffmpeg may record until the the disk is full
#elif _WIN32
QProcess::startDetached(QLatin1String("taskkill /im svcSituationReplay /f"));
#endif
// Parse Options
CmdLineParser options;
if (!options.init())
return -1;
// Set Translation
QString langIsoCode = options.langIsoCode();
if (langIsoCode.isEmpty())
langIsoCode = QLocale::system().name();
NGL::setTranslationLocale(langIsoCode.toLatin1());
// Set Style
QString stylePluginName = options.stylePluginIid();
if (stylePluginName.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "No Style Plugin Iid specified - abort.";
return -1;
}
// .. to keep some compatibility to old resource structure TODO: rm !
QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/style/icons/")));
QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/style/"))); // FIXME: does qt check for recursion ??
LtsUtils::StyleManager::getInstance()->loadStyle(stylePluginName);
// Load Plugins
LtsPluginManager::LtsPluginLoader *pluginLoader = LtsPluginManager::LtsPluginLoader::getInstance();
QString projectPluginName = options.projectPluginIid();
if (projectPluginName.isEmpty())
{
bool k;
projectPluginName = QInputDialog::getText(nullptr,
QCoreApplication::tr("No Project Plugin specified"),
QCoreApplication::tr("Please specify a Project Plugin:"), QLineEdit::Normal,
QString(), &k); // blocking, no event loop needed
if (!k || projectPluginName.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "No Project Plugin Iid specified - abort.";
return -1;
}
}
// load project plugin
if (!pluginLoader->loadPlugins(LtsPluginManager::PluginType::PROJECT, nullptr, {projectPluginName}))
{
ERROR_LOG(app.baseLogger()) << "Error loading Project Plugin - abort.";
return -1;
}
LtsUtils::ProjectSettings::initFromFile(QStringLiteral(":project/project_settings.json"));
// Setup Application
if (options.multiMonitorMode())
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setEnableMultiMonitorMode();
#ifdef LTS_DEBUG
if (options.debugMultiMonitorMode())
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setDebugMultiMonitorMode();
#endif
// show splash
LtsWidgets::Splash::getInstance()->show();
LtsAppDescriptor::App applicationId = LtsAppDescriptor::LtsAppDescriptor::appFromInteger(options.applicationId());
int exerciseId = options.exerciseId(); // -1 = invalid, 0 = admin, >0 = exercise
int replayId = options.replayId(); // -1 = invalid, TODO: comment doku
int scriptId = options.scriptId(); // <0 = invalid, 0 = default = ignore, >0 script
QString operatorName = options.operatorName();
QString password = options.password();
QStringList qos_file = QStringList(options.qosFile());
// TODO: log4cxx/NGLLlogger does not flush here !! messages not seen !
if (scriptId < 0)
{
ERROR_LOG(app.baseLogger()) << "Given scriptId " << scriptId << " is invalid - abort.";
return -1;
}
if (scriptId > 0 && applicationId != LtsAppDescriptor::App::Scripts)
{
ERROR_LOG(app.baseLogger()) << "ScriptId set but given app is not Scripts - abort.";
return -1;
}
if (scriptId > 0 && exerciseId < 1)
{
ERROR_LOG(app.baseLogger()) << "ScriptId set but no exercise is given - abort.";
return -1;
}
if (scriptId > 0 && exerciseId > 0 && applicationId == LtsAppDescriptor::App::Scripts)
{
LtsSessionData::LtsSessionData::getInstance()->setScriptId(scriptId);
}
if (options.placeScriptScreens() && applicationId != LtsAppDescriptor::App::Scripts)
{
ERROR_LOG(app.baseLogger()) << "PlaceScriptScreensn is set but given app is not Scripts - abort.";
return -1;
}
LtsSessionData::LtsSessionData::getInstance()->setPlaceScriptScreens(options.placeScriptScreens());
if (operatorName.isEmpty() != password.isEmpty()) // check for xor and warn user
{
ERROR_LOG(app.baseLogger()) << "Specify -u if -p is specified & vise versa - abort.";
return 1;
}
if (options.domainId() < 0)
{
ERROR_LOG(app.baseLogger()) << "Invalid configuration for domainId (" << options.domainId() << ") from LtsConfig - abort.";
QMessageBox::critical(nullptr, QCoreApplication::tr("Invalid Domain Id"), QCoreApplication::tr("Domain #%1 is not valid - abort.").arg(options.domainId()));
return -1;
}
// }
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
sessionData->setDomainIdPreset(options.domainId());
sessionData->setRequestedAppId(applicationId);
if (options.useTcpConnection())
sessionData->setTcpConnectionUrl(options.tcpConnectionUrl());
if (options.checkRequirements())
sessionData->setDebugFlagValue(QStringLiteral("PluginManager.CheckAppRequirements"), true);
// load JSON app description file
QFile appsFile(QStringLiteral(":project/apps.json")); // TODO: convert Json => Binary Json
if (!appsFile.open(QFile::ReadOnly))
{
ERROR_LOG(app.baseLogger()) << "Could not read App Description File";
return -1;
}
QStringList lastErrors;
QString loginPluginIid;
LtsAppDescriptor::AppDescriptionVector appDescs = LtsAppDescriptor::LtsAppDescriptor::getAppDescriptionVector(appsFile.readAll(),
lastErrors,
options.ignoredPlugins(),
loginPluginIid);
if (loginPluginIid.isEmpty())
loginPluginIid = QStringLiteral("opm.login");
QString skipScreen = options.skipScreen();
int skipScreenId = -1;
int skipSubScreenId = -1;
if (!skipScreen.isEmpty())
{
QStringList skipScreenList(skipScreen.split(QLatin1Char('.'), QString::SkipEmptyParts));
int skipScreenListCount = skipScreenList.count();
if (skipScreenListCount > 2)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
if (skipScreenListCount > 0)
{
bool k = false;
skipScreenId = skipScreenList.at(0).toInt(&k);
if (!k)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
}
if (skipScreenListCount > 1)
{
bool k = false;
skipSubScreenId = skipScreenList.at(1).toInt(&k);
if (!k)
{
qWarning() << "invalid skip screen syntax:" << skipScreen;
return -1;
}
}
}
LtsMultiMonitor::LtsMultiMonitor::getInstance()->setSkipScreen(skipScreenId, skipSubScreenId);
appsFile.close();
if (!lastErrors.isEmpty())
{
QMessageBox::critical(nullptr, QString(), lastErrors.join(QStringLiteral("\r\n\r\n")));
// TODO: log precise msg !!
return -1;
}
if(options.isForcedTabletMode())
sessionData->setAsTablet();
else if (options.isForcedWSMode())
sessionData->setAsTablet(false);
else if (!QTouchDevice::devices().isEmpty())
sessionData->setAsTablet();
LtsWidgets::LtsComboBox::setUseSelectionDialog(sessionData->isTablet());
LtsAppManager::LtsAppManager *appManager = LtsAppManager::LtsAppManager::getInstance();
appManager->setAvailableApps(appDescs);
// Projection Specific Setup
if (options.isProjectionWorkstation())
{
if (operatorName.isEmpty() && password.isEmpty())
{
ERROR_LOG(app.baseLogger()) << "Projection-User & Password has to be specified when --projection option is active!";
return 1;
}
applicationId = LtsAppDescriptor::App::Situation;
LtsProjectionWorkstation::LtsProjectionWorkstation::getInstance()->setProjectionOperatorPreset(operatorName, password);
}
// Normal Setup
else
{
if (applicationId == LtsAppDescriptor::App::Login)
INFO_LOG(app.baseLogger()) << "No Application specified - starting with App Chooser.";
// TODO: replayid can not life without exerciseId
if (exerciseId >= 0 && replayId >= 0) // do not set both!
{
ERROR_LOG(app.baseLogger()) << "Use either -e (exercise) or -r (replay) - abort.";
return 1;
}
if (!operatorName.isEmpty() && !password.isEmpty())
{
sessionData->setOperatorPreset(operatorName, password); // if -u and -p (no Login window !)
}
if (exerciseId == 0) // Admin Session was requested explicitly
{
//sessionManager->setGlobalSessionVisible();
sessionData->setPartitionPreset(exerciseId, IDL::PartitionType::Global);
}
if (!qos_file.isEmpty()) // use defined qos file
{
//sessionManager->setGlobalSessionVisible();
DEBUG_LOG(app.baseLogger()) << "QOS -> using new file" << qos_file.join(QStringLiteral(","));
sessionData->setQosFile(qos_file);
}
if (exerciseId > 0) // non Admin => Exercise
sessionData->setPartitionPreset(exerciseId, IDL::PartitionType::Exercise);
if (replayId >= 0) // Replay
sessionData->setPartitionPreset(replayId, IDL::PartitionType::Replay);
}
appManager->setLoginPluginIid(loginPluginIid);
appManager->setMainApp(applicationId);
if (applicationId == LtsAppDescriptor::App::ThpSituationReplay)
{
LtsSessionData::LtsSessionData::getInstance()->setVisibleParties({});
LtsSessionData::LtsSessionData::getInstance()->setOperatorRights({-1});
QTimer::singleShot(0, appManager, [qos_file, appManager]()
{
LtsSession::DdsConnectionParams params;
params.domainId = 1;
params.exerciseId = 2;
params.partitionId = 2;
if (!qos_file.isEmpty())
params.qos_file = qos_file;
params.partitionType = IDL::PartitionType::Replay;
appManager->createApp(LtsAppDescriptor::App::Situation, params, {}, false, true);
} );
}
else
{
appManager->showLogin();
}
// Startup Application
STARTUP_LOG(app.baseLogger());
int iRet = app.exec();
SHUTDOWN_LOG(app.baseLogger());
return iRet;
}
@@ -0,0 +1,30 @@
#include <WINVER.H>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @OPAPP_VERSION_INFO@
PRODUCTVERSION @OPAPP_VERSION_INFO@
FILEFLAGSMASK 0x0L
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "Rheinmetall Electronics GmbH"
VALUE "FileDescription", "Operator Application (64bit)"
VALUE "FileVersion", "@OPAPP_VERSION_INFO@"
VALUE "InternalName", "OpApp"
VALUE "LegalCopyright", "Copyright © 2018 Rheinmetall Electronics GmbH. Alle Rechte vorbehalten"
VALUE "OriginalFilename", "OperatorApplication.exe"
VALUE "ProductName", "Operator Application"
VALUE "ProductVersion", "@OPAPP_VERSION_INFO@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x407, 1200
END
END
@@ -0,0 +1,127 @@
#
# THP-Viewer
#
ADD_SUBDIRECTORY(THP-Starter)
FIND_PACKAGE(VLC REQUIRED)
SET(thp_viewer_SRCS
main.cpp
thp_plugin_provider.cpp
thp_plugin_provider.h
thp_situation_filter_interface_impl.cpp
thp_situation_filter_interface_impl.h
thp_viewer.cpp
thp_viewer.h
thpwidgets/thp_audio_video_widget.cpp
thpwidgets/thp_audio_video_widget.h
thpwidgets/thp_audio_video_widget_p.h
thpwidgets/thp_building_settings.cpp
thpwidgets/thp_building_settings.h
thpwidgets/thp_building_widget.cpp
thpwidgets/thp_building_widget.h
thpwidgets/thp_html_widget.cpp
thpwidgets/thp_html_widget.h
thpwidgets/thp_pdf_widget.cpp
thpwidgets/thp_pdf_widget.h
thpwidgets/thp_situation_building_base.h
thpwidgets/thp_situation_building_base.cpp
thpwidgets/thp_situation_filter_aes.cpp
thpwidgets/thp_situation_filter_aes.h
thpwidgets/thp_situation_filter_building_components.cpp
thpwidgets/thp_situation_filter_building_components.h
thpwidgets/thp_situation_filter_collapse_expand.cpp
thpwidgets/thp_situation_filter_collapse_expand.h
thpwidgets/thp_situation_filter_symbol_appearance.cpp
thpwidgets/thp_situation_filter_symbol_appearance.h
thpwidgets/thp_situation_filter_types.cpp
thpwidgets/thp_situation_filter_types.h
thpwidgets/thp_situation_widget.cpp
thpwidgets/thp_situation_widget.h
thpwidgets/thp_zoom_slider.cpp
thpwidgets/thp_zoom_slider.h
)
NGL_WRAP_UI(thp_viewer_SRCS
thp_viewer.ui
thpwidgets/thp_audio_video_widget.ui
thpwidgets/thp_building_settings.ui
thpwidgets/thp_html_widget.ui
thpwidgets/thp_pdf_widget.ui
thpwidgets/thp_situation_building_base.ui
)
IF(WIN32)
SET(JBN ${JENKINS_BUILD_NUMBER})
IF (NOT JENKINS_BUILD_NUMBER)
SET(JBN "0")
ENDIF()
SET(THP_VERSION_INFO "${LTS_VERSION_MAJOR}, ${LTS_VERSION_MINOR}, ${JBN}, ${LTS_VERSION_PATCH}")
CONFIGURE_FILE(thp_viewer.rc.cmake ${CMAKE_CURRENT_BINARY_DIR}/thp_viewer.rc)
LIST(APPEND thp_viewer_SRCS ${CMAKE_CURRENT_BINARY_DIR}/thp_viewer.rc)
ENDIF(WIN32)
NGL_ADD_APP_ICON(thp_viewer_SRCS)
NGL_ADD_EXECUTABLE(WS thp_viewer WIN32 ${thp_viewer_SRCS})
NGL_INSTALL_TARGET(thp_viewer)
NGL_TARGET_INCLUDE_DIRECTORIES(thp_viewer
PRIVATE
${VLC_INCLUDE_DIR}
)
NGL_TARGET_COMPILE_DEFINITIONS(thp_viewer
PRIVATE
${VLC_DEFINES}
)
NGL_TARGET_LINK_LIBRARIES(thp_viewer
ArcGISQt5Hack # make sure it's loaded *before* libEsriCommonQt.so
${VLC_LIBRARIES}
Qt6::Core
Qt6::Gui
Qt6::PrintSupport
Qt6::Svg
Qt6::WebEngineCore
Qt6::WebEngineWidgets
Qt6::Widgets
Qt6::Xml
RmeLightStyleQrc
AsbData
AsbForeignDataPluginThp
BuildingViewerCommon
FltAesGuiCommon
FltAesWidget
FltBldgCmpntWidget
FltParticipantWidget
IsmDataPluginThp
LtsCoordTrans
LtsCommandLineParser
NGLLogger
nglsingleapplication
ngltools
ngltranslator
nglwidgets
PdmComboBoxGui
PpmComboBoxGui
SitmapDds
SituationReplaySqlite
svcFlmCommon
svcAesClient
svcBldClient
svcCesClient
svcGimClient
svcIsmClient
svcPpmClient
svcPdmClient
svcAlmClient
)
IF(WIN32)
# NGL_INSTALL(WS FILES autorun.inf DESTINATION bin)
# NGL_INSTALL(WS FILES readme.win32.en_US.txt DESTINATION bin)
# NGL_INSTALL(WS FILES readme.win32.de_DE.txt DESTINATION bin)
ENDIF()
NGL_INSTALL(WS FILES log4cxx_thp_viewer.props DESTINATION bin)
ADD_SUBDIRECTORY(SitMapReplayTest)
@@ -0,0 +1,101 @@
#
# Hinweise für das THP
#
# $Id$
# $HeadURL$
# $Author$
#
N. Thoden, 12.03.2020
Das TakeHomePackage (für Windows) besteht aus 3 Komponenten:
============================================================
1) den THP-Viewer / THP-Starter
(inkl. Qt ; ohne MSVCRuntime, z.B. msvcr120.dll, in C:\Windows\system32 !?)
http://jenkins-master.sl.defence-elec.de:8080/job/ahtc_OC-Tablet/
thp.tgz
2) die Kartendaten
(evtl. für den Datenträger reduziert in Bezug auf die Speichergröße)
3) die Topic-Daten (Video, Audio, DB-Abzug...)
Woher kommt was?
================
1) Der THP-Viewer / THP-Starter
Kann aus der CI-Umgebung (Jenkins) heruntergeladen werden (thp.tgz)
Am besten mit 7Zip entpackt werden:
(tgz -> gepackt und anschliessend komprimiert)
- Per Kontext-Menue "entpacken nach hier"
- die resultierende Datei "thp' "entpacken nach thp~".
Dann sollte ein Verzeichnis "thp" MIT diversen Unterverzeichnissen entstehen.
Das THP-Verzeichnis wird auf jeder Workstation im Ordner /opt/THP als Quelle für den USB-Datentraeger bereitgestellt.
2) Diese werden manuell für das THP bereitgestellt (meist als tpk-Datei).
3) Die Topic-Daten werden in der OpApp mit "Präsentation exportieren" erzeugt.
Sie werden unter /tmp/THP-Export generiert.
Woher kommt das Verzeichnis 'Maps'?
===================================
Der Verzeichnisname 'Maps' ist fest und darf nicht verändert werden.
Es müssen die für das Medium (CD, DVD, Stick..) passenden Basiskarten/Kartendaten hinein.
Das sind also z.B.
- 2020-03-03_UaeAerial_32639_Lvl6.tpk
- 2020-03-03_UaeAerial_WebMerc_Lvl15.tpk
- 2020-03-03_UaeTopo_32639_Lvl6.tpk
- 2020-03-03_UaeTopo_WebMerc_Lvl15.tpk
- AHTC_utm_dgm_surface_wgs84.tif
Das layerMapping.json hat nun die Aufgabe, diese (für das Übungsgebiet) spezifischen
Dateinamen zu abstrahieren.
Die bei der Ausführung des THPs geforderten Basiskarten (Annahme: eine Topo und zwei Aerial)
können mit Hilfe der layerMapping.json auf eine geringere Anzahl Dateien gemappt werden.
Basiskarten: Mapping per layerMapping.json
in der WS-App: Dateien in Dir 'Maps'
-------------- ---------------------
- Aerial2 -> 2020-03-03_UaeAerial_32639_Lvl6.tpk
- Aerial1 -> 2020-03-03_UaeAerial_32639_Lvl6.tpk
- Topo -> 2020-03-03_UaeTopo_32639_Lvl6.tpk
Best Practise:
Daher ergibt es Sinn, eine layerMapping.json in dem Verzeichnis 'Maps' vorzubereiten
und beim Zusammengefügen in die darüberliegende Dateiebene zu kopieren.
Wie müssen die 3 Komponenten zusammengefügt werden?
===================================================
Device:
- Excersise_6
- 1 <- Übungsdaten (Komponenten 3)
- ...
- index.html <- Übungsdaten (Komponenten 3)
- ... <- Übungsdaten (Komponenten 3)
- Maps <- Kartendaten (Komponenten 2)
- *.tpk
- [layerMapping.json] <- Sonderfall AHTC 03/2020: Diese Datei in
die darüberliegende Ebenen verschieben.
- layerMapping.json <- Übungsdaten (Komponenten 2/3)
(generiert, Sonderfall AHTC 03/2020:
aus 'Maps' hierher transferieren)
- thp <- thp (Komponenten 1)
- resources <- thp (Komponenten 1), hat mal gefehlt)
- thv_viewer.exe <- thp (Komponenten 1)
- vc.redist.x64.exe <- thp (Komponenten 1), für MSVCRuntime
- thp_starter.exe <- thp (Komponenten 1)
- autorun.inf <- thp (Komponenten 1)
- [wappen.png] <- thp (Komponenten 1 oder 3) optional !?
Logfiles zuir Fehlersuche
=========================
Ein Logfile befindet sich unter ~Benutzer\AppData\Local\Temp\.
Bsp: C:\User\thoden\AppData\Local\Temp\thoden_thp.log
C:\User\thoden\AppData\Local\Temp\thoden_thp.log.1
@@ -0,0 +1,40 @@
#
# SitMapReplayTest
#
SET(SRPTEST_SRCS
srp_main.cpp
srp_main_window.cpp
srp_main_window.h
../thp_plugin_provider.cpp
../thp_plugin_provider.h
../thp_situation_filter_interface_impl.cpp
../thp_situation_filter_interface_impl.h
)
NGL_WRAP_UI(SRPTEST_SRCS
srp_main_window.ui
)
NGL_ADD_EXECUTABLE(WS SitMapReplayTest ${SRPTEST_SRCS})
NGL_TARGET_LINK_LIBRARIES(SitMapReplayTest
SituationReplaySqlite
ddsconnector
Qt6::Core
nglapplication
SitmapDds
AsbForeignDataPluginThp
BuildingViewerCommon
FltAesGuiCommon
IsmDataPluginThp
svcFlmCommon
svcAesClient
svcBldClient
svcCesClient
svcGimClient
svcIsmClient
svcPpmClient
svcPdmClient
)
@@ -0,0 +1,64 @@
/**
* @RDE_HEADER@
* Main for SitMapReplayTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include <nglapplication/nglapplication.h>
#include <DDS/DDSConnector/dds_connector.h>
#include <DDS/QosProfile/DDS_DefaultQos.h>
#include <QNetworkProxy>
#include <SituationReplay/main_handler_sqlite.h>
#include <SituationMap/situation_map_init.h>
#include <SituationMap/Utils/movement_track_manager.h>
#include "../thp_plugin_provider.h"
#include "srp_main_window.h"
#include "ui_srp_main_window.h"
int main(int argc, char **argv)
{
QNetworkProxy::setApplicationProxy(QNetworkProxy());
NGLApplication app(argc, argv);
app.createLogger("SRP");
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
SituationReplay::ConnectionParams connParams;
connParams.domainId = 1;
connParams.partitionId = 1;
std::shared_ptr<SituationReplay::MainHandlerSqlite> srpMainHandler(new SituationReplay::MainHandlerSqlite);
if (!srpMainHandler->initDds(connParams))
return 1;
// TODO: fixme or trash me
SituationReplay::ReplayInformation ri;
ri.databaseFilePath = QStringLiteral("dump.sqlite");
ri.replayConfigPath = QStringLiteral("dump.json");
if (!srpMainHandler->initDb(ri, {}, {}))
return 2;
std::shared_ptr<DDSConnector::DdsConnector> ddsConn = srpMainHandler->ddsReplay();
THP::SitMapPluginProvider *pp = new THP::SitMapPluginProvider;
pp->init(ddsConn);
SituationMap::SituationMapInit *smi = new SituationMap::SituationMapInit(nullptr);
smi->initPluginProvider(pp);
QPointer<SituationMap::MovementTrackManager> movementTrackMgr =
QPointer<SituationMap::MovementTrackManager>(new SituationMap::MovementTrackManager(smi));
smi->initAdapter(movementTrackMgr, false);
smi->initMap(true, SituationMap::Mode::Mode_2D);
SituationReplay::MainWindow *mw = new SituationReplay::MainWindow(smi, srpMainHandler->handlerSrp());
mw->show();
int ret = app.exec();
delete mw;
delete smi;
delete pp;
ddsConn.reset();
return ret;
}
@@ -0,0 +1,47 @@
/**
* @RDE_HEADER@
* MainWindow for SitMapReplayTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "srp_main_window.h"
#include "ui_srp_main_window.h"
#include <SituationMap/situation_map_init.h>
#include <SituationReplay/handler_situation_replay_sqlite.h>
namespace SituationReplay
{
MainWindow::MainWindow(SituationMap::SituationMapInit *sitMap,
const std::shared_ptr<HandlerSituationReplaySqlite> &srpHandler,
QWidget *parent)
: QMainWindow(parent)
, m_srpHandler(srpHandler)
, m_ui(new Ui::SrpMainWindow)
{
m_ui->setupUi(this);
setCentralWidget(sitMap->getWidget());
connect(m_ui->actionStart_Replay, &QAction::triggered, this, &MainWindow::onStartReplay);
connect(m_ui->actionStop_Replay, &QAction::triggered, this, &MainWindow::onStopReplay);
}
MainWindow::~MainWindow()
{
delete m_ui;
}
void MainWindow::onStartReplay()
{
const qint64 begin = (time(nullptr) - 60 * 60 * 30) * 1000;
const qint64 end = 0;
const double pace = 1;
m_srpHandler->onStartReplayRequest(begin, end, pace);
}
void MainWindow::onStopReplay()
{
m_srpHandler->onStopReplayRequest();
}
}
@@ -0,0 +1,41 @@
/**
* @RDE_HEADER@
* MainWindow for SitMapReplayTest
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <QMainWindow>
#include <memory>
namespace Ui
{
class SrpMainWindow;
}
namespace SituationMap
{
class SituationMapInit;
}
namespace SituationReplay
{
class HandlerSituationReplaySqlite;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(SituationMap::SituationMapInit *sitMap,
const std::shared_ptr<HandlerSituationReplaySqlite> &srpHandler,
QWidget *parent = nullptr);
~MainWindow();
protected slots:
void onStartReplay();
void onStopReplay();
private:
std::shared_ptr<HandlerSituationReplaySqlite> m_srpHandler;
Ui::SrpMainWindow *m_ui;
};
}
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SrpMainWindow</class>
<widget class="QMainWindow" name="SrpMainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>Fi&amp;le</string>
</property>
<addaction name="actionStart_Replay"/>
<addaction name="actionStop_Replay"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionStart_Replay">
<property name="text">
<string>Start Replay</string>
</property>
</action>
<action name="actionStop_Replay">
<property name="text">
<string>Stop Replay</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,22 @@
#
# $Id$
# $HeadURL$
# $Author$
#
IF(WIN32)
# static msvc runtime
FOREACH(flag_var
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
IF(${flag_var} MATCHES "/MD")
STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
ENDIF()
ENDFOREACH()
SET(THP_ICO "${CMAKE_SOURCE_DIR}/Lts/Common/LtsWidgets/resources/rde_icon_256.ico")
CONFIGURE_FILE(thp_starter.rc.cmake ${CMAKE_CURRENT_BINARY_DIR}/thp_starter.rc)
NGL_ADD_EXECUTABLE(WS thp_starter WIN32 thp_starter.cpp ${CMAKE_CURRENT_BINARY_DIR}/thp_starter.rc)
NGL_TARGET_LINK_LIBRARIES(thp_starter user32.lib opengl32.lib gdi32.lib)
NGL_INSTALL_TARGET(thp_starter)
ENDIF(WIN32)
@@ -0,0 +1,149 @@
/**
* @RDE_HEADER@
* Small wrapper to automatically start thp_viewer
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
static void* getGLExtensionFuncPtr(const char *funcName, const char *fallbackFuncName)
{
void* ptr = wglGetProcAddress(funcName);
if (ptr)
return ptr;
return wglGetProcAddress(fallbackFuncName);
}
static int checkOpenGL(HWND hWnd)
{
HDC hDC = GetDC( hWnd );
PIXELFORMATDESCRIPTOR pfd;
ZeroMemory( &pfd, sizeof( pfd ) );
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL |
PFD_DOUBLEBUFFER |
PFD_SWAP_EXCHANGE;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cRedBits = 8;
pfd.cGreenBits = 8;
pfd.cBlueBits = 8;
pfd.cAlphaBits = 8;
pfd.cDepthBits = 0;
pfd.iLayerType = PFD_MAIN_PLANE;
int iFormat = ChoosePixelFormat( hDC, &pfd );
if( !SetPixelFormat( hDC, iFormat, &pfd ) ) {
DWORD dw = GetLastError();
int ret = MessageBox(NULL,
L"Your system does not support some mandatory OpenGL Functions.\nThe THP-Viewer may not display all things correct!\nDo you really want to start the THP-viewer?",
L"THP-Viewer startup",
MB_ICONWARNING|MB_YESNO);
if(ret == IDNO) {
ReleaseDC( hWnd, hDC );
return 1;
}
}
HGLRC hRC = wglCreateContext( hDC );
DWORD dw = wglMakeCurrent( hDC, hRC );
void *_glCompressedTexImage2D = getGLExtensionFuncPtr( "glCompressedTexImage2D", "glCompressedTexImage2DARB" );
if(!_glCompressedTexImage2D) {
int ret = MessageBox(NULL,
L"Your system does not support the mandatory OpenGL Function glCompressedTexImage2D.\nThe THP-Viewer may not display all things correct!\nDo you really want to start the THP-viewer?",
L"THP-Viewer startup",
MB_ICONWARNING|MB_YESNO);
if(ret == IDNO) {
wglDeleteContext( hRC );
ReleaseDC( hWnd, hDC );
return 1;
}
}
wglDeleteContext( hRC );
ReleaseDC( hWnd, hDC );
return 0;
}
static int startThpViewer(HINSTANCE hInstance)
{
TCHAR fn[MAX_PATH*10];
DWORD len = GetModuleFileName(hInstance, fn, sizeof(fn));
for(int i = len - 1; i >= 0; --i) {
if(fn[i] == '\\')
break;
fn[i] = '\0';
}
wcscat(fn, L"thp\\thp_viewer.exe");
STARTUPINFO si; ZeroMemory(&si, sizeof(si));
PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
DWORD ret = CreateProcess(fn,
NULL, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
NULL, // bInheritHandles
0, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, // lpStartupInfo
&pi); // lpProcessInformation
if(ret == 0) {
MessageBox(NULL,
L"Error starting the THP-Viewer!",
L"THP-Viewer startup",
MB_ICONWARNING|MB_OK);
return 1;
}
// Wait until child process exits.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc( hWnd, message, wParam, lParam );
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wc;
ZeroMemory( &wc, sizeof(wc) );
// register window class
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"THP_Viewer_startup";
RegisterClass( &wc );
// create main window
HWND hWnd = CreateWindow(L"THP_Viewer_startup",
L"THP-Viewer startup",
WS_CAPTION | WS_POPUPWINDOW,
0, 0, 256, 256,
NULL, NULL, hInstance, NULL );
int iRet = checkOpenGL(hWnd);
if(iRet != 0)
return iRet;
return startThpViewer(hInstance);
}
@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "@THP_ICO@"
@@ -0,0 +1,6 @@
[autorun]
OPEN=thp\bin\thp_viewer.exe
ICON=thp\bin\thp_viewer.exe,0
SHELL=readit
SHELL\readit="THP-Viewer starten"
SHELL\readit\command=thp\bin\thp_viewer.exe
@@ -0,0 +1,28 @@
log4j.reset=true
log4j.rootLogger=WARN, file
#log4j.logger.GDW=WARN
#log4j.logger.GDW_CTC=WARN
#log4j.logger.KALA=WARN
#
# THP
#
#log4j.logger.THP=WARN
#log4j.logger.THP.AudioVideoWidget=WARN
#log4j.logger.THP.PlanFilter=WARN
#log4j.logger.THP.SymbolLoaderThread=WARN
#log4j.logger.THP.SHMFileHandler=WARN
# Appender
############
log4j.appender.file=Lts::RollingFileAppender
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=9
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
# Appnd_Console
###########################################################
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n
+166
View File
@@ -0,0 +1,166 @@
/**
* @RDE_HEADER@
* Main entry for thp viewer
* $Id$
* $HeadURL$
* $Author$
*/
#include <nglapplication/nglsingleapplication.h>
#include <QUrl>
#include <QDir>
#include <QMessageBox>
#include <QNetworkProxy>
#include <QSplashScreen>
#include <LtsCommandLineParser/LtsCommandLineParser.h>
#include "thp_viewer.h"
#include <NGLLogger/INGLLogger.h>
#include <ngltranslator/ngltranslator.h>
#ifdef Q_OS_WIN
# include <windows.h>
#endif
class CmdLineParser : public LtsCommandLineParser::LtsCommandLineParser
{
public:
CmdLineParser()
: LtsCommandLineParser(ProvideCommandLineParameterOptions::None)
{
addOption({{QStringLiteral("l"), QStringLiteral("lang")},
QStringLiteral("Language iso code to use (de_DE, en_GB, en_US)"),
QStringLiteral("<locale iso code>")
});
addOption({{QStringLiteral("t"), QStringLiteral("alwaysontop")},
QStringLiteral("Set as always on top window")
});
}
bool useAsAlwaysOnTop() const
{
return isSet(QStringLiteral("t"));
}
QString langIsoCode() const { return value(QStringLiteral("l")); }
};
int main(int argc, char **argv)
{
QNetworkProxy::setApplicationProxy(QNetworkProxy());
NGLSingleApplication app(argc, argv);
app.createLogger("THP", "log4cxx_thp_viewer.props");
app.setOrganizationName(QStringLiteral("RME"));
app.setApplicationName(QStringLiteral("thp_viewer"));
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/style/")));
Q_INIT_RESOURCE(rme_light_style);
// Parse Options
CmdLineParser options;
if (!options.init())
return -1;
// Set Translation
QString langIsoCode = options.langIsoCode();
if (langIsoCode.isEmpty())
langIsoCode = QLocale::system().name();
NGL::setTranslationLocale(langIsoCode.toLatin1());
//check for optional alyways on top flag
bool alwaysOnTop = options.useAsAlwaysOnTop();
QString index;
const QStringList args = options.positionalArguments();
// QString searchDir = QCoreApplication::applicationDirPath();
QString searchDir(QDir::currentPath());
if (args.count() == 1)
{
QFileInfo fi(args.first());
if (fi.isDir())
searchDir = fi.absolutePath();
else
index = fi.absoluteFilePath();
}
if (index.isEmpty())
{
const auto indexFiles = { QStringLiteral("index_%1.html").arg(langIsoCode),
QStringLiteral("index.html") };
for (const QString &indexFile : indexFiles)
{
QDir d(searchDir);
index = d.absoluteFilePath(indexFile);
while (!d.exists(index))
{
if (!d.cdUp())
break;
index = d.absoluteFilePath(indexFile);
}
if (d.exists(index))
break;
}
}
QFileInfo fi(index);
QString basePath = fi.path() + QLatin1Char('/');
if (!fi.exists() || !fi.isFile())
{
fprintf(stderr, "%s <path_to_html> basePath=%s\n", argv[0], basePath.toStdString().c_str());
QMessageBox::critical(nullptr,
QObject::tr("File missing"),
QObject::tr("Could not find a valid index.html - aborting"));
return -1;
}
index = fi.absoluteFilePath();
if (app.sendMessage(index))
return 0;
int ret = 1;
{
QString splashfile = basePath;
static const auto extension = {QStringLiteral("gif"),
QStringLiteral("jpg"),
QStringLiteral("png")};
for (const auto &ext : extension)
{
const QString fn = splashfile + QStringLiteral("/wappen.") + ext;
if (!QFile::exists(fn))
continue;
splashfile = fn;
break;
}
const QPixmap splashPixmap(splashfile);
QSplashScreen splash((splashPixmap));
if (!splashPixmap.isNull())
splash.show();
#ifdef BUILD_STATIC_LIBRARIES
Q_INIT_RESOURCE(libsymbol);
Q_INIT_RESOURCE(nglresources);
#endif
NGL::ThpViewer view(QUrl::fromLocalFile(index));
if (alwaysOnTop)
{
view.setWindowFlag(Qt::WindowStaysOnTopHint, true);
view.setWindowFlag(Qt::WindowMinimizeButtonHint, false);
}
if (!view.init())
return ret;
view.center();
QObject::connect(&app, &NGLSingleApplication::messageReceived,
&view, &NGL::ThpViewer::onNewThp);
view.show();
splash.finish(&view);
ret = app.exec();
}
return ret;
}
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* Plugin Provider for standalone SRP / THP
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "thp_plugin_provider.h"
#include <AreaEffectSimulation/aes_dtc.h>
#include <AdvancedSymbology/Data/asb_foreign_interface_impl.h>
#include <Buildings/bld_dtc.h>
#include <CentralEffectSimulation/ces_dtc.h>
#include <GuezIedManagement/gim_dtc.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <InfrastructureManagement/Data/ism_foreign_data_plugin.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <ProjectDataManagement/pdm_dtc.h>
#include <DDS/DDSConnector/dds_connector.h>
#include "thp_situation_filter_interface_impl.h"
namespace THP
{
QString SitMapPluginProvider::toString() const
{
return QString();
}
bool SitMapPluginProvider::init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector)
{
m_ddsConnector = ddsConnector;
m_buildingPluginProvider = std::make_shared<BuildingPluginProvider>();
m_aesDtc = AreaEffectSimulation::Interface::Dtc::create(ddsConnector);
m_aesDtc->enableAll();
m_bldDtc = Buildings::Interface::Dtc::create(ddsConnector);
m_bldDtc->enableAll();
m_cesDtc = CentralEffectSimulation::Interface::Dtc::create(ddsConnector);
m_cesDtc->enableAll();
m_gimDtc = GuezIedManagement::Interface::Dtc::create(ddsConnector);
m_gimDtc->enableAll();
// m_ismDtc = InfrastructureManagement::Interface::Dtc::create(ddsConnector);
// m_ismDtc->enableAll();
// m_ismForeign = std::make_shared<InfrastructureManagement::ForeignInterfaceImpl>(m_ismDtc);
m_ppmDtc = ParticipantManagement::Interface::Dtc::create(ddsConnector);
m_ppmDtc->enableAll();
m_pdmDtc = ProjectDataManagement::Interface::Dtc::create(ddsConnector);
m_pdmDtc->enableAll();
// needs m_ppmDtc
m_fltParticipantIfc = std::make_shared<FilterManagement::ThpFilterInterfaceImpl>(m_ppmDtc);
m_fltAesIfc = std::make_shared<FilterManagement::ThpFilterInterfaceImpl>(m_ppmDtc);
m_fltBuildingComponentIfc = std::make_shared<FilterManagement::ThpFilterInterfaceImpl>(m_ppmDtc);
// m_fltParticipantIfc = m_fltAesIfc; // Not sure, lets try
return m_buildingPluginProvider->init(ddsConnector, this);
}
bool BuildingPluginProvider::init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
SitMapPluginProvider *smPluginProvider)
{
Q_ASSERT(smPluginProvider);
m_smPluginProvider = smPluginProvider;
connect(m_smPluginProvider, &SitMapPluginProvider::partitionTimeChanged,
this, &BuildingPluginProvider::partitionTimeChanged);
m_connectionParams.domainId = ddsConnector->getDomainId();
m_connectionParams.partitionId = ddsConnector->getPartitionId();
m_connectionParams.exerciseId = ddsConnector->getPartitionId();
m_connectionParams.tcpConnectionUrl = ddsConnector->getTcpConnectionUrl();
m_asbIfc = std::make_shared<AdvancedSymbology::ForeignInterfaceImpl>(m_smPluginProvider->ppmDtc(),
m_smPluginProvider->aesDtc(),
m_smPluginProvider->gimDtc());
return true;
}
std::shared_ptr<InfrastructureManagement::ForeignDataInterface> SitMapPluginProvider::ismDataIfc() const
{
if (!m_ismForeign)
m_ismForeign = std::make_shared<InfrastructureManagement::ForeignInterfaceImpl>(ismDtc());
return m_ismForeign;
}
std::shared_ptr<InfrastructureManagement::Interface::Dtc> SitMapPluginProvider::ismDtc() const
{
if (!m_ismDtc)
{
m_ismDtc = InfrastructureManagement::Interface::Dtc::create(m_ddsConnector);
m_ismDtc->enableAll();
}
return m_ismDtc;
}
} // namespace THP
@@ -0,0 +1,149 @@
/**
* @RDE_HEADER@
* Plugin Provider for standalone SRP / THP
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <SituationMap/AdapterDds/plugin_provider.h>
#include <BuildingViewer/Common/bdv_plugin_provider.h>
#include <AlarmManagement/alm_info_gui_plugin_interface.h>
#include <LtsAppDescriptor/lts_app_descriptor.h>
#include <LtsSession/lts_session_data_dds.h>
#include <QtCore/QVector>
namespace DDSConnector
{
class DdsConnector;
}
namespace THP
{
class BuildingPluginProvider;
class SitMapPluginProvider final : public SituationMap::PluginProvider
{
Q_OBJECT
public:
using SituationMap::PluginProvider::PluginProvider;
QString toString() const override;
bool init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector);
void emitPartitionTimeChanged(qint64 curTime) { m_curPartitionTime = curTime; emit partitionTimeChanged(curTime); }
void setExerciseId(int exerciseId) { m_exerciseId = exerciseId; }
int exerciseId() const override { return m_exerciseId; }
bool isSessionValid() const override { return true; }
qint64 partitionTime() const override { return m_curPartitionTime; }
int partitionId() const override { return -1; }
int operatorId() const override { return 42; }
LtsAppDescriptor::App appId() const override { return LtsAppDescriptor::App::ThpSituationReplay; }
bool isVisibleParty(int) const override { return true; }
QString workspaceId() const override { return {}; }
bool isReplayPartition() const override { return false; }
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> aesDtc() const override { return m_aesDtc; }
std::shared_ptr<AreaEffectSimulation::ForeignInfoGuiInterface> aesInfoIfc() const override { return {}; }
std::shared_ptr<AlarmManagement::Interface::Dtc> almDtc() const override { return {}; }
std::shared_ptr<AlarmManagement::Interface::Ftc> almFtc() const override { return {}; }
std::shared_ptr<AlarmManagement::ForeignInfoGuiInterface> almInfoIfc() const override { return {}; }
std::shared_ptr<AssetManagement::Interface::Dtc> asmDtc() const override { return {}; }
std::shared_ptr<AssetManagement::Interface::Ftc> asmFtc() const override { return {}; }
std::shared_ptr<Buildings::Interface::Dtc> bldDtc() const override { return m_bldDtc; }
std::shared_ptr<Buildings::Interface::Ftc> bldFtc() const override { return {}; }
std::shared_ptr<Buildings::ForeignInfoGuiInterface> bldInfoIfc() const override { return {}; }
std::shared_ptr<CentralEffectSimulation::Interface::Dtc> cesDtc() const override { return m_cesDtc; }
std::shared_ptr<ExerciseManagement::Interface::Dtc> exmDtc() const override { return {}; }
std::shared_ptr<ExerciseManagement::Interface::Ftc> exmFtc() const override { return {}; }
std::shared_ptr<FilterManagement::Interface::Dtc> fltDtc() const override { return {}; }
std::shared_ptr<FilterManagement::Interface::Ftc> fltFtc() const override { return {}; }
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> fltParticipantIfc() const override { return m_fltParticipantIfc; }
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> fltAesIfc() const override { return m_fltAesIfc; }
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> fltBuildingComponentIfc() const override { return m_fltBuildingComponentIfc; }
std::shared_ptr<GuezIedManagement::Interface::Dtc> gimDtc() const override { return m_gimDtc; }
std::shared_ptr<GuezIedManagement::ForeignGuiInterface> gimIfc() const override { return {}; }
std::shared_ptr<GuezNasat::Interface::Dtc> gnaDtc() const override { return {}; }
std::shared_ptr<GuezNasat::ForeignGuiInterface> gnaIfc() const override { return {}; }
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc() const override;
std::shared_ptr<InfrastructureManagement::ForeignDataInterface> ismDataIfc() const override;
std::shared_ptr<InfrastructureManagement::ForeignResourceManagementGuiInterface> ismGuiIfc() const override { return {}; }
std::shared_ptr<OperatorManagement::Interface::Dtc> opmDtc() const override { return {}; }
std::shared_ptr<OperatorManagement::Interface::Ftc> opmFtc() const override { return {}; }
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtc() const override { return m_ppmDtc; }
std::shared_ptr<ParticipantManagement::Interface::Ftc> ppmFtc() const override { return {}; }
std::shared_ptr<ParticipantManagement::ForeignParticipantInfoGuiInterface> ppmParticipantInfoIfc() const override { return {}; }
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const override { return m_pdmDtc; }
std::shared_ptr<SituationReplay::Interface::Dtc> srpDtc() const override { return m_srpDtc; }
std::shared_ptr<OcSituationClient::ForeignParticipantInfoGuiInterface> ocParticipantInfoIfc() const override { return {}; }
std::shared_ptr<ServiceMonitor::ForeignGuiInterface> smoServiceMonitorIfc() const override { return {}; }
std::shared_ptr<TargetManagement::Interface::Dtc> tgtDtc() const override { return {}; }
std::shared_ptr<ComponentMonitor::Interface::Dtc> cmoDtc() const override { return {}; }
// for BuildingPluginProvider
BuildingPluginProvider *buildingPluginProvider() { return m_buildingPluginProvider.get(); }
private:
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> m_aesDtc;
std::shared_ptr<Buildings::Interface::Dtc> m_bldDtc;
std::shared_ptr<CentralEffectSimulation::Interface::Dtc> m_cesDtc;
std::shared_ptr<FilterManagement::ForeignFltParticipantGuiInterface> m_fltParticipantIfc;
std::shared_ptr<FilterManagement::ForeignAesGuiInterface> m_fltAesIfc;
std::shared_ptr<FilterManagement::ForeignFltBuildingComponentGuiInterface> m_fltBuildingComponentIfc;
std::shared_ptr<GuezIedManagement::Interface::Dtc> m_gimDtc;
mutable std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
mutable std::shared_ptr<InfrastructureManagement::ForeignDataInterface> m_ismForeign;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<SituationReplay::Interface::Dtc> m_srpDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
qint64 m_curPartitionTime = 0;
int m_exerciseId = 0;
std::shared_ptr<BuildingPluginProvider> m_buildingPluginProvider;
std::shared_ptr<DDSConnector::DdsConnector> m_ddsConnector;
};
class BuildingPluginProvider final : public BuildingViewer::PluginProvider
{
Q_OBJECT
public:
using BuildingViewer::PluginProvider::PluginProvider;
BuildingPluginProvider() = default;
QString toString() const override { return m_smPluginProvider->toString(); }
bool init(const std::shared_ptr<DDSConnector::DdsConnector> &ddsConnector,
SitMapPluginProvider *smPluginProvider);
void emitPartitionTimeChanged(qint64 curTime) { m_smPluginProvider->emitPartitionTimeChanged(curTime); }
int exerciseId() const override { return m_smPluginProvider->exerciseId(); }
qint64 partitionTime() const override { return m_smPluginProvider->partitionTime(); }
bool isReplayPartition() const override { return false; }
bool isVisibleParty(int partyId) const override { return m_smPluginProvider->isVisibleParty(partyId); }
LtsSession::DdsConnectionParams connParams() const override { return m_connectionParams; }
std::shared_ptr<AdvancedSymbology::ForeignDataInterface> asbIfc() const override { return m_asbIfc; }
std::shared_ptr<AudioVideoManagement::ForeignDataInterface> avmDataIfc() const override { return nullptr; } // optional
std::shared_ptr<Buildings::Interface::Dtc> bldDtc() const override { return m_smPluginProvider->bldDtc(); }
std::shared_ptr<Buildings::ForeignInfoGuiInterface> bldInfoIfc() const override { return m_smPluginProvider->bldInfoIfc(); } // optional
std::shared_ptr<InfrastructureManagement::Interface::Dtc> ismDtc() const override { return m_smPluginProvider->ismDtc(); }
std::shared_ptr<InfrastructureManagement::ForeignDataInterface> ismDataIfc() const override { return m_smPluginProvider->ismDataIfc(); }
std::shared_ptr<AreaEffectSimulation::Interface::Dtc> aesDtc() const override { return m_smPluginProvider->aesDtc(); } // optional
std::shared_ptr<ParticipantManagement::Interface::Dtc> ppmDtc() const override { return m_smPluginProvider->ppmDtc(); } // optional
std::shared_ptr<ProjectDataManagement::Interface::Dtc> pdmDtc() const override { return m_smPluginProvider->pdmDtc(); } // optional
std::shared_ptr<SituationReplay::Interface::Dtc> srpDtc() const override { return m_smPluginProvider->srpDtc(); } // optional
std::shared_ptr<ParticipantManagement::ForeignParticipantInfoGuiInterface> ppmParticipantInfoIfc() const override { return m_smPluginProvider->ppmParticipantInfoIfc(); } // optional
private:
SitMapPluginProvider *m_smPluginProvider{nullptr};
std::shared_ptr<AdvancedSymbology::ForeignDataInterface> m_asbIfc;
LtsSession::DdsConnectionParams m_connectionParams;
};
}
@@ -0,0 +1,47 @@
/**
* @RDE_HEADER@
* Simple implmentation of FilterHandling for THP
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "thp_situation_filter_interface_impl.h"
#include <FilterManagement/Gui/Aes/Common/symbol_appearance_settings_impl.h>
#include <FilterManagement/Gui/Aes/Common/aes_display_filter_impl.h>
#include <FilterManagement/Gui/Aes/Common/building_cmpnt_settings_impl.h>
namespace FilterManagement
{
ThpFilterInterfaceImpl::ThpFilterInterfaceImpl(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtcPpm)
: m_fltSymbols(std::make_shared<SymbolAppearanceSettingsImpl>(dtcPpm))
, m_fltAes(std::make_shared<AesDisplayFilterImpl>())
, m_fltBuildingComponentSettings(std::make_shared<BuildingComponentSettingsImpl>())
{
m_fltAes->setVisiblePartiesCheck([](int) -> bool { return true; });
}
std::shared_ptr<SymbolAppearanceSettings> ThpFilterInterfaceImpl::symbolAppearanceForOperator(int operatorId, const QString &context)
{
Q_UNUSED(operatorId);
Q_UNUSED(context);
return m_fltSymbols;
}
std::shared_ptr<AesDisplayFilter> ThpFilterInterfaceImpl::aesFilterForOperator(int operatorId, const QString &context)
{
Q_UNUSED(operatorId);
Q_UNUSED(context);
return m_fltAes;
}
std::shared_ptr<BuildingComponentSettings> ThpFilterInterfaceImpl::buildingComponentSettingsForOperator(int operatorId, const QString &context)
{
Q_UNUSED(operatorId);
Q_UNUSED(context);
return m_fltBuildingComponentSettings;
}
}
@@ -0,0 +1,40 @@
/**
* @RDE_HEADER@
* Simple implmentation of FilterHandling for THP
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#pragma once
#include <FilterManagement/flt_participant_plugin_interface.h>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <ParticipantManagement/ppm_dtc.h>
namespace FilterManagement
{
class ThpFilterInterfaceImpl : public ForeignFltParticipantGuiInterface,
public ForeignAesGuiInterface,
public ForeignFltBuildingComponentGuiInterface
{
public:
explicit ThpFilterInterfaceImpl(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &dtcPpm);
// from Participant
std::shared_ptr<SymbolAppearanceSettings> symbolAppearanceForOperator(int operatorId, const QString &context) override;
// from Aes
std::shared_ptr<AesDisplayFilter> aesFilterForOperator(int operatorId, const QString &context) override;
// from BuildingComponent
std::shared_ptr<BuildingComponentSettings> buildingComponentSettingsForOperator(int operatorId, const QString &context) override;
private:
std::shared_ptr<SymbolAppearanceSettings> m_fltSymbols;
std::shared_ptr<AesDisplayFilter> m_fltAes;
std::shared_ptr<BuildingComponentSettings> m_fltBuildingComponentSettings;
};
}
@@ -0,0 +1,686 @@
/**
* @RDE_HEADER@
* Main thp viewer widget
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_viewer.h"
#include "ui_thp_viewer.h"
#include "thp_plugin_provider.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QKeyEvent>
#include <QPrintDialog>
#include <QPrinter>
#include <QPainter>
#include <QMessageBox>
#include <QTextDocument>
#include <QScreen>
#include <QDesktopServices>
#include <QDomElement>
#include <QDomDocument>
#include <QUrl>
#include <QRect>
#include <QSize>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QSettings>
#include <QInputDialog>
#include <QDir>
#include <ngltranslator/ngltranslator.h>
#include <ngltools/ngltools_dtg.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace NGL
{
/*
* ThpViewer base class
*/
ThpViewer::ThpViewer(const QUrl &url, QWidget *parent)
: QWidget(parent)
, ui(new Ui::thpViewer)
, m_scaleFactor(1.0)
, m_homeLink(url)
, m_homePath(QFileInfo(m_homeLink.toLocalFile()).absolutePath())
, m_trainingName(QString())
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("ThpViewer"))
{
ui->setupUi(this);
ui->audioWidget->setAV(true);
ui->videoWidget->setAV(false);
/*
TODO
if (!conffile.isEmpty())
{
QSettings thpsettings(conffile, QSettings::IniFormat, this);
// needed?
const char timeZone = thpsettings.value(QStringLiteral("Time/Timezone"), QStringLiteral("Z")).toString().at(0).toLatin1();
DTG::setTimezone(timeZone, true);
}
*/
QString basedir(url.toLocalFile());
basedir = basedir.left(basedir.lastIndexOf(QLatin1Char('/')));
DEBUG_LOG(m_logger) << "url: " << url.toString() << " basedir: " << basedir;
QFont f = ui->mainLabel->font();
f.setBold(true);
f.setPointSizeF(f.pointSizeF() * 1.2);
ui->mainLabel->setFont(f);
connect(ui->closeButton, &QToolButton::clicked, this, &ThpViewer::close);
connect(ui->backButton, &QToolButton::clicked, this, &ThpViewer::onBack);
connect(ui->homeButton, &QToolButton::clicked, this, &ThpViewer::onHome);
connect(ui->helpButton, &QToolButton::clicked, this, &ThpViewer::onHelp);
connect(ui->printButton, &QToolButton::clicked, this, &ThpViewer::onPrint);
connect(ui->fullScreenButton, &QToolButton::clicked, this, &ThpViewer::onFullScreen);
connect(ui->htmlWidget, &THP::HtmlWidget::linkClicked, this, &ThpViewer::onLinkClicked);
connect(ui->htmlWidget, &THP::HtmlWidget::urlChanged, this, &ThpViewer::onUrlChanged);
connect(ui->htmlWidget, &THP::HtmlWidget::titleChanged, this, &ThpViewer::onTitleChanged);
ui->backButton->setEnabled(false);
ui->helpButton->setVisible(false); // Help disabled for now
#ifndef Q_WS_WIN
setWindowIcon(QIcon(QStringLiteral(":icons/16x16/rde.ico")));
#endif
setWindowTitle(tr("Combat Training Center"));
}
ThpViewer::~ThpViewer()
{
delete ui;
}
bool ThpViewer::init()
{
QFile file(m_homePath + QStringLiteral("/data.json"));
int exerciseId = 0;
if (file.exists() && file.open(QIODevice::ReadOnly))
{
const auto obj = QJsonDocument::fromJson(file.readAll()).object();
exerciseId = obj.value(QLatin1String("exerciseId")).toInt();
}
else
{
QFile file2(m_homePath + QStringLiteral("/../data.json"));
if (file2.exists() && file2.open(QIODevice::ReadOnly))
{
const auto obj = QJsonDocument::fromJson(file2.readAll()).object();
exerciseId = obj.value(QLatin1String("exerciseId")).toInt();
}
}
DEBUG_LOG(m_logger) << "Use EXERCISE" << exerciseId;
if (!ui->situationWidget->init(m_homePath, exerciseId))
{
ERROR_LOG(m_logger) << "Could not init situation widget - abort";
return false;
}
if (!ui->buildingWidget->init(ui->situationWidget->replayHandler(),
ui->situationWidget->pluginProvider()->buildingPluginProvider(),
m_homePath))
{
ERROR_LOG(m_logger) << "Could not init situation widget - abort";
return false;
}
onZoomChanged();
onLinkClicked(m_homeLink);
return true;
}
void ThpViewer::center()
{
QRect rect = QGuiApplication::primaryScreen()->availableGeometry();
// 80 %
int width = rect.width() * 4 / 5;
int height = rect.height() * 4 / 5;
int x = rect.width() / 2 - width / 2;
int y = rect.height() / 2 - height / 2;
resize(width, height);
move(x, y);
}
void ThpViewer::closeEvent(QCloseEvent *event)
{
// explicit delete those two widgets here, otherwise we may get in trouble
// on windows when the app is closed with the 'X' button from the window frame
delete ui->buildingWidget;
ui->buildingWidget = nullptr;
delete ui->situationWidget;
ui->situationWidget = nullptr;
QWidget::closeEvent(event);
}
void ThpViewer::onNewThp(const QString &index)
{
DEBUG_LOG(m_logger) << "onNewThp: " << index;
if (index.isEmpty())
return;
m_homeLink = QUrl::fromLocalFile(index);
m_homePath = QFileInfo(m_homeLink.toLocalFile()).absolutePath();
DEBUG_LOG(m_logger) << "home: " << m_homeLink.toString();
onLinkClicked(m_homeLink);
ui->backButton->setEnabled(false);
this->raise();
}
void ThpViewer::onTitleChanged(const QString &title)
{
if (title.endsWith(QLatin1String("html")) ||
title.endsWith(QLatin1String("html#!/")))
return;
if (m_trainingName.isEmpty())
m_trainingName = title;
ui->mainLabel->setText(m_trainingName == title ? m_trainingName : QStringLiteral("%1 - %2").arg(m_trainingName, title));
}
void ThpViewer::keyPressEvent(QKeyEvent *e)
{
switch (e->key())
{
case Qt::Key_Backspace:
onBack();
break;
case Qt::Key_Plus:
if (ui->stackedWidget->isVisible() && ui->stackedWidget->currentWidget() == ui->situationWidget)
return;
if (m_scaleFactor < 10)
{
m_scaleFactor *= 1.5;
onZoomChanged();
}
break;
case Qt::Key_M:
{
if (QApplication::queryKeyboardModifiers() == Qt::ControlModifier)
onLinkClicked(QUrl::fromLocalFile(QDir::currentPath() + QStringLiteral("/") + QInputDialog::getText(this, {}, QStringLiteral("href_local"))));
break;
}
case Qt::Key_Minus:
if (ui->stackedWidget->isVisible() && ui->stackedWidget->currentWidget() == ui->situationWidget)
return;
if (m_scaleFactor > 0.05)
{
m_scaleFactor /= 1.5;
onZoomChanged();
}
break;
case Qt::Key_F1:
onHelp();
break;
case Qt::Key_Escape:
if (isFullScreen())
onFullScreen();
break;
default:
QWidget::keyPressEvent(e);
break;
}
}
void ThpViewer::onHelp()
{
QFileInfo f(QStringLiteral("%1/../DATA/help/%2/help.html").arg(QCoreApplication::applicationDirPath(), QString::fromLatin1(NGL::translationLocale())));
if (f.exists())
onLinkClicked(QUrl::fromLocalFile(f.absoluteFilePath())); // TODO Pfad anpassen!
else
{
f.setFile(qApp->applicationDirPath() + QStringLiteral("/../DATA/help/en_US/help.html")); // TODO Pfad anpassen!
if (f.exists())
onLinkClicked(QUrl::fromLocalFile(f.absoluteFilePath()));
}
}
void ThpViewer::onZoomChanged()
{
ui->pdfWidget->setZoomFactor(m_scaleFactor);
ui->htmlWidget->setZoomFactor(m_scaleFactor);
// double zoom = ( int ) ( m_scaleFactor * 100. + 0.5 );
// ui->statusbar->showMessage ( tr ( "zoom: %1 %" ).arg ( zoom ) );
}
void ThpViewer::onLinkClicked(const QUrl &url)
{
DEBUG_LOG(m_logger) << "url: " << url.toString();
const QString path = url.path();
const QString lpath = path.toLower();
const QStringRef ext = lpath.splitRef(QLatin1Char('.')).last();
const QStringRef ext2Chars = ext.left(2);
ui->audioWidget->stop();
ui->videoWidget->stop();
// Video-only
if (lpath.endsWith(QLatin1String("video.pls")) ||
ext == QLatin1String("mpg") ||
ext == QLatin1String("avi") ||
ext == QLatin1String("ogg"))
{
ui->videoWidget->play(url);
ui->stackedWidget->hide();
ui->videoWidget->show();
ui->audioWidget->hide();
ui->backButton->setEnabled(true);
// Audio-only
}
else if (lpath.endsWith(QLatin1String("audio.pls")) ||
ext == QLatin1String("mp3"))
{
ui->audioWidget->play(url);
// Ermitteln der Groesse des AudioWidgets
QSize sizeAudio = ui->audioWidget->sizeHint();
QSize sizeStack = ui->stackedWidget->sizeHint();
sizeStack -= sizeAudio;
QList<int> l;
l << sizeStack.height() << sizeAudio.height();
ui->splitter_1->setSizes(l);
ui->stackedWidget->show(); // hier können wir html mit anzeigen
ui->videoWidget->hide();
ui->audioWidget->show();
ui->backButton->setEnabled(true);
// PDF-only
}
else if (ext == QLatin1String("pdf"))
{
if (ui->pdfWidget->showPDF(url))
{
ui->stackedWidget->setCurrentWidget(ui->pdfWidget);
ui->stackedWidget->show();
ui->pdfWidget->resizePdfToWidth();
ui->videoWidget->hide();
ui->audioWidget->hide();
ui->backButton->setEnabled(true);
}
else
{
QMessageBox::warning(this,
tr("Opening PDF file"),
tr("Can not open PDF file %1 - missing or corrupt file!").arg(url.toString()));
}
}
else if (lpath.endsWith(QLatin1String("sitmap.json"), Qt::CaseInsensitive))
{
// 2D oder 3D ?
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " lpath = '" << lpath << "'";
// peek into <lpath>/sitreplay.json:
QFileInfo fi(path);
const QString basePath = fi.absolutePath();
const QString replayConfigFile = basePath + QDir::separator() + QStringLiteral("sitreplay.json"); // aka replayConfigPath
NOTICE_LOG(m_logger) << LOGFUNCTION << " replayConfigFile = '" << replayConfigFile << "'";
}
// Lage-only
QString localFile = url.toLocalFile();
QString basePath = localFile;
int idx = basePath.lastIndexOf(QLatin1Char('/'));
idx = basePath.lastIndexOf(QLatin1Char('/'), idx - 1);
if (idx >= 0)
basePath.chop(basePath.size() - idx - 1);
ui->situationWidget->showSituation(url.toLocalFile(),
basePath);
ui->stackedWidget->setCurrentWidget(ui->situationWidget);
ui->stackedWidget->show();
ui->videoWidget->hide();
ui->audioWidget->hide();
ui->backButton->setEnabled(true);
}
else if (lpath.endsWith(QLatin1String("buildingViewer.json"), Qt::CaseInsensitive))
{
// BuildingViewer-only
QString localFile = url.toLocalFile();
QString basePath = localFile;
int idx = basePath.lastIndexOf(QLatin1Char('/'));
idx = basePath.lastIndexOf(QLatin1Char('/'), idx - 1);
if (idx >= 0)
basePath.chop(basePath.size() - idx - 1);
ui->buildingWidget->showSituation(url.toLocalFile(),
basePath);
ui->stackedWidget->setCurrentWidget(ui->buildingWidget);
ui->stackedWidget->show();
ui->videoWidget->hide();
ui->audioWidget->hide();
ui->backButton->setEnabled(true);
}
else if (ext == QLatin1String("html") &&
lpath.midRef(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
{
// Kombiniert
QHash<QString, QUrl> urls = getCombinedPlaybackDetails(url);
if (urls.isEmpty())
return;
if (urls.contains(QLatin1String("audio")))
{
ui->audioWidget->show();
ui->audioWidget->play(urls[QLatin1String("audio")]);
}
if (urls.contains(QLatin1String("video")))
{
ui->videoWidget->show();
ui->videoWidget->play(urls[QLatin1String("video")]);
}
// Einer von dreien:
if (urls.contains(QLatin1String("pdf")))
{
ui->stackedWidget->setCurrentWidget(ui->pdfWidget);
ui->pdfWidget->showPDF(urls[QLatin1String("pdf")]);
ui->stackedWidget->show();
}
else if (urls.contains(QLatin1String("html")))
{
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
ui->htmlWidget->showHTML(urls[QLatin1String("html")]);
ui->stackedWidget->show();
}
else if (urls.contains(QLatin1String("jpg")))
{
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
ui->htmlWidget->showHTML(urls[QLatin1String("jpg")]);
ui->stackedWidget->show();
}
else if (urls.contains(QLatin1String("png")))
{
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
ui->htmlWidget->showHTML(urls[QLatin1String("png")]);
ui->stackedWidget->show();
}
else
ui->stackedWidget->hide();
ui->backButton->setEnabled(true);
}
else if (ext2Chars == QLatin1String("do") ||
ext2Chars == QLatin1String("xl") ||
ext2Chars == QLatin1String("pp"))
QDesktopServices::openUrl(url);
else
{
DEBUG_LOG(m_logger) << "Trying to open url: " << url.toString();
ui->htmlWidget->showHTML(url);
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
ui->stackedWidget->show();
ui->videoWidget->hide();
ui->audioWidget->hide();
ui->backButton->setEnabled(url != m_homeLink);
}
}
/********************************************************************************************
* Parsed eine HTML-Dateinach allen Links, die sich direkt unterhalb des BODY-Tags befinden
* und liefert die entsprechenden Link-URLs zurück, wenn diese das Tag ID besitzen.
*
* Von der aufrufenden Funktion derzeit ausgewertete Werte für ID sind:
* html, audio, video, lage, pdf (alles klein geschrieben!)
*/
QHash<QString, QUrl> ThpViewer::getCombinedPlaybackDetails(const QUrl &url) const
{
QHash<QString, QUrl> hash;
QFile file(url.toLocalFile());
QDomDocument doc(QStringLiteral("RSS"));
if (!file.open(QIODevice::ReadOnly))
return hash;
QString errorStr;
int errorLine, errorColumn;
if (!doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn))
{
file.close();
return hash;
}
QDomElement domel = doc.documentElement();
domel = domel.firstChildElement(QStringLiteral("BODY"));
domel = domel.firstChildElement(QStringLiteral("A"));
while (!domel.isNull())
{
if (!domel.attribute(QStringLiteral("id")).isEmpty())
{
QUrl linkurl(domel.attribute(QStringLiteral("HREF")));
if (linkurl.isRelative())
linkurl = url.resolved(linkurl);
DEBUG_LOG(m_logger) << "attr: " << domel.attribute(QStringLiteral("id")).toLower() << " url: " << linkurl.toString();
hash[domel.attribute(QStringLiteral("id")).toLower()] = linkurl;
}
domel = domel.nextSiblingElement(QStringLiteral("A"));
}
file.close();
return hash;
}
void ThpViewer::onPrint()
{
if (ui->stackedWidget->isVisible())
{
if (ui->stackedWidget->currentWidget() == ui->situationWidget)
{
QImage image = ui->situationWidget->grabFrameBuffer();
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
QPainter painter;
painter.begin(&printer);
QRectF rect = painter.viewport();
double mmH = printer.logicalDpiY() / 25.4; // 25.4 = mm/inch
double mmW = printer.logicalDpiX() / 25.4;
painter.setFont(QFont(QStringLiteral("Arial"), 30));
QString title = tr("Combat Training Center");
QRectF textBox = painter.boundingRect(QRectF(), Qt::AlignLeft & Qt::TextSingleLine, title);
textBox.setWidth(painter.viewport().width());
painter.drawText(textBox, Qt::AlignCenter & Qt::TextSingleLine, title);
QPointF underImage;
if (printer.orientation() == QPrinter::Portrait)
{
const QPointF textBlockBegin = QPointF(0 * mmW, 25 * mmH); // Baseline 25mm unter Seitenbeginn
// Noch mal schauen, welches (auch übersetzt!) der breiteste Bezeichner (also 1. Spalte) ist!
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
textBox = painter.boundingRect(QRect(), Qt::AlignLeft & Qt::TextSingleLine, tr("Center point:\nExercise:\nTime:"));
// Textbox hat 3 Zeilen, also ist der Faktor pro Zeile 0.33 = 1/3
// Spalte 1
painter.drawText(textBlockBegin, tr("Exercise:"));
painter.drawText(textBlockBegin + QPointF(0, 0.33 * textBox.height()), tr("Time:"));
painter.drawText(textBlockBegin + QPointF(0, 0.66 * textBox.height()), tr("Center point:"));
// Zwischen Spalte 1 und Spalte 2 noch 3mm Platz lassen
// Spalte 2
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0), m_trainingName);
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0.33 * textBox.height()), ui->situationWidget->currentDTGTime());
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0.66 * textBox.height()), ui->situationWidget->centerPointString());
// Das Bild soll formatfüllend angezeigt werden, das Rechteck muss 45mm "flacher" sein, da es erst ab 45mm gedruckt werden soll.
QSizeF size = image.size();
rect.adjust(0, 0, 0, -45 * mmH);
size.scale(rect.size(), Qt::KeepAspectRatio);
// Und ab 45mm von oben drucken:
QRectF imgrect = QRectF(QPointF(0 * mmW, 45 * mmH), size);
painter.drawImage(imgrect, image);
// Untere linke Ecke des Bildes
underImage = imgrect.bottomLeft();
}
else // Landscape
{
// Noch mal schauen, welches (auch übersetzt!) der breiteste Bezeichner oder variable Text ist!
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
textBox = painter.boundingRect(QRect(), Qt::AlignLeft, tr("Center point:\n%1\n\nTime:\n%2\n\nExercise:\n%3")
.arg(m_trainingName,
ui->situationWidget->currentDTGTime(),
ui->situationWidget->centerPointString()));
// TextBlock fängt an: Notwendige Breite von rechts und 15 mm + 1 Zeilenhöhe (=1/8 der Textboxhöhe) von oben.
// Die Zeilenhöhe ist notwendig weil drawText(QPoint, QString) den QPoint auf die Baseline setzt!
QPointF textBlockBegin = QPointF(painter.viewport().width() - textBox.width(), 15 * mmH + 0.125 * textBox.height());
// Die Textbox hat 8 Zeilen, also ist der Faktor pro Zeile 1/8 = 0.125
// Übung
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
painter.drawText(textBlockBegin, tr("Exercise:"));
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
painter.drawText(textBlockBegin + QPointF(0, 0.125 * textBox.height()), m_trainingName);
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
painter.drawText(textBlockBegin + QPointF(0, 0.375 * textBox.height()), tr("Time:"));
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
painter.drawText(textBlockBegin + QPointF(0, 0.500 * textBox.height()), ui->situationWidget->currentDTGTime());
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
painter.drawText(textBlockBegin + QPointF(0, 0.750 * textBox.height()), tr("Center point:"));
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
painter.drawText(textBlockBegin + QPointF(0, 0.875 * textBox.height()), ui->situationWidget->centerPointString());
// Das Bild soll formatfüllend angezeigt werden, das Rechteck muss 15mm "flacher" sein, da es erst ab 15mm gedruckt werden soll.
// Ausserdem müssen in der Breite die Textboxbreite und 3mm Luft dazwischen abgezogen werden.
QSizeF size = image.size();
rect.adjust(0, 0, -textBox.width() - 3 * mmW, -15 * mmH);
size.scale(rect.size(), Qt::KeepAspectRatio);
// Und ab 15mm von oben drucken:
QRectF imgrect = QRectF(QPointF(0 * mmW, 15 * mmH), size);
painter.drawImage(imgrect, image);
// Untere linke Ecke des Bildes
underImage = imgrect.bottomLeft();
}
painter.setFont(QFont(QStringLiteral("Arial"), 12));
// String für Breite und Höhe erstellen
const QRectF extent = ui->situationWidget->extentMgrs();
QString areasize = tr("Width: %1m, Height: %2m").arg(extent.width()).arg(extent.height());
// Die Größe herausfinden
textBox = painter.boundingRect(QRect(), Qt::AlignLeft & Qt::TextSingleLine, areasize);
// Einen Dot vergrößern (siehe QRect: Die Rahmenpixel unten und rechts sind nicht Teil der Rechteckgröße)
textBox.adjust(0, 0, 1, 1);
// An die richtige Stelle bugsieren
textBox.moveBottomLeft(underImage);
// Und mit weiß und 50% Deckkraft vollmalen.
painter.fillRect(textBox, QColor(255, 255, 255, 128));
//Dann den Text drüberlegen
painter.drawText(underImage, areasize);
painter.end();
}
else if (ui->stackedWidget->currentWidget() == ui->pdfWidget)
{
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
QVector<QImage> images = ui->pdfWidget->getPrintableImages(printer.resolution());
int lastItem = images.size() - 1;
QPainter painter;
painter.begin(&printer);
for (int i = 0; i < images.size(); ++i)
{
QRect rect = painter.viewport();
QSize size = images.at(i).size();
size.scale(rect.size(), Qt::KeepAspectRatio);
painter.setViewport(rect.x(), rect.y(),
size.width(), size.height());
painter.setWindow(images.at(i).rect());
painter.drawImage(0, 0, images.at(i));
if (i != lastItem)
printer.newPage();
}
painter.end();
}
else if (ui->stackedWidget->currentWidget() == ui->htmlWidget)
{
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
ui->htmlWidget->print(&printer);
}
else // Audio/Video-Widget
{
}
}
else // nur HTML sichtbar...
{
QPrinter printer;
QPrintDialog *dialog = new QPrintDialog(&printer, this);
dialog->setWindowTitle(tr("Print Document"));
if (dialog->exec() != QDialog::Accepted)
return;
ui->htmlWidget->print(&printer);
}
}
void ThpViewer::onBack()
{
ui->audioWidget->stop();
ui->videoWidget->stop();
ui->situationWidget->stop();
ui->audioWidget->hide();
ui->videoWidget->hide();
if (ui->stackedWidget->currentWidget() == ui->htmlWidget &&
ui->stackedWidget->isVisible())
ui->htmlWidget->back();
ui->stackedWidget->show();
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
onUrlChanged(ui->htmlWidget->url());
}
void ThpViewer::onHome()
{
ui->htmlWidget->show();
onLinkClicked(m_homeLink);
}
void ThpViewer::onFullScreen()
{
setWindowState(windowState() ^ Qt::WindowFullScreen);
}
void ThpViewer::onUrlChanged(const QUrl &url)
{
ui->backButton->setEnabled(url != m_homeLink);
}
} // namespace NGL
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* Main thp viewer widget
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QMainWindow>
#include <QUrl>
namespace Ui
{
class thpViewer;
}
namespace NGL
{
class INGLLogger;
}
namespace NGL
{
class ThpViewer : public QWidget
{
Q_OBJECT
public:
explicit ThpViewer(const QUrl &url, QWidget *parent = nullptr);
~ThpViewer();
bool init();
void center();
void closeEvent(QCloseEvent *event) override;
protected:
QHash<QString, QUrl> getCombinedPlaybackDetails(const QUrl &url) const;
protected Q_SLOTS:
void onLinkClicked(const QUrl &url);
void onZoomChanged();
void onBack();
void onHome();
void onHelp();
void onPrint();
void onFullScreen();
void onTitleChanged(const QString &title);
void onUrlChanged(const QUrl &url);
public Q_SLOTS:
void onNewThp(const QString &index);
protected:
void keyPressEvent(QKeyEvent *e) override;
protected:
Ui::thpViewer *ui;
double m_scaleFactor;
QUrl m_homeLink;
QString m_homePath;
QString m_trainingName;
NGL::INGLLogger &m_logger;
};
} // namespace NGL
@@ -0,0 +1,30 @@
#include <WINVER.H>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @THP_VERSION_INFO@
PRODUCTVERSION @THP_VERSION_INFO@
FILEFLAGSMASK 0x0L
FILEFLAGS 0x0L
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "Rheinmetall Electronics GmbH"
VALUE "FileDescription", "Take Home Package Viewer"
VALUE "FileVersion", "@THP_VERSION_INFO@"
VALUE "InternalName", "THP"
VALUE "LegalCopyright", "Copyright © 2018 Rheinmetall Electronics GmbH. Alle Rechte vorbehalten"
VALUE "OriginalFilename", "thp_viewer.exe"
VALUE "ProductName", "Take Home Package Viewer (THP)"
VALUE "ProductVersion", "@THP_VERSION_INFO@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x407, 1200
END
END
@@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>thpViewer</class>
<widget class="QWidget" name="thpViewer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>793</width>
<height>675</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>793</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string notr="true">MainWindow</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QToolButton" name="backButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/go-previous.png</normaloff>:icons/16x16/go-previous.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fullScreenButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/view-fullscreen.png</normaloff>:icons/16x16/view-fullscreen.png</iconset>
</property>
<property name="shortcut">
<string>F11</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="homeButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/go-home.png</normaloff>:icons/16x16/go-home.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="mainLabel">
<property name="text">
<string notr="true">THP-Viewer</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QToolButton" name="printButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/document-print.png</normaloff>:icons/16x16/document-print.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="helpButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/system-help.png</normaloff>:icons/16x16/system-help.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="closeButton">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/dialog-close.png</normaloff>:icons/16x16/dialog-close.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter_1">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="THP::AudioVideoWidget" name="videoWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<widget class="THP::HtmlWidget" name="htmlWidget"/>
<widget class="THP::BuildingWidget" name="buildingWidget"/>
<widget class="THP::PdfWidget" name="pdfWidget"/>
<widget class="THP::SituationWidget" name="situationWidget"/>
</widget>
</widget>
<widget class="THP::AudioVideoWidget" name="audioWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>THP::AudioVideoWidget</class>
<extends>QWidget</extends>
<header>thpwidgets/thp_audio_video_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>THP::PdfWidget</class>
<extends>QFrame</extends>
<header>thpwidgets/thp_pdf_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>THP::HtmlWidget</class>
<extends>QFrame</extends>
<header>thpwidgets/thp_html_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>THP::SituationWidget</class>
<extends>QFrame</extends>
<header>thpwidgets/thp_situation_widget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>THP::BuildingWidget</class>
<extends>QFrame</extends>
<header>thpwidgets/thp_building_widget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,629 @@
/**
* @RDE_HEADER@
* Handle Audio/Video output for thp through libvlc
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_audio_video_widget.h"
#include "thp_audio_video_widget_p.h"
#include "ui_thp_audio_video_widget.h"
#include <QtCore/QCoreApplication>
#include <QDirIterator>
#include <QFileInfo>
#include <QMutex>
#include <QScreen>
#include <QtCore/QTimer>
#include <QUrl>
#include <QGuiApplication>
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <math.h>
#include <stdio.h>
#include <vlc/libvlc_version.h>
namespace THP
{
static const float s_speedArray[] =
{
1. / 8.,
1. / 4.,
1. / 2.,
1.,
2.,
4.,
8.
};
/**
* AudioVideoWidget::Private
*/
AudioVideoWidget::Private::Private(QFrame *parent)
: m_vlcinstance(nullptr)
, m_mPlayer(nullptr)
, m_mListPlayer(nullptr)
, m_mediaList(nullptr)
, currentSpeedIdx(3)
, m_parent(parent)
, m_timer(new QTimer)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("THP.AudioVideoWidget"))
{
init();
connect(ui->btnSlower, &QToolButton::clicked, this, &AudioVideoWidget::Private::onSlower);
connect(ui->btnFaster, &QToolButton::clicked, this, &AudioVideoWidget::Private::onFaster);
connect(ui->btn100pct, &QToolButton::clicked, this, &AudioVideoWidget::Private::on100pct);
connect(ui->btnPlay, &QToolButton::clicked, this, &AudioVideoWidget::Private::onPlay);
connect(ui->btnStop, &QToolButton::clicked, this, &AudioVideoWidget::Private::onStop);
connect(ui->sldPosition, &QSlider::sliderMoved, this, &AudioVideoWidget::Private::onPositionChanged);
connect(ui->sldVolume, &QSlider::sliderMoved, this, &AudioVideoWidget::Private::onVolumeChanged);
connect(m_timer, &QTimer::timeout, this, &AudioVideoWidget::Private::onUpdateGUI);
m_initThread = new AudioVideoInitThread(this);
connect(m_initThread, &AudioVideoInitThread::finished, this, &AudioVideoWidget::Private::onVLCInstanceInitializd);
m_initThread->start(QThread::LowPriority);
}
AudioVideoWidget::Private::~Private()
{
uninitVLCPlayer();
uninitVLCInstance();
delete ui;
delete m_timer;
}
bool AudioVideoWidget::Private::init()
{
if (!m_parent)
return false;
ui = new Ui::audioVideoWidget();
ui->setupUi(m_parent);
QPixmap pm(10, 10);
pm.fill(Qt::black);
ui->outputWidget->setPixmap(pm);
ui->outputWidget->setScaledContents(true);
ui->sldPosition->setMaximum(1000);
ui->sldVolume->setMaximum(100);
return true;
}
void AudioVideoWidget::Private::onVLCInstanceInitializd()
{
m_initThread->deleteLater();
m_initThread = nullptr;
}
void AudioVideoWidget::Private::setAV(bool isAudio)
{
if (isAudio)
{
delete ui->btn100pct;
delete ui->btnFaster;
delete ui->btnSlower;
delete ui->outputWidget;
ui->outputWidget = nullptr;
}
else
{
// delete ui->verticalSpacer; ui->verticalSpacer = 0;
delete ui->lblAudio;
}
}
static QMutex *serializeVLCInit()
{
static QMutex s_serializeVLCInit;
return &s_serializeVLCInit;
}
bool AudioVideoWidget::Private::initVLCInstance(bool bInInitThread)
{
QMutexLocker l(serializeVLCInit());
if (!bInInitThread && m_initThread != nullptr)
{
QLabel myLabel;
myLabel.setWindowTitle(tr("Loading VLC player..."));
myLabel.setText(tr("Waiting until VLC player is loaded"));
QRect r = QGuiApplication::primaryScreen()->availableGeometry();
myLabel.show();
myLabel.move(r.width() / 2 - myLabel.width() / 2, r.height() / 2 - myLabel.height() / 2);
myLabel.setWindowIcon(QIcon(QLatin1String(":icons/16x16/rde.ico")));
while (m_initThread)
QCoreApplication::processEvents();
}
if (m_vlcinstance != nullptr)
return true;
//preparation of the vlc command
const char *vlc_argv[20];
int iCount = 0;
vlc_argv[iCount++] = "-I";
vlc_argv[iCount++] = "dummy"; // Don't use any interface
// vlc_argv[iCount++] = "--ignore-config"; // Don't use VLC's config
#ifdef LTS_DEBUG
vlc_argv[iCount++] = "--extraintf=logger"; // log anything
vlc_argv[iCount++] = "--verbose=2"; // be much more verbose then normal for debugging purpose
#else
vlc_argv[iCount++] = "--quiet";
#endif
if (bInInitThread)
vlc_argv[iCount++] = "--no-media-library";
vlc_argv[iCount++] = "--";
vlc_argv[iCount] = nullptr;
//Initialize an instance of vlc
//create a new libvlc instance
m_vlcinstance = libvlc_new(iCount, vlc_argv); //tricky calculation of the char space used
if (!m_vlcinstance)
ERROR_LOG(m_logger) << "Error initializing libVLC - unable to play audio or video";
return (m_vlcinstance != nullptr);
}
bool AudioVideoWidget::Private::uninitVLCInstance()
{
QMutexLocker l(serializeVLCInit());
if (!uninitVLCPlayer())
return false;
if (m_vlcinstance)
{
libvlc_release(m_vlcinstance);
m_vlcinstance = nullptr;
}
return true;
}
bool AudioVideoWidget::Private::initVLCPlayer()
{
DEBUG_LOG(m_logger) << "Initializing libVLC";
if (m_mPlayer)
{
if (!ui)
return false;
ui->sldVolume->setValue(libvlc_audio_get_volume(m_mPlayer));
return true;
}
initVLCInstance();
//Initialize an instance of vlc
//Create a media player playing environement
m_mPlayer = libvlc_media_player_new(m_vlcinstance);
m_mListPlayer = libvlc_media_list_player_new(m_vlcinstance);
m_mediaList = libvlc_media_list_new(m_vlcinstance);
libvlc_media_list_player_set_media_player(m_mListPlayer, m_mPlayer);
libvlc_media_list_player_set_media_list(m_mListPlayer, m_mediaList);
ui->sldVolume->setValue(libvlc_audio_get_volume(m_mPlayer));
return true;
}
bool AudioVideoWidget::Private::uninitVLCPlayer()
{
DEBUG_LOG(m_logger) << "Uninitializing libVLC";
if (m_mListPlayer)
{
/* Stop playing */
libvlc_media_list_player_stop(m_mListPlayer);
/* Free the media_player */
libvlc_media_list_player_release(m_mListPlayer);
m_mListPlayer = nullptr;
}
if (m_mediaList)
{
clearMediaList();
libvlc_media_list_release(m_mediaList);
m_mediaList = nullptr;
}
if (m_mPlayer)
{
/* Stop playing */
libvlc_media_player_stop(m_mPlayer);
/* Free the media_player */
libvlc_media_player_release(m_mPlayer);
m_mPlayer = nullptr;
}
return true;
}
void AudioVideoWidget::Private::clearMediaList()
{
DEBUG_LOG(m_logger) << "Clearing current playlist";
if (!m_mediaList)
return;
libvlc_media_list_lock(m_mediaList);
const int cnt = libvlc_media_list_count(m_mediaList);
for (int i = cnt - 1; i >= 0; --i)
libvlc_media_list_remove_index(m_mediaList, i);
libvlc_media_list_unlock(m_mediaList);
}
bool AudioVideoWidget::Private::playVLC(const QUrl &url)
{
if (!ui)
return false;
DEBUG_LOG(m_logger) << "Try to play " << url.toString();
const QString path = url.toLocalFile();
if (path.isEmpty())
return false;
clearMediaList();
libvlc_media_list_lock(m_mediaList);
QFileInfo fi(path);
DEBUG_LOG(m_logger) << "path: " << path << " isFile: " << fi.isFile();
if (fi.isFile() && path.endsWith(QLatin1String(".pls")))
{
// pls parsing scheint nicht mehr richtig zu funktionieren - selbst machen
DEBUG_LOG(m_logger) << "Parse Playlist";
QFile f(path);
if (f.open(QIODevice::ReadOnly))
{
DEBUG_LOG(m_logger) << "Parse Playlist2 " << f.canReadLine();
QMap<int, QByteArray> fileMap;
QByteArray baAll = f.readAll();
const QList<QByteArray> lines = baAll.split('\n');
for (const QByteArray &line : lines)
{
QByteArray ba = line.trimmed();
DEBUG_LOG(m_logger) << "ba: " << ba;
if (!ba.startsWith("File "))
continue;
ba = ba.mid(qstrlen("File "));
int idx = ba.indexOf('=');
DEBUG_LOG(m_logger) << "idx: " << idx;
if (idx < 0)
continue;
bool bOk;
int id = ba.left(idx).toInt(&bOk);
DEBUG_LOG(m_logger) << "id: " << id << " bOk: " << bOk << " str: " << ba.left(idx);
if (!bOk)
continue;
QByteArray filename = ba.mid(idx + 1);
DEBUG_LOG(m_logger) << "filename: " << filename;
fileMap.insertMulti(id, fi.absolutePath().toLocal8Bit() + '/' + filename);
}
for (const QByteArray &fn : fileMap)
{
const QByteArray pNative = QDir::toNativeSeparators(QString::fromLocal8Bit(fn)).toLocal8Bit();
DEBUG_LOG(m_logger) << "pNative: " << pNative;
libvlc_media_t *media = libvlc_media_new_path(m_vlcinstance, pNative.data());
if (!media)
{
ERROR_LOG(m_logger) << "Error setting media to " << pNative;
return false;
}
//add option to disable hardware acceleration
libvlc_media_add_option(media, ":avcodec-hw=none");
libvlc_media_list_add_media(m_mediaList, media);
#if LIBVLC_VERSION_MAJOR > 2
libvlc_media_parse_with_options(media, libvlc_media_parse_local, 0);
#else
libvlc_media_parse(media);
#endif
libvlc_media_release(media); // libvlc_media_new_location(), ref-counting!
}
}
}
else if (fi.isFile())
{
const QByteArray pNative = QDir::toNativeSeparators(path).toLocal8Bit();
libvlc_media_t *media = libvlc_media_new_path(m_vlcinstance, pNative.data());
if (!media)
{
ERROR_LOG(m_logger) << "Error setting media to '" << pNative << "'";
return false;
}
libvlc_media_list_add_media(m_mediaList, media);
libvlc_media_release(media); // libvlc_media_new_location(), ref-counting!
}
else if (fi.isDir())
{
QDirIterator dirIt(fi.absoluteFilePath());
while (dirIt.hasNext())
{
const QString fn = dirIt.next();
fi.setFile(fn);
if (!fi.isFile())
continue;
const QByteArray pNative = QDir::toNativeSeparators(fn).toLocal8Bit();
libvlc_media_t *media = libvlc_media_new_path(m_vlcinstance, pNative.data());
if (!media)
{
ERROR_LOG(m_logger) << "Error setting media to " << pNative;
return false;
}
libvlc_media_list_add_media(m_mediaList, media);
#if LIBVLC_VERSION_MAJOR > 2
libvlc_media_parse_with_options(media, libvlc_media_parse_local, 0);
#else
libvlc_media_parse(media);
#endif
libvlc_media_release(media); // libvlc_media_new_location(), ref-counting!
}
}
else
{
ERROR_LOG(m_logger) << "Unknown media given: " << path;
libvlc_media_list_unlock(m_mediaList);
return false;
}
libvlc_media_list_unlock(m_mediaList);
if (ui->outputWidget)
{
#if defined(Q_OS_WIN)
libvlc_media_player_set_hwnd(m_mPlayer, (HWND)ui->outputWidget->winId());
#elif defined(Q_OS_MAC)
libvlc_media_player_set_agl(m_mPlayer, ui->outputWidget->winId()) ;
#else //Linux
libvlc_media_player_set_xwindow(m_mPlayer, ui->outputWidget->winId());
#endif
}
ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
ui->btnPlay->setEnabled(true);
ui->sldPosition->setEnabled(false);
ui->btnStop->setEnabled(true);
currentSpeedIdx = 3;
QTimer::singleShot(0, this, &AudioVideoWidget::Private::onPlay);
return true;
}
bool AudioVideoWidget::Private::stopVLC()
{
DEBUG_LOG(m_logger) << "Stop playing";
QTimer::singleShot(0, this, &AudioVideoWidget::Private::onStop);
return true;
}
void AudioVideoWidget::Private::setRate()
{
DEBUG_LOG(m_logger) << "Setting speed to " << s_speedArray[currentSpeedIdx];
libvlc_state_t state = getState();
if (state == libvlc_Playing || state == libvlc_Paused)
libvlc_media_player_set_rate(m_mPlayer, s_speedArray[currentSpeedIdx]);
}
libvlc_state_t AudioVideoWidget::Private::getState() const
{
libvlc_state_t s = libvlc_media_list_player_get_state(m_mListPlayer);
static libvlc_state_t lastState = libvlc_NothingSpecial;
if (s == lastState)
return s;
lastState = s;
if (s == libvlc_Error)
ERROR_LOG(m_logger) << "libVLC error: " << libvlc_errmsg();
return s;
}
void AudioVideoWidget::Private::onSlower()
{
if (currentSpeedIdx == 0)
return;
--currentSpeedIdx;
setRate();
onUpdateGUI();
}
void AudioVideoWidget::Private::onFaster()
{
if (currentSpeedIdx == 6)
return;
++currentSpeedIdx;
setRate();
onUpdateGUI();
}
void AudioVideoWidget::Private::on100pct()
{
currentSpeedIdx = 3;
setRate();
onUpdateGUI();
}
void AudioVideoWidget::Private::onPlay()
{
if (libvlc_media_list_player_is_playing(m_mListPlayer))
{
DEBUG_LOG(m_logger) << "Pause playing";
libvlc_media_list_player_pause(m_mListPlayer);
ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
m_timer->stop();
onUpdateGUIWithState(libvlc_Paused);
}
else
{
DEBUG_LOG(m_logger) << "Start playing";
ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-pause.png")));
ui->sldPosition->setEnabled(true);
libvlc_media_list_player_play(m_mListPlayer);
setRate();
m_timer->start(100);
onUpdateGUIWithState(libvlc_Playing);
}
}
void AudioVideoWidget::Private::onStop()
{
if (!m_mListPlayer)
return;
libvlc_state_t state = getState();
if (state == libvlc_Stopped)
return;
DEBUG_LOG(m_logger) << "Stop playing";
libvlc_media_list_player_stop(m_mListPlayer);
onUpdateGUI();
}
void AudioVideoWidget::Private::onPositionChanged(int newPosition)
{
libvlc_state_t state = getState();
DEBUG_LOG(m_logger) << "Slider changed - going to new position";
if (state != libvlc_Playing && state != libvlc_Paused)
return;
// Position geht von 0..1, Slider hat 1000 Steps
libvlc_media_player_set_position(m_mPlayer, newPosition / 1000.);
onUpdateGUI();
}
void AudioVideoWidget::Private::onVolumeChanged(int newVol)
{
libvlc_audio_set_volume(m_mPlayer, newVol);
onUpdateGUI();
}
void AudioVideoWidget::Private::onUpdateGUI()
{
onUpdateGUIWithState(libvlc_NothingSpecial);
}
void AudioVideoWidget::Private::onUpdateGUIWithState(libvlc_state_t overwrite_state)
{
libvlc_state_t state = overwrite_state;
if (state == libvlc_NothingSpecial)
state = getState();
DEBUG_LOG(m_logger) << "onUpdateGUI for state " << state;
if (state == libvlc_Playing || state == libvlc_Paused)
{
double maxTime = libvlc_media_player_get_length(m_mPlayer) / 1000.;
if (maxTime > 0.1)
{
libvlc_media_list_lock(m_mediaList);
const int cnt = libvlc_media_list_count(m_mediaList);
for (int i = cnt - 1; i >= 0; --i)
{
libvlc_media_t *media = libvlc_media_list_item_at_index(m_mediaList, i);
DEBUG_LOG(m_logger) << "Duration: " << libvlc_media_get_duration(media);
libvlc_media_release(media);
}
libvlc_media_list_unlock(m_mediaList);
}
double curTime = libvlc_media_player_get_time(m_mPlayer) / 1000.;
auto curPos = libvlc_media_player_get_position(m_mPlayer);
// Slider ist in 1000 Schritte eingeteilt
ui->sldPosition->setValue(qRound(curPos * 1000));
const int minutesCur = floor(curTime / 60.);
const int secondsCur = curTime - 60 * minutesCur;
const int minutesAbs = floor(maxTime / 60.);
const int secondsAbs = maxTime - 60 * minutesAbs;
char timeStr[64];
snprintf(timeStr, sizeof(timeStr), "%02d:%02d / %02d:%02d", minutesCur, secondsCur, minutesAbs, secondsAbs);
ui->lblPosition->setText(QString::fromLatin1(timeStr));
}
else if (state == libvlc_Stopped || state == libvlc_Ended)
{
// wir können nicht onStop() aufrufen -> rekursion!
bool bStop = true;
if (state == libvlc_Ended)
{
// Timer und VLC *nicht* stoppen da der aktuelle State ggf. genau zwischen den einzelnen
// Video-Schnipseln einer Playlist abgefragt wurde
// --> prüfen, ob es das letzte Video der Playlist war
libvlc_media_t *media = libvlc_media_player_get_media(m_mPlayer);
if (media != nullptr)
{
const int cnt = libvlc_media_list_count(m_mediaList);
if (cnt > 0)
{
libvlc_media_t *media2 = libvlc_media_list_item_at_index(m_mediaList, cnt - 1);
bStop = (media2 == media);
libvlc_media_release(media2);
}
libvlc_media_release(media);
}
if (bStop)
libvlc_media_list_player_stop(m_mListPlayer);
}
if (bStop)
{
ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
ui->sldPosition->setEnabled(false);
m_timer->stop();
ui->sldPosition->setValue(0);
ui->lblPosition->setText(QLatin1String("--:-- / --:--"));
}
}
QString speedStr = QStringLiteral("%1x").arg(s_speedArray[currentSpeedIdx], 4, 'f', 2);
ui->lblSpeed->setText(speedStr);
}
/**
* AudioVideoInitThread
*/
AudioVideoInitThread::AudioVideoInitThread(AudioVideoWidget::Private *p)
: m_private(p)
{}
void AudioVideoInitThread::run()
{
m_private->initVLCInstance(true);
}
/**
* AudioVideoWidget
*/
AudioVideoWidget::AudioVideoWidget(QWidget *parent)
: QFrame(parent)
, d(new Private(this))
{}
AudioVideoWidget::~AudioVideoWidget()
{
delete d;
}
bool AudioVideoWidget::play(const QUrl &file)
{
d->initVLCPlayer();
return d->playVLC(file);
}
bool AudioVideoWidget::stop()
{
return d->stopVLC();
}
void AudioVideoWidget::setAV(bool isAudio)
{
d->setAV(isAudio);
}
} // namespace THP
@@ -0,0 +1,32 @@
/**
* @RDE_HEADER@
* Handle Audio/Video output for thp through libvlc
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QFrame>
class QUrl;
namespace THP
{
class AudioVideoWidget : public QFrame
{
Q_OBJECT
public:
explicit AudioVideoWidget(QWidget *parent = nullptr);
~AudioVideoWidget();
bool play(const QUrl &file);
bool stop();
void setAV(bool isAudio = false);
private:
class Private;
Private *const d;
friend class AudioVideoInitThread;
};
} // namespace THP
@@ -0,0 +1,254 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>audioVideoWidget</class>
<widget class="QFrame" name="audioVideoWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>911</width>
<height>563</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="outputWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblAudio">
<property name="text">
<string>Audio</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sldPosition">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblPosition">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="text">
<string notr="true">55:55 / 55:55</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QToolButton" name="btnPlay">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Z/src/nglwidgets/nglresources.qrc">
<normaloff>:icons/16x16/media-playback-start.png</normaloff>:icons/16x16/media-playback-start.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnStop">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Z/src/nglwidgets/nglresources.qrc">
<normaloff>:icons/16x16/media-playback-stop.png</normaloff>:icons/16x16/media-playback-stop.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSlower">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Z/src/nglwidgets/nglresources.qrc">
<normaloff>:icons/16x16/media-play-slower.png</normaloff>:icons/16x16/media-play-slower.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btn100pct">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Z/src/nglwidgets/nglresources.qrc">
<normaloff>:icons/16x16/media-play-normal.png</normaloff>:icons/16x16/media-play-normal.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnFaster">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset resource="../../../Z/src/nglwidgets/nglresources.qrc">
<normaloff>:icons/16x16/media-play-faster.png</normaloff>:icons/16x16/media-play-faster.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblSpeed">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string notr="true">100 %</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sldVolume">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>60</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="../../../Z/src/nglwidgets/nglresources.qrc"/>
</resources>
<connections/>
</ui>
@@ -0,0 +1,93 @@
/**
* @RDE_HEADER@
* Handle Audio/Video output for thp through libvlc, private data
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#ifdef _MSC_VER
// no ssize_t with msdn...
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif
#include <vlc/vlc.h>
#include <QtCore/QObject>
#include <QThread>
#include <NGLLogger/INGLLogger.h>
#include "thp_audio_video_widget.h"
class QUrl;
class QFrame;
class QTimer;
namespace Ui
{
class audioVideoWidget;
}
namespace THP
{
class AudioVideoInitThread;
class AudioVideoWidget::Private : public QObject
{
Q_OBJECT
public:
explicit Private(QFrame *parent);
~Private();
bool initVLCInstance(bool bInInitThread = false);
bool uninitVLCInstance();
bool initVLCPlayer();
bool uninitVLCPlayer();
bool playVLC(const QUrl &url);
bool stopVLC();
void setAV(bool isAudio);
protected Q_SLOTS:
void onSlower();
void onFaster();
void on100pct();
void onPlay();
void onStop();
void onPositionChanged(int);
void onVolumeChanged(int);
void onUpdateGUI();
void onVLCInstanceInitializd();
protected:
void clearMediaList();
void playVLCInternal();
void onUpdateGUIWithState(libvlc_state_t overwrite_state);
bool init();
void setRate();
libvlc_state_t getState() const;
libvlc_instance_t *m_vlcinstance;
libvlc_media_player_t *m_mPlayer;
libvlc_media_list_player_t *m_mListPlayer;
libvlc_media_list_t *m_mediaList;
int currentSpeedIdx;
QFrame *m_parent;
QTimer *m_timer;
AudioVideoInitThread *m_initThread;
Ui::audioVideoWidget *ui;
NGL::INGLLogger &m_logger;
};
class AudioVideoInitThread final : public QThread
{
Q_OBJECT
public:
explicit AudioVideoInitThread(AudioVideoWidget::Private *p);
protected:
void run() override;
protected:
AudioVideoWidget::Private *m_private;
};
} // namespace THP
@@ -0,0 +1,118 @@
/**
* @RDE_HEADER@
* HMI Plugin for MeteoDataManagement - Meteo Data Overview Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#include "thp_building_settings.h"
#include "ui_thp_building_settings.h"
#include <Common/bdv_controller.h>
#include <LtsUtilsSettings/settings.h>
namespace THP
{
static QVariant getSetting(const QString &key, const QVariant &def)
{
return LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("BgViewer"), key, def);
}
static void setSetting(const QString &key, const QVariant &val)
{
LtsUtils::OperatorSettings::getInstance()->setValue(QStringLiteral("BgViewer"), key, val);
}
BgViewerSettingsView::BgViewerSettingsView(BuildingViewer::BgViewerController *pBgvController,
QWidget *parent)
: QDialog(parent)
, m_ui(new Ui::BgViewerSettingsUI)
, m_pBgvController(pBgvController)
{
m_ui->setupUi(this);
connect(m_ui->m_pCheckBoxGlobalIsoMode, &QCheckBox::clicked, this, &BgViewerSettingsView::onViewModeChanged);
connect(m_ui->m_pCheckBoxUseDotsMode, &QCheckBox::clicked, this, &BgViewerSettingsView::onPlayerDisplayModeChanged);
connect(m_ui->m_pSliderGlobalTransparency, &QSlider::valueChanged, this, &BgViewerSettingsView::onTransparencyChanged);
connect(m_ui->m_pSliderPlayerSymbolSize, &QSlider::valueChanged, this, &BgViewerSettingsView::onSymbolSizeChanged);
connect(m_ui->m_slider_SymbolFlashing, &QSlider::valueChanged, this, &BgViewerSettingsView::onSymbolFireEventFlashingChanged);
connect(m_ui->m_slider_HitlineDuration, &QSlider::valueChanged, this, &BgViewerSettingsView::onHitLineDisplayLengthChanged);
resetValues();
onSymbolFireEventFlashingChanged(m_ui->m_slider_HitlineDuration->value());
onHitLineDisplayLengthChanged(m_ui->m_slider_SymbolFlashing->value());
connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &BgViewerSettingsView::accept);
connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &BgViewerSettingsView::reject);
}
BgViewerSettingsView::~BgViewerSettingsView()
{
delete m_ui;
}
void BgViewerSettingsView::accept()
{
setSetting(QStringLiteral("GlobalTransparency"), m_ui->m_pSliderGlobalTransparency->value());
setSetting(QStringLiteral("UseIsoMode"), m_ui->m_pCheckBoxGlobalIsoMode->isChecked());
setSetting(QStringLiteral("UseDotsMode"), m_ui->m_pCheckBoxUseDotsMode->isChecked());
setSetting(QStringLiteral("PlayerSymbolSize"), m_ui->m_pSliderPlayerSymbolSize->value());
setSetting(QStringLiteral("maxHitlineDurationTime_s"), m_ui->m_slider_HitlineDuration->value());
setSetting(QStringLiteral("GearFlashTime_s"), m_ui->m_slider_SymbolFlashing->value());
setSetting(QStringLiteral("maxGearFlashTime_s"), m_ui->m_slider_SymbolFlashing->maximum());
QDialog::accept();
}
void BgViewerSettingsView::reject()
{
resetValues();
QDialog::reject();
}
void BgViewerSettingsView::resetValues()
{
m_ui->m_pSliderGlobalTransparency->setValue(getSetting(QStringLiteral("GlobalTransparency"), 75).toInt());
m_ui->m_pCheckBoxGlobalIsoMode->setChecked(getSetting(QStringLiteral("UseIsoMode"), false).toBool());
m_ui->m_pCheckBoxUseDotsMode->setChecked(getSetting(QStringLiteral("UseDotsMode"), false).toBool()) ;
m_ui->m_pSliderPlayerSymbolSize->setValue(getSetting(QStringLiteral("PlayerSymbolSize"), 20).toInt());
m_ui->m_slider_HitlineDuration->setValue(getSetting(QStringLiteral("maxHitlineDurationTime_s"), 10).toInt());
m_ui->m_slider_SymbolFlashing->setValue(getSetting(QStringLiteral("GearFlashTime_s"), 10).toInt());
}
void BgViewerSettingsView::onViewModeChanged()
{
m_pBgvController->excSetGlobalViewMode(m_ui->m_pCheckBoxGlobalIsoMode->isChecked());
}
void BgViewerSettingsView::onPlayerDisplayModeChanged()
{
m_pBgvController->excSetGlobalDotsMode(m_ui->m_pCheckBoxUseDotsMode->isChecked());
}
void BgViewerSettingsView::onSymbolSizeChanged()
{
m_pBgvController->updatePlayerSymbols(m_ui->m_pSliderPlayerSymbolSize->value());
}
void BgViewerSettingsView::onTransparencyChanged()
{
m_pBgvController->excSetGlobalTransparency(m_ui->m_pSliderGlobalTransparency->value());
}
void BgViewerSettingsView::onSymbolFireEventFlashingChanged(int val)
{
// only modified per config setting...
m_ui->m_label_durationSymbolFlashing->setText(tr("%1 sec").arg(val));
}
void BgViewerSettingsView::onHitLineDisplayLengthChanged(int val)
{
m_pBgvController->excSetHitlineDuration(val);
m_ui->m_label_HitlineDuration->setText(tr("%1 sec").arg(val));
}
} // namespace BuildingViewer
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* HMI Plugin for MeteoDataManagement - Meteo Data Overview Dialog
* $Id$
* $HeadURL$
* $Author$
* Created by Patric Karwat, SLSE, 2016-10-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
namespace Ui
{
class BgViewerSettingsUI;
}
namespace BuildingViewer
{
class BgViewerController;
}
namespace THP
{
class BgViewerSettingsView final : public QDialog
{
Q_OBJECT
public:
explicit BgViewerSettingsView(BuildingViewer::BgViewerController *pBgvController,
QWidget *parent = nullptr);
~BgViewerSettingsView();
private slots:
void onViewModeChanged();
void onPlayerDisplayModeChanged();
void onTransparencyChanged();
void onSymbolSizeChanged();
void onSymbolFireEventFlashingChanged(int val);
void onHitLineDisplayLengthChanged(int val);
private:
void accept() override;
void reject() override;
void resetValues();
Ui::BgViewerSettingsUI *m_ui;
BuildingViewer::BgViewerController *m_pBgvController;
};
} // namespace BuildingViewer
@@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BgViewerSettingsUI</class>
<widget class="QWidget" name="BgViewerSettingsUI">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string notr="true">General Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="2">
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QSlider" name="m_pSliderGlobalTransparency">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>75</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_pCheckBoxGlobalIsoMode">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Use orthographic view mode</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Player symbol size:</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="m_label_durationSymbolFlashing">
<property name="text">
<string>%1 sec</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="m_label_HitlineDuration">
<property name="text">
<string>%1 sec</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Symbol fire event flashing:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="m_pSliderPlayerSymbolSize">
<property name="minimum">
<number>10</number>
</property>
<property name="maximum">
<number>45</number>
</property>
<property name="value">
<number>20</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Transparency value:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Hit line display length:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSlider" name="m_slider_HitlineDuration">
<property name="maximum">
<number>120</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="m_pCheckBoxUseDotsMode">
<property name="text">
<string>Use dots mode</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSlider" name="m_slider_SymbolFlashing">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximum">
<number>120</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>10</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,80 @@
/**
* @RDE_HEADER@
* Handle bulding display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_building_widget.h"
#include "ui_thp_situation_building_base.h"
#include "thp_building_settings.h"
#include "thp_plugin_provider.h"
#include <QMenu>
#include <BuildingViewer/Common/bdv_controller.h>
#include <BuildingViewer/Common/bdv_plugin_provider.h>
#include <LtsScriptTimeline/track_entry.h>
#include <LtsWidgets/lts_dialog_helper.h>
#include <SituationReplay/handler_situation_replay_sqlite.h>
namespace THP
{
using namespace SituationReplay;
BuildingWidget::BuildingWidget(QWidget *parent)
: SituationBuildingBase("THP.BuildingWidget", parent)
{
QMenu *m = new QMenu(m_ui->btnFilter);
m->addAction(tr("Global Settings"), this, &BuildingWidget::onGlobalSettings);
m_ui->btnFilter->setMenu(m);
}
BuildingWidget::~BuildingWidget()
{
delete m_bgvController;
}
bool BuildingWidget::init(SituationReplay::MainHandlerSqlite *mhs, BuildingPluginProvider *pp, const QString &homePath)
{
if (!SituationBuildingBase::init(mhs))
return false;
m_replayHandler = mhs;
m_pluginProvider = pp;
// parse json, create xml, init buildingviewer
delete m_bgvController;
m_bgvController = new BuildingViewer::BgViewerController(this);
if (!m_bgvController->init(m_pluginProvider, true, homePath + QStringLiteral("/BuildingModels/")))
return false;
auto w = m_bgvController->getBgViewerWidget();
w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->vLayout->insertWidget(0, w);
return true;
}
bool BuildingWidget::showSituationInternal()
{
m_bgvController->excRestoreViewState(m_attributes[QStringLiteral("arrBuildingStates")]);
if (!SituationBuildingBase::showSituationInternal())
return false;
onInitDone();
return true;
}
void BuildingWidget::emitPartitionTimeChanged(qint64 curTime)
{
m_pluginProvider->emitPartitionTimeChanged(curTime);
}
void BuildingWidget::onGlobalSettings()
{
if (!m_globalSettingsDialog)
m_globalSettingsDialog = new BgViewerSettingsView(m_bgvController, this);
m_globalSettingsDialog->show();
}
}
@@ -0,0 +1,50 @@
/**
* @RDE_HEADER@
* Handle bulding display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <SituationReplay/main_handler_sqlite.h>
#include "thp_situation_building_base.h"
namespace BuildingViewer
{
class BgViewerController;
}
namespace Ui
{
class BuildingWidget;
}
namespace THP
{
class BuildingPluginProvider;
class BgViewerSettingsView;
class BuildingWidget : public SituationBuildingBase
{
Q_OBJECT
public:
BuildingWidget(QWidget *parent = nullptr);
~BuildingWidget();
bool init(SituationReplay::MainHandlerSqlite *mhs,
BuildingPluginProvider *pp,
const QString &homePath);
protected:
bool showSituationInternal() override;
void emitPartitionTimeChanged(qint64 curTime) override;
protected Q_SLOTS:
void onGlobalSettings();
virtual void applyCurentFilter(const SituationReplay::FilterInformation &) override {}
virtual void applyNewOverlay(int) override {}
virtual void removeOverlay(int) override {}
private:
BuildingPluginProvider *m_pluginProvider = nullptr;
BuildingViewer::BgViewerController *m_bgvController = nullptr;
BgViewerSettingsView *m_globalSettingsDialog = nullptr;
};
} // namespace THP
@@ -0,0 +1,139 @@
/**
* @RDE_HEADER@
* Handle HTML output for thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_html_widget.h"
#include "ui_thp_html_widget.h"
#include <QUrl>
#include <QWebEnginePage>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace THP
{
class MyQWebEnginePage : public QWebEnginePage
{
Q_OBJECT
public:
explicit MyQWebEnginePage(NGL::INGLLogger &logger, QObject *parent = nullptr)
: QWebEnginePage(parent)
, m_logger(logger)
{}
MyQWebEnginePage(NGL::INGLLogger &logger, QWebEngineProfile *profile, QObject *parent = nullptr)
: QWebEnginePage(profile, parent)
, m_logger(logger)
{}
bool needsToBeOpenedExternally(const QUrl &url)
{
const QString lpath = url.toString();
const QStringRef ext = lpath.splitRef(QLatin1Char('.')).last();
const QStringRef ext2Chars = ext.left(2);
if (ext == QLatin1String("html") &&
lpath.midRef(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
return true;
// PDF
if (ext == QLatin1String("pdf"))
return true;
// A/V playlist
if (ext == QLatin1String("pls"))
return true;
// Situation
if (ext == QLatin1String("json"))
return true;
// Word/Excel/PowerPoint
if (ext2Chars == QLatin1String("do") ||
ext2Chars == QLatin1String("xl") ||
ext2Chars == QLatin1String("pp"))
return true;
return false;
}
bool acceptNavigationRequest(const QUrl &url,
QWebEnginePage::NavigationType type,
bool isMainFrame) override
{
Q_UNUSED(isMainFrame)
if (type == QWebEnginePage::NavigationTypeLinkClicked)
{
if (needsToBeOpenedExternally(url))
{
emit linkClicked(url);
DEBUG_LOG(m_logger) << "acceptNavigationRequest denied for " << url.toString();
return false;
}
}
DEBUG_LOG(m_logger) << "acceptNavigationRequest allowed for " << url.toString();
return true;
}
Q_SIGNALS:
void linkClicked(const QUrl &url);
private:
NGL::INGLLogger &m_logger;
};
HtmlWidget::HtmlWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::HtmlWidget)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("THP.HtmlWidget"))
{
ui->setupUi(this);
auto webEnginePage = new MyQWebEnginePage(m_logger, this);
ui->html->setPage(webEnginePage);
connect(webEnginePage, &MyQWebEnginePage::linkClicked, this, &HtmlWidget::linkClicked);
connect(ui->html, &QWebEngineView::loadFinished, this, &HtmlWidget::loadFinished);
connect(ui->html, &QWebEngineView::urlChanged, this, &HtmlWidget::urlChanged);
connect(ui->html, &QWebEngineView::titleChanged, this, &HtmlWidget::titleChanged);
}
HtmlWidget::~HtmlWidget()
{
delete ui;
}
void HtmlWidget::back()
{
ui->html->back();
}
QString HtmlWidget::title() const
{
return ui->html->title();
}
QUrl HtmlWidget::url() const
{
return ui->html->url();
}
bool HtmlWidget::showHTML(const QUrl &url)
{
if (!url.isValid())
{
WARNING_LOG(m_logger) << "Invalid url: " << url.toString();
return false;
}
ui->html->load(url);
return true;
}
void HtmlWidget::setZoomFactor(double newZoomFactor)
{
ui->html->setZoomFactor(newZoomFactor);
}
void HtmlWidget::print(QPrinter *printer) const
{
ui->html->page()->print(printer, [](bool) {});
}
} // namespace THP
#include "thp_html_widget.moc"
@@ -0,0 +1,46 @@
/**
* @RDE_HEADER@
* Handle HTML output for thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QWidget>
#include <NGLLogger/INGLLogger.h>
class QUrl;
class QPrinter;
namespace Ui
{
class HtmlWidget;
}
namespace THP
{
class HtmlWidget : public QWidget
{
Q_OBJECT
public:
explicit HtmlWidget(QWidget *parent = nullptr);
~HtmlWidget();
void back();
void print(QPrinter *printer) const;
void setZoomFactor(double newZoomFactor);
QString title() const;
QUrl url() const;
public Q_SLOTS:
bool showHTML(const QUrl &url);
Q_SIGNALS:
void linkClicked(const QUrl &link);
void loadFinished(bool);
void urlChanged(const QUrl &url);
void titleChanged(const QString &title);
protected:
Ui::HtmlWidget *ui;
NGL::INGLLogger &m_logger;
};
} // namespace THP
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>HtmlWidget</class>
<widget class="QWidget" name="HtmlWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>719</width>
<height>540</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWebEngineView" name="html">
<property name="url">
<url>
<string>about:blank</string>
</url>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebEngineView</class>
<extends>QWidget</extends>
<header location="global">QtWebEngineWidgets/QWebEngineView</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,67 @@
/**
* @RDE_HEADER@
* Handle PDF output for thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_pdf_widget.h"
#include "ui_thp_pdf_widget.h"
namespace THP
{
PdfWidget::PdfWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PdfWidget)
{
ui->setupUi(this);
connect(ui->previousPage, &QToolButton::clicked, this, &PdfWidget::onPreviousPage);
connect(ui->nextPage, &QToolButton::clicked, this, &PdfWidget::onNextPage);
connect(ui->pdfDocument, &NGL::PDFDocument::currentPageChanged, this, &PdfWidget::pageChanged);
}
PdfWidget::~PdfWidget()
{
delete ui;
}
bool PdfWidget::showPDF(const QUrl &pdf)
{
return ui->pdfDocument->showPDF(pdf);
}
void PdfWidget::setZoomFactor(double newZoomFactor)
{
ui->pdfDocument->setZoomFactor(newZoomFactor);
}
void PdfWidget::onPreviousPage()
{
ui->pdfDocument->previousPage();
}
void PdfWidget::onNextPage()
{
ui->pdfDocument->nextPage();
}
QVector<QImage> PdfWidget::getPrintableImages(int resolution)
{
return ui->pdfDocument->getPrintableImages(resolution);
}
void PdfWidget::resizePdfToWidth()
{
ui->pdfDocument->resizePdfToWidth();
}
void PdfWidget::pageChanged(int cur, int max)
{
ui->pageLabel->setText(tr("Page %1 of %2").arg(cur).arg(max));
ui->nextPage->setEnabled(cur < max);
ui->previousPage->setEnabled(cur > 1);
}
} // namespace THP
@@ -0,0 +1,40 @@
/**
* @RDE_HEADER@
* Handle PDF output for thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QWidget>
class QUrl;
namespace Ui
{
class PdfWidget;
}
namespace THP
{
class PdfWidget : public QWidget
{
Q_OBJECT
public:
explicit PdfWidget(QWidget *parent = nullptr);
~PdfWidget();
bool showPDF(const QUrl &pdf);
void setZoomFactor(double newZoomFactor);
QVector<QImage> getPrintableImages(int resolution = 300);
void resizePdfToWidth();
protected Q_SLOTS:
void onPreviousPage();
void onNextPage();
void pageChanged(int cur, int max);
protected:
Ui::PdfWidget *ui;
};
} // namespace THP
@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PdfWidget</class>
<widget class="QWidget" name="PdfWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0" colspan="3">
<widget class="NGL::PDFDocument" name="pdfDocument" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>202</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QToolButton" name="previousPage">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/go-previous.png</normaloff>:icons/16x16/go-previous.png</iconset>
</property>
<property name="shortcut">
<string>Ctrl+P</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="pageLabel">
<property name="text">
<string>page %1 of %2</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="nextPage">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/go-next.png</normaloff>:icons/16x16/go-next.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>201</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>NGL::PDFDocument</class>
<extends>QWidget</extends>
<header location="global">nglwidgets/nglpdfdocument.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,488 @@
/**
* @RDE_HEADER@
* Handle situation display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_building_base.h"
#include "ui_thp_situation_building_base.h"
#include "thp_situation_filter_aes.h"
#include "thp_situation_filter_symbol_appearance.h"
#include "thp_situation_filter_collapse_expand.h"
#include "thp_situation_filter_types.h"
#include "thp_plugin_provider.h"
#include <QFileInfo>
#include <QtCore/QJsonArray>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QJsonParseError>
#include <QtCore/QTimer>
#include <LtsScriptTimeline/track_entry.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <SituationReplay/handler_situation_replay_sqlite.h>
#include <PlanOverlays/Gui/plan_overlay_model.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <ngltools/ngltools_dtg.h>
#include <NGLLogger/NGLLoggerBootstrap.h>
namespace THP
{
using namespace SituationReplay;
SituationBuildingBase::SituationBuildingBase(const char *loggerName,
QWidget *parent)
: QFrame(parent)
, m_playTimer(new QTimer(this))
, m_ui(new Ui::SituationBuildingBase)
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger(loggerName))
{
m_ui->setupUi(this);
m_ui->zoomSlider->hide();
m_ui->sldPosition->setMinimum(0);
m_ui->sldPosition->setMaximum(s_sliderMax);
m_playTimer->setSingleShot(true);
connect(m_playTimer, &QTimer::timeout, this, &SituationBuildingBase::onPlay);
connect(m_ui->btnSlower, &QToolButton::clicked, this, &SituationBuildingBase::onSlower);
connect(m_ui->btnFaster, &QToolButton::clicked, this, &SituationBuildingBase::onFaster);
connect(m_ui->btn100pct, &QToolButton::clicked, this, &SituationBuildingBase::on100pct);
connect(m_ui->btnPlay, &QToolButton::clicked, this, &SituationBuildingBase::onPlay);
connect(m_ui->btnStop, &QToolButton::clicked, this, &SituationBuildingBase::onStop);
connect(m_ui->sldPosition, &QSlider::sliderPressed, this, &SituationBuildingBase::onSliderPressed);
connect(m_ui->sldPosition, &QSlider::sliderMoved, this, &SituationBuildingBase::onSliderPositionChanged);
connect(m_ui->sldPosition, &QSlider::sliderReleased, this, &SituationBuildingBase::onSliderReleased);
connect(m_ui->cbPlaybackParts, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &SituationBuildingBase::onCbPlaybackPartsChanged);
}
SituationBuildingBase::~SituationBuildingBase()
{
delete m_ui;
}
bool SituationBuildingBase::init(SituationReplay::MainHandlerSqlite *replayHandler)
{
if (m_replayHandler)
m_replayHandler->disconnect(this);
m_replayHandler = replayHandler;
connect(m_replayHandler, &SituationReplay::MainHandlerSqlite::initialReplaySpeed,
this, &SituationBuildingBase::setSpeed);
connect(m_replayHandler, &SituationReplay::MainHandlerSqlite::applyCurentFilter,
this, &SituationBuildingBase::applyCurentFilter);
connect(m_replayHandler, &SituationReplay::MainHandlerSqlite::applyNewOverlay,
this, &SituationBuildingBase::applyNewOverlay);
connect(m_replayHandler, &SituationReplay::MainHandlerSqlite::removeOverlay,
this, &SituationBuildingBase::removeOverlay);
connect(m_replayHandler, &SituationReplay::MainHandlerSqlite::fileContentAvailable,
this, &SituationBuildingBase::onFileContentAvailable);
updatePositionLabel(LtsUtils::QPoint3D(), false);
updateSpeedLabel();
return true;
}
bool SituationBuildingBase::showSituation(const QString &sitReplayDescription,
const QString &basePath)
{
// read ReplayInformation from sitmap.json (key 'replays')
QFile f(sitReplayDescription); // ...Exercise_123//n/m/sitmap.json
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open file '" << sitReplayDescription << "': " << f.errorString();
return false;
}
QFileInfo fi(f);
m_scriptPath = fi.absolutePath();
QJsonParseError jError;
const QByteArray ba = f.readAll();
QJsonDocument doc(QJsonDocument::fromJson(ba, &jError));
if (jError.error)
{
WARNING_LOG(m_logger) << "Can not parse json document: " << jError.errorString();
return false;
}
const QJsonObject rootObj = doc.object();
const QJsonArray arrReplays(rootObj[QStringLiteral("replays")].toArray());
QVector<ReplayInformation> replayInformation;
replayInformation.reserve(arrReplays.size());
for (const auto &val : arrReplays)
{
const QJsonObject obj = val.toObject();
const QJsonObject objPb = obj[QStringLiteral("playbackTime")].toObject();
const QJsonObject objSqlite = obj[QStringLiteral("replayInformation")].toObject();
const QJsonObject objTip = objSqlite[QStringLiteral("timeInPresentation")].toObject();
ReplayInformation info;
info.m_id = obj[QStringLiteral("id")].toInt();
info.timeSpan = TimeSpan::fromJson(objPb);
info.relativeTimeSpan = TimeSpan::fromJson(objTip);
const int sqliteFolderId = objSqlite[QStringLiteral("id")].toInt();
info.databaseFilePath = QStringLiteral("%1/%2/sitreplay.sqlite").arg(basePath).arg(sqliteFolderId);
info.replayConfigPath = QStringLiteral("%1/%2/sitreplay.json").arg(basePath).arg(sqliteFolderId);
if (info.relativeTimeSpan.begin < 0 ||
info.relativeTimeSpan.end <= info.relativeTimeSpan.begin ||
sqliteFolderId == 0)
{
WARNING_LOG(m_logger) << "Can not parse json document: " << QJsonDocument(obj).toJson();
return false;
}
replayInformation.push_back(std::move(info));
}
const QJsonObject attributes(rootObj[QStringLiteral("attributes")].toObject());
const QJsonArray arrFilters1(attributes[QStringLiteral("Filters")].toArray());
QVector<FilterInformation> filterInformation;
QVector<FilterInformation> overlayInformation;
for (const auto &arrVal : arrFilters1)
{
// const QJsonArray arrFilters2(arrVal.toArray());
// for (const auto &val : arrFilters2)
// {
FilterInformation filter;
const QJsonObject obj = arrVal.toObject();
LtsScriptTimeline::TrackEntry te;
te.fromJson(obj);
if (te.m_topicId <= 0)
{
WARNING_LOG(m_logger) << "Can not load filter information from '" << QJsonDocument(obj).toJson() << "'";
continue;
}
if (te.m_type != LtsScriptTimeline::TrackEntryType::SituationMapFilter &&
te.m_type != LtsScriptTimeline::TrackEntryType::TacticalPlan)
{
DEBUG_LOG(m_logger) << "Unknown Filter type " << te.m_type << " - ignoring: " << QJsonDocument(obj).toJson() << "'";
continue;
}
filter.m_id = te.m_topicId;
filter.relativeTimeSpan.begin = te.m_startTime * 1000LL;
filter.relativeTimeSpan.end = te.m_length * 1000LL + filter.relativeTimeSpan.begin;
const QJsonObject details = obj[QStringLiteral("detailData")].toObject();
if (te.m_type == LtsScriptTimeline::TrackEntryType::SituationMapFilter)
{
const QJsonObject filters = te.m_detailData[QStringLiteral("filters")].toObject();
filter.aesFilters = filters[QStringLiteral("AesFilterJson")].toObject();
filter.symbolSettings = filters[QStringLiteral("SymbolAppearanceJson")].toObject();
filter.buildingComponentSettings = filters[QStringLiteral("BuildingComponentSettingsJson")].toObject();
filter.mapSettings = details[QStringLiteral("settings")].toObject();
if (filter.relativeTimeSpan.begin < 0 || filter.aesFilters.isEmpty() || filter.symbolSettings.isEmpty())
{
WARNING_LOG(m_logger) << "Can not parse json document - can not read filter data " << QJsonDocument(obj).toJson();
return false;
}
if (filter.buildingComponentSettings.isEmpty())
{
WARNING_LOG(m_logger) << "Can not parse json document - can not read filter data 'BuildingComponentSettingsJson', json= " << QJsonDocument(obj).toJson();
// return false; // support older thps with this thpViewer
}
filterInformation.push_back(std::move(filter));
}
else if (te.m_type == LtsScriptTimeline::TrackEntryType::TacticalPlan)
{
if (filter.relativeTimeSpan.begin < 0)
{
WARNING_LOG(m_logger) << "Can not parse json document - can not read filter data " << QJsonDocument(obj).toJson();
return false;
}
// old version (before 2020/02)
filter.overlayId = details[QStringLiteral("FileId")].toInt(-1);
if (filter.overlayId >= 0)
{
DEBUG_LOG(m_logger) << "Add overlay with id " << filter.overlayId;
overlayInformation.push_back(std::move(filter));
continue;
}
const QJsonArray arr = details[QStringLiteral("PlanItems")].toArray();
if (arr.isEmpty())
{
ERROR_LOG(m_logger) << "No PlanItems given - " << QJsonDocument(details).toJson();
}
for (const auto &ele : arr)
{
const auto pi = PlanOverlaysPlugin::PlanOverlayModel::PlanItem::fromJson(ele.toObject());
if (!pi.isValid() || pi.m_fileId <= 0)
{
WARNING_LOG(m_logger) << "Can not parse json document - can not read filter data " << QJsonDocument(ele.toObject()).toJson();
return false;
}
filter.overlayId = pi.m_fileId;
DEBUG_LOG(m_logger) << "Add overlay with id " << filter.overlayId;
overlayInformation.push_back(filter);
}
}
//}
}
m_replayInformation = replayInformation;
m_filterInformation = filterInformation;
m_overlayInformation = overlayInformation;
m_attributes = rootObj[QStringLiteral("attributes")].toObject();
return showSituationInternal();
}
bool SituationBuildingBase::showSituationInternal()
{
m_ui->cbPlaybackParts->setVisible(m_replayInformation.size() > 1);
if (m_replayInformation.isEmpty())
{
WARNING_LOG(m_logger) << "No replay information given...";
return false;
}
const QString sqliteDb = m_replayInformation.first().databaseFilePath;
if (sqliteDb.isEmpty())
{
WARNING_LOG(m_logger) << "No database given...";
return false;
}
QFileInfo fi(sqliteDb);
if (!fi.exists() || !fi.isFile() || !fi.isReadable())
{
Q_ASSERT(false);
WARNING_LOG(m_logger) << "Can not access sqlite database '" << fi.absoluteFilePath() << "'";
return false;
}
m_ui->cbPlaybackParts->clear();
m_ui->cbPlaybackParts->blockSignals(true);
for (int i = 0; i < m_replayInformation.size(); ++i)
{
const ReplayInformation &info = m_replayInformation.at(i);
m_ui->cbPlaybackParts->addItem(QStringLiteral("%1: %2 - %3").arg(i+1).arg(DTG::toStringMSec(info.timeSpan.begin, DTG::Long),
DTG::toStringMSec(info.timeSpan.end, DTG::Long)), i);
}
m_ui->cbPlaybackParts->setCurrentIndex(0);
m_ui->cbPlaybackParts->blockSignals(false);
onCbPlaybackPartsChanged(); // creates replayHandler for correct db etc.
return true;
}
void SituationBuildingBase::setSpeed(double newSpeed)
{
m_speedVal = qBound(0.0, newSpeed, 16.0);
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onPaceChangeRequest(m_speedVal);
updateSpeedLabel();
}
void SituationBuildingBase::onSlower()
{
// cur speed = 9 -> new speed = 7
// cur speed = 4 -> new speed = 3
int diff = lrint(sqrt(m_speedVal) - 1);
if (diff < 1)
diff = 1;
setSpeed(m_speedVal - diff);
}
void SituationBuildingBase::onFaster()
{
int diff = lrint(sqrt(m_speedVal) - 1);
if (diff < 1)
diff = 1;
setSpeed(m_speedVal + diff);
}
void SituationBuildingBase::on100pct()
{
setSpeed(1.0);
}
void SituationBuildingBase::onPlay()
{
DEBUG_LOG(m_logger) << "OnPlay " << int(m_state);
switch (m_state)
{
case State::PS_STOP:
m_state = State::PS_PLAY;
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onStartReplayRequest(m_speedVal);
break;
case State::PS_PAUSE:
m_state = State::PS_PLAY;
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onResumeReplayRequest();
break;
case State::PS_PLAY:
m_state = State::PS_PAUSE;
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onPauseReplayRequest();
break;
case State::PS_UNDEFINED:
// dürfte nicht vorkommen
break;
}
updateButtons();
}
void SituationBuildingBase::onStop()
{
auto srp = m_replayHandler->handlerSrp();
if (srp)
{
srp->onStopReplayRequest();
onUpdateTimeSlider(srp->beginTime());
}
m_state = State::PS_STOP;
updateButtons();
m_ui->sldPosition->setValue(0);
}
void SituationBuildingBase::onSliderPressed()
{
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onPauseReplayRequest();
m_state = State::PS_PAUSE;
updateButtons();
}
void SituationBuildingBase::onSliderPositionChanged(int newPosition)
{
if (!m_replayHandler->handlerSrp())
return;
auto srp = m_replayHandler->handlerSrp();
const double len = (srp->endTime() - srp->beginTime());
const double ofs = newPosition * len / s_sliderMax;
m_sliderMoveTime = qRound64(srp->beginTime() + ofs);
m_currentTimeDTGString = DTG::toStringMSec(m_sliderMoveTime, DTG::Long);
m_ui->lblPosition->setText(m_currentTimeDTGString);
}
void SituationBuildingBase::onSliderReleased()
{
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onStartReplayRequest(m_sliderMoveTime,
m_replayHandler->handlerSrp()->endTime(),
m_speedVal);
m_state = State::PS_PLAY;
updateButtons();
}
void SituationBuildingBase::onUpdateTimeSlider(qint64 curTime)
{
if (!m_replayHandler->handlerSrp() || m_state == State::PS_STOP)
return;
const auto srp = m_replayHandler->handlerSrp();
const double len = (srp->endTime() - srp->beginTime());
const double ofs = (curTime - srp->beginTime());
double pos = ofs / len * s_sliderMax;
if (pos < 0)
pos = 0;
if (pos >= s_sliderMax)
{
onStop();
return;
}
DEBUG_LOG(m_logger) << "onUpdateTimeSlider: " << curTime << ", pos: " << pos;
m_ui->sldPosition->setValue(qRound(pos));
m_currentTimeDTGString = DTG::toStringMSec(curTime, DTG::Long);
m_ui->lblPosition->setText(m_currentTimeDTGString);
emitPartitionTimeChanged(curTime);
}
void SituationBuildingBase::onCbPlaybackPartsChanged()
{
onStop();
int idx = m_ui->cbPlaybackParts->currentData().toInt();
if (idx < 0 || idx >= m_replayInformation.size())
return;
const ReplayInformation &info = m_replayInformation.at(idx);
if (!m_replayHandler->initDb(info, m_filterInformation, m_overlayInformation))
{
WARNING_LOG(m_logger) << "Can not initialize replay handler (" << info.databaseFilePath << ")";
return;
}
// currently after initDb since there handlerSrp is (re)created
connect(m_replayHandler->handlerSrp().get(), &SituationReplay::HandlerSituationReplaySqlite::replayTimeChanged,
this, &SituationBuildingBase::onUpdateTimeSlider, Qt::UniqueConnection);
}
void SituationBuildingBase::updateSpeedLabel()
{
QString speedStr = QStringLiteral("%1x").arg(m_speedVal, 4, 'f', 2);
m_ui->lblSpeed->setText(speedStr);
}
void SituationBuildingBase::updatePositionLabel(const LtsUtils::QPoint3D &geoPos, bool isValid)
{
if (isValid)
{
const LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(geoPos.x(), geoPos.y()));
m_ui->lblCursorPos->setText(coord.mgrs());
}
else
{
m_ui->lblCursorPos->setText(QString());
}
}
QString SituationBuildingBase::currentDTGTime() const
{
return m_currentTimeDTGString;
}
void SituationBuildingBase::updateButtons()
{
switch (m_state)
{
case State::PS_UNDEFINED:
m_ui->btnStop->setEnabled(false);
m_ui->btnPlay->setEnabled(false);
m_ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
m_ui->sldPosition->setEnabled(false);
break;
case State::PS_STOP:
m_ui->btnStop->setEnabled(false);
m_ui->btnPlay->setEnabled(true);
m_ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
m_ui->sldPosition->setEnabled(true);
break;
case State::PS_PLAY:
m_ui->btnStop->setEnabled(true);
m_ui->btnPlay->setEnabled(true);
m_ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-pause.png")));
m_ui->sldPosition->setEnabled(true);
break;
case State::PS_PAUSE:
m_ui->btnStop->setEnabled(true);
m_ui->btnPlay->setEnabled(true);
m_ui->btnPlay->setIcon(QIcon(QStringLiteral(":icons/16x16/media-playback-start.png")));
m_ui->sldPosition->setEnabled(true);
break;
}
}
bool SituationBuildingBase::stop()
{
onStop();
return true;
}
void SituationBuildingBase::onInitDone()
{
m_state = State::PS_STOP;
m_playTimer->start();
}
} // namespace THP
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* Handle situation / building display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QFrame>
#include <NGLLogger/INGLLogger.h>
#include <SituationReplay/main_handler_sqlite.h>
namespace FileManagement
{
namespace Database
{
struct FlmFileContent;
}
}
namespace SituationReplay
{
class MainHandlerSqlite;
}
namespace LtsUtils
{
class QPoint3D;
}
namespace Ui
{
class SituationBuildingBase;
}
namespace THP
{
enum class State
{
PS_UNDEFINED = 0,
PS_STOP = 1,
PS_PLAY = 2,
PS_PAUSE = 3
};
constexpr int s_sliderMax = 1000; // 0 .. 1000
class SituationBuildingBase : public QFrame
{
Q_OBJECT
public:
explicit SituationBuildingBase(const char *loggerName,
QWidget *parent = nullptr);
~SituationBuildingBase();
bool init(SituationReplay::MainHandlerSqlite *replayHandler);
bool showSituation(const QString &sitReplayDescription, const QString &basePath);
void updateButtons();
bool stop();
QString currentDTGTime() const;
protected:
virtual bool showSituationInternal();
protected Q_SLOTS:
void setSpeed(double newSpeed);
virtual void applyCurentFilter(const SituationReplay::FilterInformation &filter) = 0;
virtual void applyNewOverlay(int overlayId) = 0;
virtual void removeOverlay(int overlayId) = 0;
void onSlower();
void onFaster();
void on100pct();
void onPlay();
void onStop();
void onSliderPressed();
void onSliderPositionChanged(int newPosition);
void onSliderReleased();
void updateSpeedLabel();
void onUpdateTimeSlider(qint64 curTime);
void onCbPlaybackPartsChanged();
void updatePositionLabel(const LtsUtils::QPoint3D &geoPos, bool isValid);
virtual void updateZoomSlider() {}
virtual void onInitDone();
virtual void emitPartitionTimeChanged(qint64 curTime) = 0;
virtual void onFileContentAvailable(const FileManagement::Database::FlmFileContent &) {}
protected:
QVector<SituationReplay::ReplayInformation> m_replayInformation;
QVector<SituationReplay::FilterInformation> m_filterInformation;
QVector<SituationReplay::FilterInformation> m_overlayInformation;
QJsonObject m_attributes;
QString m_scriptPath; // sitmap.json
double m_speedVal = 1;
State m_state = State::PS_STOP;
QTimer *m_playTimer = nullptr;
SituationReplay::MainHandlerSqlite *m_replayHandler = nullptr;
QString m_currentTimeDTGString;
qint64 m_sliderMoveTime = 0;
Ui::SituationBuildingBase *m_ui;
NGL::INGLLogger &m_logger;
};
} // namespace THP
@@ -0,0 +1,234 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SituationBuildingBase</class>
<widget class="QFrame" name="SituationBuildingBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>911</width>
<height>563</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QVBoxLayout" name="vLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="THP::ZoomSlider" name="zoomSlider" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="sldPosition">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblPosition">
<property name="text">
<string notr="true">--:-- / --:--</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QToolButton" name="btnPlay">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/media-playback-start.png</normaloff>:icons/16x16/media-playback-start.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnStop">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/media-playback-stop.png</normaloff>:icons/16x16/media-playback-stop.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSlower">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/media-play-slower.png</normaloff>:icons/16x16/media-play-slower.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btn100pct">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/media-play-normal.png</normaloff>:icons/16x16/media-play-normal.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnFaster">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:icons/16x16/media-play-faster.png</normaloff>:icons/16x16/media-play-faster.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lblSpeed">
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="text">
<string notr="true">100 %</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QComboBox" name="cbPlaybackParts">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</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>
<item>
<widget class="QLabel" name="lblCursorPos">
<property name="text">
<string notr="true">MGRS</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnFilter">
<property name="text">
<string>Filter</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>THP::ZoomSlider</class>
<extends>QWidget</extends>
<header>thpwidgets/thp_zoom_slider.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,75 @@
/**
* @RDE_HEADER@
* AES filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_filter_aes.h"
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QPushButton>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/Gui/Aes/flt_aes_filter_widget.h>
#include <ProjectDataManagement/Gui/PdmComboBox/pdm_combobox_foreign_gui_interface_impl.h>
#include <ParticipantManagement/Gui/PpmComboBox/ppm_combobox_foreign_gui_interface_impl.h>
namespace THP
{
SituatationFilterAes::SituatationFilterAes(const std::shared_ptr<FilterManagement::AesDisplayFilter> &aesFilter,
SitMapPluginProvider *pluginProvider,
QWidget *parent)
: QDialog(parent)
, m_filterWidget(new FilterManagement::AesFilterWidget(this))
{
setWindowTitle(tr("Area Effect Simulation filter settings"));
QGridLayout *gl = new QGridLayout(this);
m_filterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gl->addWidget(m_filterWidget, 0, 0);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
gl->addWidget(line, 1, 0);
QDialogButtonBox *bb = new QDialogButtonBox(this);
gl->addWidget(bb, 2, 0);
bb->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Cancel);
connect(bb->button(QDialogButtonBox::Apply), &QPushButton::clicked,
m_filterWidget, &FilterManagement::AesFilterWidget::apply);
connect(bb->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &SituatationFilterAes::accept);
connect(bb->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
this, &SituatationFilterAes::reject);
std::shared_ptr<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>
ifcPpmCbx(std::make_shared<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>());
ifcPpmCbx->init(pluginProvider->ppmDtc());
std::shared_ptr<ProjectDataManagement::ForeignGuiInterfaceImpl>
ifcPdmCbx(std::make_shared<ProjectDataManagement::ForeignGuiInterfaceImpl>());
ifcPdmCbx->init(pluginProvider->pdmDtc());
m_filterWidget->init(ifcPpmCbx, ifcPdmCbx, aesFilter);
// make sure to update ui when filter changes
connect(m_filterWidget, &FilterManagement::AesFilterWidget::unknownChangesRecognized,
m_filterWidget, &FilterManagement::AesFilterWidget::updateGuiToFilter);
}
void SituatationFilterAes::accept()
{
m_filterWidget->apply();
QDialog::accept();
}
void SituatationFilterAes::reject()
{
m_filterWidget->dialogCanceled();
QDialog::reject();
}
}
@@ -0,0 +1,35 @@
/**
* @RDE_HEADER@
* AES filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "thp_plugin_provider.h"
#include <QDialog>
namespace FilterManagement
{
class AesFilterWidget;
class AesDisplayFilter;
}
namespace THP
{
class SitMapPluginProvider;
class SituatationFilterAes final : public QDialog
{
Q_OBJECT
public:
explicit SituatationFilterAes(const std::shared_ptr<FilterManagement::AesDisplayFilter> &aesFilter,
SitMapPluginProvider *pluginProvider,
QWidget *parent = nullptr);
protected:
void accept() override;
void reject() override;
private:
FilterManagement::AesFilterWidget *m_filterWidget;
};
}
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* Type filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_filter_building_components.h"
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QPushButton>
#include <FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <FilterManagement/Gui/BuildingComponents/flt_building_component_settings_widget.h>
namespace THP
{
SituationFilterBuildingComponent::SituationFilterBuildingComponent(const std::shared_ptr<FilterManagement::BuildingComponentSettings> &bcs,
SitMapPluginProvider *pluginProvider,
QWidget *parent)
: QDialog(parent)
, m_filterWidget(new FilterManagement::BuildingComponentSettingsWidget(this))
{
setWindowTitle(tr("Building / Component filter settings"));
QGridLayout *gl = new QGridLayout(this);
m_filterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gl->addWidget(m_filterWidget, 0, 0);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
gl->addWidget(line, 1, 0);
QDialogButtonBox *bb = new QDialogButtonBox(this);
gl->addWidget(bb, 2, 0);
bb->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Cancel);
connect(bb->button(QDialogButtonBox::Apply), &QPushButton::clicked,
m_filterWidget, &FilterManagement::BuildingComponentSettingsWidget::apply);
connect(bb->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &SituationFilterBuildingComponent::accept);
connect(bb->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
this, &SituationFilterBuildingComponent::reject);
m_filterWidget->init(bcs, pluginProvider->ismDtc());
}
void SituationFilterBuildingComponent::accept()
{
m_filterWidget->apply();
QDialog::accept();
}
void SituationFilterBuildingComponent::reject()
{
m_filterWidget->dialogCanceled();
QDialog::reject();
}
}
@@ -0,0 +1,34 @@
/**
* @RDE_HEADER@
* Type filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "thp_plugin_provider.h"
#include <QDialog>
namespace FilterManagement
{
class BuildingComponentSettings;
class BuildingComponentSettingsWidget;
}
namespace THP
{
class SitMapPluginProvider;
class SituationFilterBuildingComponent final : public QDialog
{
Q_OBJECT
public:
explicit SituationFilterBuildingComponent(const std::shared_ptr<FilterManagement::BuildingComponentSettings> &bcs,
SitMapPluginProvider *pluginProvider,
QWidget *parent = nullptr);
protected:
void accept() override;
void reject() override;
private:
FilterManagement::BuildingComponentSettingsWidget *m_filterWidget{nullptr};
};
}
@@ -0,0 +1,77 @@
/**
* @RDE_HEADER@
* AES filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_filter_collapse_expand.h"
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QPushButton>
#include <AdvancedSymbology/Data/asb_foreign_interface_impl.h>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/Gui/Participant/flt_collapse_expand_participants_widget.h>
#include <ParticipantManagement/Gui/PpmComboBox/ppm_combobox_foreign_gui_interface_impl.h>
namespace THP
{
SituatationFilterCollapseExpand::SituatationFilterCollapseExpand(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent)
: QDialog(parent)
, m_filterWidget(new FilterManagement::CollapseExpandParticipantWidget(this))
{
setWindowTitle(tr("Collapse/Expand Participant filter settings"));
QGridLayout *gl = new QGridLayout(this);
m_filterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gl->addWidget(m_filterWidget, 0, 0);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
gl->addWidget(line, 1, 0);
QDialogButtonBox *bb = new QDialogButtonBox(this);
gl->addWidget(bb, 2, 0);
bb->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Cancel);
connect(bb->button(QDialogButtonBox::Apply), &QPushButton::clicked,
m_filterWidget, &FilterManagement::CollapseExpandParticipantWidget::apply);
connect(bb->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &SituatationFilterCollapseExpand::accept);
connect(bb->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
this, &SituatationFilterCollapseExpand::reject);
std::shared_ptr<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>
ifcPpmCbx(std::make_shared<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>());
ifcPpmCbx->init(pluginProvider->ppmDtc());
std::shared_ptr<AdvancedSymbology::ForeignInterfaceImpl>
ifcAsb(std::make_shared<AdvancedSymbology::ForeignInterfaceImpl>(pluginProvider->ppmDtc(),
pluginProvider->aesDtc(),
pluginProvider->gimDtc()));
m_filterWidget->init(pluginProvider->ppmDtc(),
ifcPpmCbx,
ifcAsb,
// pluginProvider->fltParticipantIfc(),
sas);
}
void SituatationFilterCollapseExpand::accept()
{
m_filterWidget->apply();
QDialog::accept();
}
void SituatationFilterCollapseExpand::reject()
{
m_filterWidget->dialogCanceled();
QDialog::reject();
}
}
@@ -0,0 +1,34 @@
/**
* @RDE_HEADER@
* Collapse/Expand filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "thp_plugin_provider.h"
#include <QDialog>
namespace FilterManagement
{
class CollapseExpandParticipantWidget;
class SymbolAppearanceSettings;
}
namespace THP
{
class SitMapPluginProvider;
class SituatationFilterCollapseExpand final : public QDialog
{
Q_OBJECT
public:
explicit SituatationFilterCollapseExpand(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent = nullptr);
protected:
void accept() override;
void reject() override;
private:
FilterManagement::CollapseExpandParticipantWidget *m_filterWidget;
};
}
@@ -0,0 +1,63 @@
/**
* @RDE_HEADER@
* AES filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_filter_symbol_appearance.h"
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QPushButton>
#include <ParticipantManagement/ppm_dtc.h>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/Gui/Participant/flt_symbol_appearance_widget.h>
namespace THP
{
SituatationFilterSymbolAppearance::SituatationFilterSymbolAppearance(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent)
: QDialog(parent)
, m_filterWidget(new FilterManagement::SymbolAppearanceWidget(this))
{
setWindowTitle(tr("Symbol Appearance filter settings"));
QGridLayout *gl = new QGridLayout(this);
m_filterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gl->addWidget(m_filterWidget, 0, 0);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
gl->addWidget(line, 1, 0);
QDialogButtonBox *bb = new QDialogButtonBox(this);
gl->addWidget(bb, 2, 0);
bb->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Cancel);
connect(bb->button(QDialogButtonBox::Apply), &QPushButton::clicked,
m_filterWidget, &FilterManagement::SymbolAppearanceWidget::apply);
connect(bb->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &SituatationFilterSymbolAppearance::accept);
connect(bb->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
this, &SituatationFilterSymbolAppearance::reject);
m_filterWidget->init(pluginProvider->ppmDtc()->partys(), sas);
}
void SituatationFilterSymbolAppearance::accept()
{
m_filterWidget->apply();
QDialog::accept();
}
void SituatationFilterSymbolAppearance::reject()
{
m_filterWidget->dialogCanceled();
QDialog::reject();
}
}
@@ -0,0 +1,34 @@
/**
* @RDE_HEADER@
* Symbol Appearance filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "thp_plugin_provider.h"
#include <QDialog>
namespace FilterManagement
{
class SymbolAppearanceSettings;
class SymbolAppearanceWidget;
}
namespace THP
{
class SitMapPluginProvider;
class SituatationFilterSymbolAppearance final : public QDialog
{
Q_OBJECT
public:
explicit SituatationFilterSymbolAppearance(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent = nullptr);
protected:
void accept() override;
void reject() override;
private:
FilterManagement::SymbolAppearanceWidget *m_filterWidget;
};
}
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* Type filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_filter_types.h"
#include <QDialogButtonBox>
#include <QFrame>
#include <QGridLayout>
#include <QPushButton>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/Gui/Participant/flt_type_filter_widget.h>
#include <ParticipantManagement/Gui/PpmComboBox/ppm_combobox_foreign_gui_interface_impl.h>
namespace THP
{
SituatationFilterTypes::SituatationFilterTypes(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent)
: QDialog(parent)
, m_filterWidget(new FilterManagement::TypeFilterWidget(this))
{
setWindowTitle(tr("Type filter settings"));
QGridLayout *gl = new QGridLayout(this);
m_filterWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
gl->addWidget(m_filterWidget, 0, 0);
QFrame *line = new QFrame(this);
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
gl->addWidget(line, 1, 0);
QDialogButtonBox *bb = new QDialogButtonBox(this);
gl->addWidget(bb, 2, 0);
bb->setStandardButtons(QDialogButtonBox::Apply | QDialogButtonBox::Close | QDialogButtonBox::Cancel);
connect(bb->button(QDialogButtonBox::Apply), &QPushButton::clicked,
m_filterWidget, &FilterManagement::TypeFilterWidget::apply);
connect(bb->button(QDialogButtonBox::Close), &QPushButton::clicked,
this, &SituatationFilterTypes::accept);
connect(bb->button(QDialogButtonBox::Cancel), &QPushButton::clicked,
this, &SituatationFilterTypes::reject);
std::shared_ptr<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>
ifcPpmCbx(std::make_shared<ParticipantManagement::ForeignGuiComboboxInterfaceImpl>());
ifcPpmCbx->init(pluginProvider->ppmDtc());
m_filterWidget->init(pluginProvider->ppmDtc(),
ifcPpmCbx,
sas);
}
void SituatationFilterTypes::accept()
{
m_filterWidget->apply();
QDialog::accept();
}
void SituatationFilterTypes::reject()
{
m_filterWidget->dialogCanceled();
QDialog::reject();
}
}
@@ -0,0 +1,34 @@
/**
* @RDE_HEADER@
* Type filter dialog for situation map/thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "thp_plugin_provider.h"
#include <QDialog>
namespace FilterManagement
{
class SymbolAppearanceSettings;
class TypeFilterWidget;
}
namespace THP
{
class SitMapPluginProvider;
class SituatationFilterTypes final : public QDialog
{
Q_OBJECT
public:
explicit SituatationFilterTypes(const std::shared_ptr<FilterManagement::SymbolAppearanceSettings> &sas,
SitMapPluginProvider *pluginProvider,
QWidget *parent = nullptr);
protected:
void accept() override;
void reject() override;
private:
FilterManagement::TypeFilterWidget *m_filterWidget;
};
}
@@ -0,0 +1,650 @@
/**
* @RDE_HEADER@
* Handle situation display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_widget.h"
#include "ui_thp_situation_building_base.h"
#include "thp_situation_filter_aes.h"
#include "thp_situation_filter_symbol_appearance.h"
#include "thp_situation_filter_collapse_expand.h"
#include "thp_situation_filter_types.h"
#include "thp_situation_filter_building_components.h"
#include "thp_plugin_provider.h"
#include <QDir>
#include <QFileInfo>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QJsonParseError>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QSvgRenderer>
#include <QtCore/QTimer>
#include <QThread>
#include <FileManagement/flm_db_structs.h>
#include <FilterManagement/flt_participant_plugin_interface.h>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <FilterManagement/flt_building_cmpnt_settings.h>
#include <FilterManagement/Gui/Aes/Common/aes_display_filter_impl.h>
#include <FilterManagement/Gui/Aes/Common/symbol_appearance_settings_impl.h>
#include <SituationReplay/handler_situation_replay_sqlite.h>
#include <SituationMap/situation_map_init.h>
#include <SituationMap/Utils/movement_track_manager.h>
#include <SituationMap/Map/grid.h>
#include <SituationMap/Map/point3D.h>
#include <SituationMap/Map/map_initializer.h>
#include <SituationMap/Map/map_notifier.h>
#include <SituationMap/Map/operator_map_settings.h>
#include <SituationMap/Map/overlay_handler.h>
#include <SituationMap/Symbology/sidc_codec.h>
#include <PlanOverlays/Gui/plan_overlay_model.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <NGLLogger/INGLLogger.h>
#include <ngltools/ngltools_dtg.h>
namespace THP
{
class MySitMapIinit final : public SituationMap::SituationMapInit
{
Q_OBJECT
public:
using SituationMap::SituationMapInit::SituationMapInit;
bool trainingAreaExists() const override { return true; }
SituationMap::MapSettingsJson getMapSettingsFromService() const override { return m_mapSettingsJson; }
void setMapSettingsJson(const SituationMap::MapSettingsJson &settings) { m_mapSettingsJson = settings; }
const SituationMap::MapSettingsJson & getMapSettingsJson() const { return m_mapSettingsJson; }
private:
SituationMap::MapSettingsJson m_mapSettingsJson;
};
using namespace SituationReplay;
SituationWidget::SituationWidget(QWidget *parent)
: SituationBuildingBase("THP.SituationWidget", parent)
, m_pluginProvider(new SitMapPluginProvider)
, m_situationMapInit(new MySitMapIinit(nullptr))
{
QMenu *m = new QMenu(m_ui->btnFilter);
m->addAction(tr("AES Filter"), this, &SituationWidget::onFilterAes);
// 29.09.2022, Norbert Thoden
// SWPÄ 2:22 GUEZE-1477 Kartenfilter für Komponenten:
// Es wurde der Komponenten Filter eingeführt.
// in der LiveLage werden allerdings nur MountingPoints angezeigt, die auch eine
// zugeordnetes Asset haben. Die Zuordnug befindet sich in der Tabelle ism.sim_mounting_point_asset.
// ism.sim_mounting_point_asset wird derzeit nicht in die sitreplay.sqlite
// überführt. Es koennen also keine Komponenten angezeigt / gefiltert werden...
// FilterDialog nicht anzeigen:
// m->addAction(tr("Building / Component"), this, &SituationWidget::onFilterBuildingComponent);
m->addAction(tr("Symbol Appearance"), this, &SituationWidget::onFilterSymbolAppearance);
m->addAction(tr("Collpase/Expand"), this, &SituationWidget::onFilterCollapseExpand);
m->addAction(tr("Type filter"), this, &SituationWidget::onFilterTypes);
m_ui->btnFilter->setMenu(m);
}
SituationWidget::~SituationWidget()
{
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onStopReplayRequest();
delete m_situationMapInit;
delete m_pluginProvider;
delete m_replayHandler;
}
bool SituationWidget::init(const QString &homePath, int exerciseId)
{
m_pluginProvider->setExerciseId(exerciseId);
delete m_replayHandler;
m_replayHandler = new SituationReplay::MainHandlerSqlite;
if (!SituationBuildingBase::init(m_replayHandler))
return false;
loadLayerMappings(homePath);
SituationReplay::ConnectionParams connParams;
connParams.domainId = 1;
connParams.partitionId = 1;
if (!m_replayHandler->initDds(connParams))
return false;
if (!m_pluginProvider->init(m_replayHandler->ddsReplay()))
return false;
if (!m_situationMapInit->initPluginProvider(m_pluginProvider))
return false;
QPointer<SituationMap::MovementTrackManager> movementTrackMgr =
QPointer<SituationMap::MovementTrackManager>(new SituationMap::MovementTrackManager(m_situationMapInit));
FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX initMap....";
if (!m_situationMapInit->initMap(false, SituationMap::Mode::Mode_2D))
return false;
if (!m_situationMapInit->initAdapter(movementTrackMgr, false))
return false;
SituationMap::Map *map = m_situationMapInit->getWidget();
if (!map)
return false;
connect(map, &SituationMap::Map::mouseGeoPositionChanged,
this, &SituationWidget::updatePositionLabel,
Qt::UniqueConnection);
connect(map, &SituationMap::Map::extentChanged,
this, &SituationWidget::updateZoomSlider);
map->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->vLayout->insertWidget(0, map);
m_aesDisplayFilter = m_pluginProvider->fltAesIfc()->aesFilterForOperator(0, QString());
m_symbolAppaearanceSettings = m_pluginProvider->fltParticipantIfc()->symbolAppearanceForOperator(0, QString());
m_buildingComponentSettings = m_pluginProvider->fltBuildingComponentIfc()->buildingComponentSettingsForOperator(0, QString());
return true;
}
bool SituationWidget::showSituationInternal()
{
// cleanup 'old' map:
// remove old overlays from former session:
{
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map)
map->overlayHandler().removeAllOverlays();
}
m_ui->cbPlaybackParts->setVisible(m_replayInformation.size() > 1);
// MAYBE there are filter to be applied:
applyFilterFromReplayConfigFile();
if (m_replayInformation.isEmpty())
{
WARNING_LOG(m_logger) << "No replay information given...";
return false;
}
const QString sqliteDb = m_replayInformation.first().databaseFilePath;
if (sqliteDb.isEmpty())
{
WARNING_LOG(m_logger) << "No database given...";
return false;
}
QFileInfo fi(sqliteDb);
if (!fi.exists() || !fi.isFile() || !fi.isReadable())
{
Q_ASSERT(false);
WARNING_LOG(m_logger) << "Can not access sqlite database '" << fi.absoluteFilePath() << "'";
return false;
}
m_ui->cbPlaybackParts->clear();
m_ui->cbPlaybackParts->blockSignals(true);
for (int i = 0; i < m_replayInformation.size(); ++i)
{
const ReplayInformation &info = m_replayInformation.at(i);
m_ui->cbPlaybackParts->addItem(QStringLiteral("%1: %2 - %3").arg(i+1).arg(DTG::toStringMSec(info.timeSpan.begin, DTG::Long),
DTG::toStringMSec(info.timeSpan.end, DTG::Long)), i);
}
m_ui->cbPlaybackParts->setCurrentIndex(0);
m_ui->cbPlaybackParts->blockSignals(false);
onCbPlaybackPartsChanged(); // creates replayHandler for correct db etc.
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map->isReady())
QTimer::singleShot(0, this, &SituationWidget::onInitDone);
else
connect(map, &SituationMap::Map::mapReady,
this, &SituationWidget::onInitDone, Qt::QueuedConnection);
return true;
}
bool SituationWidget::applyFilterFromReplayConfigFile()
{
// Maybe the Situation topic has own Filter (AesFilter, SymbolAppearance; OperatorMapSettings)
// Read and apply them:
if (m_replayInformation.isEmpty())
{
WARNING_LOG(m_logger) << "No replay information given...";
return false;
}
const QString replayConfigFile = m_replayInformation.first().replayConfigPath; // sitreplay.json
if (replayConfigFile.isEmpty())
{
WARNING_LOG(m_logger) << "No replayConfigFile given...";
return false;
}
QFileInfo fi(replayConfigFile);
if (!fi.exists() || !fi.isFile() || !fi.isReadable())
{
WARNING_LOG(m_logger) << "Can not access replayConfigFile '" << fi.absoluteFilePath() << "'";
return false;
}
QFile f(replayConfigFile);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open replayConfigFile '" << fi.absoluteFilePath() << "': " << f.errorString();
return false;
}
QJsonParseError jError;
const QByteArray ba = f.readAll();
QJsonDocument doc(QJsonDocument::fromJson(ba, &jError));
if (jError.error)
{
WARNING_LOG(m_logger) << "Can not parse json document: "
<< ", replayConfigFile '" << fi.absoluteFilePath() << "'"
<< ", errorString= " << jError.errorString();
return false;
}
const QJsonObject &rootObj = doc.object();
const QJsonObject &filters = rootObj[QStringLiteral("filters")].toObject(); // optional!!!
if (filters.isEmpty())
{
WARNING_LOG(m_logger) << "no 'filters' provided at "
<< " replayConfigFile '" << fi.absoluteFilePath() << "'";
return false;
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " XXXX replayConfigFile='" << fi.absoluteFilePath() << "'"
<< " doc.toJson = " << doc.toJson();
// fine, put together
FilterInformation filterInfo;
filterInfo.aesFilters = filters[QStringLiteral("AesFilterJson")].toObject();
filterInfo.symbolSettings = filters[QStringLiteral("SymbolAppearanceJson")].toObject();
filterInfo.buildingComponentSettings = filters[QStringLiteral("BuildingComponentSettingsJson")].toObject();
filterInfo.mapSettings = rootObj[QStringLiteral("settings")].toObject();
applyCurentFilter(filterInfo);
return true;
}
void SituationWidget::applyCurentFilter(const SituationReplay::FilterInformation &filter)
{
m_aesDisplayFilter->setFilterFromData(filter.aesFilters);
m_symbolAppaearanceSettings->setSettings(filter.symbolSettings, FilterManagement::SymbolAppearanceSettings::Part::All);
m_buildingComponentSettings->setBldCmpSettings(filter.buildingComponentSettings);
SituationMap::OperatorMapSettings oms;
oms.fromJsonObject(filter.mapSettings);
if (oms.isEmpty() || oms.mode == SituationMap::Mode::Undefined)
return;
if (oms.mode != m_situationMapInit->getWidget()->mode())
{
INFO_LOG(m_logger) << " Switching to new mode : " << oms.mode;
SituationMap::MapSettingsJson savedMapSettingsJson;
if (m_situationMapInit)
{
savedMapSettingsJson = m_situationMapInit->getMapSettingsJson(); // containing path from layerMappping.json
m_situationMapInit->stopDisplay();
m_situationMapInit->clearObjects();
}
delete m_situationMapInit;
m_situationMapInit = new MySitMapIinit(nullptr);
m_situationMapInit->setMapSettingsJson(savedMapSettingsJson);
m_situationMapInit->initPluginProvider(m_pluginProvider);
m_situationMapInit->initMap(false, oms.mode);
QPointer<SituationMap::MovementTrackManager> movementTrackMgr =
QPointer<SituationMap::MovementTrackManager>(new SituationMap::MovementTrackManager(m_situationMapInit));
if (!m_situationMapInit->initAdapter(movementTrackMgr, false))
return;
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map)
{
connect(map, &SituationMap::Map::mouseGeoPositionChanged,
this, &SituationWidget::updatePositionLabel,
Qt::UniqueConnection);
connect(map, &SituationMap::Map::extentChanged,
this, &SituationWidget::updateZoomSlider);
map->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->vLayout->insertWidget(0, map);
static bool s_waitFlag = true;
QPointer<SituationMap::MapNotifier> mapNotifier = map->createMapNotifier();
if (mapNotifier)
{
connect(mapNotifier.data(), &SituationMap::MapNotifier::mapReadyAndSymbolDictionaryLoaded, this, [this, oms]()
{
s_waitFlag = false;
if (!restoreOperatorMapSettingsOverlays(oms))
WARNING_LOG(m_logger) << "restoreOperatorMapSettingsOverlays failed";
});
mapNotifier->run();
// snyc wait :-(
{
DEBUG_LOG(m_logger) << " WAIT_SYNC BEGIN";
int i=0;
while(i<100 && s_waitFlag) // 100 * 50ms = 5000ms
{
qApp->processEvents(QEventLoop::ExcludeUserInputEvents|QEventLoop::ExcludeSocketNotifiers, 50);
QThread::msleep(50);
++i;
DEBUG_LOG(m_logger) << " WAIT_SYNC ..., i=" << i;
}
DEBUG_LOG(m_logger) << " WAIT_SYNC ...DONE, i=" << i;
DEBUG_LOG(m_logger) << " WAIT_SYNC, m_situationMapInit->getWidget()->mode()" << m_situationMapInit->getWidget()->mode();
m_situationMapInit->startDisplay();
}
} // if (mapNotifier)
}
} else {
if (!restoreOperatorMapSettingsOverlays(oms))
WARNING_LOG(m_logger) << "restoreOperatorMapSettingsOverlays failed";
}
}
bool SituationWidget::restoreOperatorMapSettingsOverlays(const SituationMap::OperatorMapSettings &oms)
{
if (!m_situationMapInit || !m_situationMapInit->getWidget() || oms.isEmpty())
return false;
if (!m_situationMapInit->getWidget()->restoreOperatorMapSettings(oms))
WARNING_LOG(m_logger) << LOGFUNCTION << " restoreOperatorMapSettings failed";
// continue!!! maybe just LayerOrder failed.... ToDo
// 2. handle Overlays :
// SituationMap can`t restore the overlays on it´s own:
// So we have to do it here:
const QList<int> &orderedOverlayIds = oms.orderedOverlays;
DEBUG_LOG(m_logger) << LOGFUNCTION << " restore OverlayIds...";
for (auto overlayId : qAsConst(orderedOverlayIds))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " applyNewOverlay , overlayId=" << overlayId;
applyNewOverlay(overlayId);
}
return true;
}
void SituationWidget::applyNewOverlay(int overlayId)
{
const QString filename = QStringLiteral("%1/%2.TacticalPlan").arg(m_scriptPath).arg(overlayId);
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open tactical plan '" << filename << "': " << f.errorString();
return;
}
SituationMap::OverlayHandler &overlayHandler = m_situationMapInit->getWidget()->overlayHandler();
if (!overlayHandler.setOverlay(overlayId, tr("Plan '%1'").arg(overlayId), f.readAll(), false))
WARNING_LOG(m_logger) << "can not display overlay " << filename;
}
void SituationWidget::removeOverlay(int overlayId)
{
SituationMap::OverlayHandler &overlayHandler = m_situationMapInit->getWidget()->overlayHandler();
overlayHandler.removeOverlay(overlayId);
}
void SituationWidget::onFilterAes()
{
if (!m_filterAesDialog)
m_filterAesDialog = new SituatationFilterAes(m_aesDisplayFilter,
m_pluginProvider, this);
m_filterAesDialog->show();
}
void SituationWidget::onFilterSymbolAppearance()
{
if (!m_filterSymbolAppearanceDialog)
m_filterSymbolAppearanceDialog = new SituatationFilterSymbolAppearance(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterSymbolAppearanceDialog->show();
}
void SituationWidget::onFilterCollapseExpand()
{
if (!m_filterCollapseExpandDialog)
m_filterCollapseExpandDialog = new SituatationFilterCollapseExpand(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterCollapseExpandDialog->show();
}
void SituationWidget::onFilterTypes()
{
if (!m_filterTypesDialog)
m_filterTypesDialog = new SituatationFilterTypes(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterTypesDialog->show();
}
void SituationWidget::onFilterBuildingComponent()
{
if (!m_filterBuildingComponentDialog)
m_filterBuildingComponentDialog = new SituationFilterBuildingComponent(m_buildingComponentSettings,
m_pluginProvider, this);
m_filterBuildingComponentDialog->show();
}
void SituationWidget::emitPartitionTimeChanged(qint64 curTime)
{
m_pluginProvider->emitPartitionTimeChanged(curTime);
}
void SituationWidget::updateZoomSlider()
{
// this is not really what we want to display... but currently we hide the slider anyway
m_ui->zoomSlider->setZoomValue(m_situationMapInit->getWidget()->scale() / 1000);
}
QString SituationWidget::centerPointString() const
{
const QPointF center = m_situationMapInit->getWidget()->centerPoint();
LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(center.x(), center.y()));
return coord.mgrs();
}
QRectF SituationWidget::extentMgrs() const
{
const QRectF r = m_situationMapInit->getWidget()->extent();
LtsCoordTrans::UtmCoordinate topLeft(LtsCoordTrans::UtmCoordinate::fromGeographic(r.left(), r.top()));
LtsCoordTrans::UtmCoordinate bottomRight(LtsCoordTrans::UtmCoordinate::fromGeographic(r.right(), r.bottom(), topLeft.zone));
return QRectF(QPointF(topLeft.x, topLeft.y), QPointF(bottomRight.x, bottomRight.y));
}
QImage SituationWidget::grabFrameBuffer() const
{
return m_situationMapInit->getWidget()->grab().toImage();
}
void SituationWidget::onInitDone()
{
SituationBuildingBase::onInitDone();
m_situationMapInit->startDisplay();
}
void SituationWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_F10:
onMgrsGrid();
break;
case Qt::Key_PageUp:
onRotate(10);
break;
case Qt::Key_PageDown:
onRotate(-10);
break;
default:
QFrame::keyPressEvent(event);
break;
}
}
void SituationWidget::onMgrsGrid()
{
SituationMap::Map *m = m_situationMapInit->getWidget();
m->grid().setVisible(!m->grid().isVisible());
}
void SituationWidget::onRotate(int degrees)
{
SituationMap::Map *m = m_situationMapInit->getWidget();
m->setRotation(m->rotation() + degrees);
}
void SituationWidget::onFileContentAvailable(const FileManagement::Database::FlmFileContent &data)
{
if (data.fileTypeId != FileManagement::FileType::OperatorDefinedSymbol ||
data.fileStatusId != FileManagement::FileStatus::Valid ||
data.content.size() == 0)
return;
QColor colorToBeReplaced;
bool bIsXml = false;
if (!data.properties.isEmpty())
{
QJsonParseError parseError;
const QJsonDocument jDoc(QJsonDocument::fromJson(data.properties.toUtf8(), &parseError));
const QJsonObject obj(jDoc.object());
const QLatin1String imageFormatKey("imageFormat");
bIsXml = obj.value(imageFormatKey).toString() == QLatin1String("xml");
if (!bIsXml)
{
// partyColor
const QLatin1String partyColorStr("partyColor");
if (obj.contains(partyColorStr))
{
const QJsonObject partyColor = obj[partyColorStr].toObject();
if (partyColor.size() == 3) // r, g, b
{
// update map:
colorToBeReplaced.setRgb(
partyColor[QLatin1String("r")].toInt(),
partyColor[QLatin1String("g")].toInt(),
partyColor[QLatin1String("b")].toInt());
} // if (partyColor.size() == 3)
} // if (obj.contains(partyColorStr))
}
}
AdvancedSymbology::SymbologyType symType = AdvancedSymbology::SymbolDictionary::defaultSymbologyType();
AdvancedSymbology::SymbolDictionary &dict =
AdvancedSymbology::SymbolDictionary::dictionary(symType);
if (bIsXml)
{
dict.setXmlSymbol(data.m_id, data.name, QStringLiteral("OperatorName"), 0, QString::fromUtf8(data.content), AdvancedSymbology::SymbolProperties());
}
else
{
QImage image;
QSvgRenderer svgRenderer;
if (!svgRenderer.load(data.content))
return;
// prepare Image:
constexpr int size = 200; // pixel
QImage imageOut(size, size, QImage::Format_ARGB32);
imageOut.fill(Qt::transparent);
// render
QPainter painter(&imageOut);
svgRenderer.render(&painter);
painter.end();
AdvancedSymbology::SymbolProperties symProps;
symProps.setKey(data.name);
symProps.setSidc(AdvancedSymbology::SidcCodec::encodeCivilEts(data.name));
symProps.setName(data.name);
symProps.setSymbolClass(QLatin1String("3")); // 18.10.2016, N.Thoden: 3 == PointSymbol (mil2525d.stylx - QuartzRelease)
symProps.setGeometryType(AdvancedSymbology::GeometryType::Point); // ETS-Symbols are always Points
symProps.setTags({data.name});
symProps.setColorToBeReplaced(colorToBeReplaced);
dict.setEtsSymbol(data.m_id, data.name, symProps, image);
}
}
void SituationWidget::loadLayerMappings(const QString &homePath)
{
const QString mappingsFile = homePath + QStringLiteral("/layerMapping.json");
QFile f(mappingsFile);
if (!f.open(QIODevice::ReadOnly))
{
fprintf(stderr, "Could not load layer mappings from '%s' - %s\n", mappingsFile.toStdString().c_str(), f.errorString().toStdString().c_str());
WARNING_LOG(m_logger) << "Could not load layer mappings from " << mappingsFile << ": " << f.errorString();
return;
}
m_layerMapping.clear();
const QJsonObject obj = QJsonDocument::fromJson(f.readAll()).object();
SituationMap::MapSettingsJson settings;
if (!settings.fromJsonObject(obj))
{
WARNING_LOG(m_logger) << "Could not parse layer mappings from " << mappingsFile;
return;
}
settings.setMapUrls(fixupUrls(settings.getMapUrls(), homePath));
settings.setSceneUrls(fixupUrls(settings.getSceneUrls(), homePath));
settings.setSceneObjectUrls(fixupUrls(settings.getSceneObjectUrls(), homePath));
settings.setElevationUrls(fixupUrls(settings.getElevationUrls(), homePath));
m_situationMapInit->setMapSettingsJson(settings);
}
QVector<SituationMap::BaseUrl> SituationWidget::fixupUrls(const QVector<SituationMap::BaseUrl> &in,
const QString &homePath)
{
QVector<SituationMap::BaseUrl> out;
for (auto url : in)
{
const QString fn = QStringLiteral("%1/Maps/%2").arg(homePath, url.url());
if (!QFile::exists(fn))
{
fprintf(stderr, "Can not find map data file '%s'\n", fn.toStdString().c_str());
QMessageBox::warning(this, tr("Can not find map data"),
tr("Can not find map data file '%1' - can't start application").arg(fn));
QCoreApplication::quit();
return out;
}
url.setUrl(QUrl::fromLocalFile(fn).toString());
out.push_back(std::move(url));
}
return out;
}
} // namespace THP
#include "thp_situation_widget.moc"
@@ -0,0 +1,106 @@
/**
* @RDE_HEADER@
* Handle situation display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <SituationReplay/main_handler_sqlite.h>
#include "thp_situation_building_base.h"
namespace SituationReplay
{
class HandlerSituationReplaySqlite;
}
namespace SituationMap
{
class BaseUrl;
struct OperatorMapSettings;
}
namespace FilterManagement
{
class AesDisplayFilter;
class SymbolAppearanceSettings;
class BuildingComponentSettings;
}
namespace Ui
{
class SituationWidget;
}
namespace THP
{
class SitMapPluginProvider;
class SituatationFilterAes;
class SituatationFilterSymbolAppearance;
class SituatationFilterCollapseExpand;
class SituatationFilterTypes;
class SituationFilterBuildingComponent;
class MySitMapIinit;
class SituationWidget : public SituationBuildingBase
{
Q_OBJECT
public:
explicit SituationWidget(QWidget *parent = nullptr);
~SituationWidget();
bool init(const QString &homePath, int exerciseId);
SituationReplay::MainHandlerSqlite *replayHandler() { return m_replayHandler; }
SitMapPluginProvider *pluginProvider() { return m_pluginProvider; }
QImage grabFrameBuffer() const;
QString centerPointString() const;
QRectF extentMgrs() const; // mgrs
protected:
bool showSituationInternal() override;
bool applyFilterFromReplayConfigFile();
void keyPressEvent(QKeyEvent *event) override;
void loadLayerMappings(const QString &homePath);
QVector<SituationMap::BaseUrl> fixupUrls(const QVector<SituationMap::BaseUrl> &in,
const QString &homePath);
bool restoreOperatorMapSettingsOverlays(const SituationMap::OperatorMapSettings &oms);
private Q_SLOTS:
void applyCurentFilter(const SituationReplay::FilterInformation &filter) override;
void applyNewOverlay(int overlayId) override;
void removeOverlay(int overlayId) override;
void onFilterAes();
void onFilterSymbolAppearance();
void onFilterCollapseExpand();
void onFilterTypes();
void onFilterBuildingComponent();
void updateZoomSlider() override;
void onInitDone() override;
void emitPartitionTimeChanged(qint64 curTime) override;
void onMgrsGrid();
void onRotate(int degrees);
void onFileContentAvailable(const FileManagement::Database::FlmFileContent &data) override;
private:
QVector<QPair<QString, QString>> m_layerMapping;
SitMapPluginProvider *m_pluginProvider{nullptr};
MySitMapIinit *m_situationMapInit{nullptr};
SituatationFilterAes *m_filterAesDialog{nullptr};
SituatationFilterSymbolAppearance *m_filterSymbolAppearanceDialog{nullptr};
SituatationFilterCollapseExpand *m_filterCollapseExpandDialog{nullptr};
SituatationFilterTypes *m_filterTypesDialog{nullptr};
SituationFilterBuildingComponent *m_filterBuildingComponentDialog{nullptr};
std::shared_ptr<FilterManagement::AesDisplayFilter> m_aesDisplayFilter;
std::shared_ptr<FilterManagement::SymbolAppearanceSettings> m_symbolAppaearanceSettings;
std::shared_ptr<FilterManagement::BuildingComponentSettings> m_buildingComponentSettings;
};
} // namespace THP
@@ -0,0 +1,237 @@
/**
* @RDE_HEADER@
* A special slider for zooming
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_zoom_slider.h"
#include <QLabel>
#include <QSlider>
#include <QHBoxLayout>
#include <QVBoxLayout>
namespace THP
{
ZoomSlider::ZoomSlider(QWidget *parent)
: QWidget(parent)
, m_InternalMin(0.1)
, m_InternalMax(100)
, m_LowerThreshold(1)
, m_UpperThreshold(10)
, m_MinRange(0)
, m_MaxRange(280)
, m_Slider(nullptr)
, m_lbl(nullptr)
, m_text(nullptr)
, m_ZoomValue(-1)
, m_curSliderVal(-1)
{
setShowDescriptionLabel(true);
setShowValueLabel(true);
m_Slider = new QSlider(this);
connect(m_Slider, &QSlider::valueChanged, this, &ZoomSlider::calcZoomFromSlider);
connect(this, &ZoomSlider::valueChanged, this, &ZoomSlider::updateLabel);
m_Slider->setRange(m_MinRange, m_MaxRange);
m_Slider->setSingleStep(1);
m_Slider->setPageStep(1);
setOrientation(Qt::Horizontal);
//Initial calculation
calcZoomFromSlider(m_Slider->value());
}
void ZoomSlider::setZoomValue(double zoomValue)
{
if (qFuzzyCompare(m_ZoomValue, zoomValue))
return;
//ignore values outside Min/Max boundaries.
if (zoomValue < m_InternalMin || zoomValue > m_InternalMax)
return;
m_ZoomValue = zoomValue;
if (zoomValue < m_LowerThreshold)
{
//Range is 0..50%
double l_Range = (m_MaxRange - m_MinRange) / 2;
double l_ValToSet = l_Range / static_cast<double>(m_LowerThreshold - m_InternalMin) * (zoomValue - m_InternalMin);
//qDebug() << "Value1 = " << l_ValToSet;
m_curSliderVal = static_cast<int>(l_ValToSet);
}
else if (zoomValue < m_UpperThreshold)
{
//Range is 50%..75%
double l_Range = (m_MaxRange - m_MinRange) / 4;
double l_ValToSet = l_Range / static_cast<double>(m_UpperThreshold - m_LowerThreshold) * (zoomValue - m_LowerThreshold);
l_ValToSet += l_Range * 2;
//qDebug() << "Value2 = " << l_ValToSet;
m_curSliderVal = static_cast<int>(l_ValToSet);
}
else
{
//Range is 75%..100%
double l_Range = (m_MaxRange - m_MinRange) / 4;
double l_ValToSet = l_Range / static_cast<double>(m_InternalMax - m_UpperThreshold) * (zoomValue - m_UpperThreshold);
l_ValToSet += l_Range * 3;
//qDebug() << "Value3 = " << l_ValToSet;
m_curSliderVal = static_cast<int>(l_ValToSet);
}
m_Slider->setValue(m_curSliderVal);
updateLabel(m_ZoomValue);
}
double ZoomSlider::getZoomValue() const
{
return m_ZoomValue;
}
void ZoomSlider::setOrientation(Qt::Orientation a_orientation)
{
if (!m_Slider)
return;
m_Slider->setOrientation(a_orientation);
QBoxLayout *l_Layout;
if (a_orientation == Qt::Horizontal)
{
m_Slider->setMinimumWidth(40);
m_Slider->setMinimumHeight(0);
l_Layout = new QHBoxLayout;
}
else
{
m_Slider->setMinimumWidth(0);
m_Slider->setMinimumHeight(40);
l_Layout = new QVBoxLayout;
}
l_Layout->setContentsMargins(0, 0, 0, 0);
if (m_text)
l_Layout->addWidget(m_text);
if (m_Slider)
l_Layout->addWidget(m_Slider);
if (m_lbl)
l_Layout->addWidget(m_lbl);
delete layout();
setLayout(l_Layout);
}
Qt::Orientation ZoomSlider::getOrientation() const
{
if (m_Slider)
return m_Slider->orientation();
return Qt::Horizontal;
}
bool ZoomSlider::getShowDescriptionLabel() const
{
return (m_text != nullptr);
}
void ZoomSlider::setShowDescriptionLabel(bool bVisible)
{
if (getShowDescriptionLabel() == bVisible)
return;
if (bVisible)
{
m_text = new QLabel(this);
m_text->setText(tr("Zoom:"));
}
else
{
delete m_text;
m_text = nullptr;
}
setOrientation(getOrientation());
}
bool ZoomSlider::getShowValueLabel() const
{
return (m_lbl != nullptr);
}
void ZoomSlider::setShowValueLabel(bool bVisible)
{
if (getShowValueLabel() == bVisible)
return;
if (bVisible)
m_lbl = new QLabel(this);
else
{
delete m_lbl;
m_lbl = nullptr;
}
setOrientation(getOrientation());
updateLabel(m_ZoomValue);
}
void ZoomSlider::calcZoomFromSlider(int a_value)
{
if (m_curSliderVal == a_value)
return;
m_curSliderVal = a_value;
if (a_value < (m_MaxRange - m_MinRange) / 2)
{
//Range 0% - 50%
double l_R1 = (m_MaxRange - m_MinRange) / 2;
double l_R2 = (m_LowerThreshold - m_InternalMin);
double l_ValToSet = l_R2 / l_R1 * a_value;
l_ValToSet += m_InternalMin;
//qDebug() << "Value4 = " << l_ValToSet;
m_ZoomValue = l_ValToSet;
}
else if (a_value < 3 * (m_MaxRange - m_MinRange) / 4)
{
//Range 50% - 75%
double l_R1 = (m_MaxRange - m_MinRange) / 4;
double l_R2 = (m_UpperThreshold - m_LowerThreshold);
double l_SliderVal = a_value - (m_MaxRange - m_MinRange) / 2;
double l_ValToSet = l_R2 / l_R1 * l_SliderVal;
l_ValToSet += static_cast<double>(m_LowerThreshold);
//qDebug() << "Value5 = " << l_ValToSet;
m_ZoomValue = l_ValToSet;
}
else
{
//Range 75% - 100%
double l_R1 = (m_MaxRange - m_MinRange) / 4;
double l_R2 = (m_InternalMax - m_UpperThreshold);
double l_SliderVal = a_value - (m_MaxRange - m_MinRange) / 4 * 3;
double l_ValToSet = l_R2 / l_R1 * l_SliderVal;
l_ValToSet += static_cast<double>(m_UpperThreshold);
//qDebug() << "Value6 = " << l_ValToSet;
m_ZoomValue = l_ValToSet;
}
emit valueChanged(m_ZoomValue);
}
void ZoomSlider::updateLabel(double newValue)
{
if (!m_lbl)
return;
m_lbl->setText(formatValue(newValue));
}
QString ZoomSlider::formatValue(double value) const
{
if (value < m_LowerThreshold)
return QStringLiteral("%1x").arg(value, 4, 'f', 2);
if (value < m_UpperThreshold)
return QStringLiteral("%1x").arg(value, 4, 'f', 1);
return QStringLiteral("%1x").arg(value, 4, 'f', 0);
}
} // namespace THP
@@ -0,0 +1,152 @@
/**
* @RDE_HEADER@
* A special slider for zooming
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QWidget>
class QLabel;
class QSlider;
namespace THP
{
/**
* An specialized QSlider
* This slider has a special, fixed range from 0.1 - 100 which is mapped to
* suitable map scales used in the GUEZ.
* The scale is not linear - instead it has a model behind. in smaller scales -
* slider's going to 0.1 - it's resolution is finer than on larger scales -
* slider's going to 100.
* The slider accpets only values within the range 0.1....100. Other values are ignored.
*/
class ZoomSlider : public QWidget
{
Q_OBJECT
Q_PROPERTY(Qt::Orientation Orientation READ getOrientation WRITE setOrientation)
Q_PROPERTY(bool showDescriptionLabel READ getShowDescriptionLabel WRITE setShowDescriptionLabel)
Q_PROPERTY(bool showValueLabel READ getShowValueLabel WRITE setShowValueLabel)
public:
/**
* constructor
*/
explicit ZoomSlider(QWidget *parent = nullptr);
/**
* get the actual Zoom value.
* @return float with the zoom value
*/
double getZoomValue() const;
/**
* Sets the Orientation.
* @param a_orientation The slider's orientation.
*/
void setOrientation(Qt::Orientation a_orientation);
/**
*
*/
Qt::Orientation getOrientation() const;
bool getShowDescriptionLabel() const;
void setShowDescriptionLabel(bool bVisible);
bool getShowValueLabel() const;
void setShowValueLabel(bool bVisible);
/**
* format the given value as it would be shown in the zoom label
*/
QString formatValue(double value) const;
public Q_SLOTS:
/**
* set the slider's actual zoom value. This is a QT-Slot.
* The slider accpet only values within it's range 0.1...100.
* Other values are ignored.
* @param a_zoomValue the new zoom value.
*/
void setZoomValue(double zoomValue);
Q_SIGNALS:
/**
* A QT-Signal which is emitted whenever the slider's value has changed.
* The newly set zoom value is delivered as argument.
*/
void valueChanged(double a_value);
private Q_SLOTS:
/**
* Update the label text
*/
void updateLabel(double newValue);
private:
/**
* Contains the slider's minimum zoom value
*/
const double m_InternalMin;
/**
* Contains the slider's maximum zoom value
*/
const double m_InternalMax;
/**
* Contains the slider's lower threshold value.
* The lower threshold is at 50% of the sliders range.
*/
const int m_LowerThreshold;
/**
* Contains the slider's upper threshold value.
* The upper threshold is at 75% of the sliders range.
*/
const int m_UpperThreshold;
/**
* Contains the slider's range minimum value
*/
const int m_MinRange;
/**
* Contains the slider's range maximum value
*/
const int m_MaxRange;
/**
* The QSlider
*/
QSlider *m_Slider;
/**
* The label for showing the exact slider value.
*/
QLabel *m_lbl;
/**
* The label for showing the 'zoom' text
*/
QLabel *m_text;
/**
* contains the zoom value
*/
double m_ZoomValue;
/**
* The current value of the slider to avoid loops
*/
int m_curSliderVal;
private slots :
/**
* Calculates the zoom value from the slider's position
*/
void calcZoomFromSlider(int a_value);
};
} // namespace THP