summaryrefslogblamecommitdiffstats
path: root/src/server/mainwindow/mainwindow.cpp
blob: f6ff566ba71dc9bf6abfce7b08ed439f730dcd68 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                                
                    
                      


                       
                           
                                   


                                                   
                                     
                                                 



                                     
                                  

                                  
                                         

                          
                          
 
                   

                 








                                                                                 
 
                  

                
 
   



                                          
                                         

                                                                                                   
 
                                         
                                
                             
 


                                                                   
 

                      
 
                                                         
                                                   
                                                         
                          
                                                
 
                                    















                                                                        





                                                                             


                                                                                  
                                                                   
 
                                  
 

                                                                                  

                                                                                                      
                                                                                                                        
                                                                                                      
                                                                                                      
                                                                                              
                                                                                                  
                                                                                        
                                                                                              
                                                                                  
                                                                                                                  
                                                                                            
 
                                       
                         

                                              
                                                         
                                                                                  
                                     
                       





                                                             
                                  

                                                     
 



                                                                                        
                                             




                                                               




                                                                                                   
                                                                
 
                            

 


                                                                           
                                     
 

                                

 
                                                                                
                                            
                                   
                                                 

                                                     
                 
         
 
                                                                                  
                                     








                                                           
                                              
                                                
                                       
                                                                                        
                                                                

                                                                                              


                                                                 
                                        

                                               


                                                                             

 





                                         
                                                                          
                                       
 



                                          
 
 
   


                                                             
  


                                                                                

                              

                                                                           
   
                                                                                                 
 
                                                                        
                                                                          
                                               

                                                                                                
 
                       

                                                                                 

                                                  
                                                          


                                                                                   
                                                  









                                                                           


                                                                              
                                                                   




                                                                             






                                                              

                 
            

                      


                                                             
                                                            
                                    
                                           

                                              
                                                                                                          
                                                                      





                                               

 
                                               

                                                                                     
                                                                      
                                             

 
   




                                                              
                                                                                                                  
 
                                                                                   
                                      



                                                   
                                      

                                 
                                                                                                          
                                                                                                       
                                                                                                          


                  
   






                                                                     
                                                                         

                                         
                                       
                                                                     

                                                                                  
                 
         

 
   


                                                                
                                                
   

                                           
                                                                                                            
                                                 



                                                        
                       

 
   


                                                                      
                                         


                                            
                                                                                                            
                                                             

                                     
                       

 
   


                                                                       
                                        


                                               
                                                                                                            
                                                                

                                     
                       

 



                     
   



                                                        

                                           


                                                                                                                           
                                      
                
                            
         

 
   


                




                                       






                                                                               

                                                                            

                                                                                 
 
                                               

                                     
                                               


                                     

 
 
   


                
                                                    
 


                                                                                      
 
                                                       
                                           
                                                   
 








                                                                                 
         













                                                                            

                                             

                                                       
 
                                                               

                                                                                

                                                                

                 
 
                                           
                                                                                
                                        
         
 
                                           
                                          








                                                                                                                     

 
                                            
 











                                                                                                            


                                                                                                           

                                                       

 
   




                                                                                                   








                                                                        
                                                                  
                                                    
                                                                
                                                    



                 
   

               
                                 
 

                           




                                                                   
                             
                                                                                                            
                                                 
                                                          

                                                           
         
 
 




        









                                                                                                                                                 
 
   

                                                                      
   
                                                                 
 
                            
                                                    
                                                              
                                                               
 
                               

 
   


                                            

                                                       
                            
                                                      
                                                      
                             
                       


                                                          

                                             

                                  
                                    

 
   

                                              

                                     
                                                             

 
   

                       

                                      














                                                                                                          
                                    

                                                                                               
                





                                                                                        
                 
         

 
   
                                               
              
   
                                                    
 

                                                       
 
                                                                    
                                                      
                                    
 

                                   
 
                                                                
                            
                                              
                                                         
                                                                                   
                                             

                                      
         

 

                                           

                                                                                                    
                                                              


                                                                                         


                              







                                                                                                       
                                    
 
                              

                                                                    
 
                                                       
                              








                                                                                                        
 
                                                                                  
                                           

                                                                                                                   
                                                                 























                                                                                                                                       



                                                               
                             
                             

 

                                 
                                                                    
                                                                                                          

                       




                                                                                    


                                                                                                               
                                                                                                                     






                                                                                                      

                                                        



                                                     
         

 

                                      
 
                                                     
 
 


                                                      

 








                                                                                                
   






                                                                   
   


                                                                  


                                     

                                           
                                       
                                                                             
                                                      
                                                                                

                                                                            
              

                                                                   
                                


                               

                                                                                                                    
                                                       

                                                                                                                                                                        
                                 


                                                                           

 
   

                                                                     
   
                                         
 

                                           
                                          
                                                                            
                                            
                                                                             
                                                       
                                                                                  
                                                         
                                                                               
                                                      
                                                                                
              

                                                              
                                                                            
                                               




                                                 


                                                         




                                                                                  
 
                                 












                                                                                                         
         

 
   
                                               
   
                                         
 
                                 

 
 
   
                                                                   
   
                                                  
 

                                
 


                                            
                                                                              
                                                
                                                                             
                                                           
                                                                                  
                                                             
                                                                                 
                                                          
                                                                                








                                                                                        

                                

                 

















                                                                                                                                                   
                                 

                                                        
         

 
   
                                
                                                                            
                                                       
   

                                         

                                           

 
   
                                                        
                                                                             
                                         

                 

                                           

                                                     

 


                                            
                                            





                                                                                           
                                







                                                                                                                            
                                                               








                                                                
   

                                     




                               
   


                                                                             

                                     



                                                       
                                                                                           
                       
         
 


                                                              
         
                                                     
                              
                       
                                                   

                                       
         

                                    

 
   


                                                           

                                                  

                                                                                                                                  

 
   
                                  




                                                                                                 


                 


                                                                                                                                     


                                               




                                                                                                              
            
                              
                                                                                                                    
                                                    
                                         


                                           
                                                     


                                                        
                                                 











                                                                                               
   






                                                                                                                                                     

                


                                                                                                       

                                                                                                            

                                            
                                                                                                            
                                                          
                                       
                 
         
 
                                                         
                                  


                                   
         
 
                                 
                                                                                  
                                           
                             
 
 
   




                                                                                                 

                                                       
                                                          
                                
         
 
                                          
                                                       





                                                                                         

                                                                                                                                                     
                         
                 

                                                       
                
                                                                                                                    
                                                                                                                    
                                                         

                                                                                                 
                                                                         


                                                                                  
                         
                 
                                                                      

                                                                                                                    

                                                                                                         
                                                                  


                                                     


         
   




                                                                                   
                                                       
 
                                
                                                             
                                                   


                                                                                                 
 
                          



                                                                                      



                                                                                         

                                                                                                                          
                                                               


                                                                                                                       
                                         
 
                                                   
                                                                                        
                                                 
                                                           

                         
         
 
 
   

                  
                                 
 
                                  
                                            

                                     

 
   

                 
                                
 
                                 
                                            

                                    
                                                                                                  
 

 

                                 
                                            
                                                    
                               


                                                                                           
                                         

                                                                                                    
         


                                                                                                                                                


                                               


                                     
 
