/* # 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 #include #include 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::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(); }