summaryrefslogblamecommitdiffstats
path: root/src/server/mainwindow/mainwindow.cpp
blob: a52690f2572e4bcf56ccb89955838bb246865e00 (plain) (tree)



















                                                                                
                            



                                                   
                                     



                                     
                                  






                                  
 






                                                                                          

                                                                                        
 
                                                                                



                                          
                                                            
                                                                                                  
 
                                
                             
 
                                                         
                                           
                                            

                          
                                 















                                                                        


                         
                                     




                                                                                            
 

                                                                                  


                                                                                                      
                                                                                                                        
                                                                                                      
                                                                                                      
                                                                                              
                                                                                                  
                                                                                        
                                                                                  
 
 
                                       
                         


                                                        

                                                                                  







                                             
 









                                                                                        





                                                                                                   







                                         
                                                                                



                                                                     















































                                                                                                                                          
                                                                                




                                                              










                                                                                                        
                                                                                






                                           












                                                                                     
                                                                                


                                          







                                                                            
                                             


                            











                                                                                

                                                          



                                                              


                                                
 

 
                                                                                




                                                                












                                                                                                          































                                                                                                          



                     
                                                                                



                                                        

                                           
                                                                        





                                      
                                                                                


                




                                       
                                                                                


                

                                             





                                                                                                                    
         



                                                                                             
         







                                                                       

                         

 
                                                                                




                                                                                                   










                                                                        


                                                                



                 










                                                                                                          

                                                                   
 




        
                                                                                
                                                 
                                                                 

                   






                                                                                  
 
                                                                                


                                               















                                                          




























                                                                                                                               














                                                                                                          
                                                                                


                                            

                                                       
                                                      
                                        



                                                          






                                                                                

 
                                                                                

                                              




                                                        
                                                                                

                       

                                      







                                                                                                          


                                                                                                       
                                                                                
                                      
              
              

            
                                                                      
 

                         



                                                                                                                  
                 
                                                                                                            




                                                                                            





                                                                                     







                                                                                                                          

                                                         





                                                                     
                                            



                                                                    
         
 

                       
 
                                                                    

                                                                                                   
 
                                   
                                      
 
                                                                






                                                                                

 
                                                                                







                                                                                


                                                                  


                                     

                                           
                                    
                                                                             
                                                   
                                                                                

                                                                            
            
                                                                             


                                                                                


                                                                           
   
                                       
 

                                           
                                       
                                                                              
                                                 
                                                                                 
            
                                                                                

 
                                                                                

                                                                     
   
                                         
 

                                           
                                       
                                                                            
                                         
                                                                             
                                                       
                                                                                  
                                                      
                                                                               
                                                   
                                                                                
            
                                                                                                           

 
                                                                                

                                                                      
   
                                         
 

                                           
                                       
                                                                              
                                         
                                                                             
                                                       
                                                                                  
                                                      
                                                                                 
                                                   
                                                                                
            
                                                                                                           

 
 
                                                                                
                                                     
   
                                                  
 

                                           
                                       
                                                                              
                                         
                                                                             
                                                       
                                                                                  
                                                      
                                                                                 
                                                   
                                                                                
            
                                                                                                                 

 
 
                                                                                
                                
                                                                            
                                                       
   

                                         

                                           

 
                                                                                
                                                        
                                                                             
                                         

                 

                                           
                               
                
 

                                                                                                          
                                            
                                 
                                                     


         
                                                                                

                                     




                               
                                                                                


                                                                             

                                     

                                           
                                            
                                       

                                                                                           
                       
         
 
                                                                    
                                                 
         

                                                                                                          
         
                                                           


                                                                

                                                     
                                                  
                       

                                                   




                                                 

 
                                                                                


                                                           

                                                  

                                                                                                                                  

 
                                                                                
                                  




                                                                                                 


                 




































                                                                                                                                     
                                                                                






                                                                                                                                                     

                


                                                                                                       

                                                                                                            
                                    


                                                                                                          
                                                        
                                       

                                     
                                                    


                                                      






                                                            
                                                          





                                               
                                                         

                             
                                            
                                               
                                                   

                       





                                        





                                            
                                                   


                                     
                                                           

















                                                          

                              

                                 
 
                                           
 
 
                                                                                




                                                                                                 

                                                       
                                                        

                                
                                        
         
                                             
                 












                                                                                                                          
                                                                         
                 




                                                                                                                          
                                                                                         
                                                                         












                                                                                           
                                                                                                            



                                                                                 




                                                                                                                    









                                                                                                                  



                 
                                                                                




                                                                                   
                                                       
 




                                                             

                                                                                 





                                                                                                                          
                         










                                                                              

                         
         
 
 


                                                                                

                                 
                                  

                                             

 


                                                                                

                                
                                 

                                             
 