/*
 # 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 <QtWidgets>
#include <QFileDialog>
#include <QSvgRenderer>
#include <QPainter>
#include <QImage>
// Other custom UI elements
#include "../serverapp/serverapp.h"
#include "../clicklabel/clicklabel.h"
#include "../sessionnamewindow/sessionnamewindow.h"
#include "../connectionframe/connectionframe.h"
#include "../helpwindow/helpwindow.h"
#include "../reloadroomwindow/reloadroomwindow.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/platform/screensaver.h"
// Auto-generated ui class
#include "ui_mainwindow.h"
#include "ui_reloadroom.h"

#include <iostream>
#include <vector>

#define sStrTutorNdef MainWindow::tr("No tutor defined.")
#define sStrTutorOffline MainWindow::tr("Tutor is offline.")
#define sStrSourceNdef MainWindow::tr("Please select a projection source.")
#define sStrSourceOffline MainWindow::tr("The projection source is offline.")
#define sStrDestNdef MainWindow::tr("Please select a projection destination.")
#define sStrDestOffline MainWindow::tr("The projection destination is offline.")
#define sStrSourceDestSame MainWindow::tr("Selected projection target is tutor.")
#define sStrClientOnline MainWindow::tr("Selected client is currently online.")
#define sStrNoDestAv MainWindow::tr("No projection destination available.")

using std::vector;
using std::cout;
using std::endl;

/**
 * Initialize MainWindow and ListenServer.
 * @param ipListUrl
 * @param parent
 */
MainWindow::MainWindow(QWidget* parent) :
	QMainWindow(parent), ui(new Ui::MainWindow), _tbIconSize(24), _tbArea(Qt::LeftToolBarArea),
	_lastClientCount(0)
{
	qDebug() << "MainWindow(parent)";
	_mode = Mode::Multicast;
	_streamingSource = 0;

	/* default value, these will be updated a room is loaded */
	_tilesX = 10;
	_tilesY = 10;

	_virtCols = 0;
	_virtRows = 0;

	_sessionNameWindow = new SessionNameWindow(this);
	_reloadWindow = new ReloadRoomWindow(this);
	_reloadWindow->setWindowTitle(tr("Reload Room"));
	ui->setupUi(this);
	setWindowFlags(Qt::FramelessWindowHint);

	serverApp->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"));

	/* the label */
	_examModeLabel = new QLabel("Klausur-\nModus");
	_examModeLabel->setObjectName("examModeLabel");
	_examModeLabel->setAlignment(Qt::AlignCenter);
	_examModeLabel->setFixedHeight(400);
	ui->toolBar->insertWidget(ui->action_TutorToStudent, _examModeLabel);
	_dropMarker = new QLabel(ui->frmRoom);
	_dropMarker->setStyleSheet("background-color: #448; border-radius: 2px;");
	_dropMarker->hide();
	_helpWindow = new HelpWindow(ui->toolBar->actions(), this);

	serverApp->setExam(false);

	// Close button in tool bar
	connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit()));
	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_LockSingle, SIGNAL(triggered()), this, SLOT(onButtonLockSingle()));
	connect(ui->action_Help, SIGNAL(triggered()), this, SLOT(onButtonHelp()));
	connect(ui->actionReload_Room_Configuration, SIGNAL(triggered()), this, SLOT(onButtonReloadRoomConfig()));
	connect(ui->action_DeleteClient, SIGNAL(triggered()), this, SLOT(onDeleteClient()));

	/* Stuff for the button lock */
	//Setup a timeout
	_buttonLockTimer = new QTimer(this);
	_buttonLockTimer->setSingleShot(true);
	_buttonLockTimer->setInterval(BUTTON_BLOCK_TIME);
	connect(_buttonLockTimer, SIGNAL(timeout()), this, SLOT(enableButtons()));
	// Define the locking buttons
	_lockingButtons
			<< ui->action_DeleteClient
			<< ui->action_StudentToTutor
			<< ui->action_StudentToTutorExclusive
			<< ui->action_SetAsTutor
			<< ui->action_TutorToStudent
			<< ui->action_LockSingle
	        << ui->action_Lock
			<< ui->action_TutorToAll
			<< ui->action_StopProjection;

	// 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(); // Just make lable visible.

	reloadCurrentRoom();
}

