Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
#
# $Id$
# $HeadURL$
# $Author$
#
SET(pws_gui_SRCS
pws_gui_plugin.h
pws_gui_plugin.cpp
pws_control_dialog.h
pws_control_dialog.cpp
pws_vnc_viewer_dialog.h
pws_vnc_viewer_dialog.cpp
pws_operator_settings_loader.h
pws_operator_settings_loader.cpp
WebSocket/pws_web_socket_client.h
WebSocket/pws_web_socket_client.cpp
)
NGL_WRAP_UI(pws_gui_SRCS
pws_control_dialog.ui
pws_vnc_viewer_dialog.ui
)
NGL_ADD_LIBRARY(WS PwsGuiPlugin MODULE ${pws_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(PwsGuiPlugin
ngltools
NGLLogger
nglsettings
Qt5::Widgets
Qt5::Core
Qt5::Network
Qt5::OpenGL
LtsSession
LtsWidgets
LtsUtilsGuiDds
LtsUtilsSettings
LtsSessionData
LtsProjectionWorkstation
LtsWebSocketServer
PwsCommon
svcPwsCommon
AsmDataPluginInterface
FltDataPluginInterface
OpmDataPluginInterface
OpmSettingsGuiPluginInterface
)
NGL_INSTALL_TARGET(PwsGuiPlugin)
@@ -0,0 +1,33 @@
#include "pws_web_socket_client.h"
#include <LtsProjectionWorkstation/WebSocket/message_factory.h>
namespace ProjectionWorkstation
{
WebSocketClient::WebSocketClient(LtsProjectionWorkstation::MessageFactory *messageFactory, QObject *parent)
: LtsWebSocket::ClientConnection(nullptr, parent)
, m_messageFactory(messageFactory)
{
connect(this, &LtsWebSocket::ClientConnection::newTextMessage, this, &WebSocketClient::onNewTextMessage);
init();
}
void WebSocketClient::onNewTextMessage(LtsWebSocket::ClientConnection *client, const QString &message)
{
LtsWebSocket::AbstractClientMessage *clientMessage = nullptr;
auto result = m_messageFactory->parseMessage(message);
if (QJsonParseError::NoError == result.error)
clientMessage = m_messageFactory->createMessage(client, message);
else
{
// WARNING_LOG(m_logger) << "Could not parse incoming message: '" << message << "', error: " << result.errorString();
clientMessage = new LtsWebSocket::InvalidMessage(client, QString(), QStringLiteral("Got message which is not JSON conform"));
}
if (clientMessage)
clientMessage->process();
delete clientMessage;
}
} // ProjectionWorkstation
@@ -0,0 +1,23 @@
#pragma once
#include <LtsWebSocket/client_connection.h>
namespace LtsProjectionWorkstation { class MessageFactory; }
namespace ProjectionWorkstation
{
class WebSocketClient : public LtsWebSocket::ClientConnection
{
Q_OBJECT
public:
explicit WebSocketClient(LtsProjectionWorkstation::MessageFactory *messageFactory, QObject *parent = nullptr);
public slots:
void onNewTextMessage(ClientConnection *client, const QString &message);
private:
LtsProjectionWorkstation::MessageFactory *m_messageFactory;
};
} // ProjectionWorstation
@@ -0,0 +1,416 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_control_dialog.h"
#include "ui_pws_control_dialog.h"
#include "pws_vnc_viewer_dialog.h"
#include <LtsUtilsSettings/settings.h>
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <LtsProjectionWorkstation/vnc_server_manager.h>
#include <nglsettings/nglsettings_connections.h>
#include <QJsonObject>
#include <QUuid>
#include <QHostInfo>
#include <functional>
namespace ProjectionWorkstation
{
ControlDialog::ControlDialog(const QString &menuGroup, QWidget *parent)
: LtsWidgets::LtsDialog(parent)
, m_ui(new Ui::ControlDialog)
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("ControlDialog")))
, m_messageFactory(nullptr)
, m_webSocketClient(nullptr)
, m_session(nullptr)
, m_menuGroup(menuGroup)
, m_exerciseId(-1)
, m_locationId(-1)
, m_isConnected(false)
{
m_ui->setupUi(this);
connect(m_ui->pbnConnect, &QPushButton::clicked, this, &ControlDialog::slotOnConnectClicked);
connect(m_ui->pbnLogout, &QPushButton::clicked, this, &ControlDialog::slotOnLogoutClicked);
connect(m_ui->chbInThisLocationOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox);
connect(m_ui->chbProjectionWSOnly, &QCheckBox::toggled, this, &ControlDialog::fillCombobox);
connect(m_ui->cbxWorkstations, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &ControlDialog::slotCbxWorkstationsChanged);
m_ui->pbnLogout->setEnabled(m_isConnected);
m_ui->chbProjectionWSOnly->setVisible(false);
#ifdef LTS_DEBUG
m_ui->chbProjectionWSOnly->setVisible(true);
#endif
setOnlineHelpFilename(QStringLiteral("Situation-Projection_Control"));
}
ControlDialog::~ControlDialog()
{
if (m_webSocketClient)
{
// NLDEVCTRL-808: OpApp stürzt ab beim beenden (ProjWS)
// at initWebSocketHandler the underlying QWebSocket is used for several
// connections. But the disconnect was missing. Cleanup a little bit...
// disconnect in reverse order:
// 1) disconnect from underlying QWebSocket
if (m_webSocketClient->webSocket())
m_webSocketClient->webSocket()->disconnect(); // to avoid our slots to be called
// 2) disconnect from m_webSocketClient
m_webSocketClient->disconnect(this);
// add reply while destroying?
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
// m_webSocketClient->deleteLater(); // ControlDialog is parent of WebSocketClient, therefore useless!?!
}
delete m_messageFactory;
delete m_ui;
}
void ControlDialog::initWebSocketHandler()
{
if (!m_messageFactory)
m_messageFactory = new LtsProjectionWorkstation::MessageFactory(this, m_logger);
delete m_webSocketClient;
m_webSocketClient = new WebSocketClient(m_messageFactory, this);
auto s = m_webSocketClient->webSocket();
connect(s, &QWebSocket::connected, this, &ControlDialog::slotOnWebSocketConnected);
connect(s, QOverload<QAbstractSocket::SocketError>::of(&QWebSocket::error), this, &ControlDialog::slotOnWebSocketError);
connect(s, QOverload<const QList<QSslError>&>::of(&QWebSocket::sslErrors), this, &ControlDialog::slotOnWebSocketSslError);
connect(s, &QWebSocket::disconnected, this, &ControlDialog::slotOnWebSocketDisconnected);
}
bool ControlDialog::init(LtsSession::LtsSession *session,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
const std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> &opmIfc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc, int exerciseId)
{
if (!session)
return false;
m_session = session;
m_opmDtc = opmDtc;
m_opmIfc = opmIfc;
m_asmDtc = asmDtc;
if (exerciseId < 0) return false;
m_exerciseId = exerciseId;
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
auto getLocationIdFunc =
[sessionData, this](const AssetManagement::Asset & data) -> bool
{
if (data.id() == sessionData->getWorkstationId())
{
m_locationId = data.locationId();
return true;
}
return false;
};
m_asmDtc->assets(std::function<bool(const AssetManagement::Asset &data)>(getLocationIdFunc));
fillCombobox();
QString txt = m_ui->cbxWorkstations->currentText();
if (!txt.isEmpty())
{
QHostInfo info = QHostInfo::fromName(txt);
if (info.addresses().size() > 0)
m_host = info.addresses().at(0);
else
ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!";
}
return true;
}
void ControlDialog::aboutToClose()
{
for (VncViewerDialog *diag : qAsConst(m_viewers))
diag->closeRequest();
m_viewers.clear();
if (m_webSocketClient)
{
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
m_webSocketClient->webSocket()->disconnect();
}
}
void ControlDialog::slotOnConnectClicked()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this;
if (!m_isConnected)
{
initWebSocketHandler();
int port = NGL::Connections::port(NGL::Connections::eConnection::PWS_WEBSOCKET);
if (m_webSocketClient)
m_webSocketClient->webSocket()->open(QUrl(QStringLiteral("ws://%1:%2/").arg(m_host.toString()).arg(port)));
m_ui->pbnConnect->setEnabled(false);
}
else
{
if (m_webSocketClient)
{
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
m_webSocketClient->webSocket()->close();
}
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " connect clicked, this=" << this;
updateConnectButton();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this;
}
void ControlDialog::slotOnLogoutClicked()
{
if (m_webSocketClient)
{
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::LogoutRequest(QUuid::createUuid().toString()))->toReplyMessage());
m_webSocketClient->webSocket()->close();
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " logout clicked, this=" << this;
updateConnectButton();
}
void ControlDialog::slotOnWebSocketConnected()
{
LtsSessionData::LtsSessionData *sessioData = LtsSessionData::LtsSessionData::getInstance();
int userId = sessioData->getOperatorId();
int exerciseId = m_exerciseId;
OperatorManagement::User user = m_opmDtc->user(userId);
if (user.id() > 0)
{
// NLDEVCTRL-445: Projektion Filter und Kartenausschnitt
// very new app 'Lage', workspace isn`t saved yet and therefore no workspaceId.
// But callWorkspaceSaveUnsaved needs an workspaceId for transfering the workspace with filter:
QString workspaceIdToUse = m_session->workspaceId();
if (workspaceIdToUse.isEmpty())
{
workspaceIdToUse = QUuid::createUuid().toString();
INFO_LOG(m_logger) << "workspaceIdToUse " << workspaceIdToUse;
}
m_opmIfc->callWorkspaceSaveUnsaved(workspaceIdToUse);
LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance();
opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), workspaceIdToUse);
QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings();
// This does not work since m_session->setWorkspaceId() triggers loading of filters on controlling WS :-(
// if (m_session->workspaceId().isEmpty())
// {
// const QString newId(QUuid::createUuid().toString());
// INFO_LOG(m_logger) << "new WsId" << newId;
// m_session->setWorkspaceId(newId);
// }
// m_opmIfc->callWorkspaceSaveUnsaved();
// LtsUtils::OperatorSettings* opSettings = LtsUtils::OperatorSettings::getInstance();
// opSettings->setValue(QStringLiteral("PwsWorkspace"), QStringLiteral("CurrentId"), m_session->workspaceId());
// QByteArray opSettingsData = LtsUtils::OperatorSettings::getInstance()->exportSettings();
INFO_LOG(m_logger) << " connect userId=" << userId
<< ", with prio=" << user.projectionPriority()
<< ", PwsWorkspace/Id='" << workspaceIdToUse << "'";
LtsProjectionWorkstation::CTS::LoginRequest req(QUuid::createUuid().toString(),
userId,
user.projectionPriority(),
exerciseId, opSettingsData);
m_webSocketClient->addReply(req.toReplyMessage());
}
}
void ControlDialog::slotOnVncDisconnected()
{
INFO_LOG(m_logger) << " slotOnVncDisconnected";
if (m_webSocketClient)
{
const int userId = LtsSessionData::LtsSessionData::getInstance()->getOperatorId();
m_webSocketClient->addReply((new LtsProjectionWorkstation::CTS::DisconnectRequest(QUuid::createUuid().toString(), userId))->toReplyMessage());
}
m_isConnected = false;
if (!m_ui || !m_ui->pbnConnect)
return;
m_ui->pbnConnect->setEnabled(true);
DEBUG_LOG(m_logger) << LOGFUNCTION << " VNC disconnected, this=" << this;
updateConnectButton();
}
void ControlDialog::slotOnWebSocketDisconnected()
{
for (VncViewerDialog *diag : qAsConst(m_viewers))
diag->closeRequest(); // will be deleted automatically
m_viewers.clear();
m_isConnected = false;
if (m_ui && m_ui->pbnConnect)
m_ui->pbnConnect->setEnabled(true);
DEBUG_LOG(m_logger) << LOGFUNCTION << " WEBSOCKET disconnected, this=" << this;
updateConnectButton();
}
void ControlDialog::slotOnWebSocketError()
{
ERROR_LOG(m_logger) << m_webSocketClient->webSocket()->errorString();
LtsWidgets::LtsMessageDialog::showMessage(this, m_webSocketClient->webSocket()->errorString(), tr("Connection to the presentation workstation failed!"));
m_isConnected = false;
m_ui->pbnConnect->setEnabled(true);
DEBUG_LOG(m_logger) << LOGFUNCTION << " VWEBSOCKET ERROR, this=" << this;
updateConnectButton();
}
void ControlDialog::slotOnWebSocketSslError()
{
if (m_webSocketClient)
m_webSocketClient->webSocket()->ignoreSslErrors();
}
void ControlDialog::slotCbxWorkstationsChanged()
{
QString txt = m_ui->cbxWorkstations->currentText();
if (txt.isEmpty())
return;
QHostInfo info = QHostInfo::fromName(txt);
if (info.addresses().size() > 0)
m_host = info.addresses().at(0);
else
ERROR_LOG(m_logger) << "Address for host " << m_ui->cbxWorkstations->currentText() << " not found!";
}
void ControlDialog::fillCombobox()
{
m_ui->cbxWorkstations->clear();
m_asmDtc->assets([this](const AssetManagement::Asset & data)
{
if (m_ui->chbProjectionWSOnly->isChecked() &&
data.assetTypeId() != AssetManagement::AssetType_def::ProjectionWorkstation)
return;
if (m_ui->chbInThisLocationOnly->isChecked() &&
data.locationId() != m_locationId)
return;
m_ui->cbxWorkstations->addItem(QString::fromStdString(data.name()));
});
m_ui->cbxWorkstations->model()->sort(0);
}
void ControlDialog::updateConnectButton()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, this=" << this
<< ", m_isConnected=" << m_isConnected
<< ", pbnConnect->text()=" << m_ui->pbnConnect->text();
if (!m_ui)
return;
m_ui->pbnConnect->setText(m_isConnected ? tr("Disonnect") : tr("Connect"));
m_ui->pbnLogout->setEnabled(m_isConnected);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, this=" << this
<< ", m_isConnected=" << m_isConnected
<< ", pbnConnect->text()=" << m_ui->pbnConnect->text();
}
void ControlDialog::handleMessage(LtsWebSocket::AbstractClientMessage *message)
{
using namespace LtsProjectionWorkstation::STC;
LoginRequestReply *login = dynamic_cast<LtsProjectionWorkstation::STC::LoginRequestReply *>(message);
if (login)
{
if (login->reason() == LoginRequestReply::LoginSuccessful)
{
for (const LtsProjectionWorkstation::VncScreen &screen : login->screens())
{
VncViewerDialog *vnc = new VncViewerDialog(screen.name, this);
if (vnc->init(m_host, screen.port, screen.geometry.size()))
{
m_viewers.append(vnc);
connect(vnc, &VncViewerDialog::disconnected, this, &ControlDialog::slotOnVncDisconnected);
QString name = screen.name;
name.remove(QLatin1Char(' '));
addDialog(vnc, m_menuGroup, name,
LtsWidgets::LayoutArea::eDialogTab, false, false);
}
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful YES, this=" << this;
m_isConnected = true;
m_ui->pbnConnect->setEnabled(true);
updateConnectButton();
}
else
{
QString errMsg;
switch (login->reason())
{
case LoginRequestReply::LoginSuccessful:
break;
case LoginRequestReply::OtherUser:
errMsg = tr("The connection is blocked by user %1")
.arg(QString::fromStdString(m_opmDtc->user(login->reasonUserId()).name()));
break;
}
LtsWidgets::LtsMessageDialog::showMessage(this, errMsg, tr("Login failed!"));
DEBUG_LOG(m_logger) << LOGFUNCTION << " LoginSuccessful NO, this=" << this;
m_isConnected = false;
m_ui->pbnConnect->setEnabled(true);
updateConnectButton();
}
return;
}
LtsProjectionWorkstation::STC::DedicatedDisconnect *disconnect = dynamic_cast<LtsProjectionWorkstation::STC::DedicatedDisconnect *>(message);
if (disconnect)
{
INFO_LOG(m_logger) << " DedicatedDisconnect.. "
<< ", getOperatorId=" << LtsSessionData::LtsSessionData::getInstance()->getOperatorId()
<< ", disconnect->userid()=" << disconnect->userid();
if ( (disconnect->userid() != LtsSessionData::LtsSessionData::getInstance()->getOperatorId()) && m_isConnected)
{
slotOnWebSocketDisconnected();
INFO_LOG(m_logger) << " DedicatedDisconnect.. done";
// NLDEVCTRL-445: Projektion Filter und Kartenausschnitt:
// LtsWidgets::LtsMessageDialog::showMessage(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected"));
// LtsMessageDialog is blocking ; but we need unblocking:
new LtsWidgets::LtsMessageDialogNew(this, tr("There is already a user with a higher priority connected. Please wait until the user has ended the session."), tr("Disconnected"));
}
}
}
void ControlDialog::retranslate()
{
translate(m_ui, tr("Control Projection"));
}
} // ProjectionWorkstation
+90
View File
@@ -0,0 +1,90 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "WebSocket/pws_web_socket_client.h"
#include <QWebSocket>
#include <LtsWidgets/lts_dialog.h>
#include <LtsProjectionWorkstation/logger.h>
#include <LtsProjectionWorkstation/WebSocket/messages.h>
#include <LtsProjectionWorkstation/WebSocket/message_factory.h>
#include <Plugins/OperatorManagement/opm_settings_gui_plugin_interface.h>
#include <Plugins/OperatorManagement/Data/opm_data_plugin.h>
#include <Plugins/AssetManagement/Data/asm_data_plugin.h>
namespace Ui { class ControlDialog; }
namespace LtsSession { class LtsSession; }
namespace ProjectionWorkstation
{
class VncViewerDialog;
class ControlDialog : public LtsWidgets::LtsDialog, public LtsProjectionWorkstation::AbstractMessageHandler
{
Q_OBJECT
public:
explicit ControlDialog(const QString &menuGroup, QWidget *parent = nullptr);
~ControlDialog();
// AbstractMessageHandler interface
void handleMessage(LtsWebSocket::AbstractClientMessage *message) override final;
// LtsDialog interface
void retranslate() override final;
bool init(LtsSession::LtsSession *session,
const std::shared_ptr<OperatorManagement::Interface::Dtc> &opmDtc,
const std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> &opmIfc,
const std::shared_ptr<AssetManagement::Interface::Dtc> &asmDtc, int exerciseId);
protected:
// LtsDialog interface
void aboutToClose() override final;
void initWebSocketHandler();
private slots:
void slotOnConnectClicked();
void slotOnLogoutClicked();
void slotOnWebSocketConnected();
void slotOnVncDisconnected();
void slotOnWebSocketDisconnected();
void slotOnWebSocketError();
void slotOnWebSocketSslError();
void slotCbxWorkstationsChanged();
void fillCombobox();
private:
void updateConnectButton();
void closeViewers();
Ui::ControlDialog *m_ui;
LtsProjectionWorkstation::LOGGER_T m_logger;
LtsProjectionWorkstation::MessageFactory *m_messageFactory;
QVector<VncViewerDialog *> m_viewers;
WebSocketClient *m_webSocketClient;
LtsSession::LtsSession *m_session;
QHostAddress m_host;
QString m_menuGroup;
int m_exerciseId;
int m_locationId;
bool m_isConnected;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> m_opmIfc;
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
};
} // ProjectionWorkstation
+155
View File
@@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ControlDialog</class>
<widget class="QWidget" name="ControlDialog">
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Control Connection</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>8</number>
</property>
<item row="0" column="0" rowspan="2">
<layout class="QGridLayout" name="gridLayout_3">
<property name="spacing">
<number>12</number>
</property>
<item row="1" column="0" colspan="3">
<widget class="QComboBox" name="cbxWorkstations">
<property name="currentText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="5" column="0" rowspan="2" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QPushButton" name="pbnConnect">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbnLogout">
<property name="text">
<string>Logout</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="text">
<string>Available Workstations:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="4">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="chbInThisLocationOnly">
<property name="text">
<string>Show only in this location</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" rowspan="2" colspan="3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="3" rowspan="2">
<widget class="QCheckBox" name="chbProjectionWSOnly">
<property name="text">
<string>Show Projection-Workstations only</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
<property name="tristate">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>46</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+93
View File
@@ -0,0 +1,93 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_gui_plugin.h"
#include "pws_control_dialog.h"
#include "pws_operator_settings_loader.h"
#include <LtsSession/lts_session.h>
#include <LtsUtilsSettings/settings.h>
#include <LtsProjectionWorkstation/lts_projection_workstation.h>
#include <Services/ProjectionWorkstation/pws_operator_rights.h>
namespace ProjectionWorkstation
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
{
return std::make_shared<InterfaceImpl>(session);
}
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, LtsProjectionWorkstation::getLogger("Plugins.Gui.PWS"))
, m_isProjectionWorkstation(LtsProjectionWorkstation::LtsProjectionWorkstation::hasInstance())
{}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
if (m_session->isReplayPartition())
return ret;
LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance();
const bool enableProjection = ps->value(QStringLiteral("pws"),
QStringLiteral("enableProjection")).toBool();
if (!m_isProjectionWorkstation && enableProjection)
{
LtsWidgets::PluginData plData;
plData.menuGroup = menuGroup();
plData.menuText = tr("Projection Control");
plData.identifier = QLatin1String("pwsControl");
plData.menuIcon = QIcon(QLatin1String("icon:projection.svg"));
plData.operatorRight = OperatorRights::ProjectionStart;
plData.exercisePart = LtsWidgets::ExercisePart::eSpecial;
plData.createWidgetFunc = [this](QWidget * parent)
{
return LtsWidgets::initDialog(new ControlDialog(menuGroup(), parent),
m_session, m_opmDtc, m_opmIfc, m_asmDtc, m_session->exerciseId());
};
ret.append(plData);
}
return ret;
}
bool InterfaceImpl::fetchInterfaces()
{
if (m_isProjectionWorkstation)
{
LtsProjectionWorkstation::LoginInformation request = LtsProjectionWorkstation::LtsProjectionWorkstation::getInstance()->lastLoginRequest();
OperatorSettingsLoader loader(request.m_userId);
if (!loader.init(m_session))
return false;
loader.execute();
}
else
{
if (!m_session->getDtc<OperatorManagement::DataInterface>(m_opmDtc))
return false;
if (!m_session->getForeignGuiInterface<OperatorManagement::ForeignSettingsGuiInterface>(m_opmIfc))
return false;
if (!m_session->getDtc<AssetManagement::DataInterface>(m_asmDtc))
return false;
}
return true;
}
void InterfaceImpl::clearInterfaces()
{
m_opmDtc = nullptr;
m_opmIfc = nullptr;
m_asmDtc = nullptr;
}
} // namespace ProjectionWorkstation
+60
View File
@@ -0,0 +1,60 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <PluginInterfaces/gui_plugin_interface.h>
#include <LtsProjectionWorkstation/logger.h>
#include <Plugins/OperatorManagement/Data/opm_data_plugin.h>
#include <Plugins/OperatorManagement/opm_settings_gui_plugin_interface.h>
#include <Plugins/AssetManagement/Data/asm_data_plugin.h>
#include <memory>
namespace ProjectionWorkstation
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:pws")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("ProjectionWorkstationGuiPlugin"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partition) const override
{
return partition == IDL::PartitionType::Exercise ||
partition == IDL::PartitionType::Replay;
}
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
QVector<LtsWidgets::PluginData> getActions() override;
QString menuGroup() const override { return tr("Projection"); }
bool fetchInterfaces() override;
void clearInterfaces() override;
private:
bool m_isProjectionWorkstation;
std::shared_ptr<OperatorManagement::Interface::Dtc> m_opmDtc;
std::shared_ptr<OperatorManagement::ForeignSettingsGuiInterface> m_opmIfc;
std::shared_ptr<AssetManagement::Interface::Dtc> m_asmDtc;
};
} // namespace ProjectionWorkstation
@@ -0,0 +1,85 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_operator_settings_loader.h"
#include <LtsUtilsGuiDds/request_helper.h>
#include <LtsSession/lts_session_data.h>
#include <LtsSession/lts_session.h>
#include <PluginInterfaces/gui_plugin_interface.h>
namespace ProjectionWorkstation
{
OperatorSettingsLoader::OperatorSettingsLoader(int userId)
: m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("OperatorSettingsLoader")))
, m_opmFtc(nullptr)
, m_requestHelper(new LtsUtils::RequestHelper())
, m_userId(userId)
{
}
OperatorSettingsLoader::~OperatorSettingsLoader()
{
delete m_requestHelper;
}
bool OperatorSettingsLoader::init(LtsSession::LtsSession *session)
{
if (!session->getFtc<OperatorManagement::DataInterface>(m_opmFtc))
{
ERROR_LOG(m_logger) << "Failed to load OperatorManagement FTC!";
return false;
}
return true;
}
bool OperatorSettingsLoader::loadOperatorRights()
{
LtsSessionData::LtsSessionData *sessionData = LtsSessionData::LtsSessionData::getInstance();
int localUserId = sessionData ? sessionData->getOperatorId() : 0;
if (m_userId != 0 && localUserId != 0)
{
OperatorManagement::OperatorApplyForeignAclsRequest req;
req.id(localUserId);
req.remoteOperatorId(m_userId);
auto cb = [this, &req]() -> bool
{
return m_opmFtc->operatorApplyForeignAclsRequest(req, this);
};
m_requestHelper->addRequest(cb);
bool ret = m_requestHelper->startRequests();
return ret;
}
return false;
}
void OperatorSettingsLoader::onOperatorApplyForeignAclsReply(const Core::FunctionReplyObject &reply)
{
m_requestHelper->requestReply(reply);
}
void OperatorSettingsLoader::execute()
{
loadOperatorRights();
}
} // ProjectionWorkstation
@@ -0,0 +1,48 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <OperatorManagement/opm_data_plugin_interface.h>
#include <LtsProjectionWorkstation/logger.h>
namespace LtsSession { class LtsSession; }
namespace LtsUtils { class RequestHelper; }
namespace ProjectionWorkstation
{
class OperatorSettingsLoader
: public OperatorManagement::Interface::Ftc::OperatorApplyForeignAclsReplyHandler
{
public:
explicit OperatorSettingsLoader(int userId);
~OperatorSettingsLoader();
bool init(LtsSession::LtsSession *session);
// OperatorCreateModifyReplyHandler interface
void onOperatorApplyForeignAclsReply(const Core::FunctionReplyObject &reply) override final;
void execute();
private:
bool loadOperatorRights();
LtsProjectionWorkstation::LOGGER_T m_logger;
std::shared_ptr<OperatorManagement::Interface::Ftc> m_opmFtc;
LtsUtils::RequestHelper *m_requestHelper;
int m_userId;
};
} // ProjectionWorkstation
@@ -0,0 +1,398 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#include "pws_vnc_viewer_dialog.h"
#include "ui_pws_vnc_viewer_dialog.h"
#include <LtsProjectionWorkstation/logger.h>
#include <QMutexLocker>
#include <QPainter>
#include <QOpenGLPaintDevice>
#include <QOpenGLFunctions>
#include <QThread>
#include <QTimer>
#include <cmath>
namespace ProjectionWorkstation
{
// Dialog
VncViewerDialog::VncViewerDialog(const QString &name, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::SeamlessDialog, false)
, m_ui(new Ui::VncViewerDialog)
, m_handler(nullptr)
, m_renderArea(nullptr)
, m_mouseButtonMask(0)
, m_mouseScrollReleaseTimer(new QTimer(this))
, m_name(name)
, m_isConnected(false)
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncViewerDialog")))
{
m_ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
}
VncViewerDialog::~VncViewerDialog()
{
delete m_ui;
}
bool VncViewerDialog::init(const QHostAddress &host, quint16 port, const QSize &remoteResolution)
{
m_handler = new VncHandler(host, port, this);
m_renderArea = new VncRenderArea(remoteResolution, this);
m_remoteResolution = remoteResolution;
m_ui->gridLayout->addWidget(m_renderArea);
connect(m_handler, &VncHandler::frameBufferUpdateReceived, m_renderArea, &VncRenderArea::frameBufferUpdateReceived);
connect(m_handler, &VncHandler::cutTextReceived, this, &VncViewerDialog::handleServerCuttext);
connect(m_handler, &VncHandler::beepReceived, this, &QApplication::beep);
connect(m_handler, &VncHandler::connected, this, &VncViewerDialog::show);
connect(m_handler, &VncHandler::disconnected, this, &VncViewerDialog::slotOnDisconnected);
connect(m_mouseScrollReleaseTimer, &QTimer::timeout, this, &VncViewerDialog::onScrollRelease);
setMouseTracking(true);
m_mouseScrollReleaseTimer->setSingleShot(true);
m_mouseScrollReleaseTimer->setInterval(5);
m_handler->connectToHost();
hide();
return true;
}
void VncViewerDialog::retranslate()
{
translate(m_ui, m_name);
}
void VncViewerDialog::handleServerCuttext(const QString &cutText)
{
DEBUG_LOG(m_logger) << "Got cuttext: " << cutText;
}
void VncViewerDialog::slotOnDisconnected()
{
// closeRequest();
// m_renderArea->clear();
emit disconnected();
}
void VncViewerDialog::focusInEvent(QFocusEvent *event)
{
Q_UNUSED(event)
grabKeyboard();
// setCursor(Qt::BlankCursor);
if (m_renderArea)
{
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
m_handler->writeData(data);
}
}
void VncViewerDialog::focusOutEvent(QFocusEvent *event)
{
Q_UNUSED(event)
releaseKeyboard();
// unsetCursor();
}
void VncViewerDialog::onScrollRelease()
{
m_mouseButtonMask &= ~(3 << 3);
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, m_lastScrollPos.x(), m_lastScrollPos.y());
m_handler->writeData(data);
}
void VncViewerDialog::onSlotConnected()
{
m_isConnected = true;
}
void VncViewerDialog::mousePressEvent(QMouseEvent *event)
{
event->accept();
if (event->button() == Qt::LeftButton)
m_mouseButtonMask |= (1 << 0);
else if (event->button() == Qt::MidButton)
m_mouseButtonMask |= (1 << 1);
else if (event->button() == Qt::RightButton)
m_mouseButtonMask |= (1 << 2);
QPoint mapped = mapMousePosition(event->pos());
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
m_handler->writeData(data);
}
void VncViewerDialog::mouseReleaseEvent(QMouseEvent *event)
{
event->accept();
if (event->button() == Qt::LeftButton)
m_mouseButtonMask &= ~(1 << 0);
else if (event->button() == Qt::MidButton)
m_mouseButtonMask &= ~(1 << 1);
else if (event->button() == Qt::RightButton)
m_mouseButtonMask &= ~(1 << 2);
QPoint mapped = mapMousePosition(event->pos());
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
m_handler->writeData(data);
}
void VncViewerDialog::mouseMoveEvent(QMouseEvent *event)
{
event->accept();
QPoint mapped = mapMousePosition(event->pos());
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
m_handler->writeData(data);
}
void VncViewerDialog::keyPressEvent(QKeyEvent *event)
{
event->accept();
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), true);
m_handler->writeData(data);
}
void VncViewerDialog::keyReleaseEvent(QKeyEvent *event)
{
event->accept();
QByteArray data = RfbProtocol::CTS::RfbKeyEvent(event->nativeVirtualKey(), false);
m_handler->writeData(data);
}
void VncViewerDialog::wheelEvent(QWheelEvent *event)
{
if (!m_mouseScrollReleaseTimer->isActive())
{
if (event->angleDelta().y() > 0)
m_mouseButtonMask |= (1 << 3);
else if (event->angleDelta().y() < 0)
m_mouseButtonMask |= (1 << 4);
QPoint mapped = mapMousePosition(event->pos());
QByteArray data = RfbProtocol::CTS::RfbPointerEvent(m_mouseButtonMask, mapped.x(), mapped.y());
m_handler->writeData(data);
m_lastScrollPos = mapped;
m_mouseScrollReleaseTimer->start();
}
}
void VncViewerDialog::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event)
if (m_isConnected && m_renderArea)
{
emit m_renderArea->resized();
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
m_handler->writeData(data);
}
}
void VncViewerDialog::showEvent(QShowEvent *event)
{
Q_UNUSED(event)
if (m_isConnected && m_renderArea)
{
QByteArray data = RfbProtocol::CTS::RfbFrameBufferUpdateRequest(false, 0, 0, m_renderArea->frameSize().width(), m_renderArea->frameSize().height());
m_handler->writeData(data);
}
}
static inline int mapValue(double val, double maxIn, double maxOut)
{
val = qBound(0., val, maxIn);
return qRound(val * (maxOut / maxIn));
}
QPoint VncViewerDialog::mapMousePosition(const QPoint &point) const
{
QPoint clienPoint = m_renderArea->mapFromParent(point);
int x = mapValue(clienPoint.x(), m_renderArea->scaledSize().width(), m_renderArea->frameSize().width());
int y = mapValue(clienPoint.y(), m_renderArea->scaledSize().height(), m_renderArea->frameSize().height());
return QPoint(x, y);
}
// Render Area Worker
VncRenderAreaWorker::VncRenderAreaWorker()
: m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
{
qRegisterMetaType<QVector<RfbProtocol::RfbRectangle>>("QVector<RfbProtocol::RfbRectangle>");
}
void VncRenderAreaWorker::onRfbFrameBufferUpdate(const QVector<RfbProtocol::RfbRectangle> &rectangles)
{
if (rectangles.isEmpty())
return;
QPainter painter(&m_renderedImage);
// no dequeue here otherwise rectangles() is dangling
for (const RfbProtocol::RfbRectangle &rect : rectangles)
{
if (rect.pixelData.isEmpty())
{
WARNING_LOG(m_logger) << "No valid vnc pixel data... skipping frame";
continue;
}
QImage img(reinterpret_cast<const unsigned char *>(rect.pixelData.data()),
rect.width,
rect.height,
QImage::Format_RGB32);
if (img.isNull())
{
WARNING_LOG(m_logger) << "Could not decode image data... skipping vnc frame";
continue;
}
painter.drawImage(QPoint(rect.xpos, rect.ypos), img);
}
painter.end();
QImage img = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
if (img.size() != m_scaledSizeAspectRatio)
{
m_scaledSizeAspectRatio = img.size();
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
}
{
QMutexLocker l(&m_mutex);
std::swap(img, m_scaledImage);
}
emit newImageAvailable();
}
void VncRenderAreaWorker::setImageSize(QSize s)
{
QMutexLocker l(&m_mutex);
m_renderedImage = QImage(s, QImage::Format_RGB32);
}
void VncRenderAreaWorker::setScaledSize(QSize s)
{
if (m_scaledSize == s)
return;
QMutexLocker l(&m_mutex);
m_scaledSize = s;
if (!m_renderedImage.isNull())
{
m_scaledImage = m_renderedImage.scaled(m_scaledSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
m_scaledSizeAspectRatio = m_scaledImage.size();
emit newScaleSizeAspectRatio(m_scaledSizeAspectRatio);
}
}
QImage VncRenderAreaWorker::renderedImage() const
{
QMutexLocker l(&m_mutex);
return m_scaledImage;
}
//Render Area
VncRenderArea::VncRenderArea(const QSize remoteResolution, QWidget *parent)
: QOpenGLWidget(parent)
, m_imageRenderThread(new QThread(this))
, m_renderAreaWorker(new VncRenderAreaWorker)
, m_frameSize(remoteResolution)
, m_fbo(nullptr)
, m_logger(LtsProjectionWorkstation::getLogger(QStringLiteral("VncRenderArea")))
{
setAttribute(Qt::WA_TransparentForMouseEvents);
connect(this, &VncRenderArea::frameBufferUpdateReceived,
m_renderAreaWorker, &VncRenderAreaWorker::onRfbFrameBufferUpdate);
connect(m_renderAreaWorker, &VncRenderAreaWorker::newImageAvailable,
this, &VncRenderArea::prepareRender);
connect(m_renderAreaWorker, &VncRenderAreaWorker::newScaleSizeAspectRatio,
this, [this](QSize s) { m_scaledSizeAspectRatio = s; });
m_renderAreaWorker->setImageSize(remoteResolution);
m_renderAreaWorker->moveToThread(m_imageRenderThread);
m_imageRenderThread->start();
}
VncRenderArea::~VncRenderArea()
{
delete m_fbo;
m_imageRenderThread->quit();
m_imageRenderThread->wait(2000);
delete m_imageRenderThread;
delete m_renderAreaWorker;
}
void VncRenderArea::clear()
{
if (context())
{
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
update();
}
}
void VncRenderArea::initializeGL()
{
initializeOpenGLFunctions();
m_blitter.create();
}
void VncRenderArea::paintGL()
{
if (context() && m_fbo)
{
m_blitter.bind();
const QRect targetRect(QPoint(0, 0), m_fbo->size());
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(targetRect, targetRect); // no scaling here, is not anti-aliased
m_blitter.blit(m_fbo->texture(), target, QOpenGLTextureBlitter::OriginBottomLeft);
m_blitter.release();
}
}
void VncRenderArea::maybeResized()
{
rebuildFbo(size());
m_renderAreaWorker->setScaledSize(size());
}
void VncRenderArea::prepareRender()
{
if (context())
{
if (!m_fbo || m_fbo->size() != size())
maybeResized();
m_fbo->bind();
QOpenGLPaintDevice device(size());
QPainter painter(&device);
painter.drawImage(QPoint(0, 0), m_renderAreaWorker->renderedImage());
painter.end();
m_fbo->release();
update();
}
}
void VncRenderArea::rebuildFbo(QSize newSize)
{
delete m_fbo;
QOpenGLFramebufferObjectFormat format;
format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
m_fbo = new QOpenGLFramebufferObject(newSize, format);
}
} // ProjectionWorkstation
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <Plugins/ProjectionWorkstation/Common/pws_rfb_protocol.h>
#include <Plugins/ProjectionWorkstation/Common/pws_vnc_handler.h>
#include <QMutex>
#include <QOpenGLFramebufferObject>
#include <QOpenGLTextureBlitter>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QHostAddress>
namespace Ui { class VncViewerDialog; }
namespace ProjectionWorkstation
{
class VncRenderAreaWorker : public QObject
{
Q_OBJECT
public:
explicit VncRenderAreaWorker();
void setImageSize(QSize s);
void setScaledSize(QSize s);
QImage renderedImage() const;
public Q_SLOTS:
void onRfbFrameBufferUpdate(const QVector<RfbProtocol::RfbRectangle> &rectangles);
Q_SIGNALS:
void newImageAvailable();
void newScaleSizeAspectRatio(QSize s);
private:
QImage m_renderedImage; // store pixeldata
QImage m_scaledImage;
mutable QMutex m_mutex;
QSize m_scaledSize;
QSize m_scaledSizeAspectRatio;
LtsProjectionWorkstation::LOGGER_T m_logger;
};
class VncRenderArea : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit VncRenderArea(const QSize remoteResolution, QWidget *parent);
~VncRenderArea();
// QOpenGLWidget interface
void clear();
QSize frameSize() const { return m_frameSize; }
QSize scaledSize() const { return m_scaledSizeAspectRatio; }
void maybeResized();
protected:
void initializeGL() override final;
void paintGL() override final;
Q_SIGNALS:
void frameBufferUpdateReceived(const QVector<RfbProtocol::RfbRectangle> &rectangles);
public slots:
void prepareRender();
private:
void rebuildFbo(QSize newSize);
private:
QThread *m_imageRenderThread;
VncRenderAreaWorker *m_renderAreaWorker;
QSize m_scaledSizeAspectRatio;
QSize m_frameSize;
QOpenGLFramebufferObject *m_fbo;
QOpenGLTextureBlitter m_blitter;
LtsProjectionWorkstation::LOGGER_T m_logger;
}; // VncRenderArea
class VncViewerDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
VncViewerDialog(const QString &name, QWidget *parent = nullptr);
~VncViewerDialog();
bool init(const QHostAddress &host, quint16 port, const QSize &remoteResolution);
// LtsDialog interface
void retranslate() override final;
signals:
void disconnected();
private slots:
void handleServerCuttext(const QString &cutText);
void slotOnDisconnected();
void onScrollRelease();
void onSlotConnected();
protected:
// QWidget interface
void focusInEvent(QFocusEvent *event) override final;
void focusOutEvent(QFocusEvent *event) override final;
void mousePressEvent(QMouseEvent *event) override final;
void mouseReleaseEvent(QMouseEvent *event) override final;
void mouseMoveEvent(QMouseEvent *event) override final;
void keyPressEvent(QKeyEvent *event) override final;
void keyReleaseEvent(QKeyEvent *event) override final;
void wheelEvent(QWheelEvent *event) override final;
void resizeEvent(QResizeEvent *event) override final;
void showEvent(QShowEvent *event) override final;
private:
QPoint mapMousePosition(const QPoint &point) const;
Ui::VncViewerDialog *m_ui;
VncHandler *m_handler;
VncRenderArea *m_renderArea;
quint8 m_mouseButtonMask;
QTimer *m_mouseScrollReleaseTimer;
QPoint m_lastScrollPos;
QSize m_remoteResolution;
QString m_name;
bool m_isConnected;
LtsProjectionWorkstation::LOGGER_T m_logger;
}; // VncViewerDialog
} // ProjectionWorkstation
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VncViewerDialog</class>
<widget class="QWidget" name="VncViewerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">Form</string>
</property>
<layout class="QGridLayout" name="gridLayout"/>
</widget>
<resources/>
<connections/>
</ui>