/*
 # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
 #
 # This program is free software distributed under the GPL version 2.
 # See http://openslx.org/COPYING
 #
 # If you have any feedback please consult http://openslx.org/feedback and
 # send your suggestions, praise, or complaints to feedback@openslx.org
 #
 # General information about OpenSLX can be found at http://openslx.org/
 # -----------------------------------------------------------------------------
 # mainWindow.cpp
 This is the Main class for the pvsManager. The GUI is constructed here.
 # -----------------------------------------------------------------------------
 */
// Self
#include "mainwindow.h"
// QT stuff
#include <QtGui>
#include <QFileDialog>
#include <QNetworkInterface>
// Other custom UI elements
#include "../clicklabel/clicklabel.h"
#include "../sessionnamewindow/sessionnamewindow.h"
#include "../connectionframe/connectionframe.h"
#include "../helpwindow/helpwindow.h"
// Network
#include "../net/listenserver.h"
#include "../net/client.h"
#include "../net/discoverylistener.h"
#include "../net/filedownloader.h"
// Others
#include "../../shared/settings.h"
#include "../util/util.h"
#include "../util/global.h"
// Auto-generated ui class
#include "ui_mainwindow.h"


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

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

	_sessionNameWindow = new SessionNameWindow(this);
	_helpWindow = new HelpWindow(this);
	_helpWindow->setWindowTitle("Help");
	ui->setupUi(this);

	Global::setSessionName();

	//conWin = new ConnectionWindow(ui->widget);
	//ui->VconWinLayout->addWidget(conWin);
	//conList = new ConnectionList(ui->ClWidget);
	//ui->ClientGLayout->addWidget(conList);

	// Show only session name label, not textbox
	_sessionNameLabel = new ClickLabel(this);
	ui->mainLayout->addWidget(_sessionNameLabel);

	ui->frmRoom->setStyleSheet("background-color:#444");

	// toolbar and actions in pvsmgr
	ui->action_Exit->setStatusTip(tr("Exit"));
	ui->action_Lock->setStatusTip(tr("Lock or Unlock all Clients"));

	_tileWidth = 10;
	_tileHeight = 10;

	// Initialize FileDownloader.
	if (!ipListUrl.isEmpty())
	{
		_fileDownloader.connectSlot(this, SLOT(onTutorListDownloaded(QByteArray&)));
		_fileDownloader.downloadFile(QUrl(ipListUrl));
	}

	// Close button in tool bar
	connect(ui->action_Exit, SIGNAL(triggered()), this, SLOT(onButtonExit()));
	connect(ui->action_BroadcastScreen, SIGNAL(triggered()), this, SLOT(onButtonStudentToAll()));
	connect(ui->action_TutorToAll, SIGNAL(triggered()), this, SLOT(onButtonTutorToAll()));
	connect(ui->action_StudentToTutor, SIGNAL(triggered()), this, SLOT(onButtonStudentToTutor()));
	connect(ui->action_StudentToTutorExclusive, SIGNAL(triggered()), this, SLOT(onButtonStudentToTutorExclusive()));
	connect(ui->action_TutorToStudent, SIGNAL(triggered()), this, SLOT(onButtonTutorToStudent()));
	connect(ui->action_StopProjection, SIGNAL(triggered()), this, SLOT(onButtonStopProjection()));
	connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonSetAsTutor()));
	connect(ui->action_SetAsTutor, SIGNAL(triggered()), this, SLOT(onButtonStopProjection()));
	connect(ui->action_Lock, SIGNAL(toggled(bool)), this, SLOT(onButtonLock(bool)));
	connect(ui->action_Help, SIGNAL(triggered()), this, SLOT(onButtonHelp()));


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

	// Clicking the session name label shows the edit field for it
	connect(_sessionNameLabel, SIGNAL(clicked()), this, SLOT(onSessionNameClick()));
	// Listen to updates to the session name through the session name window
	connect(_sessionNameWindow, SIGNAL(updateSessionName()), this,
	   SLOT(onSessionNameUpdate()));

	setAttribute(Qt::WA_QuitOnClose);
	setUnifiedTitleAndToolBarOnMac(true);
	this->showMaximized(); // show the Mainwindow maximized

	_listenServer = new ListenServer(CLIENT_PORT);
	connect(_listenServer, SIGNAL(newClient(Client*)), this, SLOT(onClientConnected(Client*)));
	_discoveryListener = new DiscoveryListener();

	// Finally
	this->onSessionNameUpdate();
}

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