/** this function determines if the number of clients in exam mode comprise
* more than 50%. In that case the whole manager switches to exam mode,
* disabling many features in the toolbar **/
void MainWindow::clientCountChanged()
{
	int examClientCount = 0;
	int clientCount = 0;


	for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
		Client* c = (*it)->client();
		if (c != nullptr) {
			bool b = c->isExamMode();
			examClientCount += b ? 1 : 0;
			clientCount++;
		}
	}

	serverApp->setExam(examClientCount * 2 >= clientCount && clientCount > 0);
	bool e = serverApp->isExam();
	qDebug() << "isExam is " << e;
	ui->action_TutorToAll->setVisible(!e);
	ui->action_StudentToTutor->setVisible(!e);
	ui->action_StudentToTutorExclusive->setVisible(!e);
	ui->action_TutorToStudent->setVisible(!e);
	ui->action_StopProjection->setVisible(!e);
	_examModeLabel->setVisible(e);
	_examModeLabel->setFixedHeight(e ? 400 : 0);

	if (_lastClientCount != clientCount) {
		// Client count actually changed
		if (clientCount == 0) {
			// Last client must have disconnected, enable screen saver again
			ScreenSaver::allowSaverAndStandby(true);
		} else if (_lastClientCount == 0) {
			// We didn't have a client before, but now we do, disable screen saver
			ScreenSaver::forceUnlockAndScreenOn();
			ScreenSaver::allowSaverAndStandby(false);
		}
		// Remember client count
		_lastClientCount = clientCount;
	}
	// Also we might have to enable/disable buttons in the toolbar if the
	// client frame is currently selected
	updateContextButtonStates();
}

MainWindow::~MainWindow()
{
	_sessionNameLabel->deleteLater();
	delete ui;
}

/** Squared euclidean distance (why is this not implemented in QPoint?) */
static int distance(QPoint a, QPoint b)
{
	const int dx = a.x() - b.x();
	const int dy = a.y() - b.y();
	const int sum = dx * dx + dy * dy;
	return sum;
}

/**
 * \brief: find the closest available frame.
 *
 * Closest: smallest Euclidean distance from center to center
 *
 * 'Algorithm': ideally, go in circles around the preferred point until you find
 * a free spot. Lazy way: store all free positions in an array and find later
 * there the closest (<- that's what I do)
 *
 * @param preferred in Pixels!
 * @param toIgnore, ignore this connectionframe when considering free slots
 * @return the free slot
 */
QPoint MainWindow::closestFreeSlot(const QPoint preferredPixels, const ConnectionFrame* toIgnore)
{
	const bool pickFirstOne = ( preferredPixels == QPoint(-1, -1) );
	const QSize& clientSize = serverApp->getCurrentRoom()->clientSize;
#define GRID(X,Y) (grid[((X) * _tilesY) + (Y)])
	bool *grid = new bool[_tilesX * _tilesY];
	memset(grid, 0, sizeof(bool) * size_t(_tilesX * _tilesY)); /* set everything to false */

	/* fill grid */
	for (auto  it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {

		if (*it == toIgnore) { continue; }

		const QPoint p = (*it)->getGridPosition();

		for (int x = p.x(); x < p.x() + clientSize.width(); x++) {
			for (int y = p.y(); y < p.y() + clientSize.height(); y++) {
				GRID(x, y) = true;
			}
		}
	}

	QList<QPoint> freePositions;
	/* check all positions to see if they are available */
	for (int x = 0; x <= _tilesX - clientSize.width(); x++) {
		for ( int y = 0; y <= _tilesY - clientSize.height(); y++) {
			/* check if (x,y) is free */
			bool isFree = true;
			int dx, dy = 0;
			for (dx = 0; dx < clientSize.width(); dx++) {
				for (dy = 0; dy < clientSize.height(); dy++) {
					if (GRID(x + dx, y + dy)) {
						isFree = false;
						goto endLoop; // double-break
					}
				}
			}
endLoop: ;
			if (isFree) {
				freePositions << QPoint(x, y);
				if (pickFirstOne) {
					goto endSearch;
				}
			}
		}
	}
endSearch: ;
#undef GRID
	delete[] grid;
	if (freePositions.isEmpty() && toIgnore != nullptr) {
		return toIgnore->getGridPosition();
	}
	/* among all the free positions, find the closest */
	int min_distance = 10000000;
	QPoint bestPosition = QPoint(0, 0);

	for (QPoint freePos : freePositions) {
		const QPoint freePosPx( freePos.x() * getTileWidthPx(), freePos.y() * getTileHeightPx() );
		const int dist = distance(freePosPx, preferredPixels);
		if (dist < min_distance) {
			min_distance = dist;
			bestPosition = freePos;
		}
	}
	return bestPosition;
}

/* place frame in the closest available spot */
void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame, QPoint preferredPixels)
{
	QPoint bestPosition = closestFreeSlot(preferredPixels, frame);
	frame->setGridPosition(bestPosition);
}

