summaryrefslogblamecommitdiffstats
path: root/src/gui/frame.cpp
blob: 60540651569e00da5de5808c6f231b502d3c4c5a (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/
# -----------------------------------------------------------------------------
# src/gui/frame.cpp
#	This is the drawingarea, it will be set to the suitable ConnectionFrame.
#	Each instance of this class has is own VNCThread, this way is more simple
# 	to show, update and scale the image from the suitable client. Only the point
#	on the image that need to be drawed or scaled will be processed. Thus the CPU
#	performance will not be affected. The pvsmgr can manage in this way a lot of clients.
# -----------------------------------------------------------------------------
*/

#include <src/input/inputEvent.h>
#include "frame.h"
#include <src/gui/mainWindow.h>
#include <iostream>
#include <QPixmap>

#define MOUSE_MOTION_SEND_INTERVAL 100 /* msecs */
#define SPECIAL_EVENT_WAIT_TIME 5000 /* msecs */

Frame::Frame(const QString & text, QWidget * parent) :
        QLabel(parent), _clientVNCThread(0)
{
	_clientVNCThread = NULL;

	X = 0;
	Y = 0;
	setBackgroundRole(QPalette::Base);

	setAlignment(Qt::AlignCenter);

	setAutoFillBackground(true);
	setFrameStyle(QFrame::StyledPanel | QFrame::Plain);

	_isLocked = false;
	_dozent   = false;
	_uy = _ux = 0;
	_isCloseUp = false;

	//QIcon icon;
	//icon.addFile(QString::fromUtf8(), QSize(), QIcon::Normal, QIcon::Off);
//	button_closeUp   = createToolButton(tr("View"), QIcon(":/restore"),SLOT(closeUp()));
//	button_closeUp->setCheckable(true);
//	button_foto      = createToolButton(tr("Foto"), QIcon(":/photos"),SLOT(foto()));
//	button_lock      = createToolButton(tr("Lock this client"), QIcon(":/lock"),SLOT(setLock()));
	//button_unlock    = createToolButton(tr("Unlock this client"), QIcon(":/lock"),SLOT(setLock()));
//	button_dozent    = createToolButton(tr("Set as Superclient"), QIcon(":/dozent2"),SLOT(setDozent()));
//	button_dozent->setCheckable(true);
	button_control   = createToolButton(tr("Enable Remote Control"), QIcon(":/remotecontrol"), SLOT(remoteControlClicked()));
	button_control->setCheckable(true);
	button_control_all = createToolButton(tr("Remote Control All Clients"), QIcon(":/remotecontrolall"), SLOT(remoteControlAllClicked()));
	button_control_all->setCheckable(true);

	connect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
	ip = "";
	setToolButtonListVisible(false);

	_remoteControlEnabled = false;
	_remoteControlToAll = false;

	_mouseMotionEventTimer = new QTimer(this);
	_mouseMotionEventTimer->setInterval(MOUSE_MOTION_SEND_INTERVAL);
	_mouseMotionEventTimer->setSingleShot(false);
	connect(_mouseMotionEventTimer, SIGNAL(timeout()), this, SLOT(sendMouseMotionEvent()));

	_mousePositionChanged = true;

	_specialEventTimer = new QTimer(this);
	_specialEventTimer->setInterval(SPECIAL_EVENT_WAIT_TIME);
	_specialEventTimer->setSingleShot(true);
	connect(_specialEventTimer, SIGNAL(timeout()), this, SLOT(showSpecialEventMenu()));

}

Frame::~Frame()
{
	if (_clientVNCThread)
	{
		disconnect(_clientVNCThread, SIGNAL(imageUpdated(int,int,int,int)), this,
	            SLOT(updateImage(int,int,int,int)));
		disconnect(_clientVNCThread, SIGNAL(finished()), this,
		            SLOT(iamDown()));
		if (_clientVNCThread->isRunning())
		{
			_clientVNCThread->terminate = true;
			_clientVNCThread->wait(_clientVNCThread->getUpdatefreq()+2000);
		}
	}
}


void Frame::setVNCThreadConnection(VNCClientThread * vncClientThread)
{
    // initialize the vncthread for this connection
	//printf("Starting VNC thread for %s\n", ip.toUtf8().data());
	_clientVNCThread = vncClientThread;

	connect(_clientVNCThread, SIGNAL(imageUpdated(int,int,int,int)), this,
            SLOT(updateImage(int,int,int,int)), Qt::BlockingQueuedConnection);
	connect(_clientVNCThread, SIGNAL(finished()), this,
	            SLOT(iamDown()));

    // start the thread
	if(!_clientVNCThread->terminate)
		_clientVNCThread->start();
}

/*
 * To stop the vncThreadConnection, we only disconnect all connected signals on _clientVNCThread
 * We don't need here to terminate the thread, this will appear in vncconnection, because
 * we have set this thread there (in vncconnection).
 */
void Frame::stopVNCThreadConnection()
{
	disconnect(_clientVNCThread, SIGNAL(imageUpdated(int,int,int,int)), this,
			SLOT(updateImage(int,int,int,int)));
	disconnect(_clientVNCThread, SIGNAL(finished()), this,
			SLOT(iamDown()));
}

void Frame::updateImage(int x, int y, int w, int h)
{
	if (_clientVNCThread == NULL)
		return;

    if (_clientVNCThread->getSize() != size())
    {
        // grow the update rectangle to avoid artifacts
        x -= 3;
        y -= 3;
        w += 6;
        h += 6;

        _clientImg = _clientVNCThread->getImage().copy(x, y, w, h);

        qreal sx = qreal(width()) / qreal(_clientVNCThread->getSize().width());
        qreal sy = qreal(height()) / qreal(_clientVNCThread->getSize().height());

        x = qRound(qreal(x) * sx);
        y = qRound(qreal(y) * sy);
        w = qRound(qreal(w) * sx);
        h = qRound(qreal(h) * sy);
    }
    else
    {
        _clientImg = _clientVNCThread->getImage().copy(x, y, w, h);
    }

    _ux = w;
    _uy = h;
    repaint(x, y, w, h); // this will trigger the PaintEvent
}

void Frame::iamDown()
{
	if (_clientVNCThread == NULL) return;
	_clientVNCThread = NULL;
	update();
}

void Frame::paintEvent(QPaintEvent *event)
{
	QPainter painter(this);
	QRect r = event->rect();
	event->accept();

    if (_clientImg.isNull())
    {
    	_clientImg = QImage(":/terminal");
    	painter.drawImage(28,0, _clientImg.scaled(145,123, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    	setAlignment(Qt::AlignCenter);
        return;
    }

    if(_isLocked)
    {
    	_clientImg = QImage(":/lock");
		painter.drawImage(28,0, _clientImg.scaled(145,123, Qt::KeepAspectRatio, Qt::SmoothTransformation));
		setAlignment(Qt::AlignCenter);
		return;
    }

    if (_clientVNCThread != NULL)
    {
    	if (r.width() != _ux || r.height() != _uy)
		{
			_clientImg = _clientVNCThread->getImage(); // redraw complete image (e.g. on resize)
			r = rect();
		}
    	else
    	{
    		_ux = -1;
    	}

		if (_clientVNCThread->getSize() == size())
		{

			painter.drawImage(r.topLeft(), _clientImg); // don't scale
		}
		else
		{
			QImage i = _clientImg.scaled(r.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
			painter.drawImage(r.topLeft(), i);
		}
    }
    else
    {
    	_clientImg = QImage(":/terminal");
    	painter.drawImage(28,0, _clientImg.scaled(145,123, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    	setAlignment(Qt::AlignCenter);
    }
}

void Frame::slotClicked()
{
	if (ip != "") {
		for (int i=0; i<MainWindow::getConnectionList()->model->rowCount(QModelIndex()); i++)
		{
			if (MainWindow::getConnectionList()->model->index(i, 1, QModelIndex()).data(Qt::DisplayRole).toString()
					.compare(ip) == 0)
			{
				MainWindow::getConnectionList()->selectRow(i);
				MainWindow::getConnectionList()->setCurrentIndex(MainWindow::getConnectionList()->currentIndex());

				// red border for selected client
				QList<ConnectionFrame*> clients = MainWindow::getConnectionWindow()->getAllFrameOnWindow();
			    foreach (ConnectionFrame *client, clients)
			        client->setStyleSheet(STYLE_DEFAULT);
		        _cFrame->setStyleSheet(STYLE_SELECTED);
				break;
			}
		}
	}
}

void Frame::mousePressEvent(QMouseEvent* event)
{
	if(!_remoteControlEnabled)
	{
		emit clicked();
		if (event->button() == Qt::RightButton)
			{
				/*if (!_dummy)
					DelDummy->setDisabled(true);
				menu->exec(QCursor::pos());*/
			}
			else
			{

			}
		QLabel::mousePressEvent(event);
	}
	else
	{
		event->accept();
		ConsoleLog writeLine("Captured remote control mousePressEvent");

		updateMousePosition(event);
		sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons()));
	}
}

void Frame::mouseReleaseEvent ( QMouseEvent * event )
{
	if(!_remoteControlEnabled)
	{
		QLabel::mouseReleaseEvent(event);
	}
	else
	{
		event->accept();
		ConsoleLog writeLine("Captured remote control mouseReleaseEvent");

		updateMousePosition(event);
		sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons()));
	}
}

QToolButton* Frame::createToolButton(const QString &toolTip, const QIcon &icon, const char *member)
 {
     QToolButton *button = new QToolButton(this);
     button->setToolTip(toolTip);
     button->setIcon(icon);
     button->setIconSize(QSize(30, 30));
     addButton(button);
     toolButtonList.append(button);
     connect(button, SIGNAL(clicked()), this, member);
     return button;
}

void Frame::addButton(QToolButton *button)
{
	button->move(X,Y);
	Y += button->size().height() + 10;
}

void Frame::setToolButtonListVisible(bool visible)
{
	foreach (QToolButton* tb, toolButtonList)
		tb->setVisible(visible);
//	if (visible && MainWindow::getConnectionWindow()->hasDozent && !_dozent)
//		button_dozent->setVisible(false);//At this time this button should only be visible on the dozent machine (the superclient).

}

void Frame::setLockStatus(bool lock)
{
	if (lock)
	{
		//button_lock->setToolTip(tr("Unlock this client"));
		//button_lock->setIcon() TODO
	}
	else
	{
		//button_lock->setToolTip(tr("Lock this client"));
		//button_lock->setIcon() TODO
	}
	_isLocked = lock;
}

QImage Frame::getImageForFoto()
{
	return _clientVNCThread->getImage();
}

void Frame::closeUp()
{
	emit clicked();

	if(_isCloseUp)
		MainWindow::getWindow()->unCloseUp(getConFrame());
	else
		MainWindow::getWindow()->closeUp(getConFrame());
}

void Frame::foto()
{
	emit clicked();
	MainWindow::getWindow()->foto();
}

void Frame::setLock()
{
	if (!_dozent)
	{
		emit clicked();
		if (_isLocked)
			MainWindow::getConnectionWindow()->unlockStations();
		else
			MainWindow::getConnectionWindow()->lockStations();
	}
	else
	{
		QString message = QString(tr("You can't lock a Superclient-machine."));
		QMessageBox::information(this, "PVS", message);
	}
}

void Frame::setDozent()
{
	if (_dozent)
	{
		//button_dozent->setToolTip(tr("Set client as Superclient"));
		_dozent = false;
		MainWindow::getConnectionWindow()->hasDozent = false;
		getConFrame()->setDozent(false);
	}
	else
	{
		//button_dozent->setToolTip(tr("Unset client as Superclient"));
		_dozent = true;
		MainWindow::getConnectionWindow()->hasDozent = true;
		getConFrame()->setDozent(true);
	}
}

void Frame::setCloseUp(bool value)
{
	_isCloseUp = value;
	//button_closeUp->setChecked(value);
}

void Frame::remoteControlClicked()
{
	if(_remoteControlEnabled)
	{
		setMouseTracking(false);
		_mouseMotionEventTimer->stop();
		button_control->setToolTip(tr("Enable Remote Control"));
		_remoteControlEnabled = false;
		button_control->setChecked(false);
		releaseKeyboard();
	}
	else
	{
		button_control->setToolTip(tr("Disable Remote Control"));
		_remoteControlEnabled = true;
		button_control->setChecked(true);
		_mouseMotionEventTimer->start();
		setMouseTracking(true);
		if(_mouseOver)
			grabKeyboard();
	}
}

void Frame::remoteControlAllClicked()
{
	if(_remoteControlToAll)
	{
		button_control_all->setToolTip(tr("Remote Control only this Client"));
		button_control_all->setChecked(false);
		_remoteControlToAll = false;
	}
	else
	{
		button_control_all->setToolTip(tr("Remote Control All Clients"));
		button_control_all->setChecked(true);
		_remoteControlToAll = true;
	}
}



void Frame::sendMouseMotionEvent()
{
	InputEvent evt = InputEvent::mouseMotion(_lastRecordedMousePosition.x(), _lastRecordedMousePosition.y());

	if(!_mousePositionChanged)
		return;

	_mousePositionChanged = false;
	sendInputEvent(evt);
}

void Frame::sendInputEvent(InputEvent const& evt)
{
	QString str;
	eventToString(evt, str);
	PVSMsg msg(PVSCOMMAND, "INPUTEVENT", str);

	if(_remoteControlEnabled)
	{
		if(_remoteControlToAll)
		{
			ConsoleLog writeLine(QString("sendInputEvent(%1) to one").arg(evt.toString()));
			PVSConnectionManager::getManager()->getServer()->sendToAll(msg);
		}
		else
		{
			ConsoleLog writeLine(QString("sendInputEvent(%1) to all").arg(evt.toString()));
			_cFrame->getConnection()->sendMessage(msg);
		}
	}
	else
	{
		ConsoleLog writeLine("sendMouseMotionEvent() disabled");
	}
}

void Frame::mouseMoveEvent(QMouseEvent* event)
{
	QPoint newPosition = rescalePosition(event->posF());
	if(newPosition != _lastRecordedMousePosition) {
		_lastRecordedMousePosition = newPosition;
		_mousePositionChanged = true;
		ConsoleLog writeLine(QString("Mouse moved to (%1,%2)").arg(_lastRecordedMousePosition.x()).arg(_lastRecordedMousePosition.y()));
	}
}

QPoint Frame::rescalePosition(QPointF guipos)
{
	if(!_clientVNCThread)
		return QPoint();

	QSize s = size();
	QSize t = _clientVNCThread->getSize();
	qreal px, py;
	px = guipos.x() * t.width() / (qreal)s.width();
	py = guipos.y() * t.height() / (qreal)s.height();
	return QPoint((int)px, (int)py);
}

void Frame::updateMousePosition(QMouseEvent* event)
{
	QPoint oldPosition = _lastRecordedMousePosition;
	_lastRecordedMousePosition = rescalePosition(event->posF());
	_mousePositionChanged = oldPosition != _lastRecordedMousePosition;
	sendMouseMotionEvent();
}

void Frame::enterEvent(QEvent* event)
{
    QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
	_mouseOver = true;
	if(_remoteControlEnabled)
	{
		grabKeyboard();
	}
}

void Frame::leaveEvent(QEvent* event)
{
    QApplication::setOverrideCursor(QCursor(Qt::OpenHandCursor));
	_mouseOver = false;
	if(_remoteControlEnabled)
	{
		releaseKeyboard();
	}
}

void Frame::keyPressEvent(QKeyEvent* event)
{
	if(_remoteControlEnabled)
	{
		if(event->key() == Qt::Key_Menu)
		{
			qDebug("Menu has been pressed");
			if(!event->isAutoRepeat())
				_specialEventTimer->start();
		}
		else
		{
			// The action of the keyboard may depend on the position of the pointer
			sendMouseMotionEvent();

			int key = event->key();

			if(key >= ' ' && key < 0x100)
			{
				// the key is a Latin1 key. We need to find out the correct code to send.
				QString text = event->text();
				if(text.length() == 1 && text.at(0).row() == 0)
				{
					QChar c = text.at(0);

					// The next problem is that it may be a control character.
					// This happens when Ctrl is pressed, so we only
					// modify keys if they are lowercase or uppercase
					// versions of the keycode.

					if(c.toLower().toLatin1() == (char)key || c.toUpper().toLatin1() == (char)key)
					{
						// We found a Latin1 char and pray it is the correct case.
						key = c.cell();
					}
				}
			}
			sendInputEvent(InputEvent::keyboardPress(key, event->modifiers()));
		}
	}
	else
	{
		QLabel::keyPressEvent(event);
	}
}

void Frame::keyReleaseEvent(QKeyEvent* event)
{
	if(_remoteControlEnabled)
	{
		sendMouseMotionEvent();
		if(event->key() == Qt::Key_Menu)
		{
			if(!event->isAutoRepeat())
			{
				qDebug("Menu has been released");
				if(_specialEventTimer->isActive())
				{
					qDebug("Pressing key on client");
					// Pressing the key has been deferred, so do it now:
					sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers()));
				}
				sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers()));
				_specialEventTimer->stop();
			}
		}
		else
		{
			// The action of the keyboard may depend on the position of the pointer
			sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers()));
		}
	}
	else
	{
		QLabel::keyReleaseEvent(event);
	}
}