/***************************************************************************//**
 * Place the frames at possible Position.
 * If current position out of range, place frame into next free cell.
 * @param frame
 */
void MainWindow::placeFrameInFreeSlot(ConnectionFrame* frame)
{
	// Get occupied cell of each frame and store status in an array
	const int elems = _tilesX * _tilesY;
	int currentIndex = 0;
	bool grid[elems];
	memset(grid, 0, sizeof(bool) * elems); // set everything to false
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		const QPoint &oldpos = (*it)->frameGeometry().topLeft();
		const int tx = oldpos.x() / _tileWidth;
		const int ty = oldpos.y() / _tileHeight;
		const int index = tx + ty * _tilesX;
		if (frame == *it)
		{
			// Current frame is the frame to place, remember its old index
			currentIndex = index;
			continue;
		}
		if (index < 0 || index >= elems)
		{
			// Current frame is out of bounds, place in top left corner
			qDebug("Move A");
			(*it)->move(0, 0);
			grid[0] = true;
			continue;
		}
		// Just mark cell as occupied in array
		grid[index] = true;
	}
	// Safety
	if (currentIndex < 0 || currentIndex >= elems)
		currentIndex = 0;
	// Now place in next free cell, start looking at current position of frame
	for (int i = 0; i < elems; ++i)
	{
		const int index = (i + currentIndex) % elems;
		if (grid[index])
			continue;
		const int x = (index % _tilesX) * _tileWidth;
		const int y = (index / _tilesX) * _tileHeight;
		//qDebug() << "Index=" << index << " i=" << i << " cI=" << currentIndex << " elems=" << elems << " x=" << x << " y=" << y;
		qDebug("Move B");
		frame->move(x, y);
		break;
	}
}

/***************************************************************************//**
 * Create new Frame.
 * Create new frame and add to current available frame list.
 * Also connect signals frameMoved() and clicked() with slots.
 * @return ConnectionFrame*
 */
ConnectionFrame* MainWindow::createFrame()
{
	// Allocate and resize
	ConnectionFrame *cf = new ConnectionFrame(ui->frmRoom, _tileWidth, _tileHeight);
	_clientFrames.append(cf);
	cf->show();
	connect(cf, SIGNAL(frameMoved(ConnectionFrame *)), this, SLOT(onPlaceFrame(ConnectionFrame *)));
	connect(cf, SIGNAL(clicked(ConnectionFrame *)), this, SLOT(onFrameClicked(ConnectionFrame *)));
	return cf;
}

/***************************************************************************//**
 * Load position.
 * @param settings
 * @param id
 * @param x
 * @param y
 * @return If loadPosition was successfull.
 */
bool MainWindow::loadPosition(QSettings& settings, const QString& id, int& x, int& y)
{
	settings.beginGroup("client_position");
	const QVariant retval(settings.value(id));
	settings.endGroup();
	if (retval.type() != QVariant::Point)
		return false;
	const QPoint point(retval.toPoint());
	x = point.x();
	y = point.y();
	return true;
}

/***************************************************************************//**
 * Save position of given connectionFrame.
 * @param cf
 */
void MainWindow::savePosition(ConnectionFrame *cf)
{
	Client *client = cf->client();
	if (client == NULL)
		return;
	QPoint pos(cf->pos().x() / _tileWidth, cf->pos().y() / _tileHeight);
	USER_SETTINGS(settings);
	settings.beginGroup("client_position");
	settings.setValue(client->ip(), pos);
	settings.endGroup();
}

