/* # 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 #include #include // 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."); /***************************************************************************//** * 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) { _tutorFrame = NULL; _selectedFrame = NULL; _mode = Mode::Multicast; _streamingSource = NULL; _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")); // 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 */ _buttonLockTimer = new QTimer(this); _buttonLockTimer->setSingleShot(true); _buttonLockTimer->setInterval(_buttonBlockTime); _lockingButtons << ui->action_Lock << ui->action_BroadcastScreen << ui->action_TutorToAll << ui->action_StudentToTutor << ui->action_TutorToStudent << ui->action_StopProjection << ui->action_SetAsTutor; // Disable the buttons if a button is clicked foreach (QAction* a, _lockingButtons) connect(a, SIGNAL(triggered()), this, SLOT(DisableButtons())); // Start the timer a button is clicked foreach (QAction* a, _lockingButtons) connect(a, SIGNAL(triggered()), _buttonLockTimer, SLOT(start())); // Enable the buttons if the timer fires connect(_buttonLockTimer, SIGNAL(timeout()), this, SLOT(EnableButtons())); // 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 _tileWidth = 10; _tileHeight = 10; _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::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->startVncClient(_streamingSource); } else if (_mode == Mode::LockedMulticast) client->lockScreen(true); } /***************************************************************************//** * Checks if client and manager runs on same machine. * @param client * @return Return true, if pvsmanager is running on client. */ bool MainWindow::isManagerMachine(Client* client) { foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) if (address != QHostAddress(QHostAddress::LocalHost) && client != NULL && client->ip() == address.toString()) return true; return false; } Client* MainWindow::getClientFromId(int id) { for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { if ((*it)->client() != NULL) { if ((*it)->client()->id() == id) return (*it)->client(); } } 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::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); } _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 (_selectedFrame != NULL) { _selectedFrame->setSelection(false); _selectedFrame = NULL; } } } /***************************************************************************//** * @brief reset */ void MainWindow::reset() { // Unlock all clients for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) if ((*it)->client() != NULL) (*it)->client()->lockScreen(false); // Stop server (Clients get stopped on ACK) if (_streamingSource != NULL) _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; qDebug("Move D"); frame->move(x, y); savePosition(frame); const QPoint &newpos = frame->frameGeometry().topLeft(); for (QList::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 (_selectedFrame == frame) return; // If another frame has been selected, unselect it // Set the ui selected and set a new reference if (_selectedFrame != NULL) _selectedFrame->setSelection(false); _selectedFrame = frame; _selectedFrame->setSelection(true); } /***************************************************************************//** * Show session name, after it was clicked on. */ void MainWindow::onSessionNameClick() { _sessionNameWindow->show(Global::sessionName()); } /***************************************************************************//** * Update session name. */ void MainWindow::onSessionNameUpdate() { _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) { if (mode == Mode::Broadcast) { // Set all clients as watchers for (QList::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) if ((*it)->client() != NULL && (*it)->client() != _streamingSource) _watchers.insert((*it)->client()->id(), (*it)->client()); } 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(); // If "to" already watches "from" stop it if (_watchers.contains(to->id())) _watchers.remove(to->id()); else // list "to" as watcher _watchers.insert(to->id(), to); } // Set the mode _mode = mode; // if there is a server running which is not "from" stop it. if (_streamingSource != NULL && _streamingSource != from) _streamingSource->stopVncServer(); // Set new streaming source _streamingSource = from; // If streaming source is already active avoid a restart if (_streamingSource->isActiveVncServer()) this->onVncServerStateChange(_streamingSource); else // Could not take shortcut, (re)start VNC server on source _streamingSource->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 (_tutorFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorNdef); else if (_tutorFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorOffline); else changeProjection(_tutorFrame->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 (_selectedFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceNdef); if (_selectedFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceOffline); else changeProjection(_selectedFrame->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 (_selectedFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrDestNdef); else if (_tutorFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorNdef); else if (_selectedFrame == _tutorFrame) QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame); else if (_selectedFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrDestOffline); else if (_tutorFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorOffline); else changeProjection(_tutorFrame->client(), Mode::Multicast, _selectedFrame->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 (_selectedFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceNdef); else if (_tutorFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorNdef); else if (_tutorFrame == _selectedFrame) QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame); else if (_selectedFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceOffline); else if (_tutorFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorOffline); else changeProjection(_selectedFrame->client(), Mode::Multicast, _tutorFrame->client()); } /***************************************************************************//** * @brief MainWindow::onButtonStudentToTutorExclusive */ void MainWindow::onButtonStudentToTutorExclusive() { ui->action_Lock->setChecked(false); if (_selectedFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceNdef); else if (_tutorFrame == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorNdef); else if (_tutorFrame == _selectedFrame) QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame); else if (_selectedFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrSourceOffline); else if (_tutorFrame->client() == NULL) QMessageBox::critical(this, tr("Projection"), sStrTutorOffline); else changeProjection(_selectedFrame->client(), Mode::LockedMulticast, _tutorFrame->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::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { // Check if client is Tutor or the manager is also running on this machine. bool isManager = isManagerMachine((*it)->client()); if ((*it)->client() == NULL || isManager) 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 (_selectedFrame == NULL) { QMessageBox::critical(this, tr("Selection"), tr("No client is selected.")); return; } // If frame of inactive client has been selected unselect it if (_selectedFrame->client() == NULL) { QMessageBox::critical(this, tr("Selection"), tr("The selected client is not connected.")); return; } else // If selected client is locked, first unlock _selectedFrame->client()->lockScreen(false); // If same frame is already tutor, do nothing if (_selectedFrame == _tutorFrame) return; // Else unset the old and set the new tutor if (_tutorFrame != NULL) _tutorFrame->setTutor(false); _tutorFrame = _selectedFrame; _tutorFrame->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::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::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->isActiveVncServer()) { if (_mode == Mode::Broadcast) { for (QList::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::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) { // Ignore offline clients if ( (*it)->client() != NULL) { if (_watchers.contains((*it)->client()->id())) { // 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 bool isManager = isManagerMachine((*it)->client()); (*it)->client()->lockScreen(_mode == Mode::LockedMulticast && isManager != true); (*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(); } } } /***************************************************************************//** * 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) { // VNC Client stopped -> remove from watchers if (!client->isActiveVncClient()){ _watchers.remove(client->id()); // If noboody is watching the multicast stop VNC server if (_watchers.isEmpty() && _mode != Mode::Broadcast) _streamingSource->stopVncServer(); } } /***************************************************************************//** * DisableButtons. */ void MainWindow::DisableButtons() { foreach (QAction* a, _lockingButtons) a->setDisabled(true); } /***************************************************************************//** * EnableButtons. */ void MainWindow::EnableButtons() { foreach (QAction* a, _lockingButtons) a->setEnabled(true); }