#6338 Use either manual or auto config in main window

Only enable/disable auto-config when saving settings, and only start the server/client when clicking Start/Stop/Apply.
This commit is contained in:
Nick Bolton 2018-08-08 14:41:37 +01:00
parent 3bae5f3cc6
commit 87d8fc1e14
10 changed files with 139 additions and 74 deletions

View File

@ -34,12 +34,16 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
BonjourWindows::BonjourWindows(SettingsDialog* settingsDialog, MainWindow* mainWindow) : BonjourWindows::BonjourWindows(
SettingsDialog* settingsDialog,
MainWindow* mainWindow,
AppConfig& appConfig) :
m_pSettingsDialog(settingsDialog), m_pSettingsDialog(settingsDialog),
m_pMainWindow(mainWindow), m_pMainWindow(mainWindow),
m_pBonjourInstall(nullptr), m_pBonjourInstall(nullptr),
m_pDownloadMessageBox(nullptr), m_pDownloadMessageBox(nullptr),
m_pDataDownloader(nullptr) m_pDataDownloader(nullptr),
m_appConfig(appConfig)
{ {
} }
@ -58,17 +62,17 @@ BonjourWindows::~BonjourWindows()
} }
} }
void BonjourWindows::download() void BonjourWindows::downloadAndInstall()
{ {
QUrl url; QUrl url;
int arch = getProcessorArch(); int arch = getProcessorArch();
if (arch == kProcessorArchWin32) { if (arch == kProcessorArchWin32) {
url.setUrl(bonjourBaseUrl + bonjourFilename32); url.setUrl(bonjourBaseUrl + bonjourFilename32);
m_pMainWindow->appendLogInfo("downloading 32-bit bonjour"); m_pMainWindow->appendLogInfo("downloading bonjour (32-bit)");
} }
else if (arch == kProcessorArchWin64) { else if (arch == kProcessorArchWin64) {
url.setUrl(bonjourBaseUrl + bonjourFilename64); url.setUrl(bonjourBaseUrl + bonjourFilename64);
m_pMainWindow->appendLogInfo("downloading 64-bit bonjour"); m_pMainWindow->appendLogInfo("downloading bonjour (64-bit)");
} }
else { else {
QMessageBox::critical( QMessageBox::critical(
@ -79,7 +83,7 @@ void BonjourWindows::download()
if (m_pDataDownloader == nullptr) { if (m_pDataDownloader == nullptr) {
m_pDataDownloader = new DataDownloader(this); m_pDataDownloader = new DataDownloader(this);
connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour())); connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(downloadFinished()));
} }
m_pDataDownloader->download(url); m_pDataDownloader->download(url);
@ -90,7 +94,7 @@ void BonjourWindows::download()
} }
m_pDownloadMessageBox = new QMessageBox(m_pSettingsDialog); m_pDownloadMessageBox = new QMessageBox(m_pSettingsDialog);
m_pDownloadMessageBox->setWindowTitle("Synergy"); m_pDownloadMessageBox->setWindowTitle("Synergy Auto Config");
m_pDownloadMessageBox->setIcon(QMessageBox::Information); m_pDownloadMessageBox->setIcon(QMessageBox::Information);
m_pDownloadMessageBox->setText("Installing Bonjour, please wait..."); m_pDownloadMessageBox->setText("Installing Bonjour, please wait...");
QAbstractButton* cancel = m_pDownloadMessageBox->addButton( QAbstractButton* cancel = m_pDownloadMessageBox->addButton(
@ -103,8 +107,16 @@ void BonjourWindows::download()
} }
} }
void BonjourWindows::downloadFinished()
{
m_pMainWindow->appendLogInfo("bonjour downloaded");
install();
}
void BonjourWindows::install() void BonjourWindows::install()
{ {
m_pMainWindow->appendLogInfo("installing bonjour");
QString tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation); QString tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
QString filename = tempLocation; QString filename = tempLocation;
@ -137,8 +149,7 @@ void BonjourWindows::install()
m_pBonjourInstall = new CommandProcess("msiexec", arguments); m_pBonjourInstall = new CommandProcess("msiexec", arguments);
QThread* thread = new QThread; QThread* thread = new QThread;
connect(m_pBonjourInstall, SIGNAL(finished()), this, connect(m_pBonjourInstall, SIGNAL(finished()), this, SLOT(installFinished()));
SLOT(bonjourInstallFinished()));
connect(m_pBonjourInstall, SIGNAL(finished()), thread, SLOT(quit())); connect(m_pBonjourInstall, SIGNAL(finished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
@ -184,7 +195,8 @@ bool BonjourWindows::isRunning() const
void BonjourWindows::installFinished() void BonjourWindows::installFinished()
{ {
m_pMainWindow->appendLogInfo("bonjour install finished"); m_pMainWindow->appendLogInfo("bonjour installed");
m_appConfig.setAutoConfig(true);
m_pSettingsDialog->allowAutoConfig(); m_pSettingsDialog->allowAutoConfig();
} }

View File

@ -32,29 +32,34 @@ class SettingsDialog;
class MainWindow; class MainWindow;
class CommandProcess; class CommandProcess;
class DataDownloader; class DataDownloader;
class AppConfig;
class BonjourWindows : public QObject class BonjourWindows : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
BonjourWindows(SettingsDialog* settingsDialog, MainWindow* mainWindow); BonjourWindows(SettingsDialog* settingsDialog, MainWindow* mainWindow, AppConfig& appConfig);
virtual ~BonjourWindows(); virtual ~BonjourWindows();
public: public:
void download(); void downloadAndInstall();
void install();
bool isRunning() const; bool isRunning() const;
protected slots: protected slots:
void downloadFinished();
void installFinished(); void installFinished();
private:
void install();
private: private:
SettingsDialog* m_pSettingsDialog; SettingsDialog* m_pSettingsDialog;
MainWindow* m_pMainWindow; MainWindow* m_pMainWindow;
CommandProcess* m_pBonjourInstall; CommandProcess* m_pBonjourInstall;
QMessageBox* m_pDownloadMessageBox; QMessageBox* m_pDownloadMessageBox;
DataDownloader* m_pDataDownloader; DataDownloader* m_pDataDownloader;
AppConfig& m_appConfig;
}; };
#endif // Q_OS_WIN #endif // Q_OS_WIN

View File

@ -34,6 +34,7 @@ void DataDownloader::complete(QNetworkReply* reply)
{ {
m_Data = reply->readAll(); m_Data = reply->readAll();
reply->deleteLater(); reply->deleteLater();
m_pReply = nullptr;
if (!m_Data.isEmpty()) { if (!m_Data.isEmpty()) {
m_IsFinished = true; m_IsFinished = true;
@ -48,7 +49,9 @@ QByteArray DataDownloader::data() const
void DataDownloader::cancel() void DataDownloader::cancel()
{ {
m_pReply->abort(); if (m_pReply != nullptr) {
m_pReply->abort();
}
} }
void DataDownloader::download(QUrl url) void DataDownloader::download(QUrl url)

View File

@ -97,12 +97,14 @@ MainWindow::MainWindow (QSettings& settings, AppConfig& appConfig,
m_pMenuWindow(NULL), m_pMenuWindow(NULL),
m_pMenuHelp(NULL), m_pMenuHelp(NULL),
m_pCancelButton(NULL), m_pCancelButton(NULL),
m_SuppressAutoConfigWarning(false),
m_SuppressEmptyServerWarning(false),
m_ExpectedRunningState(kStopped), m_ExpectedRunningState(kStopped),
m_pSslCertificate(NULL), m_pSslCertificate(NULL),
m_SecureSocket(false) m_SecureSocket(false)
{ {
#ifndef SYNERGY_ENTERPRISE
m_pZeroconf = new Zeroconf(this);
#endif
setupUi(this); setupUi(this);
createMenuBar(); createMenuBar();
@ -131,11 +133,6 @@ MainWindow::MainWindow (QSettings& settings, AppConfig& appConfig,
setMinimumSize(size()); setMinimumSize(size());
#endif #endif
m_SuppressAutoConfigWarning = true;
m_pLabelAutoDetected->setVisible(appConfig.autoConfig());
m_pComboServerList->setVisible(appConfig.autoConfig());
m_SuppressAutoConfigWarning = false;
m_trialWidget->hide(); m_trialWidget->hide();
// hide padlock icon // hide padlock icon
@ -179,10 +176,8 @@ MainWindow::MainWindow (QSettings& settings, AppConfig& appConfig,
#endif #endif
#ifndef SYNERGY_ENTERPRISE #ifndef SYNERGY_ENTERPRISE
m_pZeroconf = new Zeroconf(this); updateZeroconfService();
if (m_AppConfig->autoConfig()) { updateAutoConfigWidgets();
m_pZeroconf->startService();
}
#endif #endif
} }
@ -216,9 +211,7 @@ void MainWindow::open()
// auto hiding before the user has configured synergy (which of course // auto hiding before the user has configured synergy (which of course
// confuses first time users, who think synergy has crashed). // confuses first time users, who think synergy has crashed).
if (appConfig().startedBefore() && appConfig().processMode() == Desktop) { if (appConfig().startedBefore() && appConfig().processMode() == Desktop) {
m_SuppressEmptyServerWarning = true;
startSynergy(); startSynergy();
m_SuppressEmptyServerWarning = false;
} }
} }
@ -761,15 +754,13 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
if (m_pLineEditHostname->text().isEmpty()) { if (m_pLineEditHostname->text().isEmpty()) {
show(); show();
if (!m_SuppressEmptyServerWarning) { QMessageBox::warning(
QMessageBox::warning(this, tr("Hostname is empty"), this, tr("Hostname is empty"),
tr("Please fill in a hostname for the synergy client to connect to.")); tr("Please fill in a hostname for the synergy client to connect to."));
}
return false; return false;
} }
args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port()); args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port());
return true; return true;
} }
@ -1080,8 +1071,12 @@ void MainWindow::changeEvent(QEvent* event)
void MainWindow::zeroconfServerDetected(const QString name) void MainWindow::zeroconfServerDetected(const QString name)
{ {
// don't add yourself to the server list.
if (getIPAddresses().contains(name)) {
return;
}
if (m_pComboServerList->findText(name) == -1) { if (m_pComboServerList->findText(name) == -1) {
// Note: the first added item triggers startSynergy
m_pComboServerList->addItem(name); m_pComboServerList->addItem(name);
} }
@ -1180,6 +1175,10 @@ MainWindow::licenseManager() const
void MainWindow::on_m_pGroupClient_toggled(bool on) void MainWindow::on_m_pGroupClient_toggled(bool on)
{ {
m_pGroupServer->setChecked(!on); m_pGroupServer->setChecked(!on);
// only call in either client or server toggle, but not both
// since the toggle functions call eachother indirectly.
updateZeroconfService();
} }
void MainWindow::on_m_pGroupServer_toggled(bool on) void MainWindow::on_m_pGroupServer_toggled(bool on)
@ -1219,9 +1218,43 @@ void MainWindow::on_m_pActionAbout_triggered()
dlg.exec(); dlg.exec();
} }
void MainWindow::updateZeroconfService()
{
#ifndef SYNERGY_ENTERPRISE
if (m_pZeroconf != nullptr) {
if (appConfig().autoConfig()) {
m_pZeroconf->startService();
}
else {
m_pZeroconf->stopService();
}
}
#endif
}
void MainWindow::updateAutoConfigWidgets()
{
if (appConfig().autoConfig()) {
m_pLabelAutoDetected->show();
m_pComboServerList->show();
m_pLabelServerName->hide();
m_pLineEditHostname->hide();
}
else {
m_pLabelServerName->show();
m_pLineEditHostname->show();
m_pLabelAutoDetected->hide();
m_pComboServerList->hide();
}
}
void MainWindow::on_m_pActionSettings_triggered() void MainWindow::on_m_pActionSettings_triggered()
{ {
ProcessMode lastProcessMode = appConfig().processMode(); ProcessMode lastProcessMode = appConfig().processMode();
bool lastAutoConfig = appConfig().autoConfig();
SettingsDialog dlg(this, appConfig()); SettingsDialog dlg(this, appConfig());
dlg.exec(); dlg.exec();
@ -1230,6 +1263,11 @@ void MainWindow::on_m_pActionSettings_triggered()
{ {
onModeChanged(true, true); onModeChanged(true, true);
} }
if (lastAutoConfig != appConfig().autoConfig()) {
updateAutoConfigWidgets();
updateZeroconfService();
}
} }
void MainWindow::autoAddScreen(const QString name) void MainWindow::autoAddScreen(const QString name)
@ -1292,13 +1330,6 @@ void MainWindow::on_m_pButtonApply_clicked()
restartSynergy(); restartSynergy();
} }
void MainWindow::on_m_pComboServerList_currentIndexChanged(const QString &arg1)
{
if (m_pComboServerList->count() != 0) {
restartSynergy();
}
}
#ifndef SYNERGY_ENTERPRISE #ifndef SYNERGY_ENTERPRISE
int MainWindow::raiseActivationDialog() int MainWindow::raiseActivationDialog()
{ {

View File

@ -125,6 +125,8 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase
int raiseActivationDialog(); int raiseActivationDialog();
#endif #endif
void updateZeroconfService();
public slots: public slots:
void setEdition(Edition edition); void setEdition(Edition edition);
#ifndef SYNERGY_ENTERPRISE #ifndef SYNERGY_ENTERPRISE
@ -224,19 +226,17 @@ public slots:
QMenu* m_pMenuWindow; QMenu* m_pMenuWindow;
QMenu* m_pMenuHelp; QMenu* m_pMenuHelp;
QAbstractButton* m_pCancelButton; QAbstractButton* m_pCancelButton;
bool m_SuppressAutoConfigWarning;
bool m_SuppressEmptyServerWarning;
qRuningState m_ExpectedRunningState; qRuningState m_ExpectedRunningState;
QMutex m_StopDesktopMutex; QMutex m_StopDesktopMutex;
SslCertificate* m_pSslCertificate; SslCertificate* m_pSslCertificate;
bool m_SecureSocket; bool m_SecureSocket;
void updateAutoConfigWidgets();
private slots: private slots:
void on_m_pButtonApply_clicked(); void on_m_pButtonApply_clicked();
void on_windowShown(); void on_windowShown();
void on_m_pComboServerList_currentIndexChanged(const QString &arg1);
signals: signals:
void windowShown(); void windowShown();
}; };