/***************************************************************************//**
 * Tells the new client the current situation in class room.
 * Set the specific parameters like projection source or lock screen.
 * @param client
 */
void MainWindow::tellClientCurrentSituation(Client* client)
{
	// If clients are currently locked, tell this new client
	if (ui->action_Lock->isChecked())
		client->lockScreen(true);

	if (_mode == Mode::Broadcast){
		// _watchers.insert(client->id(), client);
		client->setWatcher(true);
		Client* c = getClientFromId(_streamingSource);
		if (c != NULL) {
			client->startVncClient(c);
		}
	}
	else if (_mode == Mode::LockedMulticast)
		client->lockScreen(true);

}

/***************************************************************************//**
 * Returns connected client which belongs to given id.
 * Iterating over ConnectionFrames and comparing id to given id.
 * @param id
 * @return Client with given id, if not NULL.
 */
Client* MainWindow::getClientFromId(int id)
{
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		if ((*it)->client() != NULL)
		{
			if ((*it)->client()->id() == id)
				return (*it)->client();
		}
	}
	return NULL;
}

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

/***************************************************************************//**
 * Return the Frame, which is currently selected by user.
 * Iterating over all ConnectionFrame and looking for flag _isSelected.
 * @return Frame with flag _isSelected = true,
 * else NULL if no frame is selected.
 */
ConnectionFrame* MainWindow::getSelectedFrame()
{
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		if (((*it) != NULL) && ((*it)->isSelected()))
			return (*it);
	}
	return NULL;
}

/*
 * Overridden methods
 */

/***************************************************************************//**
 * Handle closeEvent.
 * If user calls closing MainWindow, close, else ignore.
 * @param e
 */
void MainWindow::closeEvent(QCloseEvent* e)
{
	int ret = QMessageBox::question(this, "Test", "Exit?", 0, 1, 2);
	if (ret == 1)
		QApplication::exit(0);
	else
		e->ignore();
}

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

/***************************************************************************//**
 * Resize event.
 * @param e
 */
void MainWindow::resizeEvent(QResizeEvent* e)
{
	// Resize all connection windows
	if (ui->frmRoom->size().width() < 100 || ui->frmRoom->size().height() < 100 || _tilesX <= 0 || _tilesY <= 0)
		return;
	const int nw = ui->frmRoom->size().width() / _tilesX;
	const int nh = ui->frmRoom->size().height() / _tilesY;
	for (QList<ConnectionFrame*>::iterator it = _clientFrames.begin(); it != _clientFrames.end(); ++it)
	{
		const QPoint &oldpos = (*it)->frameGeometry().topLeft();
		qDebug("Move C");
		(*it)->move((oldpos.x() / _tileWidth) * nw, (oldpos.y() / _tileHeight) * nh);
		(*it)->setSize(nw, nh);
	}

	// Resize trash and set position.
	const int width = ui->frmRoom->geometry().width() - (nw + 1);
	const int height = ui->frmRoom->geometry().height() - (nh + 1);
	ui->trash->move(width, height);
	ui->trash->resize(nw, nh);
	// qDebug() << "Trash pos: " << ui->trash->pos();

	_tileWidth = nw;
	_tileHeight = nh;
}

/***************************************************************************//**
 * Handle Mouse Release Event.
 * Check if click was inside the frame and if that is the case, set the selection of each connected
 * client to false.
 * @param e
 */
void MainWindow::mouseReleaseEvent(QMouseEvent* e)
{
	// Try to figure out if the user clicked inside the "room" frame
	// No idea if there is a more elegant way to do this without
	// sub-classing that frame and overriding its mouseReleaseEvent
	const QPoint pos(ui->frmRoom->mapFrom(this, e->pos()));
	if (pos.x() < 0 || pos.y() < 0)
		return;
	const QSize frame(ui->frmRoom->size());
	if (frame.width() > pos.x() && frame.height() > pos.y())
	{
		if (getSelectedFrame() != NULL)
		{
			getSelectedFrame()->setSelection(false);
		}
	}
}

/***************************************************************************//**
 * @brief reset
 */