/**
 * 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(const QString &computerId, const QPoint* gridPosition, bool fromRoomplan)
{
	ConnectionFrame *cf = new ConnectionFrame(this, ui->frmRoom, fromRoomplan);
	if (gridPosition == nullptr) {
		placeFrameInFreeSlot(cf);
	} else {
		cf->setGridPosition(*gridPosition);
	}
	cf->setComputerId(computerId);
	_clientFrames.append(cf);
	cf->show();
	connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onFrameDropped(ConnectionFrame *)));
	connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *)));
	connect(cf, SIGNAL(frameMoving(ConnectionFrame *)), this, SLOT(onFrameMoving(ConnectionFrame *)));
	return cf;
}

/**
 * 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() || _mode == Mode::LockedUnicast)
		client->lockScreen(true);

	if (_mode == Mode::Broadcast) {
		client->setDesiredProjectionSource(_streamingSource);
		if (_streamingSource != 0) {
			client->startVncClient(getClientFromId(_streamingSource));
		}
	}
}

/**
 * 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 nullptr.
 */
Client* MainWindow::getClientFromId(int id)
{
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
		if ((*it)->client() != nullptr) {
			if ((*it)->client()->id() == id)
				return (*it)->client();
		}
	}
	return nullptr;
}

/**
 * Return the Frame, which is currently beeing Tutor.
 * Iterating over all ConnectionFrames, and looking for flag _isTutor.
 * @return Frame with flag _isTutor = true,
 * else nullptr if no Tutor is available.
 */
ConnectionFrame* MainWindow::getTutorFrame()
{
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
		if (((*it) != nullptr) && ((*it)->isTutor()))
			return (*it);
	}
	return nullptr;
}

/**
 * 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 nullptr if no frame is selected.
 */
ConnectionFrame* MainWindow::getSelectedFrame()
{
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
		if (((*it) != nullptr) && ((*it)->isSelected()))
			return (*it);
	}
	return nullptr;
}

/*
 * Overridden methods
 */

/**
 * Handle closeEvent.
 * If user calls closing MainWindow, close, else ignore.
 * @param e
 */
void MainWindow::closeEvent(QCloseEvent* e)
{
	QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Question"), tr("Are you sure you want to exit?"),
			QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
	if (ret == QMessageBox::Yes) {
		QApplication::exit(0);
	} else {
		e->ignore();
	}
}

/**
 * Change Event.
 * @param e
 */
void MainWindow::changeEvent(QEvent* e)
{
	QMainWindow::changeEvent(e);
}

enum AspectStatus { GRID_OK, GRID_TOO_WIDE, GRID_TOO_TALL };

/**
 * check the difference in the aspect ratio between the frame size and the grid
 * size. The parameters in here are hand-adjusted. Feel free to make it more or
 * less sensitive.
 * */
AspectStatus checkAspectRatio(const QSize& frameSize, const QSize& gridSize)
{
	float aspectRoom  = float(gridSize.height()) / float(gridSize.width());
	float aspectFrame = float(frameSize.height()) / float(frameSize.width());

	if (aspectRoom / aspectFrame <  0.8f) {
		return GRID_TOO_WIDE;
	}
	if ( aspectFrame / aspectRoom < 0.8f) {
		return GRID_TOO_TALL;
	}
	return GRID_OK;
}


/**
 * Resize event.
 * @param e
 */
void MainWindow::resizeEvent(QResizeEvent* /* e */ )
{
	if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100) {
		return;
	}

	const Room* room = serverApp->getCurrentRoom();
	QSize newGridSize = room->gridSize;
	const QSize& clientSize = room->clientSize;

	// Check if any frames have been moved beyond the room dimensions
	for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
		QPoint bounds = (*it)->getGridPosition();
		while (bounds.x() + clientSize.width() > newGridSize.width()) {
			newGridSize += QSize(1, 0);
		}
		while (bounds.y() + clientSize.height() > newGridSize.height()) {
			newGridSize += QSize(0, 1);
		}
	}

	/* do we have to add virtual columns or rows? */
	AspectStatus status;
	do {
		status = checkAspectRatio(ui->frmRoom->size(), newGridSize);
		if (status == GRID_TOO_WIDE) {
			/* add row */
			newGridSize += QSize(0, 1);
		}
		if (status == GRID_TOO_TALL) {
			/* add column */
			newGridSize += QSize(1, 0);
		}
	} while (status != GRID_OK);
	this->_tilesX = newGridSize.width();
	this->_tilesY = newGridSize.height();
	const int maxX = _tilesX - clientSize.width();
	const int maxY = _tilesY - clientSize.height();

	/* Bring back frames which are now out of the screen */
	for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
		const QPoint gp = (*it)->getGridPosition();
		if ( gp.x() > maxX || gp.y() > maxY ) {
			placeFrameInFreeSlot(*it, (*it)->pos());
		}
	}

	/* Resize all connection windows */
	for (auto it = _clientFrames.begin(); it != _clientFrames.end(); ++it) {
		(*it)->updateGeometry();
	}

	/* update background image label */
	if (_backgroundImage != nullptr) {
		int w = ui->frmRoom->width() - 5; /* to make sure that we don't enlarge the window */
		int h = ui->frmRoom->height() - 5;
		ui->imageLabel->hide();
		ui->imageLabel->setScaledContents(true);
		ui->imageLabel->setPixmap(QPixmap::fromImage(*_backgroundImage).scaled(w, h, Qt::IgnoreAspectRatio));
		ui->imageLabel->show();
	} else {
		ui->imageLabel->clear();
	}
}

