Pool Video Switch v2
Software video switch for distributed remote display in a lecture environment
connectionframe.cpp
Go to the documentation of this file.
1 /*
2 # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
3 #
4 # This program is free software distributed under the GPL version 2.
5 # See http://openslx.org/COPYING
6 #
7 # If you have any feedback please consult http://openslx.org/feedback and
8 # send your suggestions, praise, or complaints to feedback@openslx.org
9 #
10 # General information about OpenSLX can be found at http://openslx.org/
11 # -----------------------------------------------------------------------------
12 # src/gui/connectionFrame.cpp
13 # -----------------------------------------------------------------------------
14 */
15 
16 
17 #include "connectionframe.h"
18 #include "../mainwindow/mainwindow.h"
19 #include "../../shared/util.h"
20 #include "../net/client.h"
21 
22 #include <QImage>
23 #include <QResizeEvent>
24 #include <QBoxLayout>
25 #include <QLabel>
26 #include <QApplication>
27 #include <QPainter>
28 
29 static const QString style_student(
30  "QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"
31  " QGroupBox { background-color: #AAA; margin: 2px; border-radius: 4px}"
32 );
33 static const QString style_tutor(
34  "QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"
35  " QGroupBox { background-color: #70C670; margin: 2px; border-radius: 4px}"
36 );
37 static const QString style_attention(
38  "QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"
39  " QGroupBox { background-color: #C88; margin: 2px; border-radius: 4px}"
40 );
41 static const QString style_selectedStudent(
42  "QLabel{ background-color: #FFF; border-radius: 2px; color: black; }"
43  " QGroupBox { background-color: #ccebff; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
44 );
45 static const QString style_selectedTutor(
46  "QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"
47  " QGroupBox { background-color: #9f9; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
48 );
49 static const QString style_selectedAttention(
50  "QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"
51  " QGroupBox { background-color: #E99; margin: 0px; border-radius: 4px; border: 4px solid #6C8CF0;}"
52 );
53 static const QString style_exam (
54  "QLabel{ background-color: #919191; color: black; }"
55  " QGroupBox { background-color: #d35400; margin: 1px; border-radius: 4px}"
56 );
57 static const QString style_exam_selected (
58  "QLabel{ background-color: #919191; color: black; }"
59  " QGroupBox { background-color: #cc743a; margin: 1px; border-radius: 4px}"
60 );
61 static const QString style_disconnected(
62  "QLabel{ background-color: #919191; border-radius: 2px; color: black; }"
63  " QGroupBox { background-color: #7F7F7F; margin: 1px; border-radius: 4px}"
64 );
65 
66 static QIcon *term = nullptr, *cam = nullptr, *eye = nullptr, *lock = nullptr;
67 
68 static const int START_DRAG_DISTANCE = 40;
69 
71 
79  : QGroupBox(parent)
80  , _isFromRoomplan(fromRoomplan)
81  , _mainWindow(main)
82 {
83 
84  //defines the ui-stuff
85  // load icons first
86  if (term == nullptr) {
87  term = new QIcon(":terminal");
88  cam = new QIcon(":cf_cam");
89  eye = new QIcon(":cf_eye");
90  lock = new QIcon(":cf_lock");
91  }
92 
93  _mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this);
94  _mainLayout->setSpacing(1);
95  _mainLayout->setMargin(3);
96  _mainLayout->setAlignment(Qt::AlignHCenter);
97  this->setStyleSheet(style_student);
98 
99  _iconLayout = new QBoxLayout(QBoxLayout::RightToLeft, nullptr);
100  _iconLayout->setSpacing(1);
101  _iconLayout->setMargin(3);
102 
103  _lblUserName = new QLabel("Test", this);
104  _lblUserName->setAlignment(Qt::AlignHCenter);
105 
106  _lblHostName = new QLabel("PC", this);
107  _lblHostName->setAlignment(Qt::AlignHCenter);
108 
109  _lblExamMode = new QLabel(tr("EXAM-MODE"), this);
110  _lblExamMode->setAlignment(Qt::AlignHCenter);
111  _lblExamMode->setVisible(false);
112 
113  _icoCam = addIcon(cam);
114  _icoEye = addIcon(eye);
115  _icoLock = addIcon(lock);
116 
117  _iconLayout->addWidget(_icoCam);
118  _iconLayout->addWidget(_icoEye);
119  _iconLayout->addWidget(_icoLock);
120  _iconLayout->addStretch();
121 
122  _mainLayout->addLayout(_iconLayout);
123  _mainLayout->addStretch();
124  _mainLayout->addWidget(_lblExamMode);
125  _mainLayout->addStretch();
126  _mainLayout->addWidget(_lblUserName);
127  _mainLayout->addWidget(_lblHostName);
128  this->setLayout(_mainLayout);
129  this->updateAppearance();
130  this->updateLabels();
131 }
132 
134 {
135  if (_client != nullptr) {
136  _client->deleteLater();
137  }
138  _iconLayout->deleteLater();
139 }
140 
141 void ConnectionFrame::setGridPosition(const QPoint& pos)
142 {
143  _gridPosition = pos;
144  updateGeometry();
145 }
146 
148 {
149  setGridPosition(QPoint(x, y));
150 }
151 
153 {
154  const QRect rect = _mainWindow->calcFrameGeometry(this);
155  setGeometry(rect);
156  if (this->_client == nullptr)
158 }
159 
165 QLabel* ConnectionFrame::addIcon(const QIcon* icon)
166 {
167  auto *label = new QLabel(this);
168  label->setPixmap(icon->pixmap(24, 24, QIcon::Normal, QIcon::On));
169  label->setAttribute(Qt::WA_TranslucentBackground);
170  label->hide();
171  _icons.append(label);
172  return label;
173 }
174 
181 {
182  assert(_client == nullptr);
183  _client = client;
189  _computerId = client->ip();
190  updateLabels();
192  if (_timerId == 0)
193  _timerId = startTimer(slxrand() % 150 + 1000);
194  this->updateAppearance();
196 }
197 
202 {
203  _remoteScreen = term->pixmap(_desiredThumbSize, QIcon::Normal, QIcon::On).toImage();
204  this->update();
205 }
206 
207 void ConnectionFrame::calcDesiredThumbSize(const QSize &frameSize)
208 {
209  const int width = frameSize.width() - 10;
210  const int height = frameSize.height() - 12 - _lblHostName->height() - (_lblUserName->isHidden() ? 0 : _lblUserName->height());
211  _desiredThumbSize = QSize(width, height);
212 }
213 
215 {
216  _lblHostName->setText(_computerId);
217  if (_client == nullptr) {
218  _lblUserName->setText(QString());
219  _lblUserName->hide();
220  } else {
221  _lblHostName->setToolTip(_client->host());
222  _lblUserName->setText(_client->name());
223  _lblUserName->show();
224  }
225  calcDesiredThumbSize(this->size());
226 }
227 
232 void ConnectionFrame::enterEvent(QEvent* event)
233 {
234  QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
235  event->accept();
236 }
237 
242 void ConnectionFrame::leaveEvent(QEvent* event)
243 {
244  QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
245  event->accept();
246 }
247 
252 void ConnectionFrame::mousePressEvent(QMouseEvent *event)
253 {
254  event->accept();
255  if (event->button() == Qt::RightButton) {
256  // Menu...
257  } else if (event->button() == Qt::LeftButton) {
258  _clickPoint = event->pos();
259  _previousPosition = this->pos();
260  QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
261  }
262  // On click, the window has to be on the top-level.
263  raise();
264  update();
265 }
266 
271 void ConnectionFrame::mouseMoveEvent(QMouseEvent *event)
272 {
273  event->accept();
274  QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
275  move(mapToParent(event->pos() - _clickPoint));
276  if ((this->pos() - _previousPosition).manhattanLength() > START_DRAG_DISTANCE) {
277  emit frameMoving(this);
278  }
279 }
280 
286 void ConnectionFrame::mouseReleaseEvent(QMouseEvent* event)
287 {
288  event->accept();
289  QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
290  if (event->button() == Qt::LeftButton) {
291  // Only recognize a move if the distance is larger than _startDragDistance
292  if ((this->pos() - _previousPosition).manhattanLength() > START_DRAG_DISTANCE) {
293  emit frameMoved(this);
294  } else {
295  move(_previousPosition);
296  emit clicked(this);
297  }
298  }
299 }
300 
306 {
307  emit doubleClicked(this);
308  event->accept();
309 }
310 
315 void ConnectionFrame::paintEvent(QPaintEvent *event)
316 {
318  return;
319  }
320  QGroupBox::paintEvent(event);
321  if (_remoteScreen.isNull()) {
322  return;
323  }
324 
325  if (!_desiredThumbSize.isEmpty()) {
326  if (abs(100 - ((_remoteScreen.width() * 100) / _desiredThumbSize.width())) > 5
327  && abs(100 - ((_remoteScreen.height() * 100) / _desiredThumbSize.height())) > 5) {
328  qDebug() << "Rescale thumb" << _remoteScreen.size() << "to" << _desiredThumbSize;
329  _remoteScreen = _remoteScreen.scaled(_desiredThumbSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
330  }
331  }
332 
333  QPainter painter(this);
334  painter.drawImage((this->width() - _remoteScreen.width()) / 2, 4, _remoteScreen);
335  event->accept();
336 }
337 
342 void ConnectionFrame::timerEvent(QTimerEvent* /* event */ )
343 {
344  if (_client == nullptr)
345  return;
346  ++_timerCounter;
347  if (_client->isActiveVncServer() && _timerCounter % 5 != 0)
348  return;
350 }
351 
356 void ConnectionFrame::setSelection(bool selected)
357 {
358  if (_isSelected == selected)
359  return;
360  _isSelected = selected;
361  this->updateAppearance();
362 }
363 
369 {
370  if (_isTutor != b && _client != nullptr)
371  _client->setTutor(b);
372  _isTutor = b;
373  this->updateAppearance();
374 }
375 
381 {
382  if (_client == nullptr) {
383  // Unconnected Frame
384  if (_isSelected) {
385  this->setStyleSheet(style_selectedStudent);
386  } else if (_isTutor) {
387  this->setStyleSheet(style_tutor);
388  } else {
389  this->setStyleSheet(style_disconnected);
390  }
391  for (auto *_icon : _icons) {
392  _icon->hide();
393  }
394  return;
395  }
396  _icoCam->setVisible(_client->isActiveVncServer());
397  _icoEye->setVisible(_client->isActiveVncClient());
398  _icoLock->setVisible(_client->isLocked());
399 
400 
401  _lblExamMode->setVisible(_client->isExamMode());
402  // Normal client, no special stuff active
403 
404  if (_isSelected && _isTutor) {
405  this->setStyleSheet(style_selectedTutor);
406  } else if (_isTutor) {
407  this->setStyleSheet(style_tutor);
408  } else if (_isSelected && _client->wantsAttention()) {
409  this->setStyleSheet(style_selectedAttention);
410  } else if (_client->wantsAttention()) {
411  this->setStyleSheet(style_attention);
412  } else if (_client->isExamMode()) {
413  if (_isSelected) {
414  this->setStyleSheet(style_exam_selected);
415  } else {
416  this->setStyleSheet(style_exam);
417  }
418  } else if (_isSelected) {
419  this->setStyleSheet(style_selectedStudent);
420  } else {
421  this->setStyleSheet(style_student);
422  }
423 }
424 
425 /*
426  * Slots
427  */
428 
434 {
435  if (_timerId != 0) {
436  killTimer(_timerId);
437  _timerId = 0;
438  }
439  _client = nullptr;
440  updateLabels();
443 }
444 
450 void ConnectionFrame::onThumbUpdated(Client* client, const QImage& thumb)
451 {
452  assert(client == _client);
453  _remoteScreen = thumb;
454  this->update();
455 }
456 
457 void ConnectionFrame::resizeEvent(QResizeEvent *event) {
458  calcDesiredThumbSize(event->size());
459  QGroupBox::resizeEvent(event);
460 }
461 
QLabel * _lblHostName
QRect calcFrameGeometry(ConnectionFrame *frame) const
Definition: mainwindow.cpp:814
void setTutor(bool enable)
Definition: client.h:47
void onClientDisconnected()
Handle if client was disconnected.
bool isLocked() const
Definition: client.h:39
void leaveEvent(QEvent *event) override
Handle mouse leaves frame.
const QString & host() const
Definition: client.h:34
QString ip() const
Definition: client.cpp:349
~ConnectionFrame() override
void setTutor(bool b)
Set tutor status of frame.
QLabel * _lblUserName
Client * client() const
void timerEvent(QTimerEvent *event) override
Handle timer event.
void onThumbUpdated(Client *client, const QImage &thumb)
Update thumb of specific client.
void stateChanged()
static QIcon * term
void paintEvent(QPaintEvent *event) override
Draw remote screen in frame.
void setGridPosition(int x, int y)
void doubleClicked(ConnectionFrame *frame)
void mousePressEvent(QMouseEvent *event) override
Handle mouse press on frame.
static const 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;}")
QBoxLayout * _iconLayout
void resizeEvent(QResizeEvent *event) override
void disconnected()
static const QString style_disconnected("QLabel{ background-color: #919191; border-radius: 2px; color: black; }"" QGroupBox { background-color: #7F7F7F; margin: 1px; border-radius: 4px}")
static const 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;}")
void showDefaultThumb()
Show default thumg instead of remote screen in connectionFrame.
QLabel * addIcon(const QIcon *icon)
Add icon to connection frame.
static bool paintDisabled
static const QString style_student("QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"" QGroupBox { background-color: #AAA; margin: 2px; border-radius: 4px}")
bool wantsAttention() const
Definition: client.h:43
void updateAppearance()
Update appearence of frame.
static const QString style_exam("QLabel{ background-color: #919191; color: black; }"" QGroupBox { background-color: #d35400; margin: 1px; border-radius: 4px}")
QList< QLabel * > _icons
#define slxrand()
Definition: util.h:19
static const 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;}")
void mouseMoveEvent(QMouseEvent *event) override
Hanle mouse movement over frame.
MainWindow * _mainWindow
QPoint _previousPosition
static const QString style_attention("QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"" QGroupBox { background-color: #C88; margin: 2px; border-radius: 4px}")
bool isActiveVncClient() const
Definition: client.h:37
Definition: client.h:23
const QString & name() const
Definition: client.h:33
void thumbUpdated(Client *client, const QImage &thumb)
void requestThumb(const QSize &size)
Definition: client.cpp:111
void setSelection(bool selected)
Set frame as selected or not.
QLabel * _lblExamMode
bool isActiveVncServer() const
Definition: client.h:38
void enterEvent(QEvent *event) override
Handle if mouse reaches frame.
void frameMoved(ConnectionFrame *frame)
static const QString style_tutor("QLabel{ background-color: #FFF; border-radius: 2px; color: black;}"" QGroupBox { background-color: #70C670; margin: 2px; border-radius: 4px}")
void vncClientStateChange(Client *client)
void calcDesiredThumbSize(const QSize &frameSize)
void clicked(ConnectionFrame *frame)
void vncServerStateChange(Client *client)
ConnectionFrame(MainWindow *main, QWidget *parent, bool fromRoomplan=false)
Initialize frame for connected client.
static QIcon * eye
QBoxLayout * _mainLayout
void assignClient(Client *client)
Assign client to connectionFrame.
static const int START_DRAG_DISTANCE
static QIcon * lock
static const QString style_exam_selected("QLabel{ background-color: #919191; color: black; }"" QGroupBox { background-color: #cc743a; margin: 1px; border-radius: 4px}")
void mouseDoubleClickEvent(QMouseEvent *event) override
Handle double click event on frame.
int isExamMode() const
Definition: client.h:42
Initializing MainWindow.
Definition: mainwindow.h:27
void frameMoving(ConnectionFrame *frame)
void mouseReleaseEvent(QMouseEvent *e) override
Handle mouse release event on frame.
static QIcon * cam
int main(int argc, char **argv)
Definition: main.cpp:12