void MainWindow::reset()
{
	// Unlock all clients
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
		if ((*it)->client() != NULL)
			(*it)->client()->lockScreen(false);

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

/*
 * Slots
 */

/***************************************************************************//**
 * Extract information from downloaded tutorList.
 * Split downloaded file by new line and store IPs in _tutorList.
 * @param tutorList
 */
void MainWindow::onTutorListDownloaded(QByteArray& tutorList)
{
	// qDebug() << tutorList;
	QString data = QString::fromUtf8(tutorList.constData(), tutorList.size());
	_tutorList = data.split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
	qDebug() << _tutorList;
}

/***************************************************************************//**
 * Place Frame to from user specified position.
 * @param frame
 */
void MainWindow::onPlaceFrame(ConnectionFrame* frame)
{
	if (_tilesX <= 0 || _tilesY <= 0)
		return;
	const QPoint &p = frame->frameGeometry().center();
	const QSize &s = ui->frmRoom->geometry().size();
	int x = (p.x() / _tileWidth) * _tileWidth;
	int y = (p.y() / _tileHeight) * _tileHeight;
	if (x < 0)
		x = 0;
	else if (x > s.width() - _tileWidth)
		x = (_tilesX - 1) * _tileWidth;
	if (y < 0)
		y = 0;
	else if (y > s.height() - _tileHeight)
		y = (_tilesY - 1) * _tileHeight;

	const QRect &trashCenter = ui->trash->geometry();
	// Check if x coordinate is in trash position.
	if (trashCenter.contains(p))
	{
		// Do not offer deleting online client.
		if (frame->client() != NULL)
		{
			QMessageBox::critical(this, tr("Selection"), sStrClientOnline);
			frame->move(frame->getPreviousPosition());
			return;
		}
		else
		{
			int ret = QMessageBox::question(this, "Warning", "Sure, You want to delete selected client?", 0, 1, 2);
			if (ret == 1)
			{
				frame->hide();
				frame->deleteLater();
				_clientFrames.removeOne(frame);
				return;
			}
			else
			{
				frame->move(frame->getPreviousPosition());
				return;
			}
		}
	}
	qDebug("Move D");
	frame->move(x, y);
	savePosition(frame);
	const QPoint &newpos = frame->frameGeometry().topLeft();
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		if (*it == frame)
			continue;
		if ((*it)->frameGeometry().topLeft() == newpos)
		{
			placeFrameInFreeSlot(*it);
		}
	}
}

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

	// If another frame has been selected, unselect it
	// Set the ui selected and set a new reference
	if (getSelectedFrame() != NULL)
	{
		getSelectedFrame()->setSelection(false);
	}
	frame->setSelection(true);
	qDebug() << "ID of frame: " << frame->computerId();
	qDebug() << "ID of selectedFrame: " << getSelectedFrame()->computerId();
}

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

/***************************************************************************//**
 * Update session name.
 */
void MainWindow::onSessionNameUpdate()
{
	// Stop all projections and clear all clients, which where connected to old sessionName.
	reset();
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		(*it)->hide();
		(*it)->deleteLater();
	}
	_clientFrames.clear();
	_sessionNameLabel->setText(tr("Session Name: %1  [click to edit]").arg(Global::sessionName()));
}

/***************************************************************************//**
 * @brief MainWindow::changeProjection
 * @param from
 * @param mode
 * @param to
 */
void MainWindow::changeProjection(Client *from, Mode mode, Client *to)
{
	DisableButtons();

	if (mode == Mode::Broadcast)
	{
		// Set all clients as watchers
		for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
		{
			if ((*it)->client() != NULL && (*it)->client() != getClientFromId(_streamingSource))
			{
				// _watchers.insert((*it)->client()->id(), (*it)->client());
				(*it)->client()->setWatcher(true);
			}
		}
	}
	else // mode != Mode::Broadcast
	{
		// If this is the first call in this mode clear the watchers
		if ((mode == Mode::LockedMulticast && _mode != Mode::LockedMulticast)
			|| (mode == Mode::Multicast && _mode != Mode::Multicast))
		{
			// _watchers.clear();
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
			{
				if ((*it)->client() != NULL)
					(*it)->client()->setWatcher(false);
			}
		}

		// If "to" already watches "from" stop it
		// if (_watchers.contains(to->id()))
		if (getClientFromId(to->id())->isWatcher())
		{
			// _watchers.remove(to->id());
			getClientFromId(to->id())->setWatcher(false);
		}
		else // list "to" as watcher
		{
			// _watchers.insert(to->id(), to);
			getClientFromId(to->id())->setWatcher(true);
		}
	}

	// Set the mode
	_mode = mode;

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

	// Set new streaming source
	_streamingSource = from->id();

	// If streaming source is already active avoid a restart
	Client* c = getClientFromId(_streamingSource);
	if (c != NULL) {
		if (c->isActiveVncServer())
			this->onVncServerStateChange(c);
		else  // Could not take shortcut, (re)start VNC server on source
			c->startVncServer();
	}
}