View File

@ -289,9 +289,9 @@
<enum>QFormLayout::AllNonFixedFieldsGrow</enum> <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property> </property>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="m_pLabelComputerName">
<property name="text"> <property name="text">
<string>Screen name:</string> <string>Client name:</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -305,7 +305,7 @@
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="m_pLabelServerName"> <widget class="QLabel" name="m_pLabelServerName">
<property name="text"> <property name="text">
<string>&amp;Server IP:</string> <string>&amp;Server:</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>m_pLineEditHostname</cstring> <cstring>m_pLineEditHostname</cstring>
@ -313,7 +313,11 @@
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QLineEdit" name="m_pLineEditHostname"/> <widget class="QLineEdit" name="m_pLineEditHostname">
<property name="toolTip">
<string>Hostname or IP address of the server computer.</string>
</property>
</widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QComboBox" name="m_pComboServerList"> <widget class="QComboBox" name="m_pComboServerList">
@ -334,7 +338,7 @@
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="m_pLabelAutoDetected"> <widget class="QLabel" name="m_pLabelAutoDetected">
<property name="text"> <property name="text">
<string>Auto detected:</string> <string>Server:</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -59,10 +59,9 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide()); m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide());
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
m_pBonjourWindows = new BonjourWindows(this, m_pMainWindow); m_pBonjourWindows = new BonjourWindows(this, m_pMainWindow, m_appConfig);
if (m_pBonjourWindows->isRunning()) { if (m_pBonjourWindows->isRunning()) {
allowAutoConfig(); allowAutoConfig();
m_pCheckBoxAutoConfig->setChecked(m_appConfig.autoConfig());
} }
m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode())); m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode()));
@ -93,6 +92,8 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
m_pCheckBoxEnableCrypto->setEnabled(isPro); m_pCheckBoxEnableCrypto->setEnabled(isPro);
m_pLabelProUpgrade->setVisible(!isPro); m_pLabelProUpgrade->setVisible(!isPro);
m_pCheckBoxAutoConfig->setChecked(appConfig().autoConfig());
#endif #endif
} }
@ -107,6 +108,7 @@ void SettingsDialog::accept()
appConfig().setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()); appConfig().setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString());
appConfig().setElevateMode(static_cast<ElevateMode>(m_pComboElevate->currentIndex())); appConfig().setElevateMode(static_cast<ElevateMode>(m_pComboElevate->currentIndex()));
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked()); appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
appConfig().setAutoConfig(m_pCheckBoxAutoConfig->isChecked());
appConfig().saveSettings(); appConfig().saveSettings();
QDialog::accept(); QDialog::accept();
} }
@ -148,6 +150,7 @@ void SettingsDialog::allowAutoConfig()
{ {
m_pLabelInstallBonjour->hide(); m_pLabelInstallBonjour->hide();
m_pCheckBoxAutoConfig->setEnabled(true); m_pCheckBoxAutoConfig->setEnabled(true);
m_pCheckBoxAutoConfig->setChecked(m_appConfig.autoConfig());
} }
void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i)
@ -191,18 +194,6 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_toggled(bool checked)
void SettingsDialog::on_m_pLabelInstallBonjour_linkActivated(const QString&) void SettingsDialog::on_m_pLabelInstallBonjour_linkActivated(const QString&)
{ {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
m_pBonjourWindows->download(); m_pBonjourWindows->downloadAndInstall();
#endif
}
void SettingsDialog::on_m_pCheckBoxAutoConfig_toggled(bool checked)
{
#ifndef SYNERGY_ENTERPRISE
if (checked) {
m_pMainWindow->zeroconf().startService();
}
else {
m_pMainWindow->zeroconf().stopService();
}
#endif #endif
} }