bool Frame::event(QEvent* event)
{
	if(_remoteControlEnabled)
	{
		bool recognized;
		switch(event->type())
		{
		case QEvent::ShortcutOverride:
			recognized = true;
			event->accept();
			break;
		case QEvent::KeyPress:
			recognized = true;
			keyPressEvent(static_cast<QKeyEvent*>(event));
			break;
		case QEvent::KeyRelease:
			recognized = true;
			keyReleaseEvent(static_cast<QKeyEvent*>(event));
			break;
		default:
			recognized = false;
		}
		if(recognized && event->isAccepted())
			return true;
	}
	return QLabel::event(event);
}

void Frame::showSpecialEventMenu()
{
	qDebug("Trying to show menu...");
	QMenu* menu = new QMenu(this);
	QList<SpecialInputEventDescription> specialEvents = SpecialInputEventDescription::describeSpecialEvents();
	QListIterator<SpecialInputEventDescription> iter(specialEvents);
	int i;
	for(i = 0;
			iter.hasNext();
			i++)
	{
		QAction* act = menu->addAction(iter.next().description);
		act->setData(i);
	}
	QAction* selected = menu->exec(QCursor::pos());
	if(selected)
	{
		int index = selected->data().toInt();
		sendInputEvent(specialEvents.at(index).toEvent());
	}
	delete menu;
}