diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/CMakeLists.txt new file mode 100755 index 000000000..83a19ecab --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/CMakeLists.txt @@ -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) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aarApp.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aarApp.ui new file mode 100755 index 000000000..185a56721 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aarApp.ui @@ -0,0 +1,162 @@ + + + AarApplication + + + Qt::WindowModal + + + + 0 + 0 + 846 + 382 + + + + Database administration + + + + + + Qt::ToolButtonIconOnly + + + QTabWidget::Rounded + + + + + + + + + Status: + + + + + + + + 75 + true + + + + No active database + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Databases: + + + + + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Activate network database + + + + + + + Activate local database + + + + + + + Remove database + + + + + + + Close + + + + + + + + + + &Show + + + + + &Start + + + + + St&op + + + + + + + + oeffnenClicked() + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.cpp new file mode 100755 index 000000000..ec846ca95 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.cpp @@ -0,0 +1,501 @@ +/* + * @RDE_HEADER@ + * + * $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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 &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::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::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::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::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 l = findChildren(); + for (QPushButton *pb : l) + pb->setEnabled(bEnable); + } + { + const QList l = findChildren(); + for (QAction *a : l) + a->setEnabled(bEnable); + } + if (bEnable) + enaDisa(); +} + +} // namespace AarApplication diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.h b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.h new file mode 100755 index 000000000..e6f57af64 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/aar_application.h @@ -0,0 +1,78 @@ +/* + * @RDE_HEADER@ + * + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Malte Schröder, SLSE, 2016-06-08 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 m_dbMap; + bool m_stopping; + Exercise m_databaseToRemove; + QProcess *m_proc; + QSqlDatabase m_dbPostgres; + QProgressDialog *m_progressDialog; + QVector m_widgets; + + LOGGER_T m_logger; +}; +} // namespace AARApplication diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.cpp new file mode 100755 index 000000000..72aebf169 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.cpp @@ -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 +#include + +#include +#include + +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(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(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.h b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.h new file mode 100755 index 000000000..310e79aa5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/databases_model.h @@ -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 + +#include +#include + + +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 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; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/log4cxx_aarApp.props b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/log4cxx_aarApp.props new file mode 100755 index 000000000..4121f7803 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/log4cxx_aarApp.props @@ -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: diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.cpp new file mode 100755 index 000000000..8555f4448 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.cpp @@ -0,0 +1,37 @@ +/** + * @RDE_HEADER@ + * Building Config - logging facility + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "logger.h" + +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.h b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.h new file mode 100755 index 000000000..e09954e43 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/logger.h @@ -0,0 +1,23 @@ +/** + * @RDE_HEADER@ + * Logging for Evaluations + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include + + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/main.cpp new file mode 100755 index 000000000..2db9648f4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/AarApp/main.cpp @@ -0,0 +1,80 @@ +/* + * @RDE_HEADER@ + * + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Malte Schröder, SLSE, 2016-06-08 + */ + +#include "aar_application.h" +#include +#include +#include +#include +#include + +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("") + }); + } + 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 app; + app.reset(bHeadless ? dynamic_cast(new NGLCoreApplication(argc, argv)) + : dynamic_cast(new NGLApplication(argc, argv))); + QCoreApplication::setApplicationName(QLatin1String("AarApplication")); + QCoreApplication::setOrganizationName(QLatin1String("RME")); + + if (!bHeadless) + app->setAttribute(Qt::AA_UseHighDpiPixmaps); + + + NGL::LoggerHandler *lh = dynamic_cast(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; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/CMakeLists.txt new file mode 100755 index 000000000..91294f50c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +ADD_SUBDIRECTORY(AarApp) +ADD_SUBDIRECTORY(OpApp) +ADD_SUBDIRECTORY(THP) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/CMakeLists.txt new file mode 100755 index 000000000..1fc717a67 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/CMakeLists.txt @@ -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") diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/log4cxx_opapp.props b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/log4cxx_opapp.props new file mode 100755 index 000000000..78aab386a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/log4cxx_opapp.props @@ -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 +# diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/main.cpp new file mode 100755 index 000000000..15d1843a9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/main.cpp @@ -0,0 +1,523 @@ +/** + * @RDE_HEADER@ + * main for OperatorApplication + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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: )"), 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("") + }); + + 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; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/opapp.rc.cmake b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/opapp.rc.cmake new file mode 100755 index 000000000..01c63c9cf --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/OpApp/opapp.rc.cmake @@ -0,0 +1,30 @@ +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/CMakeLists.txt new file mode 100755 index 000000000..a1fa02a8d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/CMakeLists.txt @@ -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) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/README.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/README.txt new file mode 100755 index 000000000..56388c104 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/README.txt @@ -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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/CMakeLists.txt new file mode 100755 index 000000000..4af2ca45a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/CMakeLists.txt @@ -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 +) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main.cpp new file mode 100755 index 000000000..34268f7a8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main.cpp @@ -0,0 +1,64 @@ +/** + * @RDE_HEADER@ + * Main for SitMapReplayTest + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Christian Ehrlicher, SLSE, 2016-02-29 + */ + +#include +#include +#include +#include +#include +#include +#include +#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 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 ddsConn = srpMainHandler->ddsReplay(); + THP::SitMapPluginProvider *pp = new THP::SitMapPluginProvider; + pp->init(ddsConn); + + SituationMap::SituationMapInit *smi = new SituationMap::SituationMapInit(nullptr); + smi->initPluginProvider(pp); + + QPointer movementTrackMgr = + QPointer(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; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.cpp new file mode 100755 index 000000000..9b98756d3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.cpp @@ -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 +#include + +namespace SituationReplay +{ +MainWindow::MainWindow(SituationMap::SituationMapInit *sitMap, + const std::shared_ptr &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(); +} +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.h new file mode 100755 index 000000000..b653e0b6a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.h @@ -0,0 +1,41 @@ +/** + * @RDE_HEADER@ + * MainWindow for SitMapReplayTest + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Christian Ehrlicher, SLSE, 2016-02-29 + */ +#pragma once + +#include +#include + +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 &srpHandler, + QWidget *parent = nullptr); + ~MainWindow(); +protected slots: + void onStartReplay(); + void onStopReplay(); +private: + std::shared_ptr m_srpHandler; + Ui::SrpMainWindow *m_ui; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.ui new file mode 100755 index 000000000..aa92e573a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/SitMapReplayTest/srp_main_window.ui @@ -0,0 +1,49 @@ + + + SrpMainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 29 + + + + + Fi&le + + + + + + + + + + Start Replay + + + + + Stop Replay + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/CMakeLists.txt new file mode 100755 index 000000000..9d60bb14f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/CMakeLists.txt @@ -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) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.cpp new file mode 100755 index 000000000..9f457ce41 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.cpp @@ -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 +#include +#include + +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); +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.rc.cmake b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.rc.cmake new file mode 100755 index 000000000..bd838c67a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/THP-Starter/thp_starter.rc.cmake @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "@THP_ICO@" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/autorun.inf b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/autorun.inf new file mode 100755 index 000000000..a4a4ec31a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/autorun.inf @@ -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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/log4cxx_thp_viewer.props b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/log4cxx_thp_viewer.props new file mode 100755 index 000000000..f30520ea3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/log4cxx_thp_viewer.props @@ -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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/main.cpp new file mode 100755 index 000000000..50cb9cfe9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/main.cpp @@ -0,0 +1,166 @@ +/** + * @RDE_HEADER@ + * Main entry for thp viewer + * $Id$ + * $HeadURL$ + * $Author$ + */ +#include +#include +#include +#include +#include +#include +#include + +#include "thp_viewer.h" +#include +#include + +#ifdef Q_OS_WIN +# include +#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("") + }); + 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 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; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.cpp new file mode 100755 index 000000000..0d4b73d9c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "thp_situation_filter_interface_impl.h" + +namespace THP +{ + +QString SitMapPluginProvider::toString() const +{ + return QString(); +} + +bool SitMapPluginProvider::init(const std::shared_ptr &ddsConnector) +{ + m_ddsConnector = ddsConnector; + + m_buildingPluginProvider = std::make_shared(); + + 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(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(m_ppmDtc); + m_fltAesIfc = std::make_shared(m_ppmDtc); + m_fltBuildingComponentIfc = std::make_shared(m_ppmDtc); + // m_fltParticipantIfc = m_fltAesIfc; // Not sure, lets try + + return m_buildingPluginProvider->init(ddsConnector, this); +} + + +bool BuildingPluginProvider::init(const std::shared_ptr &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(m_smPluginProvider->ppmDtc(), + m_smPluginProvider->aesDtc(), + m_smPluginProvider->gimDtc()); + + return true; +} + + +std::shared_ptr SitMapPluginProvider::ismDataIfc() const +{ + if (!m_ismForeign) + m_ismForeign = std::make_shared(ismDtc()); + + return m_ismForeign; +} + + +std::shared_ptr SitMapPluginProvider::ismDtc() const +{ + if (!m_ismDtc) + { + m_ismDtc = InfrastructureManagement::Interface::Dtc::create(m_ddsConnector); + m_ismDtc->enableAll(); + } + + return m_ismDtc; +} + +} // namespace THP diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.h new file mode 100755 index 000000000..0226b29de --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_plugin_provider.h @@ -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 +#include +#include +#include +#include +#include + +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); + + 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 aesDtc() const override { return m_aesDtc; } + std::shared_ptr aesInfoIfc() const override { return {}; } + std::shared_ptr almDtc() const override { return {}; } + std::shared_ptr almFtc() const override { return {}; } + std::shared_ptr almInfoIfc() const override { return {}; } + std::shared_ptr asmDtc() const override { return {}; } + std::shared_ptr asmFtc() const override { return {}; } + std::shared_ptr bldDtc() const override { return m_bldDtc; } + std::shared_ptr bldFtc() const override { return {}; } + std::shared_ptr bldInfoIfc() const override { return {}; } + std::shared_ptr cesDtc() const override { return m_cesDtc; } + std::shared_ptr exmDtc() const override { return {}; } + std::shared_ptr exmFtc() const override { return {}; } + std::shared_ptr fltDtc() const override { return {}; } + std::shared_ptr fltFtc() const override { return {}; } + std::shared_ptr fltParticipantIfc() const override { return m_fltParticipantIfc; } + std::shared_ptr fltAesIfc() const override { return m_fltAesIfc; } + std::shared_ptr fltBuildingComponentIfc() const override { return m_fltBuildingComponentIfc; } + std::shared_ptr gimDtc() const override { return m_gimDtc; } + std::shared_ptr gimIfc() const override { return {}; } + std::shared_ptr gnaDtc() const override { return {}; } + std::shared_ptr gnaIfc() const override { return {}; } + std::shared_ptr ismDtc() const override; + std::shared_ptr ismDataIfc() const override; + std::shared_ptr ismGuiIfc() const override { return {}; } + std::shared_ptr opmDtc() const override { return {}; } + std::shared_ptr opmFtc() const override { return {}; } + std::shared_ptr ppmDtc() const override { return m_ppmDtc; } + std::shared_ptr ppmFtc() const override { return {}; } + std::shared_ptr ppmParticipantInfoIfc() const override { return {}; } + std::shared_ptr pdmDtc() const override { return m_pdmDtc; } + std::shared_ptr srpDtc() const override { return m_srpDtc; } + std::shared_ptr ocParticipantInfoIfc() const override { return {}; } + std::shared_ptr smoServiceMonitorIfc() const override { return {}; } + std::shared_ptr tgtDtc() const override { return {}; } + std::shared_ptr cmoDtc() const override { return {}; } + + // for BuildingPluginProvider + BuildingPluginProvider *buildingPluginProvider() { return m_buildingPluginProvider.get(); } + +private: + std::shared_ptr m_aesDtc; + std::shared_ptr m_bldDtc; + std::shared_ptr m_cesDtc; + std::shared_ptr m_fltParticipantIfc; + std::shared_ptr m_fltAesIfc; + std::shared_ptr m_fltBuildingComponentIfc; + std::shared_ptr m_gimDtc; + mutable std::shared_ptr m_ismDtc; + mutable std::shared_ptr m_ismForeign; + std::shared_ptr m_ppmDtc; + std::shared_ptr m_srpDtc; + std::shared_ptr m_pdmDtc; + + qint64 m_curPartitionTime = 0; + int m_exerciseId = 0; + + std::shared_ptr m_buildingPluginProvider; + std::shared_ptr 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, + 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 asbIfc() const override { return m_asbIfc; } + std::shared_ptr avmDataIfc() const override { return nullptr; } // optional + std::shared_ptr bldDtc() const override { return m_smPluginProvider->bldDtc(); } + std::shared_ptr bldInfoIfc() const override { return m_smPluginProvider->bldInfoIfc(); } // optional + std::shared_ptr ismDtc() const override { return m_smPluginProvider->ismDtc(); } + std::shared_ptr ismDataIfc() const override { return m_smPluginProvider->ismDataIfc(); } + std::shared_ptr aesDtc() const override { return m_smPluginProvider->aesDtc(); } // optional + std::shared_ptr ppmDtc() const override { return m_smPluginProvider->ppmDtc(); } // optional + std::shared_ptr pdmDtc() const override { return m_smPluginProvider->pdmDtc(); } // optional + std::shared_ptr srpDtc() const override { return m_smPluginProvider->srpDtc(); } // optional + std::shared_ptr ppmParticipantInfoIfc() const override { return m_smPluginProvider->ppmParticipantInfoIfc(); } // optional + +private: + SitMapPluginProvider *m_smPluginProvider{nullptr}; + std::shared_ptr m_asbIfc; + LtsSession::DdsConnectionParams m_connectionParams; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.cpp new file mode 100755 index 000000000..8154bbeb4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.cpp @@ -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 +#include +#include + +namespace FilterManagement +{ +ThpFilterInterfaceImpl::ThpFilterInterfaceImpl(const std::shared_ptr &dtcPpm) + : m_fltSymbols(std::make_shared(dtcPpm)) + , m_fltAes(std::make_shared()) + , m_fltBuildingComponentSettings(std::make_shared()) +{ + m_fltAes->setVisiblePartiesCheck([](int) -> bool { return true; }); +} + +std::shared_ptr ThpFilterInterfaceImpl::symbolAppearanceForOperator(int operatorId, const QString &context) +{ + Q_UNUSED(operatorId); + Q_UNUSED(context); + return m_fltSymbols; +} + +std::shared_ptr ThpFilterInterfaceImpl::aesFilterForOperator(int operatorId, const QString &context) +{ + Q_UNUSED(operatorId); + Q_UNUSED(context); + return m_fltAes; +} + +std::shared_ptr ThpFilterInterfaceImpl::buildingComponentSettingsForOperator(int operatorId, const QString &context) +{ + Q_UNUSED(operatorId); + Q_UNUSED(context); + return m_fltBuildingComponentSettings; + +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.h new file mode 100755 index 000000000..d82e6fda4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_situation_filter_interface_impl.h @@ -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 +#include +#include +#include + +namespace FilterManagement +{ +class ThpFilterInterfaceImpl : public ForeignFltParticipantGuiInterface, + public ForeignAesGuiInterface, + public ForeignFltBuildingComponentGuiInterface +{ +public: + explicit ThpFilterInterfaceImpl(const std::shared_ptr &dtcPpm); + + // from Participant + std::shared_ptr symbolAppearanceForOperator(int operatorId, const QString &context) override; + + // from Aes + std::shared_ptr aesFilterForOperator(int operatorId, const QString &context) override; + + // from BuildingComponent + std::shared_ptr buildingComponentSettingsForOperator(int operatorId, const QString &context) override; + + +private: + std::shared_ptr m_fltSymbols; + std::shared_ptr m_fltAes; + std::shared_ptr m_fltBuildingComponentSettings; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.cpp new file mode 100755 index 000000000..6a90c528e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 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 /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 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 ThpViewer::getCombinedPlaybackDetails(const QUrl &url) const +{ + QHash 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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.h new file mode 100755 index 000000000..e8e60a811 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.h @@ -0,0 +1,60 @@ +/** + * @RDE_HEADER@ + * Main thp viewer widget + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#include + +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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.rc.cmake b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.rc.cmake new file mode 100755 index 000000000..481c71151 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.rc.cmake @@ -0,0 +1,30 @@ +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.ui new file mode 100755 index 000000000..a8e3788d9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thp_viewer.ui @@ -0,0 +1,216 @@ + + + thpViewer + + + + 0 + 0 + 793 + 675 + + + + + 793 + 0 + + + + MainWindow + + + + + + + + + + + + + + :icons/16x16/go-previous.png:icons/16x16/go-previous.png + + + + + + + + + + + :icons/16x16/view-fullscreen.png:icons/16x16/view-fullscreen.png + + + F11 + + + + + + + + + + + :icons/16x16/go-home.png:icons/16x16/go-home.png + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + THP-Viewer + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + + + :icons/16x16/document-print.png:icons/16x16/document-print.png + + + + + + + + + + + :icons/16x16/system-help.png:icons/16x16/system-help.png + + + + + + + + + + + :icons/16x16/dialog-close.png:icons/16x16/dialog-close.png + + + + + + + + + + + Qt::Vertical + + + + Qt::Horizontal + + + + + 0 + 0 + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + THP::AudioVideoWidget + QWidget +
thpwidgets/thp_audio_video_widget.h
+ 1 +
+ + THP::PdfWidget + QFrame +
thpwidgets/thp_pdf_widget.h
+ 1 +
+ + THP::HtmlWidget + QFrame +
thpwidgets/thp_html_widget.h
+ 1 +
+ + THP::SituationWidget + QFrame +
thpwidgets/thp_situation_widget.h
+ 1 +
+ + THP::BuildingWidget + QFrame +
thpwidgets/thp_building_widget.h
+ 1 +
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.cpp new file mode 100755 index 000000000..76a761d94 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +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 fileMap; + QByteArray baAll = f.readAll(); + const QList 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.h new file mode 100755 index 000000000..6a653ef2c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.h @@ -0,0 +1,32 @@ +/** + * @RDE_HEADER@ + * Handle Audio/Video output for thp through libvlc + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.ui new file mode 100755 index 000000000..2101cd62b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget.ui @@ -0,0 +1,254 @@ + + + audioVideoWidget + + + + 0 + 0 + 911 + 563 + + + + + 0 + 0 + + + + Form + + + QFrame::Panel + + + QFrame::Sunken + + + + + + + + + 0 + 0 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + + + + + Audio + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + 70 + 0 + + + + 55:55 / 55:55 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + false + + + + + + + :icons/16x16/media-playback-start.png:icons/16x16/media-playback-start.png + + + + + + + false + + + + + + + :icons/16x16/media-playback-stop.png:icons/16x16/media-playback-stop.png + + + + + + + + + + + :icons/16x16/media-play-slower.png:icons/16x16/media-play-slower.png + + + + + + + + + + + :icons/16x16/media-play-normal.png:icons/16x16/media-play-normal.png + + + + + + + + + + + :icons/16x16/media-play-faster.png:icons/16x16/media-play-faster.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + QFrame::Sunken + + + 100 % + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Volume: + + + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + 100 + 16777215 + + + + 100 + + + 60 + + + Qt::Horizontal + + + + + + + + + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget_p.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget_p.h new file mode 100755 index 000000000..25d6d480f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_audio_video_widget_p.h @@ -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 +typedef SSIZE_T ssize_t; +#endif + +#include +#include +#include +#include +#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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.cpp new file mode 100755 index 000000000..bde8fe762 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.cpp @@ -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 + +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.h new file mode 100755 index 000000000..30da61be5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.h @@ -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 + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.ui new file mode 100755 index 000000000..f108dda16 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_settings.ui @@ -0,0 +1,181 @@ + + + BgViewerSettingsUI + + + + 300 + 0 + + + + General Settings + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + 100 + 0 + + + + 100 + + + 75 + + + Qt::Horizontal + + + + + + + true + + + Use orthographic view mode + + + true + + + + + + + Player symbol size: + + + + + + + %1 sec + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + %1 sec + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Symbol fire event flashing: + + + + + + + 10 + + + 45 + + + 20 + + + Qt::Horizontal + + + + + + + Transparency value: + + + + + + + Hit line display length: + + + + + + + 120 + + + 5 + + + 10 + + + Qt::Horizontal + + + + + + + Use dots mode + + + + + + + true + + + 120 + + + 5 + + + 10 + + + Qt::Horizontal + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.cpp new file mode 100755 index 000000000..4810623ce --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.cpp @@ -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 + +#include +#include +#include +#include + +#include + +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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.h new file mode 100755 index 000000000..573eb8a97 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_building_widget.h @@ -0,0 +1,50 @@ +/** + * @RDE_HEADER@ + * Handle bulding display in thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.cpp new file mode 100755 index 000000000..56a399671 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.cpp @@ -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 +#include +#include + + +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" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.h new file mode 100755 index 000000000..3864a6630 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.h @@ -0,0 +1,46 @@ +/** + * @RDE_HEADER@ + * Handle HTML output for thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once +#include +#include + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.ui new file mode 100755 index 000000000..2de91b1a5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_html_widget.ui @@ -0,0 +1,49 @@ + + + HtmlWidget + + + + 0 + 0 + 719 + 540 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + about:blank + + + + + + + + + QWebEngineView + QWidget +
QtWebEngineWidgets/QWebEngineView
+
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.cpp new file mode 100755 index 000000000..223b439cf --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.cpp @@ -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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.h new file mode 100755 index 000000000..02f6b3c46 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.h @@ -0,0 +1,40 @@ +/** + * @RDE_HEADER@ + * Handle PDF output for thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once +#include + + +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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.ui new file mode 100755 index 000000000..9d7f14f46 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_pdf_widget.ui @@ -0,0 +1,166 @@ + + + PdfWidget + + + + 0 + 0 + 580 + 396 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 202 + 20 + + + + + + + + + + + 0 + 0 + + + + + + + + :icons/16x16/go-previous.png:icons/16x16/go-previous.png + + + Ctrl+P + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + page %1 of %2 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 0 + 0 + + + + + + + + :icons/16x16/go-next.png:icons/16x16/go-next.png + + + + 16 + 16 + + + + Ctrl+N + + + + + + + + + Qt::Horizontal + + + + 201 + 20 + + + + + + + + + NGL::PDFDocument + QWidget +
nglwidgets/nglpdfdocument.h
+ 1 +
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.cpp new file mode 100755 index 000000000..72d4e0133 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +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::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.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; + QVector 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.h new file mode 100755 index 000000000..b37d4b89e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.h @@ -0,0 +1,105 @@ +/** + * @RDE_HEADER@ + * Handle situation / building display in thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#include +#include + +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 m_replayInformation; + QVector m_filterInformation; + QVector 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.ui b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.ui new file mode 100755 index 000000000..7469397ea --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_building_base.ui @@ -0,0 +1,234 @@ + + + SituationBuildingBase + + + + 0 + 0 + 911 + 563 + + + + + 0 + 0 + + + + Form + + + QFrame::Panel + + + QFrame::Sunken + + + + + + + + + + + 0 + 0 + + + + + 40 + 0 + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + --:-- / --:-- + + + + + + + + + + + false + + + + + + + :icons/16x16/media-playback-start.png:icons/16x16/media-playback-start.png + + + + + + + false + + + + + + + :icons/16x16/media-playback-stop.png:icons/16x16/media-playback-stop.png + + + + + + + + + + + :icons/16x16/media-play-slower.png:icons/16x16/media-play-slower.png + + + + + + + + + + + :icons/16x16/media-play-normal.png:icons/16x16/media-play-normal.png + + + + + + + + + + + :icons/16x16/media-play-faster.png:icons/16x16/media-play-faster.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + QFrame::Sunken + + + 100 % + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 100 + 0 + + + + QComboBox::AdjustToContents + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + MGRS + + + + + + + Filter + + + QToolButton::InstantPopup + + + + + + + + + + + + THP::ZoomSlider + QWidget +
thpwidgets/thp_zoom_slider.h
+
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.cpp new file mode 100755 index 000000000..aceb812a8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.cpp @@ -0,0 +1,75 @@ +/** + * @RDE_HEADER@ + * AES filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_situation_filter_aes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace THP +{ +SituatationFilterAes::SituatationFilterAes(const std::shared_ptr &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 + ifcPpmCbx(std::make_shared()); + ifcPpmCbx->init(pluginProvider->ppmDtc()); + + std::shared_ptr + ifcPdmCbx(std::make_shared()); + 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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.h new file mode 100755 index 000000000..a74935ce2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_aes.h @@ -0,0 +1,35 @@ +/** + * @RDE_HEADER@ + * AES filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include "thp_plugin_provider.h" +#include + +namespace FilterManagement +{ +class AesFilterWidget; +class AesDisplayFilter; +} +namespace THP +{ +class SitMapPluginProvider; +class SituatationFilterAes final : public QDialog +{ + Q_OBJECT +public: + explicit SituatationFilterAes(const std::shared_ptr &aesFilter, + SitMapPluginProvider *pluginProvider, + QWidget *parent = nullptr); + +protected: + void accept() override; + void reject() override; +private: + FilterManagement::AesFilterWidget *m_filterWidget; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.cpp new file mode 100755 index 000000000..86bf6d075 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.cpp @@ -0,0 +1,62 @@ +/** + * @RDE_HEADER@ + * Type filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_situation_filter_building_components.h" + +#include +#include +#include +#include + +#include +#include + +namespace THP +{ +SituationFilterBuildingComponent::SituationFilterBuildingComponent(const std::shared_ptr &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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.h new file mode 100755 index 000000000..d715deee2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_building_components.h @@ -0,0 +1,34 @@ +/** + * @RDE_HEADER@ + * Type filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include "thp_plugin_provider.h" +#include + +namespace FilterManagement +{ +class BuildingComponentSettings; +class BuildingComponentSettingsWidget; +} +namespace THP +{ +class SitMapPluginProvider; +class SituationFilterBuildingComponent final : public QDialog +{ + Q_OBJECT +public: + explicit SituationFilterBuildingComponent(const std::shared_ptr &bcs, + SitMapPluginProvider *pluginProvider, + QWidget *parent = nullptr); +protected: + void accept() override; + void reject() override; +private: + FilterManagement::BuildingComponentSettingsWidget *m_filterWidget{nullptr}; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.cpp new file mode 100755 index 000000000..dbdf935a7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.cpp @@ -0,0 +1,77 @@ +/** + * @RDE_HEADER@ + * AES filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_situation_filter_collapse_expand.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace THP +{ +SituatationFilterCollapseExpand::SituatationFilterCollapseExpand(const std::shared_ptr &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 + ifcPpmCbx(std::make_shared()); + ifcPpmCbx->init(pluginProvider->ppmDtc()); + + std::shared_ptr + ifcAsb(std::make_shared(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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.h new file mode 100755 index 000000000..d69a01e5c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_collapse_expand.h @@ -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 + +namespace FilterManagement +{ +class CollapseExpandParticipantWidget; +class SymbolAppearanceSettings; +} +namespace THP +{ +class SitMapPluginProvider; +class SituatationFilterCollapseExpand final : public QDialog +{ + Q_OBJECT +public: + explicit SituatationFilterCollapseExpand(const std::shared_ptr &sas, + SitMapPluginProvider *pluginProvider, + QWidget *parent = nullptr); +protected: + void accept() override; + void reject() override; +private: + FilterManagement::CollapseExpandParticipantWidget *m_filterWidget; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.cpp new file mode 100755 index 000000000..4b830e722 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.cpp @@ -0,0 +1,63 @@ +/** + * @RDE_HEADER@ + * AES filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_situation_filter_symbol_appearance.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace THP +{ +SituatationFilterSymbolAppearance::SituatationFilterSymbolAppearance(const std::shared_ptr &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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.h new file mode 100755 index 000000000..8b6bb699d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_symbol_appearance.h @@ -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 + +namespace FilterManagement +{ +class SymbolAppearanceSettings; +class SymbolAppearanceWidget; +} +namespace THP +{ +class SitMapPluginProvider; +class SituatationFilterSymbolAppearance final : public QDialog +{ + Q_OBJECT +public: + explicit SituatationFilterSymbolAppearance(const std::shared_ptr &sas, + SitMapPluginProvider *pluginProvider, + QWidget *parent = nullptr); +protected: + void accept() override; + void reject() override; +private: + FilterManagement::SymbolAppearanceWidget *m_filterWidget; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.cpp new file mode 100755 index 000000000..188905e54 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.cpp @@ -0,0 +1,69 @@ +/** + * @RDE_HEADER@ + * Type filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_situation_filter_types.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace THP +{ +SituatationFilterTypes::SituatationFilterTypes(const std::shared_ptr &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 + ifcPpmCbx(std::make_shared()); + 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(); +} + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.h new file mode 100755 index 000000000..8d53761e5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_filter_types.h @@ -0,0 +1,34 @@ +/** + * @RDE_HEADER@ + * Type filter dialog for situation map/thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include "thp_plugin_provider.h" +#include + +namespace FilterManagement +{ +class SymbolAppearanceSettings; +class TypeFilterWidget; +} +namespace THP +{ +class SitMapPluginProvider; +class SituatationFilterTypes final : public QDialog +{ + Q_OBJECT +public: + explicit SituatationFilterTypes(const std::shared_ptr &sas, + SitMapPluginProvider *pluginProvider, + QWidget *parent = nullptr); +protected: + void accept() override; + void reject() override; +private: + FilterManagement::TypeFilterWidget *m_filterWidget; +}; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.cpp new file mode 100755 index 000000000..1c18fc70e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 movementTrackMgr = + QPointer(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 movementTrackMgr = + QPointer(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 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 &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 SituationWidget::fixupUrls(const QVector &in, + const QString &homePath) +{ + QVector 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" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.h new file mode 100755 index 000000000..f888f48d1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.h @@ -0,0 +1,106 @@ +/** + * @RDE_HEADER@ + * Handle situation display in thp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#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 fixupUrls(const QVector &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> 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 m_aesDisplayFilter; + std::shared_ptr m_symbolAppaearanceSettings; + std::shared_ptr m_buildingComponentSettings; + +}; +} // namespace THP diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.cpp b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.cpp new file mode 100755 index 000000000..c78d2b971 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.cpp @@ -0,0 +1,237 @@ +/** + * @RDE_HEADER@ + * A special slider for zooming + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "thp_zoom_slider.h" +#include +#include +#include +#include + +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(m_LowerThreshold - m_InternalMin) * (zoomValue - m_InternalMin); + //qDebug() << "Value1 = " << l_ValToSet; + m_curSliderVal = static_cast(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(m_UpperThreshold - m_LowerThreshold) * (zoomValue - m_LowerThreshold); + l_ValToSet += l_Range * 2; + //qDebug() << "Value2 = " << l_ValToSet; + m_curSliderVal = static_cast(l_ValToSet); + + } + else + { + //Range is 75%..100% + double l_Range = (m_MaxRange - m_MinRange) / 4; + double l_ValToSet = l_Range / static_cast(m_InternalMax - m_UpperThreshold) * (zoomValue - m_UpperThreshold); + l_ValToSet += l_Range * 3; + //qDebug() << "Value3 = " << l_ValToSet; + m_curSliderVal = static_cast(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(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(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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.h b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.h new file mode 100755 index 000000000..2e0122152 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_zoom_slider.h @@ -0,0 +1,152 @@ +/** + * @RDE_HEADER@ + * A special slider for zooming + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include + +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