void MainWindow::updateContextButtonStates()
{
	if (_buttonLockTimer->isActive()) // If buttons are disabled for cooldown, don't do anything
		return;
	const ConnectionFrame *selected = getSelectedFrame();
	const ConnectionFrame *tutor = getTutorFrame();
	const bool somethingSelected = (selected != nullptr);
	const bool selectedOnline = (selected != nullptr && selected->client() != nullptr);
	const bool tutorOnline = (tutor != nullptr && tutor->client() != nullptr);
	// Deletion only for offline clients, tutor if anything is selected, locking only for online clients
	ui->action_DeleteClient->setEnabled(somethingSelected && !selectedOnline);
	ui->action_SetAsTutor->setEnabled(somethingSelected);
	ui->action_LockSingle->setEnabled(selectedOnline && selected != tutor);
	// Don't allow projection to self
	ui->action_StudentToTutorExclusive->setEnabled(selectedOnline && tutorOnline && selected != tutor);
	ui->action_StudentToTutor->setEnabled(selectedOnline && tutorOnline && selected != tutor);
	ui->action_TutorToStudent->setEnabled(selectedOnline && tutorOnline && selected != tutor);
	// Only allow tutor broadcast if they're online
	ui->action_TutorToAll->setEnabled(tutorOnline);
}

/**
 * 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() != nullptr) {
			getSelectedFrame()->setSelection(false);
			updateContextButtonStates();
		}
	}
}

/**
 * @brief reset
 */
void MainWindow::reset(bool lock)
{
	_mode = Mode::None;

	// Stop server (Clients get stopped on ACK)
	if (getClientFromId(_streamingSource) != nullptr) {
		getClientFromId(_streamingSource)->stopVncServer();
	}

	// Unlock all clients
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
		if ((*it)->client() != nullptr) {
			(*it)->client()->lockScreen(lock);
			(*it)->client()->removeAttention();
		}
	}

}

/*
 * Slots
 */

void MainWindow::onFrameMoving(ConnectionFrame* connectionFrame)
{
	QPoint slot = closestFreeSlot(connectionFrame->pos(), connectionFrame);
	_dropMarker->setGeometry(slot.x() * getTileWidthPx(), slot.y() * getTileHeightPx(), connectionFrame->width(), connectionFrame->height());
	if (!_dropMarker->isVisible()) {
		_dropMarker->lower();
		ui->imageLabel->lower();
		_dropMarker->show();
	}
}

/**
* Place frame to from user specified position. Should be called when a
* connectionFrame is dropped during the "drag-n-drop".
 */
void MainWindow::onFrameDropped(ConnectionFrame* connectionFrame)
{
	_dropMarker->hide();
	// if (_tilesX <= 0 || _tilesY <= 0) return;
	const QPoint preferredPixels = connectionFrame->pos();
	placeFrameInFreeSlot(connectionFrame, preferredPixels);

	//resizeEvent(nullptr);
}

/**
 * Mark given frame after it was clicked on.
 * @param frame
 */
void MainWindow::onFrameClicked(ConnectionFrame* frame)
{
	_dropMarker->hide();
	ConnectionFrame *current = getSelectedFrame();
	// If same frame is clicked again,, do nothing
	if (current == frame)
		return;

	// If another frame has been selected, unselect it
	// Set the ui selected and set a new reference
	if (current != nullptr) {
		current->setSelection(false);
	}
	frame->setSelection(true);
	updateContextButtonStates();
}

/**
 * Show session name, after it was clicked on.
 */
void MainWindow::onSessionNameClick()
{
	_sessionNameWindow->show((serverApp->sessionName()));
}

/**
 * Update session name.
 */
