/*
# Copyright (c) 2009, 2010 - 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/
# -----------------------------------------------------------------------------
# pvsClientGUI.cpp
# - main loop for pvs client GUI.
# - draws toolbar and system tray icon.
# - uses DBUS to communicate with pvs client.
# -----------------------------------------------------------------------------
*/
#include <getopt.h>
#include "pvsgui.h"
#include "version.h"
PVSGUI::PVSGUI(QWidget *parent) :
QWidget(parent)
{
setupUi(this);
if (!QFile::exists(_settings.fileName()))
{
QDir::root().mkpath(QFileInfo(_settings.fileName()).path());
QFile::copy("/etc/openslx/pvsgui.conf", _settings.fileName());
}
// stop running pvs
qDebug("[%s] Stopping pvs daemon.", metaObject()->className());
QProcess::execute("pvs -c stop");
_menu = new QMenu(this);
_hostMenu = new QMenu(tr("Connect"), this);
_hosts = new QHash<QString, QAction*> ();
_vncViewer = new ClientVNCViewer();
_chatDialog = new ClientChatDialog();
_configDialog = new ClientConfigDialog();
_infoDialog = new ClientInfoDialog();
_aboutDialog = new AboutDialog();
_hostMenu->setEnabled(false);
hostButton->setEnabled(false);
setupMenu();
_trayIcon = new QSystemTrayIcon(QIcon(":cam_off32.svg"), this);
_trayIcon->setContextMenu(_menu);
_trayIcon->setVisible(true);
_chatDialog->setTrayIcon(_trayIcon);
// connect to D-Bus and get interface
QDBusConnection dbus = QDBusConnection::sessionBus();
_ifaceDBus = new OrgOpenslxPvsInterface("org.openslx.pvs", "/", dbus, this);
if (dbus.isConnected())
qDebug("[%s] Connection to DBus successful.", metaObject()->className());
else
qDebug("[%s] ERROR: Could not connect to DBus!", metaObject()->className());
// start pvs daemon
qDebug("[%s] Starting pvs daemon.", metaObject()->className());
QDBusPendingReply<bool> reply0 = _ifaceDBus->start();
reply0.waitForFinished();
if (reply0.isValid() && reply0.value())
qDebug("[%s] Connection to PVS daemon successful.", metaObject()->className());
else
qDebug("[%s] ERROR: Could not connect to PVS daemon!", metaObject()->className());
// get available hosts
QDBusPendingReply<QStringList> reply1 = _ifaceDBus->getAvailableHosts();
reply1.waitForFinished();
QStringList hosts = reply1.value();
if (reply1.isValid() && !hosts.isEmpty())
foreach (QString host, hosts)
addHost(host);
// already connected?
QDBusPendingReply<QString> reply2 = _ifaceDBus->isConnected();
reply2.waitForFinished();
QString host = reply2.value();
if (reply2.isValid() && host != "")
connected(host);
else
disconnected();
// listen on port 29481 for incoming file transfers
_serverSocket = new QTcpServer();
_serverSocket->listen(QHostAddress::Any, 29481);
connect(_serverSocket, SIGNAL(newConnection()), this, SLOT(receiveFile()));
// signals & slots - menu
connect(_showAction, SIGNAL(toggled(bool)), this, SLOT(setVisible(bool)));
connect(_disconnectAction, SIGNAL(triggered()), this, SLOT(pvsDisconnect()));
connect(_startChatAction, SIGNAL(triggered()), _chatDialog, SLOT(open()));
connect(_sendFileAction, SIGNAL(triggered()), this, SLOT(sendFile()));
connect(_configAction, SIGNAL(triggered()), _configDialog, SLOT(open()));
connect(_showInfoAction, SIGNAL(triggered()), _infoDialog, SLOT(open()));
connect(_aboutAction, SIGNAL(triggered()), _aboutDialog, SLOT(open()));
connect(_quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(_configDialog, SIGNAL(configChanged()), this, SLOT(updateConfig()));
// signals & slots - toolbar
connect(_menu, SIGNAL(aboutToHide()), this, SLOT(hide()));
connect(_hostMenu, SIGNAL(aboutToHide()), this, SLOT(hide()));
connect(_hostMenu, SIGNAL(triggered(QAction*)), this,
SLOT(pvsConnect(QAction*)));
// signals & slots - dbus
connect(_ifaceDBus, SIGNAL(showMessage(QString, QString, bool)), this,
SLOT(showMessage(QString, QString, bool)));
connect(_ifaceDBus, SIGNAL(connected(QString)), this,
SLOT(connected(QString)));
connect(_ifaceDBus, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(_ifaceDBus, SIGNAL(addHost(QString)), this, SLOT(addHost(QString)));
connect(_ifaceDBus, SIGNAL(delHost(QString)), this, SLOT(delHost(QString)));
connect(_ifaceDBus, SIGNAL(incomingMulticastTransferNew(qulonglong, QString, QString, qulonglong)), SLOT(incomingMulticastFile(qulonglong, QString, QString, qulonglong)));
// show toolbar
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
setAttribute(Qt::WA_AlwaysShowToolTips);
}
PVSGUI::~PVSGUI()
{
_ifaceDBus->quit();
}
////////////////////////////////////////////////////////////////////////////////
// Public
void PVSGUI::updateConfig()
{
if (_settings.value("Display/location").isNull())
setPosition(POSITION_TOP_CENTER);
else
setPosition(_settings.value("Display/location").toInt());
}
void PVSGUI::setVisible(bool visible)
{
QWidget::setVisible(visible);
_showAction->setChecked(isVisible());
}
void PVSGUI::setPosition(int position)
{
_position = position;
switch (_position)
{
case POSITION_TOP_LEFT:
move(0, 0);
break;
case POSITION_TOP_CENTER:
move((QApplication::desktop()->width() - width()) / 2, 0);
break;
case POSITION_TOP_RIGHT:
move(QApplication::desktop()->width() - width(), 0);
break;
case POSITION_BOTTOM_LEFT:
move(0, QApplication::desktop()->height() - height());
break;
case POSITION_BOTTOM_CENTER:
move((QApplication::desktop()->width() - width()) / 2,
QApplication::desktop()->height() - height());
break;
case POSITION_BOTTOM_RIGHT:
move(QApplication::desktop()->width() - width(),
QApplication::desktop()->height() - height());
break;
default:
updateConfig();
}
}
void PVSGUI::hide()
{
if (!_menu->isVisible() && !_hostMenu->isVisible())
hide(true);
}
////////////////////////////////////////////////////////////////////////////////
// Protected
void PVSGUI::leaveEvent(QEvent * e)
{
if (!_menu->isVisible() && !_hostMenu->isVisible())
hide(true);
QWidget::leaveEvent(e);
}
void PVSGUI::enterEvent(QEvent * e)
{
hide(false);
QWidget::enterEvent(e);
}
void PVSGUI::mousePressEvent(QMouseEvent *event)
{
QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor));
_clickPoint = event->pos();
}
void PVSGUI::mouseReleaseEvent(QMouseEvent *event)
{
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
}
void PVSGUI::mouseMoveEvent(QMouseEvent *event)
{
if (event->globalX() - _clickPoint.x() >= 0 && event->globalX()
- _clickPoint.x() + width() <= QApplication::desktop()->width())
{
move(event->globalX() - _clickPoint.x(), y());
}
}
////////////////////////////////////////////////////////////////////////////////
// Private
void PVSGUI::setupMenu()
{
// setup actions
_showAction = new QAction(tr("Show &toolbar"), this);
_showAction->setCheckable(true);
_disconnectAction = new QAction(tr("&Disconnect"), this);
_startChatAction = new QAction(tr("C&hat"), this);
_sendFileAction = new QAction(tr("&Send File"), this);
_configAction = new QAction(tr("&Config"), this);
_showInfoAction = new QAction(tr("&Information"), this);
_aboutAction = new QAction(tr("&About"), this);
_quitAction = new QAction(tr("&Quit"), this);
// setup menu
_menu->addAction(_showAction);
_menu->addMenu(_hostMenu);
_menu->addAction(_disconnectAction);
_menu->addAction(_showInfoAction);
_menu->addSeparator();
_menu->addAction(_startChatAction);
_menu->addAction(_sendFileAction);
_menu->addSeparator();
_menu->addAction(_configAction);
_menu->addAction(_aboutAction);
_menu->addSeparator();
_menu->addAction(_quitAction);
pvsButton->setMenu(_menu);
hostButton->setMenu(_hostMenu);
}
void PVSGUI::hide(bool b)
{
if (b)
{
if (_position <= POSITION_TOP_RIGHT)
move(x(), 2 - height());
else
move(x(), QApplication::desktop()->height() - 2);
}
else
{
if (_position <= POSITION_TOP_RIGHT)
move(x(), 0);
else
move(x(), QApplication::desktop()->height() - height());
}
}
void PVSGUI::pvsConnect(QAction *action)
{
QString host = action->text();
action->setChecked(false); // we set it manually
// already connected?
if (host == hostButton->text())
{
action->setChecked(true);
return;
}
// ask user for passwd
bool ok = false;
QString passwd = QInputDialog::getText(0, tr("PVS Connection"), tr(
"Please enter password (If not needed leave blank):"),
QLineEdit::Password, QString(), &ok);
if (ok)
{
_ifaceDBus->pvsConnect(host, passwd); // send via dbus
_passwd = passwd; // TODO: we have to ask the backend for passwd!
qDebug("[%s] Host '%s' send via DBus.", metaObject()->className(),
qPrintable(host));
}
}
void PVSGUI::pvsDisconnect()
{
QMessageBox::StandardButton result = QMessageBox::question(0, tr(
"PVS Connection"), tr("Are you sure you want to disconnect?"),
QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);
if (result == QMessageBox::Ok)
_ifaceDBus->pvsDisconnect();
}
void PVSGUI::showMessage(QString title, QString msg, bool useDialog)
{
// show balloon message if supported, otherwise show message dialog
if (!useDialog && _trayIcon && QSystemTrayIcon::supportsMessages())
_trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, 10000);
else
QMessageBox::about(0, title, msg);
}
void PVSGUI::connected(QString host)
{
statusLabel->setText(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\"><html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head><body style=\" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;\"><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" color:#00aa00;\">Online</span></p></body></html>");
showMessage(tr("PVS connection"), tr("Connected to ") + host);
if (_hosts->contains(host))
_hosts->value(host)->setChecked(true);
hostButton->setText(host);
_disconnectAction->setEnabled(true);
_startChatAction->setEnabled(true);
_sendFileAction->setEnabled(true);
_showInfoAction->setEnabled(true);
_infoDialog->setHost(host);
_infoDialog->setPasswd(_passwd);
if (_trayIcon)
{
_trayIcon->setIcon(QIcon(":cam_on32.svg"));
_trayIcon->setToolTip(tr("Connected to ") + host);
}
}
void PVSGUI::disconnected()
{
statusLabel->setText(
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\"><html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">p, li { white-space: pre-wrap; }</style></head><body style=\" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;\"><p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><span style=\" color:#ff0000;\">Offline</span></p></body></html>");
if (_hosts->contains(hostButton->text()))
_hosts->value(hostButton->text())->setChecked(false);
hostButton->setText("-");
_disconnectAction->setEnabled(false);
_startChatAction->setEnabled(false);
_sendFileAction->setEnabled(false);
_showInfoAction->setEnabled(false);
_passwd = "";
if (_trayIcon)
{
_trayIcon->setIcon(QIcon(":cam_off32.svg"));
_trayIcon->setToolTip(tr("Disconnected"));
}
_vncViewer->close();
}
void PVSGUI::addHost(QString host)
{
// avoid duplicates
if (_hosts->contains(host))
return;
QAction *action = _hostMenu->addAction(host);
action->setCheckable(true);
_hosts->insert(host, action);
if (!_hosts->isEmpty())
{
_hostMenu->setEnabled(true);
hostButton->setEnabled(true);
}
if (hostButton->text() == "-")
showMessage(tr("PVS Connection"), tr("New host available: ") + host);
}
void PVSGUI::delHost(QString host)
{
if (_hosts->contains(host))
{
_hostMenu->removeAction(_hosts->value(host));
_hosts->remove(host);
}
if (_hosts->isEmpty())
{
_hostMenu->setEnabled(false);
hostButton->setEnabled(false);
}
}
void PVSGUI::sendFile()
{
ClientFileSendDialog *d = new ClientFileSendDialog();
d->open();
}
void PVSGUI::receiveFile()
{
QTcpSocket *socket = _serverSocket->nextPendingConnection();
ClientFileReceiveDialog *d = new ClientFileReceiveDialog(socket);
d->open();
}
void PVSGUI::incomingMulticastFile(qulonglong transferID, QString sender, QString basename, qulonglong size)
{
ClientFileReceiveDialog *d = new ClientFileReceiveDialog(sender, transferID, basename, size, _ifaceDBus, this);
d->open();
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Main
void printHelp()
{
QTextStream qout(stdout);
qout << QObject::tr("Usage: pvsgui [OPTIONS]...") << endl;
qout << QObject::tr("Start the Pool Video Switch GUI.") << endl;
qout << QObject::tr("Options:") << endl << endl;
qout << "-b or --toolbar" << "\t\t" << QObject::tr("Start with toolbar.") << endl;
qout << "-p or --position" << "\t" << QObject::tr("Set toolbar position (0-5)") << endl;
qout << "-h or --help" << "\t\t" << QObject::tr("Show this help text and quit.") << endl;
qout << "-v or --version" << "\t\t" << QObject::tr("Show version and quit.") << endl;
qout << endl;
qout.flush();
exit(0);
}
void printVersion()
{
QTextStream qout(stdout);
qout << QObject::tr("Version: ") << VERSION_STRING << endl;
qout << endl;
qout.flush();
exit(0);
}
int main(int argc, char *argv[])
{
bool visible = false;
int position = -1;
// parse command line arguments
int opt = 0;
int longIndex = 0;
static const char *optString = "hvp:b?";
static const struct option longOpts[] =
{
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
{ "position", required_argument, NULL, 'p' },
{ "toolbar", no_argument, NULL, 'b' }
};
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
while( opt != -1 )
{
switch( opt )
{
case 'h':
printHelp();
break;
case 'v':
printVersion();
break;
case 'p':
position = atoi(optarg);
break;
case 'b':
visible = true;
break;
case '?':
exit(1);
}
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
}
QtSingleApplication app(argc, argv);
app.setQuitOnLastWindowClosed(false);
app.setOrganizationName("openslx");
app.setOrganizationDomain("openslx.org");
app.setApplicationName("pvsgui");
// only one instance should be allowed
if (app.sendMessage(""))
{
qDebug("[PVSGUI] ERROR: Already running. Exiting");
return 0;
}
// use system locale as language to translate gui
QTranslator translator;
translator.load(":pvsgui");
app.installTranslator(&translator);
PVSGUI pvsgui;
pvsgui.setPosition(position);
pvsgui.setVisible(visible);
pvsgui.hide();
return app.exec();
}