View File

@ -58,7 +58,6 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase
void on_m_pCheckBoxLogToFile_stateChanged(int ); void on_m_pCheckBoxLogToFile_stateChanged(int );
void on_m_pButtonBrowseLog_clicked(); void on_m_pButtonBrowseLog_clicked();
void on_m_pLabelInstallBonjour_linkActivated(const QString &link); void on_m_pLabelInstallBonjour_linkActivated(const QString &link);
void on_m_pCheckBoxAutoConfig_toggled(bool checked);
}; };
#endif #endif

View File

@ -2,6 +2,14 @@
<ui version="4.0"> <ui version="4.0">
<class>SettingsDialogBase</class> <class>SettingsDialogBase</class>
<widget class="QDialog" name="SettingsDialogBase"> <widget class="QDialog" name="SettingsDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>357</width>
<height>496</height>
</rect>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Settings</string> <string>Settings</string>
</property> </property>
@ -154,12 +162,12 @@
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeType"> <property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum> <enum>QSizePolicy::Minimum</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>15</height> <height>10</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -230,9 +238,6 @@
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>
</property> </property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
@ -243,6 +248,9 @@
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>
</property> </property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -256,12 +264,12 @@
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeType"> <property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum> <enum>QSizePolicy::Minimum</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>15</height> <height>10</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -362,7 +370,7 @@
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>15</height> <height>10</height>
</size> </size>
</property> </property>
</spacer> </spacer>

View File

@ -33,14 +33,26 @@ Zeroconf::~Zeroconf()
void Zeroconf::startService() void Zeroconf::startService()
{ {
stopService(); if (m_pZeroconfService != nullptr) {
m_pMainWindow->appendLogInfo("restarting zeroconf service");
delete m_pZeroconfService;
m_pZeroconfService = nullptr;
}
else {
m_pMainWindow->appendLogInfo("starting zeroconf service");
}
m_pZeroconfService = new ZeroconfService(m_pMainWindow); m_pZeroconfService = new ZeroconfService(m_pMainWindow);
m_pMainWindow->appendLogInfo("started zeroconf service");
} }
void Zeroconf::stopService() void Zeroconf::stopService()
{ {
if (m_pZeroconfService != nullptr) { if (m_pZeroconfService != nullptr) {
m_pMainWindow->appendLogInfo("stopping zeroconf service");
delete m_pZeroconfService; delete m_pZeroconfService;
m_pZeroconfService = nullptr; m_pZeroconfService = nullptr;
m_pMainWindow->appendLogInfo("stopped zeroconf service");
} }
} }