void MainWindow::onSessionNameUpdate()
{
	_sessionNameLabel->setText(tr("Session Name: %1  [click to edit]").arg(serverApp->sessionName()));
	bool haveAdditionalClient = false;
	for (QMutableListIterator<ConnectionFrame*> it(_clientFrames); it.hasNext(); ) {
		if (!it.next()->isFromRoomplan()) {
			haveAdditionalClient = true;
			break;
		}
	}
	if (!haveAdditionalClient)
		return; // No additional client exists, don't ask about reset
	// Current layout contains additional clients (voa session name), ask to delete
	QMessageBox::StandardButton ret = QMessageBox::question(this,
			tr("Question"), tr("Do you want to delete and disconnect any clients\n"
				"not belonging to the current room layout?"),
			QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
	if (ret != QMessageBox::Yes)
		return;
	// Stop all projections and clear all clients, which were connected to old sessionName.
	reset();
	for (QMutableListIterator<ConnectionFrame*> it(_clientFrames); it.hasNext(); ) {
		ConnectionFrame *cf = it.next();
		if (!cf->isFromRoomplan()) {
			cf->hide();
			cf->deleteLater();
			it.remove();
		}
	}
}

/**
 * @brief MainWindow::startVncServerIfNecessary
 * @param from
 */
void MainWindow::startVncServerIfNecessary(int from)
{
	Client* os = getClientFromId(_streamingSource);
	Client* ns = getClientFromId(from);

	// if there is a server running which is not "from" stop it.
	if (os != nullptr && _streamingSource != from)
		os->stopVncServer();

	// Set new streaming source
	_streamingSource = from;

	// If streaming source is already active avoid a restart
	if (ns != nullptr) {
		if (ns->isActiveVncServer()) {
			this->onVncServerStateChange(ns);
		} else { // Could not take shortcut, (re)start VNC server on source
			ns->startVncServer();
		}
		ns->removeAttention();
	}
}

void MainWindow::onButtonReloadRoomConfig()
{
	connect(_reloadWindow->ui->buttonBox, SIGNAL(accepted()), this, SLOT(onReloadRoomOk()));
	connect(_reloadWindow->ui->buttonBox, SIGNAL(rejected()), this, SLOT(onReloadRoomCancel()));
	QList<QString> keyList = serverApp->getRooms().keys();
	for (QList<QString>::iterator it = keyList.begin(); it != keyList.end() ; it++) {
		_reloadWindow->ui->roomList->addItem(*it);
	}
	_reloadWindow->show();
}

void MainWindow::onReloadRoomCancel()
{
	disconnect(_reloadWindow->ui->buttonBox, SIGNAL(accepted()), this, SLOT(onReloadRoomOk()));
	disconnect(_reloadWindow->ui->buttonBox, SIGNAL(rejected()), this, SLOT(onReloadRoomCancel()));
	_reloadWindow->ui->roomList->clear();
	_reloadWindow->hide();
}

void MainWindow::reloadCurrentRoom()
{
	/* delete old image */
	if (_backgroundImage != nullptr) {delete _backgroundImage; }
	_backgroundImage = nullptr;

	const Room *room = serverApp->getCurrentRoom();
	if (room != nullptr) {
		/* set tiles */
		_tilesX = room->gridSize.width();
		_tilesY = room->gridSize.height();


		/* place connection frames */
		for (auto it = room->clientPositions.begin(); it != room->clientPositions.end(); ++it) {
			QString computerId = it.key();
			QPoint pos = it.value();

			ConnectionFrame *cf = createFrame(computerId, &pos, true);
			onFrameDropped(cf);
			if (computerId == room->tutorIP) {
				qDebug() << "set computer with id " << computerId << " as tutor per configuration";
				if (getTutorFrame() != nullptr) {
					getTutorFrame()->setTutor(false);
				}
				cf->setTutor(true);
			}
		}

		/* load background image */
		QString imgPath = room->imagePath;
		qDebug() << "imgPath is " << imgPath;

		if (imgPath != "") {
			qDebug() << "set background image path: " << imgPath;
			if (imgPath.endsWith("svg")) {
				/* render once with maximal screen size */
				const QSize &s  =  QApplication::desktop()->screenGeometry().size(); // ui->frmRoom->geometry().size();
				QSvgRenderer renderer(imgPath);
				_backgroundImage = new QImage(s, QImage::Format_ARGB32);
				_backgroundImage->fill(Qt::lightGray); /* background color */
				QPainter painter(_backgroundImage);
				renderer.render(&painter);
			} else {
				_backgroundImage = new QImage();
				_backgroundImage->load(imgPath);
			}
		}
	}

	/* and force a resize event (this scales the image)  */
	resizeEvent(nullptr);
	clientCountChanged();
}

void MainWindow::onReloadRoomOk()
{
	if (_reloadWindow->ui->roomList->currentItem() == nullptr) {
		QMessageBox::critical(this, "Warning", tr("No item selected, please select room!"), 0, 1);
		return;
	}
	QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Warning"),
			tr("Are you sure you want to reload the room?\n"
				"Note that all clients will be deleted."),
			QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
	if (ret == QMessageBox::Yes) {
		disconnect(_reloadWindow->ui->buttonBox, SIGNAL(accepted()), this, SLOT(onReloadRoomOk()));
		disconnect(_reloadWindow->ui->buttonBox, SIGNAL(rejected()), this, SLOT(onReloadRoomCancel()));
		// Delete all clients.
		for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); it++) {
			(*it)->hide();
			(*it)->deleteLater();
		}
		_clientFrames.clear();

		// Load new room configuration.
		QString roomToReload = _reloadWindow->ui->roomList->currentItem()->data(0).toString();
		serverApp->setCurrentRoom(roomToReload);
		reloadCurrentRoom();

		_reloadWindow->ui->roomList->clear();
		_reloadWindow->hide();

	}
}

int MainWindow::getTileWidthPx() const
{

	return ui->frmRoom->size().width() / _tilesX;
}

