/*
# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
#
# This program is free software distributed under the GPL version 2.
# See http://openslx.org/COPYING
#
# If you have any feedback please consult http://openslx.org/feedback and
# send your suggestions, praise, or complaints to feedback@openslx.org
#
# General information about OpenSLX can be found at http://openslx.org/
# -----------------------------------------------------------------------------
# src/gui/connectionFrame.cpp
# -----------------------------------------------------------------------------
*/
#include "connectionframe.h"
#include "../mainwindow/mainwindow.h"
#include "../net/client.h"
#include <QImage>
#include <cassert>
#include <cmath>
static QString style_student(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black;} \
QGroupBox { background-color: #AAA; margin: 2px; border-radius: 4px}"
);
static QString style_tutor(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black;} \
QGroupBox { background-color: #70C670; margin: 2px; border-radius: 4px}"
);
static QString style_attention(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black;} \
QGroupBox { background-color: #C88; margin: 2px; border-radius: 4px}"
);
static QString style_selectedStudent(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black; } \
QGroupBox { background-color: #ccebff; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
);
static QString style_selectedTutor(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black;} \
QGroupBox { background-color: #9f9; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
);
static QString style_selectedAttention(
"QLabel{ background-color: #FFF; border-radius: 2px; color: black;} \
QGroupBox { background-color: #E99; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
);
static QString style_exam (
"QLabel{ background-color: #919191; color: black; } \
QGroupBox { background-color: #d35400; margin: 1px; border-radius: 4px}"
);
static QString style_exam_selected (
"QLabel{ background-color: #919191; color: black; } \
QGroupBox { background-color: #cc743a; margin: 1px; border-radius: 4px}"
);
static QString style_disconnected(
"QLabel{ background-color: #919191; border-radius: 2px; color: black; } \
QGroupBox { background-color: #7F7F7F; margin: 1px; border-radius: 4px}"
);
static QIcon *term = NULL, *cam = NULL, *eye = NULL, *lock = NULL;
bool ConnectionFrame::paintDisabled = false;
/**
* Initialize frame for connected client.
* @param parent
* @param width
* @param height
*/
ConnectionFrame::ConnectionFrame(MainWindow* main, QWidget *parent, bool fromRoomplan) :
QGroupBox(parent), _client(NULL), _timerId(0), _timerCounter(0), _isSelected(false), _isTutor(false),
_isFromRoomplan(fromRoomplan), _mainWindow(main)
{
//defines the ui-stuff
// load icons first
if (term == NULL) {
term = new QIcon(":terminal");
cam = new QIcon(":cf_cam");
eye = new QIcon(":cf_eye");
lock = new QIcon(":cf_lock");
}
_mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
_mainLayout->setSpacing(1);
_mainLayout->setMargin(3);
_mainLayout->setAlignment(Qt::AlignHCenter);
this->setStyleSheet(style_student);
_iconLayout = new QBoxLayout(QBoxLayout::RightToLeft, NULL);
_iconLayout->setSpacing(1);
_iconLayout->setMargin(3);
_lblUserName = new QLabel("Test", this);
_lblUserName->setAlignment(Qt::AlignHCenter);
_lblHostName = new QLabel("PC", this);
_lblHostName->setAlignment(Qt::AlignHCenter);
_lblExamMode = new QLabel(tr("EXAM-MODE"), this);
_lblExamMode->setAlignment(Qt::AlignHCenter);
_lblExamMode->setVisible(false);
_icoCam = addIcon(cam);
_icoEye = addIcon(eye);
_icoLock = addIcon(lock);
_iconLayout->addWidget(_icoCam);
_iconLayout->addWidget(_icoEye);
_iconLayout->addWidget(_icoLock);
_iconLayout->addStretch();
_mainLayout->addLayout(_iconLayout);
_mainLayout->addStretch();
_mainLayout->addWidget(_lblExamMode);
_mainLayout->addStretch();
_mainLayout->addWidget(_lblUserName);
_mainLayout->addWidget(_lblHostName);
this->setLayout(_mainLayout);
this->updateAppearance();
this->updateLabels();
}
ConnectionFrame::~ConnectionFrame()
{
if (_client != NULL) {
_client->deleteLater();
}
_iconLayout->deleteLater();
}
void ConnectionFrame::setGridPosition(const QPoint& pos)
{
_gridPosition = pos;
updateGeometry();
}
void ConnectionFrame::setGridPosition(int x, int y)
{
setGridPosition(QPoint(x, y));
}
void ConnectionFrame::updateGeometry()
{
const QRect rect = _mainWindow->calcFrameGeometry(this);
setGeometry(rect);
if (this->_client == NULL)
showDefaultThumb();
}
/**
* Add icon to connection frame.
* @param icon
* @return Pointer to new Icon.
*/
QLabel* ConnectionFrame::addIcon(const QIcon* icon)
{
QLabel *label = new QLabel(this);
label->setPixmap(icon->pixmap(24, 24, QIcon::Normal, QIcon::On));
label->setAttribute(Qt::WA_TranslucentBackground);
label->hide();
_icons.append(label);
return label;
}
/**
* Assign client to connectionFrame.
* Set hostName, userName and tutor status to display.
* @param client
*/
void ConnectionFrame::assignClient(Client* client)
{
assert(_client == NULL);
connect( client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()) );
connect( client, SIGNAL(thumbUpdated(Client*, const QImage&)), this, SLOT(onThumbUpdated(Client*, const QImage&)) );
connect( client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(updateAppearance()));
connect( client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(updateAppearance()));
connect( client, SIGNAL(stateChanged()), this, SLOT(updateAppearance()));
_client = client;
_computerId = client->ip();
updateLabels();
showDefaultThumb();
if (_timerId == 0)
_timerId = startTimer(1000 + qrand() % 150);
this->updateAppearance();
_client->setTutor(_isTutor);
}
/**
* Show default thumg instead of remote screen in connectionFrame.
*/
void ConnectionFrame::showDefaultThumb()
{
_remoteScreen = term->pixmap(_desiredThumbSize, QIcon::Normal, QIcon::On).toImage();
this->update();
}
void ConnectionFrame::calcDesiredThumbSize(const QSize &frameSize)
{
const int width = frameSize.width() - 10;
const int height = frameSize.height() - 12 - _lblHostName->height() - (_lblUserName->isHidden() ? 0 : _lblUserName->height());
_desiredThumbSize = QSize(width, height);
}
void ConnectionFrame::updateLabels()
{
_lblHostName->setText(_computerId);
if (_client == NULL) {
_lblUserName->setText(QString());
_lblUserName->hide();
} else {
_lblHostName->setToolTip(_client->host());
_lblUserName->setText(_client->name());
_lblUserName->show();
}
calcDesiredThumbSize(this->size());
}
/**
* Handle mouse release event on frame.
* Check if frame was clicked or moved, if not moved enough, the event is handled as click.
* @param event
*/
void ConnectionFrame::mouseReleaseEvent(QMouseEvent* event)
{
event->accept();
if (event->button() == Qt::LeftButton) {
QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
// Only recognize a move if the distance is larger than _startDragDistance
if ((this->pos() - _previousPosition).manhattanLength() > _startDragDistance ) {
emit frameMoved(this);
} else {
move(_previousPosition);
emit clicked(this);
}
}
}
/**
* Handle if mouse reaches frame.
* @param event
*/
void ConnectionFrame::enterEvent(QEvent* event)
{
QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
event->accept();
}
/**
* Handle mouse leaves frame.
* @param event
*/
void ConnectionFrame::leaveEvent(QEvent* event)
{
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
event->accept();
}
/**
* Handle mouse press on frame.
* @param event
*/
void ConnectionFrame::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {
// Menu...
} else {
_clickPoint = event->pos();
_previousPosition = this->pos();
QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
}
// On click, the window has to be on the top-level.
activateWindow();
raise();
update();
event->accept();
}
/**
* Hanle mouse movement over frame.
* @param event
*/
void ConnectionFrame::mouseMoveEvent(QMouseEvent *event)
{
QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
move(mapToParent(event->pos() - _clickPoint));
event->accept();
}
/**
* Handle double click event on frame.
* @param event
*/
void ConnectionFrame::mouseDoubleClickEvent(QMouseEvent* event)
{
emit doubleClicked(this);
event->accept();
}
/**
* Draw remote screen in frame.
* @param event
*/
void ConnectionFrame::paintEvent(QPaintEvent *event)
{
if (ConnectionFrame::paintDisabled) {
return;
}
QGroupBox::paintEvent(event);
if (_remoteScreen.isNull()) {
return;
}
if (!_desiredThumbSize.isEmpty()) {
if (abs(100 - ((_remoteScreen.width() * 100) / _desiredThumbSize.width())) > 5
&& abs(100 - ((_remoteScreen.height() * 100) / _desiredThumbSize.height())) > 5) {
qDebug() << "Rescale thumb" << _remoteScreen.size() << "to" << _desiredThumbSize;
_remoteScreen = _remoteScreen.scaled(_desiredThumbSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
}
QPainter painter(this);
painter.drawImage((this->width() - _remoteScreen.width()) / 2, 4, _remoteScreen);
event->accept();
}
/**
* Handle timer event.
* @param event
*/
void ConnectionFrame::timerEvent(QTimerEvent* /* event */ )
{
if (_client == NULL)
return;
++_timerCounter;
if (_client->isActiveVncServer() && _timerCounter % 5 != 0)
return;
_client->requestThumb(_desiredThumbSize);
}
/**
* Set frame as selected or not.
* @param selected
*/
void ConnectionFrame::setSelection(bool selected)
{
if (_isSelected == selected)
return;
_isSelected = selected;
this->updateAppearance();
}
/**
* Set tutor status of frame.
* @param b
*/
void ConnectionFrame::setTutor(bool b)
{
if (_isTutor != b && _client != NULL)
_client->setTutor(b);
_isTutor = b;
this->updateAppearance();
}
/**
* Update appearence of frame.
* Check tutor status, vnc status and set background or icon.
*/
void ConnectionFrame::updateAppearance()
{
if (_client == NULL) {
// Unconnected Frame
if (_isSelected) {
this->setStyleSheet(style_selectedStudent);
} else if (_isTutor) {
this->setStyleSheet(style_tutor);
} else {
this->setStyleSheet(style_disconnected);
}
for (QList<QLabel*>::iterator it(_icons.begin()); it != _icons.end(); ++it) {
(**it).hide();
}
return;
}
_icoCam->setVisible(_client->isActiveVncServer());
_icoEye->setVisible(_client->isActiveVncClient());
_icoLock->setVisible(_client->isLocked());
_lblExamMode->setVisible(_client->isExamMode());
// Normal client, no special stuff active
if (_isSelected && _isTutor) {
this->setStyleSheet(style_selectedTutor);
} else if (_isTutor) {
this->setStyleSheet(style_tutor);
} else if (_isSelected && _client->wantsAttention()) {
this->setStyleSheet(style_selectedAttention);
} else if (_client->wantsAttention()) {
this->setStyleSheet(style_attention);
} else if (_client->isExamMode()) {
if (_isSelected) {
this->setStyleSheet(style_exam_selected);
} else {
this->setStyleSheet(style_exam);
}
} else if (_isSelected) {
this->setStyleSheet(style_selectedStudent);
} else {
this->setStyleSheet(style_student);
}
}
/*
* Slots
*/
/**
* Handle if client was disconnected.
* Kill timer and set appearence to default.
*/
void ConnectionFrame::onClientDisconnected()
{
if (_timerId != 0) {
killTimer(_timerId);
_timerId = 0;
}
_client = NULL;
updateLabels();
updateAppearance();
showDefaultThumb();
}
/**
* Update thumb of specific client.
* @param client
* @param thumb
*/
void ConnectionFrame::onThumbUpdated(Client* client, const QImage& thumb)
{
assert(client == _client);
_remoteScreen = thumb;
this->update();
}