/***************************************************************************//**
 * Display popup which explains possible actions about the buttons.
 */
void MainWindow::onButtonHelp()
{
	_helpWindow->show();
}

/***************************************************************************//**
 * Handle projection from tutor to all.
 * Get the client who is tutor and set the projectionSource of all
 * clients, except the tutor ones, to false.
 */
void MainWindow::onButtonTutorToAll()
{
	ui->action_Lock->setChecked(false);

	if (getTutorFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getTutorFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else if (_clientFrames.size() == 1)
		QMessageBox::critical(this, tr("Projection"), sStrNoDestAv);
	else
		changeProjection(getTutorFrame()->client(), Mode::Broadcast);
}

/***************************************************************************//**
 * Button projection from one student to all the others.
 * First get which client is projectionSource and set this one as from.
 * Set projection source for all clients,except the selected one, to false.
 */
void MainWindow::onButtonStudentToAll()
{
	ui->action_Lock->setChecked(false);

	if (getSelectedFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
	if (getSelectedFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
	else
		changeProjection(getSelectedFrame()->client(), Mode::Broadcast);
}

/***************************************************************************//**
 * Handle the projection from Tutor to specific student.
 * Set the client who is tutor as from and the selected client as to.
 */
void MainWindow::onButtonTutorToStudent()
{
	ui->action_Lock->setChecked(false);

	if (getSelectedFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrDestNdef);
	else if (getTutorFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getSelectedFrame() == getTutorFrame())
		QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
	else if (getSelectedFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrDestOffline);
	else if (getTutorFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else
		changeProjection(getTutorFrame()->client(), Mode::Multicast, getSelectedFrame()->client());
}

/***************************************************************************//**
 * Handle projection from one student to tutor.
 * Get the client to project from and get client, who is tutor, as to.
 */
void MainWindow::onButtonStudentToTutor()
{
	ui->action_Lock->setChecked(false);

	if (getSelectedFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
	else if (getTutorFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getTutorFrame() == getSelectedFrame())
		QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
	else if (getSelectedFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
	else if (getTutorFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else
		changeProjection(getSelectedFrame()->client(), Mode::Multicast, getTutorFrame()->client());
}


/***************************************************************************//**
 * @brief MainWindow::onButtonStudentToTutorExclusive
 */
void MainWindow::onButtonStudentToTutorExclusive()
{
	ui->action_Lock->setChecked(false);

	if (getSelectedFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceNdef);
	else if (getTutorFrame() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorNdef);
	else if (getTutorFrame() == getSelectedFrame())
		QMessageBox::critical(this, tr("Projection"), sStrSourceDestSame);
	else if (getSelectedFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrSourceOffline);
	else if (getTutorFrame()->client() == NULL)
		QMessageBox::critical(this, tr("Projection"), sStrTutorOffline);
	else
		changeProjection(getSelectedFrame()->client(), Mode::LockedMulticast, getTutorFrame()->client());
}


/***************************************************************************//**
 * Handle Button StopProjection.
 * Set ProjectionSource of each client to false,  stop the active VNC Server
 * and the active VNC Client(s) and unlock all screens.
 */
void MainWindow::onButtonStopProjection()
{
	ui->action_Lock->setChecked(false);
	reset();
}

/***************************************************************************//**
 * Handle button to lock or unlock screens of client(s).
 * If already locked, do nothing, else lock or unlock the clients, except the
 * tutor and the manager running machine.
 * @param checked
 */
void MainWindow::onButtonLock(bool checked)
{
	// Stop all projections
	reset();

	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		if ((*it)->client() == NULL)
			continue;
		(*it)->client()->lockScreen(checked);
	}
}

/***************************************************************************//**
 * On button exit, close application.
 */
void MainWindow::onButtonExit()
{
	this->close();
}

/***************************************************************************//**
 * Handle button to set specific client as tutor.
 * Unset active tutor and set selected client, if not inactive, as new tutor.
 */
void MainWindow::onButtonSetAsTutor()
{
	ui->action_Lock->setChecked(false);

	// If no frame is selected, warning.
	if (getSelectedFrame() == NULL)
	{
		QMessageBox::critical(this, tr("Selection"), tr("No client is selected."));
		return;
	}

	// If frame of inactive client has been selected unselect it
	if (getSelectedFrame()->client() == NULL)
	{
		QMessageBox::critical(this, tr("Selection"), tr("The selected client is not connected."));
		return;
	}
	else  // If selected client is locked, first unlock
	{
		getSelectedFrame()->client()->lockScreen(false);
	}

	// If same frame is already tutor, do nothing
	if (getSelectedFrame() == getTutorFrame())
		return;

	// Else unset the old and set the new tutor
	if (getTutorFrame() != NULL)
	{
		getTutorFrame()->setTutor(false);
	}
	getSelectedFrame()->setTutor(true);
}

/***************************************************************************//**
 * Handle from ListenServer signaled new client connection.
 * @param client
 */
void MainWindow::onClientConnected(Client* client)
{
	connect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
	connect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
}

/***************************************************************************//**
 * Authenticate new Client client.
 * Check if incoming client ip already exists,
 *							if yes --> return.
 * Check if same name of client already exist,
 *							if yes --> name new client as name(x).
 *							x = number of clients with the same name.
 * @param client
 * @param request
 */
void MainWindow::onClientAuthenticating(Client* client, ClientLogin* request)
{
	disconnect(client, SIGNAL(authenticating(Client*, ClientLogin*)), this, SLOT(onClientAuthenticating(Client*, ClientLogin*)));
	if (!request->accept) // Another receiver of that signal already rejected the client, so nothing to do
		return;
	bool inuse;
	QString check = request->name;
	int addnum = 1;
	do
	{
		inuse = false;
		for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
		{
			Client *c = (**it).client();
			if (c == NULL)
				continue;
			if (!c->isAuthed())
				continue;
			if (c->ip() == request->ip)
			{
				request->accept = false;
				return;
			}
			if (c->name() == check)
			{
				inuse = true;
				check = request->name + " (" + QString::number(++addnum) + ")";
				break;
			}
		}
	} while (inuse && addnum < 100);
	if (inuse)
		request->accept = false;
	else
		request->name = check;
}

/***************************************************************************//**
 * New client was authenticated, make several checks.
 * Check if new clients ip already exists,
 *								if yes --> not necessary to create new clientFrame.
 *								if not --> create a new one and adapt this one to current
 *												   situation in class room(projection / lock screen).
 * Check if avctiv tutor is available,
 *								if not --> check if new client is possible tutor.
 * @param client
 */
void MainWindow::onClientAuthenticated(Client* client)
{
	disconnect(client, SIGNAL(authenticated(Client*)), this, SLOT(onClientAuthenticated(Client*)));
	connect(client, SIGNAL(vncServerStateChange(Client*)), this, SLOT(onVncServerStateChange(Client*)));
	connect(client, SIGNAL(vncClientStateChange(Client*)), this, SLOT(onVncClientStateChange(Client*)));
	bool hasActiveTutor = false;
	ConnectionFrame *existing = NULL;
	for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
	{
		if ((*it)->computerId() == client->ip())
			existing = *it;
		if ((*it)->isTutor())
		{
			if ((*it)->client() != NULL)
				hasActiveTutor = true;
		}
	}

	bool isTutor = false;
	if (!hasActiveTutor)
	{
		for (int i = 0; i < _tutorList.size(); i++)
		{
			// Check if client is possible tutor
			if (client->ip() == _tutorList[i])
			{
				isTutor = true;
				break;
			}
		}
	}
	// Clients ip already exists, but was not active.
	if (existing != NULL)
	{
		existing->setTutor(isTutor);
		existing->assignClient(client);
		tellClientCurrentSituation(client);
		return;
	}

	if (_clientFrames.size() > 50) {
		client->deleteLater();
		return;
	}

	// New one, create
	ConnectionFrame *cf = createFrame();
	// Try to load last known position
	int x, y;
	bool ok;
	USER_SETTINGS(usr);
	ok = loadPosition(usr, client->ip(), x, y);
	if (!ok)
	{
		SYSTEM_SETTINGS(sys);
		ok = loadPosition(sys, client->ip(), x, y);
	}
	if (x >= _tilesX || y >= _tilesY)
		ok = false;
	if (ok)
	{
		if (x < 0)
			x = 0;
		if (y < 0)
			y = 0;
		qDebug("Move E");
		cf->move(x * _tileWidth, y * _tileHeight);
		onPlaceFrame(cf);
	}
	else
	{
		// Move to any free tile
		placeFrameInFreeSlot(cf);
	}
	// Set Tutor option
	cf->setTutor(isTutor);
	// Assign client instance
	cf->assignClient(client);

	tellClientCurrentSituation(client);
}

/***************************************************************************//**
 * Handle if VNC Server State has changed.
 * Check if VNC server has been started/stopped on some client and start VNC server/reset pending
 * VNC projection information.
 * @param client
 */
void MainWindow::onVncServerStateChange(Client* client)
{
	if (client == getClientFromId(_streamingSource))
		EnableButtons();

	if (client->isActiveVncServer())
	{
		if (_mode == Mode::Broadcast)
		{
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
			{
				if ((*it)->client() != NULL)
				{
					// Unlock all online clients
					(*it)->client()->lockScreen(false);

					// Start VNCclients on all online clients but source
					if ((*it)->client() != client)
						(*it)->client()->startVncClient(client);
				}
			}
		}
		else  // !Mode::Broadcast  --> Mode::LockedMC || Mode::MC
		{
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
			{
				// Ignore offline clients
				if ( (*it)->client() != NULL)
				{
					// if (_watchers.contains((*it)->client()->id()))
					if ((*it)->client()->isWatcher())
					{
						// Unlock destination and connect VNCclient
						(*it)->client()->lockScreen(false);
						(*it)->client()->startVncClient(client);
					}
					else if ((*it)->client()->id() == client->id() )
					{
						// Unlock source
						(*it)->client()->lockScreen(false);
					}
					else
					{
						// Lock others and stop their clients
						(*it)->client()->lockScreen(_mode == Mode::LockedMulticast);
						(*it)->client()->stopVncClient();
					}
				}
			}
		}
	}
	else
	{
		// VNC server stopped on some client or failed to start - reset local pending projection information
		// foreach (Client *c, _watchers) {
		//   c->stopVncClient();
		// }
		for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
		{
			if ((*it)->client() != NULL)
			{
				if ((*it)->client()->isWatcher())
					(*it)->client()->stopVncClient();
			}
		}
	}
}

/***************************************************************************//**
 * Handle VNC client state change.
 * Check if any vnc client is in use, and if that is not the case, stop VNC Server.
 * @param client
 * @param lastProjectionSource
 */
void MainWindow::onVncClientStateChange(Client* client)
{
	if (client != NULL)
	{
		// VNC Client stopped -> remove from watchers
		if (!client->isActiveVncClient()){
			// _watchers.remove(client->id());
			if (getClientFromId(client->id()) != NULL)
				getClientFromId(client->id())->setWatcher(false);

			// If noboody is watching the multicast stop VNC server
			// if (_watchers.isEmpty() && _mode != Mode::Broadcast)
			//	 _streamingSource->stopVncServer();
			bool noWatchers = true;
			for (QList<ConnectionFrame*>::iterator it(_clientFrames.begin()); it != _clientFrames.end(); ++it)
			{
				if ((*it)->client() != NULL)
				{
					if ((*it)->client()->isWatcher())
						noWatchers = false;
				}
			}
			if (noWatchers && _mode != Mode::Broadcast)
			{
				Client* c = getClientFromId(_streamingSource);
				if (c != NULL)
					c->stopVncServer();
			}
		}
	}
}

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

/***************************************************************************//**
 * EnableButtons.
 */
void MainWindow::EnableButtons()
{
	_buttonLockTimer->stop();
	foreach (QAction* a, _lockingButtons)
		a->setEnabled(true);
}