/*
# 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/
# -----------------------------------------------------------------------------
# mainWindow.cpp
This is the Main class for the pvsManager. The GUI is constructed here.
# -----------------------------------------------------------------------------
*/
// Self
#include "mainwindow.h"
// QT stuff
#include <QtGui>
#include <QFileDialog>
#include <QNetworkInterface>
// Other custom UI elements
#include "../clicklabel/clicklabel.h"
#include "../sessionnamewindow/sessionnamewindow.h"
#include "../connectionframe/connectionframe.h"
#include "../helpwindow/helpwindow.h"
// Network
#include "../net/listenserver.h"
#include "../net/client.h"
#include "../net/discoverylistener.h"
#include "../net/filedownloader.h"
// Others
#include "../../shared/settings.h"
#include "../util/util.h"
#include "../util/global.h"
// Auto-generated ui class
#include "ui_mainwindow.h"
const QString MainWindow::sStrTutorNdef = tr("No tutor defined.");
const QString MainWindow::sStrTutorOffline = tr("Tutor is offline.");
const QString MainWindow::sStrSourceNdef = tr("Please select a projection source.");
const QString MainWindow::sStrSourceOffline = tr("The projection source is offline.");
const QString MainWindow::sStrDestNdef = tr("Please select a projection destination.");
const QString MainWindow::sStrDestOffline = tr("The projection destination is offline.");
const QString MainWindow::sStrSourceDestSame = tr("Selected projection target is tutor.");
const QString MainWindow::sStrClientOnline = tr("Selected client is currently online.");
const QString MainWindow::sStrNoDestAv = tr("No projection destination available.");
/***************************************************************************//**
* Initialize MainWindow and ListenServer.
* @param ipListUrl
* @param parent
*/
MainWindow::MainWindow(QString ipListUrl, QWidget* parent) :
QMainWindow(parent), ui(new Ui::MainWindow), _tbIconSize(24), _tbArea(Qt::LeftToolBarArea)
{
_mode = Mode::Multicast;
_streamingSource = 0;
_sessionNameWindow = new SessionNameWindow(this);
_helpWindow = new HelpWindow(this);
_helpWindow->setWindowTitle("Help");
ui->setupUi(this);
Global::setSessionName();
//conWin = new ConnectionWindow(ui->widget);
//ui->VconWinLayout->addWidget(conWin);
//conList = new ConnectionList(ui->ClWidget);
//ui->ClientGLayout->addWidget(conList);
// Show only session name label, not textbox
_sessionNameLabel = new ClickLabel(this);
ui->mainLayout->addWidget(_sessionNameLabel);
ui->frmRoom->setStyleSheet("background-color:#444");
// toolbar and actions in pvsmgr
ui->action_Exit->setStatusTip(tr("Exit"));
ui->action_Lock->setStatusTip(tr("Lock or Unlock all Clients"));
_tileWidth = 10;
_tileHeight = 10;
// Initialize FileDownloader.
if (!ipListUrl.isEmpty())
{
_fileDownloader.connectSlot(this, SLOT(onTutorListDownloaded(QByteArray&)));
_fileDownloader.downloadFile(QUrl(ipListUrl));
}
// Close button in tool bar
connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit()));
connect(ui->action_BroadcastScreen, SIGNAL(triggered()), this, SLOT(onButtonStudentToAll()));
connect(ui->action_TutorToAll, SIGNAL(triggered()), this, SLOT(onButtonTutorToAll()));
connect(ui->action_StudentToTutor, SIGNAL(triggered()), this, SLOT(onButtonStudentToTutor()));
connect(ui->action_StudentToTutorExclusive, SIGNAL(triggered()), this, SLOT(onButtonStudentToTutorExclusive()));
connect(ui->action_TutorToStudent, SIGNAL(triggered()), this, SLOT(onButtonTutorToStudent()));
connect(ui->action_StopProjection, SIGNAL(triggered()), this, SLOT(onButtonStopProjection()));
connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonSetAsTutor()));
connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonStopProjection()));
connect(ui->action_Lock, SIGNAL(toggled(bool)), this, SLOT(onButtonLock(bool)));
connect(ui->action_Help, SIGNAL(triggered()), this, SLOT(onButtonHelp()));
/* Stuff for the button lock */
//Setup a timeout
_buttonLockTimer = new QTimer(this);
_buttonLockTimer->setSingleShot(true);
_buttonLockTimer->setInterval(_buttonBlockTime);
connect(_buttonLockTimer, SIGNAL(timeout()), this, SLOT(EnableButtons()));
// Define the locking buttons
_lockingButtons
<< ui->action_Lock
<< ui->action_BroadcastScreen
<< ui->action_TutorToAll
<< ui->action_StudentToTutor
<< ui->action_TutorToStudent
<< ui->action_StopProjection
<< ui->action_SetAsTutor;
// Clicking the session name label shows the edit field for it
connect(_sessionNameLabel, SIGNAL(clicked()), this, SLOT(onSessionNameClick()));
// Listen to updates to the session name through the session name window
connect(_sessionNameWindow, SIGNAL(updateSessionName()), this,
SLOT(onSessionNameUpdate()));
setAttribute(Qt::WA_QuitOnClose);
setUnifiedTitleAndToolBarOnMac(true);
this->showMaximized(); // show the Mainwindow maximized
_listenServer = new ListenServer(CLIENT_PORT);
connect(_listenServer, SIGNAL(newClient(Client*)), this, SLOT(onClientConnected(Client*)));
_discoveryListener = new DiscoveryListener();
// Finally
this->onSessionNameUpdate();
}
MainWindow::~MainWindow()
{
_sessionNameLabel->deleteLater();
delete ui;
}
/***************************************************************************//**
* Place the frames at possible Position.
* If current position out of range, place frame into next free cell.
* @param frame
*/
void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame)
{
// Get occupied cell of each frame and store status in an array
const int elems = _tilesX * _tilesY;
int currentIndex = 0;
bool grid[elems];
memset(grid, 0, sizeof(bool) * elems); // set everything to false
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
const QPoint &oldpos = (*it)->frameGeometry().topLeft();
const int tx = oldpos.x() / _tileWidth;
const int ty = oldpos.y() / _tileHeight;
const int index = tx + ty * _tilesX;
if (frame == *it)
{
// Current frame is the frame to place, remember its old index
currentIndex = index;
continue;
}
if (index < 0 || index >= elems)
{
// Current frame is out of bounds, place in top left corner
qDebug("Move A");
(*it)->move(0, 0);
grid[0] = true;
continue;
}
// Just mark cell as occupied in array
grid[index] = true;
}
// Safety
if (currentIndex < 0 || currentIndex >= elems)
currentIndex = 0;
// Now place in next free cell, start looking at current position of frame
for (int i = 0; i < elems; ++i)
{
const int index = (i + currentIndex) % elems;
if (grid[index])
continue;
const int x = (index % _tilesX) * _tileWidth;
const int y = (index / _tilesX) * _tileHeight;
//qDebug() << "Index=" << index << " i=" << i << " cI=" << currentIndex << " elems=" << elems << " x=" << x << " y=" << y;
qDebug("Move B");
frame->move(x, y);
break;
}
}
/***************************************************************************//**
* Create new Frame.
* Create new frame and add to current available frame list.
* Also connect signals frameMoved() and clicked() with slots.
* @return ConnectionFrame*
*/
ConnectionFrame* MainWindow::createFrame()
{
// Allocate and resize
ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, _tileWidth, _tileHeight);
_clientFrames.append(cf);
cf->show();
connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *)));
connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *)));
return cf;
}
/***************************************************************************//**
* Load position.
* @param settings
* @param id
* @param x
* @param y
* @return If loadPosition was successfull.
*/
bool MainWindow::loadPosition(QSettings& settings, const QString& id, int& x, int& y)
{
settings.beginGroup("client_position");
const QVariant retval(settings.value(id));
settings.endGroup();
if (retval.type() != QVariant::Point)
return false;
const QPoint point(retval.toPoint());
x = point.x();
y = point.y();
return true;
}
/***************************************************************************//**
* Save position of given connectionFrame.
* @param cf
*/
void MainWindow::savePosition(ConnectionFrame *cf)
{
Client *client = cf->client();
if (client == NULL)
return;
QPoint pos(cf->pos().x() / _tileWidth, cf->pos().y() / _tileHeight);
USER_SETTINGS(settings);
settings.beginGroup("client_position");
settings.setValue(client->ip(), pos);
settings.endGroup();
}
/***************************************************************************//**
* Tells the new client the current situation in class room.
* Set the specific parameters like projection source or lock screen.
* @param client
*/
void MainWindow::tellClientCurrentSituation(Client* client)
{
// If clients are currently locked, tell this new client
if (ui->action_Lock->isChecked())
client->lockScreen(true);
if (_mode == Mode::Broadcast){
// _watchers.insert(client->id(), client);
client->setWatcher(true);
Client* c = getClientFromId(_streamingSource);
if (c != NULL) {
client->startVncClient(c);
}
}
else if (_mode == Mode::LockedMulticast)
client->lockScreen(true);
}
/***************************************************************************//**
* Returns connected client which belongs to given id.
* Iterating over ConnectionFrames and comparing id to given id.
* @param id
* @return Client with given id, if not NULL.
*/
Client* MainWindow::getClientFromId(int id)
{
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL)
{
if ((*it)->client()->id() == id)
return (*it)->client();
}
}
return NULL;
}
/***************************************************************************//**
* Return the Frame, which is currently beeing Tutor.
* Iterating over all ConnectionFrames, and looking for flag _isTutor.
* @return Frame with flag _isTutor = true,
* else NULL if no Tutor is available.
*/
ConnectionFrame* MainWindow::getTutorFrame()
{
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if (((*it) != NULL) && ((*it)->isTutor()))
return (*it);
}
return NULL;
}
/***************************************************************************//**
* Return the Frame, which is currently selected by user.
* Iterating over all ConnectionFrame and looking for flag _isSelected.
* @return Frame with flag _isSelected = true,
* else NULL if no frame is selected.
*/
ConnectionFrame* MainWindow::getSelectedFrame()
{
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if (((*it) != NULL) && ((*it)->isSelected()))
return (*it);
}
return NULL;
}
/*
* Overridden methods
*/
/***************************************************************************//**
* Handle closeEvent.
* If user calls closing MainWindow, close, else ignore.
* @param e
*/
void MainWindow::closeEvent(QCloseEvent* e)
{
int ret = QMessageBox::question(this, "Test", "Exit?", 0, 1, 2);
if (ret == 1)
QApplication::exit(0);
else
e->ignore();
}
/***************************************************************************//**
* Change Event.
* @param e
*/
void MainWindow::changeEvent(QEvent* e)
{
QMainWindow::changeEvent(e);
}
/***************************************************************************//**
* Resize event.
* @param e
*/
void MainWindow::resizeEvent(QResizeEvent* e)
{
// Resize all connection windows
if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100 || _tilesX <= 0 || _tilesY <= 0)
return;
const int nw = ui->frmRoom->size().width() / _tilesX;
const int nh = ui->frmRoom->size().height() / _tilesY;
for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
{
const QPoint &oldpos = (*it)->frameGeometry().topLeft();
qDebug("Move C");
(*it)->move((oldpos.x() / _tileWidth) * nw, (oldpos.y() / _tileHeight) * nh);
(*it)->setSize(nw, nh);
}
// Resize trash and set position.
const int width = ui->frmRoom->geometry().width() - (nw + 1);
const int height = ui->frmRoom->geometry().height() - (nh + 1);
ui->trash->move(width, height);
ui->trash->resize(nw, nh);
// qDebug() << "Trash pos: " << ui->trash->pos();
_tileWidth = nw;
_tileHeight = nh;
}
/***************************************************************************//**
* Handle Mouse Release Event.
* Check if click was inside the frame and if that is the case, set the selection of each connected
* client to false.
* @param e
*/
void MainWindow::mouseReleaseEvent(QMouseEvent* e)
{
// Try to figure out if the user clicked inside the "room" frame
// No idea if there is a more elegant way to do this without
// sub-classing that frame and overriding its mouseReleaseEvent
const QPoint pos(ui->frmRoom->mapFrom(this, e->pos()));
if (pos.x() < 0 || pos.y() < 0)
return;
const QSize frame(ui->frmRoom->size());
if (frame.width() > pos.x() && frame.height() > pos.y())
{
if (getSelectedFrame() != NULL)
{
getSelectedFrame()->setSelection(false);
}
}
}
/***************************************************************************//**
* @brief reset
*/
void MainWindow::reset()
{
// Unlock all clients
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
if ((*it)->client() != NULL)
(*it)->client()->lockScreen(false);
// Stop server (Clients get stopped on ACK)
if (getClientFromId(_streamingSource) != NULL)
getClientFromId(_streamingSource)->stopVncServer();
}
/*
* Slots
*/
/***************************************************************************//**
* Extract information from downloaded tutorList.
* Split downloaded file by new line and store IPs in _tutorList.
* @param tutorList
*/
void MainWindow::onTutorListDownloaded(QByteArray& tutorList)
{
// qDebug() << tutorList;
QString data = QString::fromUtf8(tutorList.constData(), tutorList.size());
_tutorList = data.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
qDebug() << _tutorList;
}
/***************************************************************************//**
* Place Frame to from user specified position.
* @param frame
*/
void MainWindow::onPlaceFrame(ConnectionFrame* frame)
{
if (_tilesX <= 0 || _tilesY <= 0)
return;
const QPoint &p = frame->frameGeometry().center();
const QSize &s = ui->frmRoom->geometry().size();
int x = (p.x() / _tileWidth) * _tileWidth;
int y = (p.y() / _tileHeight) * _tileHeight;
if (x < 0)
x = 0;
else if (x > s.width() - _tileWidth)
x = (_tilesX - 1) * _tileWidth;
if (y < 0)
y = 0;
else if (y > s.height() - _tileHeight)
y = (_tilesY - 1) * _tileHeight;
const QRect &trashCenter = ui->trash->geometry();
// Check if x coordinate is in trash position.
if (trashCenter.contains(p))
{
// Do not offer deleting online client.
if (frame->client() != NULL)
{
QMessageBox::critical(this, tr("Selection"), sStrClientOnline);
frame->move(frame->getPreviousPosition());
return;
}
else
{
int ret = QMessageBox::question(this, "Warning", "Sure, You want to delete selected client?", 0, 1, 2);
if (ret == 1)
{
frame->hide();
frame->deleteLater();
_clientFrames.removeOne(frame);
return;
}
else
{
frame->move(frame->getPreviousPosition());
return;
}
}
}
qDebug("Move D");
frame->move(x, y);
savePosition(frame);
const QPoint &newpos = frame->frameGeometry().topLeft();
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if (*it == frame)
continue;
if ((*it)->frameGeometry().topLeft() == newpos)
{
placeFrameInFreeSlot(*it);
}
}
}
/***************************************************************************//**
* Mark given frame after it was clicked on.
* @param frame
*/
void MainWindow::onFrameClicked(ConnectionFrame* frame)
{
// If same frame is clicked again,, do nothing
if (getSelectedFrame() == frame)
return;
// If another frame has been selected, unselect it
// Set the ui selected and set a new reference
if (getSelectedFrame() != NULL)
{
getSelectedFrame()->setSelection(false);
}
frame->setSelection(true);
qDebug() << "ID of frame: " << frame->computerId();
qDebug() << "ID of selectedFrame: " << getSelectedFrame()->computerId();
}
/***************************************************************************//**
* Show session name, after it was clicked on.
*/
void MainWindow::onSessionNameClick()
{
_sessionNameWindow->show(Global::sessionName());
}
/***************************************************************************//**
* Update session name.
*/
void MainWindow::onSessionNameUpdate()
{
// Stop all projections and clear all clients, which where connected to old sessionName.
reset();
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
(*it)->hide();
(*it)->deleteLater();
}
_clientFrames.clear();
_sessionNameLabel->setText(tr("Session Name: %1 [click to edit]").arg(Global::sessionName()));
}
/***************************************************************************//**
* @brief MainWindow::changeProjection
* @param from
* @param mode
* @param to
*/
void MainWindow::changeProjection(Client *from, Mode mode, Client *to)
{
DisableButtons();
if (mode == Mode::Broadcast)
{
// Set all clients as watchers
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL && (*it)->client() != getClientFromId(_streamingSource))
{
// _watchers.insert((*it)->client()->id(), (*it)->client());
(*it)->client()->setWatcher(true);
}
}
}
else // mode != Mode::Broadcast
{
// If this is the first call in this mode clear the watchers
if ((mode == Mode::LockedMulticast && _mode != Mode::LockedMulticast)
|| (mode == Mode::Multicast && _mode != Mode::Multicast))
{
// _watchers.clear();
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL)
(*it)->client()->setWatcher(false);
}
}
// If "to" already watches "from" stop it
// if (_watchers.contains(to->id()))
if (getClientFromId(to->id())->isWatcher())
{
// _watchers.remove(to->id());
getClientFromId(to->id())->setWatcher(false);
}
else // list "to" as watcher
{
// _watchers.insert(to->id(), to);
getClientFromId(to->id())->setWatcher(true);
}
}
// Set the mode
_mode = mode;
// if there is a server running which is not "from" stop it.
if (getClientFromId(_streamingSource) != NULL && getClientFromId(_streamingSource) != from)
getClientFromId(_streamingSource)->stopVncServer();
// Set new streaming source
_streamingSource = from->id();
// If streaming source is already active avoid a restart
Client* c = getClientFromId(_streamingSource);
if (c != NULL) {
if (c->isActiveVncServer())
this->onVncServerStateChange(c);
else // Could not take shortcut, (re)start VNC server on source
c->startVncServer();
}
}
/***************************************************************************//**
* Display popup which explains possible actions about the buttons.
*/
void MainWindow::onButtonHelp()
{
_helpWindow->show();
}
/***************************************************************************//**
* Handle projection from tutor to all.
* Get the client who is tutor and set the projectionSource of all
* clients, except the tutor ones, to false.
*/
void MainWindow::onButtonTutorToAll()
{
ui->action_Lock->setChecked(false);
if (getTutorFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
else if (getTutorFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
else if (_clientFrames.size() == 1)
QMessageBox::critical(this, tr("Projection"), sStrNoDestAv);
else
changeProjection(getTutorFrame()->client(), Mode::Broadcast);
}
/***************************************************************************//**
* Button projection from one student to all the others.
* First get which client is projectionSource and set this one as from.
* Set projection source for all clients,except the selected one, to false.
*/
void MainWindow::onButtonStudentToAll()
{
ui->action_Lock->setChecked(false);
if (getSelectedFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
if (getSelectedFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
else
changeProjection(getSelectedFrame()->client(), Mode::Broadcast);
}
/***************************************************************************//**
* Handle the projection from Tutor to specific student.
* Set the client who is tutor as from and the selected client as to.
*/
void MainWindow::onButtonTutorToStudent()
{
ui->action_Lock->setChecked(false);
if (getSelectedFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrDestNdef);
else if (getTutorFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
else if (getSelectedFrame() == getTutorFrame())
QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
else if (getSelectedFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrDestOffline);
else if (getTutorFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
else
changeProjection(getTutorFrame()->client(), Mode::Multicast, getSelectedFrame()->client());
}
/***************************************************************************//**
* Handle projection from one student to tutor.
* Get the client to project from and get client, who is tutor, as to.
*/
void MainWindow::onButtonStudentToTutor()
{
ui->action_Lock->setChecked(false);
if (getSelectedFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
else if (getTutorFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
else if (getTutorFrame() == getSelectedFrame())
QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
else if (getSelectedFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
else if (getTutorFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
else
changeProjection(getSelectedFrame()->client(), Mode::Multicast, getTutorFrame()->client());
}
/***************************************************************************//**
* @brief MainWindow::onButtonStudentToTutorExclusive
*/
void MainWindow::onButtonStudentToTutorExclusive()
{
ui->action_Lock->setChecked(false);
if (getSelectedFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
else if (getTutorFrame() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
else if (getTutorFrame() == getSelectedFrame())
QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
else if (getSelectedFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
else if (getTutorFrame()->client() == NULL)
QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
else
changeProjection(getSelectedFrame()->client(), Mode::LockedMulticast, getTutorFrame()->client());
}
/***************************************************************************//**
* Handle Button StopProjection.
* Set ProjectionSource of each client to false, stop the active VNC Server
* and the active VNC Client(s) and unlock all screens.
*/
void MainWindow::onButtonStopProjection()
{
ui->action_Lock->setChecked(false);
reset();
}
/***************************************************************************//**
* Handle button to lock or unlock screens of client(s).
* If already locked, do nothing, else lock or unlock the clients, except the
* tutor and the manager running machine.
* @param checked
*/
void MainWindow::onButtonLock(bool checked)
{
// Stop all projections
reset();
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() == NULL)
continue;
(*it)->client()->lockScreen(checked);
}
}
/***************************************************************************//**
* On button exit, close application.
*/
void MainWindow::onButtonExit()
{
this->close();
}
/***************************************************************************//**
* Handle button to set specific client as tutor.
* Unset active tutor and set selected client, if not inactive, as new tutor.
*/
void MainWindow::onButtonSetAsTutor()
{
ui->action_Lock->setChecked(false);
// If no frame is selected, warning.
if (getSelectedFrame() == NULL)
{
QMessageBox::critical(this, tr("Selection"), tr("No client is selected."));
return;
}
// If frame of inactive client has been selected unselect it
if (getSelectedFrame()->client() == NULL)
{
QMessageBox::critical(this, tr("Selection"), tr("The selected client is not connected."));
return;
}
else // If selected client is locked, first unlock
{
getSelectedFrame()->client()->lockScreen(false);
}
// If same frame is already tutor, do nothing
if (getSelectedFrame() == getTutorFrame())
return;
// Else unset the old and set the new tutor
if (getTutorFrame() != NULL)
{
getTutorFrame()->setTutor(false);
}
getSelectedFrame()->setTutor(true);
}
/***************************************************************************//**
* Handle from ListenServer signaled new client connection.
* @param client
*/
void MainWindow::onClientConnected(Client* client)
{
connect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
connect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
}
/***************************************************************************//**
* Authenticate new Client client.
* Check if incoming client ip already exists,
* if yes --> return.
* Check if same name of client already exist,
* if yes --> name new client as name(x).
* x = number of clients with the same name.
* @param client
* @param request
*/
void MainWindow::onClientAuthenticating(Client* client, ClientLogin* request)
{
disconnect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
if (!request->accept) // Another receiver of that signal already rejected the client, so nothing to do
return;
bool inuse;
QString check = request->name;
int addnum = 1;
do
{
inuse = false;
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
Client *c = (**it).client();
if (c == NULL)
continue;
if (!c->isAuthed())
continue;
if (c->ip() == request->ip)
{
request->accept = false;
return;
}
if (c->name() == check)
{
inuse = true;
check = request->name + " (" + QString::number(++addnum) + ")";
break;
}
}
} while (inuse && addnum < 100);
if (inuse)
request->accept = false;
else
request->name = check;
}
/***************************************************************************//**
* New client was authenticated, make several checks.
* Check if new clients ip already exists,
* if yes --> not necessary to create new clientFrame.
* if not --> create a new one and adapt this one to current
* situation in class room(projection / lock screen).
* Check if avctiv tutor is available,
* if not --> check if new client is possible tutor.
* @param client
*/
void MainWindow::onClientAuthenticated(Client* client)
{
disconnect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)));
connect(client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(onVncClientStateChange(Client*)));
bool hasActiveTutor = false;
ConnectionFrame *existing = NULL;
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->computerId() == client->ip())
existing = *it;
if ((*it)->isTutor())
{
if ((*it)->client() != NULL)
hasActiveTutor = true;
}
}
bool isTutor = false;
if (!hasActiveTutor)
{
for (int i = 0; i < _tutorList.size(); i++)
{
// Check if client is possible tutor
if (client->ip() == _tutorList[i])
{
isTutor = true;
break;
}
}
}
// Clients ip already exists, but was not active.
if (existing != NULL)
{
existing->setTutor(isTutor);
existing->assignClient(client);
tellClientCurrentSituation(client);
return;
}
if (_clientFrames.size() > 50) {
client->deleteLater();
return;
}
// New one, create
ConnectionFrame *cf = createFrame();
// Try to load last known position
int x, y;
bool ok;
USER_SETTINGS(usr);
ok = loadPosition(usr, client->ip(), x, y);
if (!ok)
{
SYSTEM_SETTINGS(sys);
ok = loadPosition(sys, client->ip(), x, y);
}
if (x >= _tilesX || y >= _tilesY)
ok = false;
if (ok)
{
if (x < 0)
x = 0;
if (y < 0)
y = 0;
qDebug("Move E");
cf->move(x * _tileWidth, y * _tileHeight);
onPlaceFrame(cf);
}
else
{
// Move to any free tile
placeFrameInFreeSlot(cf);
}
// Set Tutor option
cf->setTutor(isTutor);
// Assign client instance
cf->assignClient(client);
tellClientCurrentSituation(client);
}
/***************************************************************************//**
* Handle if VNC Server State has changed.
* Check if VNC server has been started/stopped on some client and start VNC server/reset pending
* VNC projection information.
* @param client
*/
void MainWindow::onVncServerStateChange(Client* client)
{
if (client == getClientFromId(_streamingSource))
EnableButtons();
if (client->isActiveVncServer())
{
if (_mode == Mode::Broadcast)
{
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL)
{
// Unlock all online clients
(*it)->client()->lockScreen(false);
// Start VNCclients on all online clients but source
if ((*it)->client() != client)
(*it)->client()->startVncClient(client);
}
}
}
else // !Mode::Broadcast --> Mode::LockedMC || Mode::MC
{
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
// Ignore offline clients
if ( (*it)->client() != NULL)
{
// if (_watchers.contains((*it)->client()->id()))
if ((*it)->client()->isWatcher())
{
// Unlock destination and connect VNCclient
(*it)->client()->lockScreen(false);
(*it)->client()->startVncClient(client);
}
else if ((*it)->client()->id() == client->id() )
{
// Unlock source
(*it)->client()->lockScreen(false);
}
else
{
// Lock others and stop their clients
(*it)->client()->lockScreen(_mode == Mode::LockedMulticast);
(*it)->client()->stopVncClient();
}
}
}
}
}
else
{
// VNC server stopped on some client or failed to start - reset local pending projection information
// foreach (Client *c, _watchers) {
// c->stopVncClient();
// }
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL)
{
if ((*it)->client()->isWatcher())
(*it)->client()->stopVncClient();
}
}
}
}
/***************************************************************************//**
* Handle VNC client state change.
* Check if any vnc client is in use, and if that is not the case, stop VNC Server.
* @param client
* @param lastProjectionSource
*/
void MainWindow::onVncClientStateChange(Client* client)
{
if (client != NULL)
{
// VNC Client stopped -> remove from watchers
if (!client->isActiveVncClient()){
// _watchers.remove(client->id());
if (getClientFromId(client->id()) != NULL)
getClientFromId(client->id())->setWatcher(false);
// If noboody is watching the multicast stop VNC server
// if (_watchers.isEmpty() && _mode != Mode::Broadcast)
// _streamingSource->stopVncServer();
bool noWatchers = true;
for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
{
if ((*it)->client() != NULL)
{
if ((*it)->client()->isWatcher())
noWatchers = false;
}
}
if (noWatchers && _mode != Mode::Broadcast)
{
Client* c = getClientFromId(_streamingSource);
if (c != NULL)
c->stopVncServer();
}
}
}
}
/***************************************************************************//**
* DisableButtons.
*/
void MainWindow::DisableButtons()
{
_buttonLockTimer->start();
foreach (QAction* a, _lockingButtons)
a->setDisabled(true);
}
/***************************************************************************//**
* EnableButtons.
*/
void MainWindow::EnableButtons()
{
_buttonLockTimer->stop();
foreach (QAction* a, _lockingButtons)
a->setEnabled(true);
}