687 lines
23 KiB
C++
Executable File
687 lines
23 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Main thp viewer widget
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
#include "thp_viewer.h"
|
|
#include "ui_thp_viewer.h"
|
|
#include "thp_plugin_provider.h"
|
|
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QKeyEvent>
|
|
#include <QPrintDialog>
|
|
#include <QPrinter>
|
|
#include <QPainter>
|
|
#include <QMessageBox>
|
|
#include <QTextDocument>
|
|
#include <QScreen>
|
|
#include <QDesktopServices>
|
|
#include <QDomElement>
|
|
#include <QDomDocument>
|
|
#include <QUrl>
|
|
#include <QRect>
|
|
#include <QSize>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QDir>
|
|
#include <QSettings>
|
|
#include <QInputDialog>
|
|
#include <QDir>
|
|
|
|
#include <ngltranslator/ngltranslator.h>
|
|
#include <ngltools/ngltools_dtg.h>
|
|
#include <NGLLogger/NGLLoggerBootstrap.h>
|
|
|
|
namespace NGL
|
|
{
|
|
/*
|
|
* ThpViewer base class
|
|
*/
|
|
ThpViewer::ThpViewer(const QUrl &url, QWidget *parent)
|
|
: QWidget(parent)
|
|
, ui(new Ui::thpViewer)
|
|
, m_scaleFactor(1.0)
|
|
, m_homeLink(url)
|
|
, m_homePath(QFileInfo(m_homeLink.toLocalFile()).absolutePath())
|
|
, m_trainingName(QString())
|
|
, m_logger(NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("ThpViewer"))
|
|
{
|
|
ui->setupUi(this);
|
|
ui->audioWidget->setAV(true);
|
|
ui->videoWidget->setAV(false);
|
|
|
|
/*
|
|
TODO
|
|
if (!conffile.isEmpty())
|
|
{
|
|
QSettings thpsettings(conffile, QSettings::IniFormat, this);
|
|
|
|
// needed?
|
|
const char timeZone = thpsettings.value(QStringLiteral("Time/Timezone"), QStringLiteral("Z")).toString().at(0).toLatin1();
|
|
DTG::setTimezone(timeZone, true);
|
|
}
|
|
*/
|
|
|
|
QString basedir(url.toLocalFile());
|
|
basedir = basedir.left(basedir.lastIndexOf(QLatin1Char('/')));
|
|
|
|
DEBUG_LOG(m_logger) << "url: " << url.toString() << " basedir: " << basedir;
|
|
|
|
QFont f = ui->mainLabel->font();
|
|
f.setBold(true);
|
|
f.setPointSizeF(f.pointSizeF() * 1.2);
|
|
ui->mainLabel->setFont(f);
|
|
|
|
connect(ui->closeButton, &QToolButton::clicked, this, &ThpViewer::close);
|
|
connect(ui->backButton, &QToolButton::clicked, this, &ThpViewer::onBack);
|
|
connect(ui->homeButton, &QToolButton::clicked, this, &ThpViewer::onHome);
|
|
connect(ui->helpButton, &QToolButton::clicked, this, &ThpViewer::onHelp);
|
|
connect(ui->printButton, &QToolButton::clicked, this, &ThpViewer::onPrint);
|
|
connect(ui->fullScreenButton, &QToolButton::clicked, this, &ThpViewer::onFullScreen);
|
|
connect(ui->htmlWidget, &THP::HtmlWidget::linkClicked, this, &ThpViewer::onLinkClicked);
|
|
connect(ui->htmlWidget, &THP::HtmlWidget::urlChanged, this, &ThpViewer::onUrlChanged);
|
|
connect(ui->htmlWidget, &THP::HtmlWidget::titleChanged, this, &ThpViewer::onTitleChanged);
|
|
|
|
ui->backButton->setEnabled(false);
|
|
ui->helpButton->setVisible(false); // Help disabled for now
|
|
#ifndef Q_WS_WIN
|
|
setWindowIcon(QIcon(QStringLiteral(":icons/16x16/rde.ico")));
|
|
#endif
|
|
setWindowTitle(tr("Combat Training Center"));
|
|
}
|
|
|
|
ThpViewer::~ThpViewer()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
bool ThpViewer::init()
|
|
{
|
|
QFile file(m_homePath + QStringLiteral("/data.json"));
|
|
int exerciseId = 0;
|
|
if (file.exists() && file.open(QIODevice::ReadOnly))
|
|
{
|
|
const auto obj = QJsonDocument::fromJson(file.readAll()).object();
|
|
exerciseId = obj.value(QLatin1String("exerciseId")).toInt();
|
|
}
|
|
else
|
|
{
|
|
|
|
QFile file2(m_homePath + QStringLiteral("/../data.json"));
|
|
if (file2.exists() && file2.open(QIODevice::ReadOnly))
|
|
{
|
|
const auto obj = QJsonDocument::fromJson(file2.readAll()).object();
|
|
exerciseId = obj.value(QLatin1String("exerciseId")).toInt();
|
|
}
|
|
}
|
|
DEBUG_LOG(m_logger) << "Use EXERCISE" << exerciseId;
|
|
|
|
if (!ui->situationWidget->init(m_homePath, exerciseId))
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not init situation widget - abort";
|
|
return false;
|
|
}
|
|
if (!ui->buildingWidget->init(ui->situationWidget->replayHandler(),
|
|
ui->situationWidget->pluginProvider()->buildingPluginProvider(),
|
|
m_homePath))
|
|
{
|
|
ERROR_LOG(m_logger) << "Could not init situation widget - abort";
|
|
return false;
|
|
}
|
|
|
|
onZoomChanged();
|
|
onLinkClicked(m_homeLink);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ThpViewer::center()
|
|
{
|
|
QRect rect = QGuiApplication::primaryScreen()->availableGeometry();
|
|
// 80 %
|
|
int width = rect.width() * 4 / 5;
|
|
int height = rect.height() * 4 / 5;
|
|
int x = rect.width() / 2 - width / 2;
|
|
int y = rect.height() / 2 - height / 2;
|
|
resize(width, height);
|
|
move(x, y);
|
|
}
|
|
|
|
void ThpViewer::closeEvent(QCloseEvent *event)
|
|
{
|
|
// explicit delete those two widgets here, otherwise we may get in trouble
|
|
// on windows when the app is closed with the 'X' button from the window frame
|
|
delete ui->buildingWidget;
|
|
ui->buildingWidget = nullptr;
|
|
delete ui->situationWidget;
|
|
ui->situationWidget = nullptr;
|
|
QWidget::closeEvent(event);
|
|
}
|
|
|
|
void ThpViewer::onNewThp(const QString &index)
|
|
{
|
|
DEBUG_LOG(m_logger) << "onNewThp: " << index;
|
|
|
|
if (index.isEmpty())
|
|
return;
|
|
m_homeLink = QUrl::fromLocalFile(index);
|
|
m_homePath = QFileInfo(m_homeLink.toLocalFile()).absolutePath();
|
|
DEBUG_LOG(m_logger) << "home: " << m_homeLink.toString();
|
|
onLinkClicked(m_homeLink);
|
|
ui->backButton->setEnabled(false);
|
|
this->raise();
|
|
}
|
|
|
|
void ThpViewer::onTitleChanged(const QString &title)
|
|
{
|
|
if (title.endsWith(QLatin1String("html")) ||
|
|
title.endsWith(QLatin1String("html#!/")))
|
|
return;
|
|
if (m_trainingName.isEmpty())
|
|
m_trainingName = title;
|
|
ui->mainLabel->setText(m_trainingName == title ? m_trainingName : QStringLiteral("%1 - %2").arg(m_trainingName, title));
|
|
}
|
|
|
|
void ThpViewer::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
switch (e->key())
|
|
{
|
|
case Qt::Key_Backspace:
|
|
onBack();
|
|
break;
|
|
case Qt::Key_Plus:
|
|
if (ui->stackedWidget->isVisible() && ui->stackedWidget->currentWidget() == ui->situationWidget)
|
|
return;
|
|
if (m_scaleFactor < 10)
|
|
{
|
|
m_scaleFactor *= 1.5;
|
|
onZoomChanged();
|
|
}
|
|
break;
|
|
case Qt::Key_M:
|
|
{
|
|
if (QApplication::queryKeyboardModifiers() == Qt::ControlModifier)
|
|
onLinkClicked(QUrl::fromLocalFile(QDir::currentPath() + QStringLiteral("/") + QInputDialog::getText(this, {}, QStringLiteral("href_local"))));
|
|
break;
|
|
}
|
|
case Qt::Key_Minus:
|
|
if (ui->stackedWidget->isVisible() && ui->stackedWidget->currentWidget() == ui->situationWidget)
|
|
return;
|
|
if (m_scaleFactor > 0.05)
|
|
{
|
|
m_scaleFactor /= 1.5;
|
|
onZoomChanged();
|
|
}
|
|
break;
|
|
case Qt::Key_F1:
|
|
onHelp();
|
|
break;
|
|
case Qt::Key_Escape:
|
|
if (isFullScreen())
|
|
onFullScreen();
|
|
break;
|
|
default:
|
|
QWidget::keyPressEvent(e);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ThpViewer::onHelp()
|
|
{
|
|
QFileInfo f(QStringLiteral("%1/../DATA/help/%2/help.html").arg(QCoreApplication::applicationDirPath(), QString::fromLatin1(NGL::translationLocale())));
|
|
if (f.exists())
|
|
onLinkClicked(QUrl::fromLocalFile(f.absoluteFilePath())); // TODO Pfad anpassen!
|
|
else
|
|
{
|
|
f.setFile(qApp->applicationDirPath() + QStringLiteral("/../DATA/help/en_US/help.html")); // TODO Pfad anpassen!
|
|
if (f.exists())
|
|
onLinkClicked(QUrl::fromLocalFile(f.absoluteFilePath()));
|
|
}
|
|
}
|
|
|
|
void ThpViewer::onZoomChanged()
|
|
{
|
|
ui->pdfWidget->setZoomFactor(m_scaleFactor);
|
|
ui->htmlWidget->setZoomFactor(m_scaleFactor);
|
|
|
|
// double zoom = ( int ) ( m_scaleFactor * 100. + 0.5 );
|
|
// ui->statusbar->showMessage ( tr ( "zoom: %1 %" ).arg ( zoom ) );
|
|
}
|
|
|
|
void ThpViewer::onLinkClicked(const QUrl &url)
|
|
{
|
|
DEBUG_LOG(m_logger) << "url: " << url.toString();
|
|
|
|
const QString path = url.path();
|
|
const QString lpath = path.toLower();
|
|
|
|
const QStringRef ext = lpath.splitRef(QLatin1Char('.')).last();
|
|
const QStringRef ext2Chars = ext.left(2);
|
|
|
|
ui->audioWidget->stop();
|
|
ui->videoWidget->stop();
|
|
// Video-only
|
|
if (lpath.endsWith(QLatin1String("video.pls")) ||
|
|
ext == QLatin1String("mpg") ||
|
|
ext == QLatin1String("avi") ||
|
|
ext == QLatin1String("ogg"))
|
|
{
|
|
ui->videoWidget->play(url);
|
|
|
|
ui->stackedWidget->hide();
|
|
ui->videoWidget->show();
|
|
ui->audioWidget->hide();
|
|
|
|
ui->backButton->setEnabled(true);
|
|
// Audio-only
|
|
}
|
|
else if (lpath.endsWith(QLatin1String("audio.pls")) ||
|
|
ext == QLatin1String("mp3"))
|
|
{
|
|
ui->audioWidget->play(url);
|
|
|
|
// Ermitteln der Groesse des AudioWidgets
|
|
QSize sizeAudio = ui->audioWidget->sizeHint();
|
|
QSize sizeStack = ui->stackedWidget->sizeHint();
|
|
sizeStack -= sizeAudio;
|
|
QList<int> l;
|
|
l << sizeStack.height() << sizeAudio.height();
|
|
ui->splitter_1->setSizes(l);
|
|
|
|
ui->stackedWidget->show(); // hier können wir html mit anzeigen
|
|
ui->videoWidget->hide();
|
|
ui->audioWidget->show();
|
|
|
|
ui->backButton->setEnabled(true);
|
|
// PDF-only
|
|
}
|
|
else if (ext == QLatin1String("pdf"))
|
|
{
|
|
if (ui->pdfWidget->showPDF(url))
|
|
{
|
|
|
|
ui->stackedWidget->setCurrentWidget(ui->pdfWidget);
|
|
ui->stackedWidget->show();
|
|
ui->pdfWidget->resizePdfToWidth();
|
|
ui->videoWidget->hide();
|
|
ui->audioWidget->hide();
|
|
|
|
ui->backButton->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning(this,
|
|
tr("Opening PDF file"),
|
|
tr("Can not open PDF file %1 - missing or corrupt file!").arg(url.toString()));
|
|
}
|
|
}
|
|
else if (lpath.endsWith(QLatin1String("sitmap.json"), Qt::CaseInsensitive))
|
|
{
|
|
|
|
// 2D oder 3D ?
|
|
{
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " lpath = '" << lpath << "'";
|
|
// peek into <lpath>/sitreplay.json:
|
|
|
|
QFileInfo fi(path);
|
|
const QString basePath = fi.absolutePath();
|
|
const QString replayConfigFile = basePath + QDir::separator() + QStringLiteral("sitreplay.json"); // aka replayConfigPath
|
|
NOTICE_LOG(m_logger) << LOGFUNCTION << " replayConfigFile = '" << replayConfigFile << "'";
|
|
}
|
|
|
|
// Lage-only
|
|
QString localFile = url.toLocalFile();
|
|
QString basePath = localFile;
|
|
int idx = basePath.lastIndexOf(QLatin1Char('/'));
|
|
idx = basePath.lastIndexOf(QLatin1Char('/'), idx - 1);
|
|
if (idx >= 0)
|
|
basePath.chop(basePath.size() - idx - 1);
|
|
|
|
ui->situationWidget->showSituation(url.toLocalFile(),
|
|
basePath);
|
|
|
|
ui->stackedWidget->setCurrentWidget(ui->situationWidget);
|
|
ui->stackedWidget->show();
|
|
ui->videoWidget->hide();
|
|
ui->audioWidget->hide();
|
|
|
|
ui->backButton->setEnabled(true);
|
|
}
|
|
else if (lpath.endsWith(QLatin1String("buildingViewer.json"), Qt::CaseInsensitive))
|
|
{
|
|
// BuildingViewer-only
|
|
QString localFile = url.toLocalFile();
|
|
QString basePath = localFile;
|
|
int idx = basePath.lastIndexOf(QLatin1Char('/'));
|
|
idx = basePath.lastIndexOf(QLatin1Char('/'), idx - 1);
|
|
if (idx >= 0)
|
|
basePath.chop(basePath.size() - idx - 1);
|
|
ui->buildingWidget->showSituation(url.toLocalFile(),
|
|
basePath);
|
|
|
|
ui->stackedWidget->setCurrentWidget(ui->buildingWidget);
|
|
ui->stackedWidget->show();
|
|
ui->videoWidget->hide();
|
|
ui->audioWidget->hide();
|
|
|
|
ui->backButton->setEnabled(true);
|
|
}
|
|
else if (ext == QLatin1String("html") &&
|
|
lpath.midRef(lpath.lastIndexOf(QLatin1Char('/')) + 1).startsWith(QLatin1String("combined_")))
|
|
{
|
|
// Kombiniert
|
|
QHash<QString, QUrl> urls = getCombinedPlaybackDetails(url);
|
|
|
|
if (urls.isEmpty())
|
|
return;
|
|
|
|
if (urls.contains(QLatin1String("audio")))
|
|
{
|
|
ui->audioWidget->show();
|
|
ui->audioWidget->play(urls[QLatin1String("audio")]);
|
|
}
|
|
if (urls.contains(QLatin1String("video")))
|
|
{
|
|
ui->videoWidget->show();
|
|
ui->videoWidget->play(urls[QLatin1String("video")]);
|
|
}
|
|
|
|
// Einer von dreien:
|
|
if (urls.contains(QLatin1String("pdf")))
|
|
{
|
|
ui->stackedWidget->setCurrentWidget(ui->pdfWidget);
|
|
ui->pdfWidget->showPDF(urls[QLatin1String("pdf")]);
|
|
ui->stackedWidget->show();
|
|
}
|
|
else if (urls.contains(QLatin1String("html")))
|
|
{
|
|
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
|
|
ui->htmlWidget->showHTML(urls[QLatin1String("html")]);
|
|
ui->stackedWidget->show();
|
|
}
|
|
else if (urls.contains(QLatin1String("jpg")))
|
|
{
|
|
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
|
|
ui->htmlWidget->showHTML(urls[QLatin1String("jpg")]);
|
|
ui->stackedWidget->show();
|
|
}
|
|
else if (urls.contains(QLatin1String("png")))
|
|
{
|
|
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
|
|
ui->htmlWidget->showHTML(urls[QLatin1String("png")]);
|
|
ui->stackedWidget->show();
|
|
}
|
|
else
|
|
ui->stackedWidget->hide();
|
|
ui->backButton->setEnabled(true);
|
|
}
|
|
else if (ext2Chars == QLatin1String("do") ||
|
|
ext2Chars == QLatin1String("xl") ||
|
|
ext2Chars == QLatin1String("pp"))
|
|
QDesktopServices::openUrl(url);
|
|
else
|
|
{
|
|
DEBUG_LOG(m_logger) << "Trying to open url: " << url.toString();
|
|
ui->htmlWidget->showHTML(url);
|
|
|
|
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
|
|
ui->stackedWidget->show();
|
|
ui->videoWidget->hide();
|
|
ui->audioWidget->hide();
|
|
|
|
ui->backButton->setEnabled(url != m_homeLink);
|
|
}
|
|
}
|
|
/********************************************************************************************
|
|
* Parsed eine HTML-Dateinach allen Links, die sich direkt unterhalb des BODY-Tags befinden
|
|
* und liefert die entsprechenden Link-URLs zurück, wenn diese das Tag ID besitzen.
|
|
*
|
|
* Von der aufrufenden Funktion derzeit ausgewertete Werte für ID sind:
|
|
* html, audio, video, lage, pdf (alles klein geschrieben!)
|
|
*/
|
|
QHash<QString, QUrl> ThpViewer::getCombinedPlaybackDetails(const QUrl &url) const
|
|
{
|
|
QHash<QString, QUrl> hash;
|
|
QFile file(url.toLocalFile());
|
|
QDomDocument doc(QStringLiteral("RSS"));
|
|
if (!file.open(QIODevice::ReadOnly))
|
|
return hash;
|
|
QString errorStr;
|
|
int errorLine, errorColumn;
|
|
if (!doc.setContent(&file, true, &errorStr, &errorLine, &errorColumn))
|
|
{
|
|
file.close();
|
|
return hash;
|
|
}
|
|
QDomElement domel = doc.documentElement();
|
|
domel = domel.firstChildElement(QStringLiteral("BODY"));
|
|
domel = domel.firstChildElement(QStringLiteral("A"));
|
|
|
|
while (!domel.isNull())
|
|
{
|
|
if (!domel.attribute(QStringLiteral("id")).isEmpty())
|
|
{
|
|
QUrl linkurl(domel.attribute(QStringLiteral("HREF")));
|
|
if (linkurl.isRelative())
|
|
linkurl = url.resolved(linkurl);
|
|
DEBUG_LOG(m_logger) << "attr: " << domel.attribute(QStringLiteral("id")).toLower() << " url: " << linkurl.toString();
|
|
hash[domel.attribute(QStringLiteral("id")).toLower()] = linkurl;
|
|
}
|
|
domel = domel.nextSiblingElement(QStringLiteral("A"));
|
|
}
|
|
file.close();
|
|
return hash;
|
|
}
|
|
|
|
void ThpViewer::onPrint()
|
|
{
|
|
if (ui->stackedWidget->isVisible())
|
|
{
|
|
if (ui->stackedWidget->currentWidget() == ui->situationWidget)
|
|
{
|
|
QImage image = ui->situationWidget->grabFrameBuffer();
|
|
|
|
QPrinter printer;
|
|
QPrintDialog *dialog = new QPrintDialog(&printer, this);
|
|
dialog->setWindowTitle(tr("Print Document"));
|
|
if (dialog->exec() != QDialog::Accepted)
|
|
return;
|
|
|
|
QPainter painter;
|
|
painter.begin(&printer);
|
|
|
|
QRectF rect = painter.viewport();
|
|
|
|
double mmH = printer.logicalDpiY() / 25.4; // 25.4 = mm/inch
|
|
double mmW = printer.logicalDpiX() / 25.4;
|
|
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 30));
|
|
QString title = tr("Combat Training Center");
|
|
QRectF textBox = painter.boundingRect(QRectF(), Qt::AlignLeft & Qt::TextSingleLine, title);
|
|
textBox.setWidth(painter.viewport().width());
|
|
painter.drawText(textBox, Qt::AlignCenter & Qt::TextSingleLine, title);
|
|
QPointF underImage;
|
|
|
|
if (printer.orientation() == QPrinter::Portrait)
|
|
{
|
|
const QPointF textBlockBegin = QPointF(0 * mmW, 25 * mmH); // Baseline 25mm unter Seitenbeginn
|
|
|
|
// Noch mal schauen, welches (auch übersetzt!) der breiteste Bezeichner (also 1. Spalte) ist!
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
|
|
textBox = painter.boundingRect(QRect(), Qt::AlignLeft & Qt::TextSingleLine, tr("Center point:\nExercise:\nTime:"));
|
|
|
|
// Textbox hat 3 Zeilen, also ist der Faktor pro Zeile 0.33 = 1/3
|
|
// Spalte 1
|
|
painter.drawText(textBlockBegin, tr("Exercise:"));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.33 * textBox.height()), tr("Time:"));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.66 * textBox.height()), tr("Center point:"));
|
|
|
|
// Zwischen Spalte 1 und Spalte 2 noch 3mm Platz lassen
|
|
// Spalte 2
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
|
|
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0), m_trainingName);
|
|
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0.33 * textBox.height()), ui->situationWidget->currentDTGTime());
|
|
painter.drawText(textBlockBegin + QPointF(textBox.width() + 3 * mmW, 0.66 * textBox.height()), ui->situationWidget->centerPointString());
|
|
|
|
// Das Bild soll formatfüllend angezeigt werden, das Rechteck muss 45mm "flacher" sein, da es erst ab 45mm gedruckt werden soll.
|
|
QSizeF size = image.size();
|
|
rect.adjust(0, 0, 0, -45 * mmH);
|
|
size.scale(rect.size(), Qt::KeepAspectRatio);
|
|
|
|
// Und ab 45mm von oben drucken:
|
|
QRectF imgrect = QRectF(QPointF(0 * mmW, 45 * mmH), size);
|
|
painter.drawImage(imgrect, image);
|
|
|
|
// Untere linke Ecke des Bildes
|
|
underImage = imgrect.bottomLeft();
|
|
|
|
}
|
|
else // Landscape
|
|
{
|
|
// Noch mal schauen, welches (auch übersetzt!) der breiteste Bezeichner oder variable Text ist!
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
|
|
textBox = painter.boundingRect(QRect(), Qt::AlignLeft, tr("Center point:\n%1\n\nTime:\n%2\n\nExercise:\n%3")
|
|
.arg(m_trainingName,
|
|
ui->situationWidget->currentDTGTime(),
|
|
ui->situationWidget->centerPointString()));
|
|
|
|
// TextBlock fängt an: Notwendige Breite von rechts und 15 mm + 1 Zeilenhöhe (=1/8 der Textboxhöhe) von oben.
|
|
// Die Zeilenhöhe ist notwendig weil drawText(QPoint, QString) den QPoint auf die Baseline setzt!
|
|
QPointF textBlockBegin = QPointF(painter.viewport().width() - textBox.width(), 15 * mmH + 0.125 * textBox.height());
|
|
|
|
// Die Textbox hat 8 Zeilen, also ist der Faktor pro Zeile 1/8 = 0.125
|
|
// Übung
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
|
|
painter.drawText(textBlockBegin, tr("Exercise:"));
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.125 * textBox.height()), m_trainingName);
|
|
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.375 * textBox.height()), tr("Time:"));
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.500 * textBox.height()), ui->situationWidget->currentDTGTime());
|
|
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Bold, false));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.750 * textBox.height()), tr("Center point:"));
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 14, QFont::Normal, false));
|
|
painter.drawText(textBlockBegin + QPointF(0, 0.875 * textBox.height()), ui->situationWidget->centerPointString());
|
|
|
|
// Das Bild soll formatfüllend angezeigt werden, das Rechteck muss 15mm "flacher" sein, da es erst ab 15mm gedruckt werden soll.
|
|
// Ausserdem müssen in der Breite die Textboxbreite und 3mm Luft dazwischen abgezogen werden.
|
|
QSizeF size = image.size();
|
|
rect.adjust(0, 0, -textBox.width() - 3 * mmW, -15 * mmH);
|
|
size.scale(rect.size(), Qt::KeepAspectRatio);
|
|
|
|
// Und ab 15mm von oben drucken:
|
|
QRectF imgrect = QRectF(QPointF(0 * mmW, 15 * mmH), size);
|
|
painter.drawImage(imgrect, image);
|
|
|
|
// Untere linke Ecke des Bildes
|
|
underImage = imgrect.bottomLeft();
|
|
}
|
|
|
|
painter.setFont(QFont(QStringLiteral("Arial"), 12));
|
|
// String für Breite und Höhe erstellen
|
|
const QRectF extent = ui->situationWidget->extentMgrs();
|
|
QString areasize = tr("Width: %1m, Height: %2m").arg(extent.width()).arg(extent.height());
|
|
// Die Größe herausfinden
|
|
textBox = painter.boundingRect(QRect(), Qt::AlignLeft & Qt::TextSingleLine, areasize);
|
|
// Einen Dot vergrößern (siehe QRect: Die Rahmenpixel unten und rechts sind nicht Teil der Rechteckgröße)
|
|
textBox.adjust(0, 0, 1, 1);
|
|
// An die richtige Stelle bugsieren
|
|
textBox.moveBottomLeft(underImage);
|
|
// Und mit weiß und 50% Deckkraft vollmalen.
|
|
painter.fillRect(textBox, QColor(255, 255, 255, 128));
|
|
//Dann den Text drüberlegen
|
|
painter.drawText(underImage, areasize);
|
|
|
|
painter.end();
|
|
}
|
|
else if (ui->stackedWidget->currentWidget() == ui->pdfWidget)
|
|
{
|
|
QPrinter printer;
|
|
QPrintDialog *dialog = new QPrintDialog(&printer, this);
|
|
dialog->setWindowTitle(tr("Print Document"));
|
|
if (dialog->exec() != QDialog::Accepted)
|
|
return;
|
|
|
|
QVector<QImage> images = ui->pdfWidget->getPrintableImages(printer.resolution());
|
|
int lastItem = images.size() - 1;
|
|
|
|
QPainter painter;
|
|
painter.begin(&printer);
|
|
for (int i = 0; i < images.size(); ++i)
|
|
{
|
|
QRect rect = painter.viewport();
|
|
QSize size = images.at(i).size();
|
|
size.scale(rect.size(), Qt::KeepAspectRatio);
|
|
painter.setViewport(rect.x(), rect.y(),
|
|
size.width(), size.height());
|
|
painter.setWindow(images.at(i).rect());
|
|
painter.drawImage(0, 0, images.at(i));
|
|
if (i != lastItem)
|
|
printer.newPage();
|
|
}
|
|
painter.end();
|
|
}
|
|
else if (ui->stackedWidget->currentWidget() == ui->htmlWidget)
|
|
{
|
|
QPrinter printer;
|
|
QPrintDialog *dialog = new QPrintDialog(&printer, this);
|
|
dialog->setWindowTitle(tr("Print Document"));
|
|
if (dialog->exec() != QDialog::Accepted)
|
|
return;
|
|
ui->htmlWidget->print(&printer);
|
|
}
|
|
else // Audio/Video-Widget
|
|
{
|
|
}
|
|
}
|
|
else // nur HTML sichtbar...
|
|
{
|
|
QPrinter printer;
|
|
QPrintDialog *dialog = new QPrintDialog(&printer, this);
|
|
dialog->setWindowTitle(tr("Print Document"));
|
|
if (dialog->exec() != QDialog::Accepted)
|
|
return;
|
|
ui->htmlWidget->print(&printer);
|
|
}
|
|
}
|
|
|
|
void ThpViewer::onBack()
|
|
{
|
|
ui->audioWidget->stop();
|
|
ui->videoWidget->stop();
|
|
ui->situationWidget->stop();
|
|
|
|
ui->audioWidget->hide();
|
|
ui->videoWidget->hide();
|
|
if (ui->stackedWidget->currentWidget() == ui->htmlWidget &&
|
|
ui->stackedWidget->isVisible())
|
|
ui->htmlWidget->back();
|
|
ui->stackedWidget->show();
|
|
ui->stackedWidget->setCurrentWidget(ui->htmlWidget);
|
|
onUrlChanged(ui->htmlWidget->url());
|
|
}
|
|
|
|
void ThpViewer::onHome()
|
|
{
|
|
ui->htmlWidget->show();
|
|
onLinkClicked(m_homeLink);
|
|
}
|
|
|
|
void ThpViewer::onFullScreen()
|
|
{
|
|
setWindowState(windowState() ^ Qt::WindowFullScreen);
|
|
}
|
|
|
|
void ThpViewer::onUrlChanged(const QUrl &url)
|
|
{
|
|
ui->backButton->setEnabled(url != m_homeLink);
|
|
}
|
|
|
|
} // namespace NGL
|