int MainWindow::getTileHeightPx() const
{
	return ui->frmRoom->size().height() / _tilesY;
}

QRect MainWindow::calcFrameGeometry(ConnectionFrame* frame) const
{
	const QPoint pos = frame->getGridPosition();
	const QSize& clientSize = serverApp->getCurrentRoom()->clientSize;
	const int w = getTileWidthPx();
	const int h = getTileHeightPx();
	return QRect(pos.x() * w, pos.y() * h, w * clientSize.width(), h * clientSize.height());
}

/**
 * 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() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getTutorFrame()->client() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else if (_clientFrames.size() == 1)
		QMessageBox::critical(this, tr("Projection"), sStrNoDestAv);
	else {
		// If this mode is already active, reset everything
		if (_mode == Mode::Broadcast) {
			reset();
			return;
		}

		// Set all clients as watchers of tutor. Except for the tutors desired source, which hase to be none
		for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
			if ((*it)->client() != nullptr)
				(*it)->client()->setDesiredProjectionSource((*it)->client() == getTutorFrame()->client() ? NO_SOURCE : getTutorFrame()->client()->id());

		disableButtons();
		_mode = Mode::Broadcast;
		startVncServerIfNecessary(getTutorFrame()->client()->id());
	}
}

/**
 * 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() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrDestNdef);
	else if (getTutorFrame() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getSelectedFrame() == getTutorFrame())
		QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
	else if (getSelectedFrame()->client() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrDestOffline);
	else if (getTutorFrame()->client() == nullptr)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else {
        auto sourceClient = getTutorFrame()->client();
        auto destinationClient = getSelectedFrame()->client();
		// If this is the first call in this mode clear the watchers
		if (_mode != Mode::Multicast) {
            for (auto c : _clientFrames) {
                if (c->client() != nullptr) {
                    c->client()->stopVncClient();
                }
            }
		}

		// If "to" already watches "from" stop it
        if (destinationClient->desiredProjectionSource() == sourceClient->id() ) {
            destinationClient->stopVncClient();
        } else {
            destinationClient->setDesiredProjectionSource(sourceClient->id());
        }

		disableButtons();
        int numClients = 0;
        for (auto c : _clientFrames) {
            if (c->client() != nullptr && c->client()->desiredProjectionSource() == sourceClient->id()) {
                numClients++;
            }
        }
        if (numClients == 0) {
            _mode = Mode::None;
            sourceClient->stopVncServer();
        } else {
            _mode = Mode::Multicast;
            startVncServerIfNecessary(sourceClient->id());
        }
	}
}

/**
 * Handle projection from one student to tutor.
 */
void MainWindow::onButtonStudentToTutor()
{
	this->vncOneOnOne(false);
}


/**
 * Handle projection from one student to tutor, lock everyone else.
 */
void MainWindow::onButtonStudentToTutorExclusive()
{
	this->vncOneOnOne(true);
}

void MainWindow::vncOneOnOne(bool exclusive)
{
	if (getSelectedFrame() == nullptr) {
		QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
	} else if (getTutorFrame() == nullptr) {
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	} else if (getTutorFrame() == getSelectedFrame()) {
		QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
	} else if (getSelectedFrame()->client() == nullptr) {
		QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
	} else if (getTutorFrame()->client() == nullptr) {
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	} else {
		const bool wasLocked = ui->action_Lock->isChecked();
		ui->action_Lock->setChecked(false);
		Client *source = getSelectedFrame()->client();
		Client *dest = getTutorFrame()->client();
		const Mode mode = exclusive ? Mode::LockedUnicast : Mode::Unicast;

		if (_mode == mode && source->id() == dest->desiredProjectionSource()) {
			// Button clicked again with same selection, treat this as reset
			reset();
			return;
		}

		for (ConnectionFrame* f : _clientFrames) {
			if (f->client() == nullptr) // Disconnected frame
				continue;
			if (source != f->client() && dest != f->client()) { //Not source or destination
				f->client()->setDesiredProjectionSource(NO_SOURCE);
				if (_mode == Mode::Unicast) { // Already in unlocked unicast mode
					if (exclusive) { // Only change lock state (to locked) if switching to locked unicast
						f->client()->lockScreen(true); // (don't change otherwise, would reset individually locked clients)
					}
				} else if (wasLocked) { // Was in "lock all" mode, update lock mode for everyone
					f->client()->lockScreen(exclusive);
				}
			}
		}
		dest->lockScreen(false);
		source->lockScreen(false);
		dest->setDesiredProjectionSource(source->id());
		source->setDesiredProjectionSource(NO_SOURCE);
		disableButtons();
		_mode = mode;
		startVncServerIfNecessary(source->id());
	}
}

