/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "MainWindow.h"
#include "AboutDialog.h"
#include "ServerConfigDialog.h"
#include "SettingsDialog.h"
#include "LogDialog.h"
#include "WindowsServices.h"
#include
#include
#include
#if defined(Q_OS_WIN)
static const char synergyConfigName[] = "synergy.sgc";
static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.sgc);;All files (*.*)"));
#else
static const char synergyConfigName[] = "synergy.conf";
static const QString synergyConfigFilter(QObject::tr("Synergy Configurations (*.conf);;All files (*.*)"));
#endif
static const char* synergyIconFiles[] =
{
":/res/icons/16x16/synergy-disconnected.png",
":/res/icons/16x16/synergy-connected.png"
};
MainWindow::MainWindow(QWidget* parent) :
QMainWindow(parent),
MainWindowBase(),
m_Settings(),
m_AppConfig(&m_Settings),
m_pSynergy(NULL),
m_SynergyState(synergyDisconnected),
m_ServerConfig(&m_Settings, 5, 3),
m_pTempConfigFile(NULL),
m_pLogDialog(new LogDialog(this, synergyProcess())),
m_pTrayIcon(NULL),
m_pTrayIconMenu(NULL)
{
setupUi(this);
createTrayIcon();
createMenuBar();
loadSettings();
initConnections();
// HACK - surely window should be visible by default?
setVisible(true);
if (appConfig().autoConnect())
startSynergy();
}
MainWindow::~MainWindow()
{
stopSynergy();
saveSettings();
}
void MainWindow::setStatus(const QString &status)
{
m_pStatusLabel->setText(status);
}
void MainWindow::createTrayIcon()
{
#if !defined(Q_OS_MAC)
m_pTrayIconMenu = new QMenu(this);
m_pTrayIconMenu->addAction(m_pActionStartSynergy);
m_pTrayIconMenu->addAction(m_pActionStopSynergy);
m_pTrayIconMenu->addSeparator();
m_pTrayIconMenu->addAction(m_pActionMinimize);
m_pTrayIconMenu->addAction(m_pActionRestore);
m_pTrayIconMenu->addSeparator();
m_pTrayIconMenu->addAction(m_pActionQuit);
m_pTrayIcon = new QSystemTrayIcon(this);
m_pTrayIcon->setContextMenu(m_pTrayIconMenu);
setIcon(synergyDisconnected);
m_pTrayIcon->show();
#else
setIcon(synergyDisconnected);
#endif
}
void MainWindow::createMenuBar()
{
QMenuBar* menubar = new QMenuBar(this);
QMenu* pMenuFile = new QMenu(tr("&File"), menubar);
QMenu* pMenuEdit = new QMenu(tr("&Edit"), menubar);
QMenu* pMenuView = new QMenu(tr("&View"), menubar);
QMenu* pMenuWindow = new QMenu(tr("&Window"), menubar);
QMenu* pMenuHelp = new QMenu(tr("&Help"), menubar);
menubar->addAction(pMenuFile->menuAction());
menubar->addAction(pMenuEdit->menuAction());
menubar->addAction(pMenuView->menuAction());
#if !defined(Q_OS_MAC)
menubar->addAction(pMenuWindow->menuAction());
#endif
menubar->addAction(pMenuHelp->menuAction());
pMenuFile->addAction(m_pActionStartSynergy);
pMenuFile->addAction(m_pActionStopSynergy);
pMenuFile->addSeparator();
pMenuFile->addAction(m_pActionSave);
pMenuFile->addSeparator();
pMenuFile->addAction(m_pActionQuit);
pMenuEdit->addAction(m_pActionSettings);
#if defined(Q_OS_WIN)
pMenuEdit->addAction(m_pActionServices);
#endif
pMenuView->addAction(m_pActionLogOutput);
pMenuWindow->addAction(m_pActionMinimize);
pMenuWindow->addAction(m_pActionRestore);
pMenuHelp->addAction(m_pActionAbout);
setMenuBar(menubar);
}
void MainWindow::loadSettings()
{
// the next two must come BEFORE loading groupServerChecked and groupClientChecked or
// disabling and/or enabling the right widgets won't automatically work
m_pRadioExternalConfig->setChecked(settings().value("externalConfig", false).toBool());
m_pRadioInternalConfig->setChecked(settings().value("internalConfig", true).toBool());
m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool());
m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + synergyConfigName).toString());
m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool());
m_pLineEditHostname->setText(settings().value("serverHostname").toString());
}
void MainWindow::initConnections()
{
connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide()));
connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal()));
connect(m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(startSynergy()));
connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy()));
connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
if (m_pTrayIcon)
connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
}
void MainWindow::saveSettings()
{
// program settings
settings().setValue("groupServerChecked", m_pGroupServer->isChecked());
settings().setValue("externalConfig", m_pRadioExternalConfig->isChecked());
settings().setValue("configFile", m_pLineEditConfigFile->text());
settings().setValue("internalConfig", m_pRadioInternalConfig->isChecked());
settings().setValue("groupClientChecked", m_pGroupClient->isChecked());
settings().setValue("serverHostname", m_pLineEditHostname->text());
settings().sync();
}
void MainWindow::setIcon(qSynergyState state)
{
QIcon icon;
icon.addFile(synergyIconFiles[state]);
if (m_pTrayIcon)
m_pTrayIcon->setIcon(icon);
setWindowIcon(icon);
}
void MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
if (reason == QSystemTrayIcon::DoubleClick)
setVisible(!isVisible());
}
void MainWindow::startSynergy()
{
stopSynergy();
QString app;
QStringList args;
args << "-f" << "--debug" << appConfig().logLevelText();
if (!appConfig().screenName().isEmpty())
args << "--name" << appConfig().screenName();
setSynergyProcess(new QProcess(this));
if ((synergyType() == synergyClient && !clientArgs(args, app))
|| (synergyType() == synergyServer && !serverArgs(args, app)))
{
stopSynergy();
return;
}
connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), m_pLogDialog, SLOT(readSynergyOutput()));
connect(synergyProcess(), SIGNAL(readyReadStandardError()), m_pLogDialog, SLOT(readSynergyOutput()));
m_pLogDialog->append(tr("\n\nRunning synergy: %1 %2\n\n").arg(app).arg(args.join(" ")));
synergyProcess()->start(app, args);
if (!synergyProcess()->waitForStarted())
{
stopSynergy();
QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable
%1
could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app)));
return;
}
setSynergyState(synergyConnected);
}
bool MainWindow::clientArgs(QStringList& args, QString& app)
{
app = appPath(appConfig().synergycName(), appConfig().synergyc());
if (!QFile::exists(app))
{
if (QMessageBox::warning(this, tr("Synergy client not found"), tr("The executable for the synergy client does not exist. Do you want to browse for the synergy client now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return false;
app = SettingsDialog::browseForSynergyc(this, appConfig().synergyProgramDir(), appConfig().synergycName());
if (app.isEmpty())
return false;
appConfig().setSynergyc(app);
}
if (m_pLineEditHostname->text().isEmpty())
{
QMessageBox::warning(this, tr("Hostname is empty"), tr("Please fill in a hostname for the synergy client to connect to."));
return false;
}
if (appConfig().logToFile())
{
appConfig().persistLogDir();
args << "--log" << appConfig().logFilename();
}
args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port());
return true;
}
QString MainWindow::configFilename()
{
QString filename;
if (m_pRadioInternalConfig->isChecked())
{
// TODO: no need to use a temporary file, since we need it to
// be permenant (since it'll be used for Windows services, etc).
m_pTempConfigFile = new QTemporaryFile();
if (!m_pTempConfigFile->open())
{
QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start synergy can not be written."));
return false;
}
serverConfig().save(*m_pTempConfigFile);
filename = m_pTempConfigFile->fileName();
m_pTempConfigFile->close();
}
else
{
if (!QFile::exists(m_pLineEditConfigFile->text()))
{
if (QMessageBox::warning(this, tr("Configuration filename invalid"),
tr("You have not filled in a valid configuration file for the synergy server. "
"Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes
|| !on_m_pButtonBrowseConfigFile_clicked())
return false;
}
filename = m_pLineEditConfigFile->text();
}
return filename;
}
QString MainWindow::address()
{
return (!appConfig().interface().isEmpty() ? appConfig().interface() : "") + ":" + QString::number(appConfig().port());
}
QString MainWindow::appPath(const QString& name, const QString& defaultPath)
{
QString app;
if (appConfig().autoDetectPaths())
{
// actually returns bool, but ignore for now
appConfig().detectPath(name, app);
}
else
{
app = defaultPath;
}
return app;
}
bool MainWindow::serverArgs(QStringList& args, QString& app)
{
app = appPath(appConfig().synergysName(), appConfig().synergys());
if (!QFile::exists(app))
{
if (QMessageBox::warning(this, tr("Synergy server not found"), tr("The executable for the synergy server does not exist. Do you want to browse for the synergy server now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
return false;
app = SettingsDialog::browseForSynergys(this, appConfig().synergyProgramDir(), appConfig().synergysName());
if (app.isEmpty())
return false;
appConfig().setSynergys(app);
}
if (appConfig().logToFile())
{
appConfig().persistLogDir();
args << "--log" << appConfig().logFilename();
}
args << "-c" << configFilename() << "--address" << address();
return true;
}
void MainWindow::stopSynergy()
{
if (synergyProcess())
{
if (synergyProcess()->isOpen())
synergyProcess()->close();
delete synergyProcess();
setSynergyProcess(NULL);
setSynergyState(synergyDisconnected);
}
// HACK: deleting the object deletes the physical file, which is
// bad, since it could be in use by the Windows service!
//delete m_pTempConfigFile;
m_pTempConfigFile = NULL;
}
void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus)
{
// on Windows, we always seem to have an exit code != 0.
#if !defined(Q_OS_WIN)
if (exitCode != 0)
{
QMessageBox::critical(this, tr("Synergy terminated with an error"), QString(tr("Synergy terminated unexpectedly with an exit code of %1.
Please see the log output for details.")).arg(exitCode));
stopSynergy();
}
#else
Q_UNUSED(exitCode);
#endif
setSynergyState(synergyDisconnected);
// do not call stopSynergy() in case of clean synergy shutdown, because this must have (well, should have...)
// come from our own delete synergyProcess() in stopSynergy(), so we would do a double-delete...
}
void MainWindow::setSynergyState(qSynergyState state)
{
if (synergyState() == state)
return;
if (state == synergyConnected)
{
disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
m_pButtonToggleStart->setText(tr("&Stop"));
}
else
{
disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
m_pButtonToggleStart->setText(tr("&Start"));
}
m_pGroupClient->setEnabled(state == synergyDisconnected);
m_pGroupServer->setEnabled(state == synergyDisconnected);
m_pActionStartSynergy->setEnabled(state == synergyDisconnected);
m_pActionStopSynergy->setEnabled(state == synergyConnected);
setStatus(state == synergyConnected ? QString(tr("Synergy %1 is running.")).arg(synergyType() == synergyServer ? tr("server") : tr("client")) : tr("Synergy is not running."));
setIcon(state);
m_SynergyState = state;
}
void MainWindow::setVisible(bool visible)
{
m_pActionMinimize->setEnabled(visible);
m_pActionRestore->setEnabled(!visible);
QMainWindow::setVisible(visible);
}
bool MainWindow::on_m_pButtonBrowseConfigFile_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a synergys config file"), QString(), synergyConfigFilter);
if (!fileName.isEmpty())
{
m_pLineEditConfigFile->setText(fileName);
return true;
}
return false;
}
bool MainWindow::on_m_pActionSave_triggered()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."));
if (!fileName.isEmpty() && !serverConfig().save(fileName))
{
QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file."));
return true;
}
return false;
}
void MainWindow::on_m_pActionAbout_triggered()
{
AboutDialog dlg(this, appConfig().synergyc());
dlg.exec();
}
void MainWindow::on_m_pActionSettings_triggered()
{
SettingsDialog dlg(this, appConfig());
dlg.exec();
}
void MainWindow::on_m_pActionServices_triggered()
{
WindowsServices dlg(this, appConfig());
dlg.exec();
}
void MainWindow::on_m_pActionLogOutput_triggered()
{
Q_ASSERT(m_pLogDialog);
m_pLogDialog->show();
m_pLogDialog->raise();
m_pLogDialog->activateWindow();
}
void MainWindow::on_m_pButtonConfigureServer_clicked()
{
ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName());
dlg.exec();
}