/**
 * 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 and lock if requested
	reset(checked);
}

void MainWindow::onButtonLockSingle()
{
	// If no frame is selected, warning.
	if (getSelectedFrame() == nullptr) {
		QMessageBox::critical(this, tr("Selection"), tr("No client is selected."));
		return;
	}
	Client *client = getSelectedFrame()->client();

	// If frame of inactive client has been selected unselect it
	if (client == nullptr) {
		QMessageBox::critical(this, tr("Selection"), tr("The selected client is not connected."));
		return;
	} else { // If selected client is locked, first unlock
		bool newState = !client->isLocked();
		client->lockScreen(newState);
		if (!newState) {
			// If no more clients are locked, reset button
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
				if ((*it)->client() == nullptr)
					continue;
				if ((*it)->client()->isLocked())
					return;
			}
			ui->action_Lock->setChecked(false);
		}
	}
}

/**
 * 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()
{
	ConnectionFrame *selected = getSelectedFrame();
	ConnectionFrame *tutor = getTutorFrame();

	if (selected == nullptr) {
		QMessageBox::critical(this, tr("Selection"), tr("No client is selected."));
		return;
	}

	// Unlock client about to become a tutor, just in case
	if (selected->client() != nullptr) {
		selected->client()->lockScreen(false);
	}
	// If same frame is already tutor, do nothing
	if (selected == tutor)
		return;
	// Else unset the old and set the new tutor
	if (tutor != nullptr) {
		tutor->setTutor(false);
	}
	selected->setTutor(true);
	updateContextButtonStates();
}

/**
 * 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*)));
	/* copy examMode */
	client->setExamMode(request->examMode);

	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 == nullptr)
				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*)));
	ConnectionFrame *existing = nullptr;
	ConnectionFrame *cf = nullptr;
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
		if ((*it)->computerId() == client->ip()) {
			existing = *it;
		}
	}

	// Clients ip already exists, but was not active.
	if (existing != nullptr) {
		cf = existing;
	} else {
		cf = createFrame();
	}

	cf->assignClient(client);
	connect(client, SIGNAL(disconnected()), this, SLOT(clientCountChanged()));
	tellClientCurrentSituation(client);
	clientCountChanged();
}

/**
 * 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()) {
		// apply the desired projection sources
		for (ConnectionFrame *frame : _clientFrames) {
			if (frame->client() == nullptr) // Ignore offline clients
				continue;
			if (frame->client()->desiredProjectionSource() == client->id()) {
				frame->client()->startVncClient(client);
				client->lockScreen(false);
			} else {
				frame->client()->lockScreen(frame->client()->desiredProjectionSource() == NO_SOURCE && _mode == Mode::LockedUnicast);
			}
		}
		// Dont forget to unlock the vnc server
		client->lockScreen(false);
	} else {
		// VNC server stopped on some client or failed to start - reset local pending projection information
		for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it) {
			if ((*it)->client() != nullptr) {
				if ((*it)->client()->desiredProjectionSource() == client->id()) {
					(*it)->client()->setDesiredProjectionSource(NO_SOURCE);
					(*it)->client()->stopVncClient();
					if (_mode == Mode::LockedUnicast)
						(*it)->client()->lockScreen(true);
				}
			}
		}
		// Dont forget to unlock the vnc server (if necesarry)
		client->lockScreen((client->desiredProjectionSource() == NO_SOURCE &&  _mode == Mode::LockedUnicast)
						   || ui->action_Lock->isChecked());

		// If this was the current source remember that there is no source anymore and reset mode
		if (client == getClientFromId(_streamingSource)) {
			_streamingSource = NO_SOURCE;
			_mode = Mode::None;
		}
	}
}

/**
 * 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 != nullptr) {
		// VNC Client stopped -> remove from watchers
		if (!client->isActiveVncClient()) {
			// Only unset a desired Projection source if it has not changed meanwhile
			if (client->projectionSource() == client->desiredProjectionSource())
				client->setDesiredProjectionSource(NO_SOURCE);

			/*
			 * If nobody is watching the VNC server of the pvsclient that
			 * stopped its vncclient stop it. (The _LAST_ vncserver, there
			 * may be a new one)
			 * This is necessary for the race condition when a server
			 * is stopped and another started at the same time, since the new
			 * server would be killed if all client disconnect before any of
			 * the new connect.
			 */
			bool serverHasWatchers = false;
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
				if ((*it)->client() != nullptr)
					if ((*it)->client()->desiredProjectionSource() == client->projectionSource()) {
						serverHasWatchers = true;
						break;
					}

			if ( !serverHasWatchers ) {
				Client* c = getClientFromId(client->projectionSource());
				if (c != nullptr)
					c->stopVncServer();
			}
		}
	}
}

/**
 * DisableButtons.
 */
void MainWindow::disableButtons()
{
	_buttonLockTimer->start();
	for (QAction *a : _lockingButtons) {
		a->setDisabled(true);
	}
}

/**
 * EnableButtons.
 */
void MainWindow::enableButtons()
{
	_buttonLockTimer->stop();
	for (QAction *a : _lockingButtons) {
		a->setEnabled(true);
	}
	updateContextButtonStates(); // In case user changed selection while buttons were disabled
}


void MainWindow::onDeleteClient()
{
	// If no frame is selected, warning.
	ConnectionFrame* frame = getSelectedFrame();
	if (frame == nullptr) {
		QMessageBox::critical(this, tr("Selection"), tr("No client is selected."));
		return;
	}
	if (frame->client() != nullptr) {
		QMessageBox::critical(this, tr("Selection"), tr("This client is still connected."));
		return;
	}
	QMessageBox::StandardButton ret = QMessageBox::question(this, tr("Warning"), tr("Are you sure you want to delete the selected client?"),
			QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
	if (ret == QMessageBox::Yes) {
		frame->hide();
		frame->deleteLater();
		_clientFrames.removeAll(frame);
		clientCountChanged();
		return;
	}
}