diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 34894099..e573482f 100755 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -1,299 +1,309 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "connectdlg.h" - - -/* Implementation *************************************************************/ -CConnectDlg::CConnectDlg ( QWidget* parent, Qt::WindowFlags f ) - : QDialog ( parent, f ), - bServerListReceived ( false ) -{ - setupUi ( this ); - - - // Add help text to controls ----------------------------------------------- - // server address - QString strServAddrH = tr ( "Server Address: The IP address or URL " - "of the server running the llcon server software must be set here. " - "A list of the most recent used server URLs is available for " - "selection. If an invalid address was chosen, an error message is " - "shown in the status bar." ); - - TextLabelServerAddr->setWhatsThis ( strServAddrH ); - LineEditServerAddr->setWhatsThis ( strServAddrH ); - - LineEditServerAddr->setAccessibleName ( tr ( "Server address edit box" ) ); - LineEditServerAddr->setAccessibleDescription ( tr ( "Holds the current server " - "URL. It also stores old URLs in the combo box list." ) ); - - - // init server address combo box (max MAX_NUM_SERVER_ADDR_ITEMS entries) - LineEditServerAddr->setMaxCount ( MAX_NUM_SERVER_ADDR_ITEMS ); - LineEditServerAddr->setInsertPolicy ( QComboBox::InsertAtTop ); - - // load data from ini file - for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) - { -// if ( !pClient->vstrIPAddress[iLEIdx].isEmpty() ) - { - -// TODO -// LineEditServerAddr->addItem ( pClient->vstrIPAddress[iLEIdx] ); - - - } - } - - // set up list view for connected clients - ListViewServers->setColumnWidth ( 0, 170 ); - ListViewServers->setColumnWidth ( 1, 130 ); - ListViewServers->setColumnWidth ( 2, 55 ); - ListViewServers->setColumnWidth ( 3, 65 ); - ListViewServers->setColumnWidth ( 4, 140 ); - ListViewServers->clear(); - - - // Connections ------------------------------------------------------------- - // line edits - QObject::connect ( LineEditServerAddr, SIGNAL ( editTextChanged ( const QString ) ), - this, SLOT ( OnLineEditServerAddrTextChanged ( const QString ) ) ); - - QObject::connect ( LineEditServerAddr, SIGNAL ( activated ( int ) ), - this, SLOT ( OnLineEditServerAddrActivated ( int ) ) ); - - // timers - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); - - QObject::connect ( &TimerReRequestServList, SIGNAL ( timeout() ), - this, SLOT ( OnTimerReRequestServList() ) ); -} - -void CConnectDlg::showEvent ( QShowEvent* ) -{ - // reset flag (on opening the connect dialg, we always want to request a new - // updated server list per definition) - bServerListReceived = false; - - -// TEST -QString strNAddr = "llcon.dyndns.org:22122"; - - - // get the IP address of the central server (using the ParseNetworAddress - // function) when the connect dialog is opened, this seems to be the correct - // time to do it - if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, - CentralServerAddress ) ) - { - // send the request for the server list - emit ReqServerListQuery ( CentralServerAddress ); - - // start timer, if this message did not get any respond to retransmit - // the server list request message - TimerReRequestServList.start ( SERV_LIST_REQ_UPDATE_TIME_MS ); - } -} - -void CConnectDlg::hideEvent ( QHideEvent* ) -{ - // if window is closed, stop timers - TimerPing.stop(); - TimerReRequestServList.stop(); -} - -void CConnectDlg::OnTimerReRequestServList() -{ - // if the server list is not yet received, retransmit the request for the - // server list - if ( !bServerListReceived ) - { - emit ReqServerListQuery ( CentralServerAddress ); - } -} - -void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, - const CVector& vecServerInfo ) -{ - // set flag - bServerListReceived = true; - - // first clear list - ListViewServers->clear(); - - // add list item for each server in the server list - const int iServerInfoLen = vecServerInfo.Size(); - - for ( int iIdx = 0; iIdx < iServerInfoLen; iIdx++ ) - { - QTreeWidgetItem* pNewListViewItem = - new QTreeWidgetItem ( ListViewServers ); - - // make the entry invisible (will be set to visible on successful ping - // result) - pNewListViewItem->setHidden ( true ); - - // server name - pNewListViewItem->setText ( 0, vecServerInfo[iIdx].strName ); - - // server country - pNewListViewItem->setText ( 1, - QLocale::countryToString ( vecServerInfo[iIdx].eCountry ) ); - - // number of clients - pNewListViewItem->setText ( 2, - QString().setNum ( vecServerInfo[iIdx].iNumClients ) ); - - // the ping time shall be shown in bold font - QFont CurPingTimeFont = pNewListViewItem->font( 3 ); - CurPingTimeFont.setBold ( true ); - pNewListViewItem->setFont ( 3, CurPingTimeFont ); - - // get the host address, note that for the very first entry which is - // the central server, we have to use the receive host address - // instead - CHostAddress CurHostAddress; - if ( iIdx > 0 ) - { - CurHostAddress = vecServerInfo[iIdx].HostAddr; - } - else - { - // substitude the receive host address for central server - CurHostAddress = InetAddr; - } - - // IP address and port (use IP number without last byte) - // Definition: If the port number is the default port number, we do not - // show it. - if ( vecServerInfo[iIdx].HostAddr.iPort == LLCON_DEFAULT_PORT_NUMBER ) - { - // only show IP number, no port number - pNewListViewItem->setText ( 4, CurHostAddress. - toString ( CHostAddress::SM_IP_NO_LAST_BYTE ) ); - } - else - { - // show IP number and port - pNewListViewItem->setText ( 4, CurHostAddress. - toString ( CHostAddress::SM_IP_NO_LAST_BYTE_PORT ) ); - } - - // store host address - pNewListViewItem->setData ( 0, Qt::UserRole, - CurHostAddress.toString() ); - } - - // immediately issue the ping measurements and start the ping timer since - // the server list is filled now - OnTimerPing(); - TimerPing.start ( PING_UPDATE_TIME_SERVER_LIST_MS ); -} - -void CConnectDlg::OnLineEditServerAddrTextChanged ( const QString ) -{ - // if the maximum number of items in the combo box is reached, - // delete the last item so that the new item can be added (first - // in - first out) - if ( LineEditServerAddr->count() == MAX_NUM_SERVER_ADDR_ITEMS ) - { - LineEditServerAddr->removeItem ( MAX_NUM_SERVER_ADDR_ITEMS - 1 ); - } -} - -void CConnectDlg::OnLineEditServerAddrActivated ( int index ) -{ - // move activated list item to the top - const QString strCurIPAddress = LineEditServerAddr->itemText ( index ); - LineEditServerAddr->removeItem ( index ); - LineEditServerAddr->insertItem ( 0, strCurIPAddress ); - LineEditServerAddr->setCurrentIndex ( 0 ); -} - -void CConnectDlg::OnTimerPing() -{ - // send ping messages to the servers in the list - const int iServerListLen = ListViewServers->topLevelItemCount(); - - for ( int iIdx = 0; iIdx < iServerListLen; iIdx++ ) - { - CHostAddress CurServerAddress; - - // try to parse host address string which is stored as user data - // in the server list item GUI control element - if ( LlconNetwUtil().ParseNetworkAddress ( - ListViewServers->topLevelItem ( iIdx )-> - data ( 0, Qt::UserRole ).toString(), - CurServerAddress ) ) - { - // if address is valid, send ping - emit CreateCLPingMes ( CurServerAddress ); - } - } -} - -void CConnectDlg::SetPingTimeResult ( CHostAddress& InetAddr, - const int iPingTime, - const int iPingTimeLEDColor ) -{ - // apply the received ping time to the correct server list entry - const int iServerListLen = ListViewServers->topLevelItemCount(); - - for ( int iIdx = 0; iIdx < iServerListLen; iIdx++ ) - { - // compare the received address with the user data string of the - // host address by a string compare - if ( !ListViewServers->topLevelItem ( iIdx )-> - data ( 0, Qt::UserRole ).toString(). - compare ( InetAddr.toString() ) ) - { - // update the color of the ping time font - switch ( iPingTimeLEDColor ) - { - case MUL_COL_LED_GREEN: - ListViewServers-> - topLevelItem ( iIdx )->setTextColor ( 3, Qt::darkGreen ); - break; - - case MUL_COL_LED_YELLOW: - ListViewServers-> - topLevelItem ( iIdx )->setTextColor ( 3, Qt::darkYellow ); - break; - - case MUL_COL_LED_RED: - ListViewServers-> - topLevelItem ( iIdx )->setTextColor ( 3, Qt::red ); - break; - } - - // update ping text - ListViewServers->topLevelItem ( iIdx )-> - setText ( 3, QString().setNum ( iPingTime ) + " ms" ); - - // a ping time was received, set item to visible - ListViewServers->topLevelItem ( iIdx )->setHidden ( false ); - } - } -} +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "connectdlg.h" + + +/* Implementation *************************************************************/ +CConnectDlg::CConnectDlg ( QWidget* parent, Qt::WindowFlags f ) + : QDialog ( parent, f ), + bServerListReceived ( false ) +{ + setupUi ( this ); + + + // Add help text to controls ----------------------------------------------- + // server address + QString strServAddrH = tr ( "Server Address: The IP address or URL " + "of the server running the llcon server software must be set here. " + "A list of the most recent used server URLs is available for " + "selection. If an invalid address was chosen, an error message is " + "shown in the status bar." ); + + TextLabelServerAddr->setWhatsThis ( strServAddrH ); + LineEditServerAddr->setWhatsThis ( strServAddrH ); + + LineEditServerAddr->setAccessibleName ( tr ( "Server address edit box" ) ); + LineEditServerAddr->setAccessibleDescription ( tr ( "Holds the current server " + "URL. It also stores old URLs in the combo box list." ) ); + + + // init server address combo box (max MAX_NUM_SERVER_ADDR_ITEMS entries) + LineEditServerAddr->setMaxCount ( MAX_NUM_SERVER_ADDR_ITEMS ); + LineEditServerAddr->setInsertPolicy ( QComboBox::InsertAtTop ); + + // set up list view for connected clients + ListViewServers->setColumnWidth ( 0, 170 ); + ListViewServers->setColumnWidth ( 1, 130 ); + ListViewServers->setColumnWidth ( 2, 55 ); + ListViewServers->setColumnWidth ( 3, 65 ); + ListViewServers->setColumnWidth ( 4, 140 ); + ListViewServers->clear(); + + + // Connections ------------------------------------------------------------- + // line edits + QObject::connect ( LineEditServerAddr, SIGNAL ( editTextChanged ( const QString ) ), + this, SLOT ( OnLineEditServerAddrTextChanged ( const QString ) ) ); + + QObject::connect ( LineEditServerAddr, SIGNAL ( activated ( int ) ), + this, SLOT ( OnLineEditServerAddrActivated ( int ) ) ); + + // timers + QObject::connect ( &TimerPing, SIGNAL ( timeout() ), + this, SLOT ( OnTimerPing() ) ); + + QObject::connect ( &TimerReRequestServList, SIGNAL ( timeout() ), + this, SLOT ( OnTimerReRequestServList() ) ); +} + +void CConnectDlg::LoadStoredServers ( const CVector& vstrIPAddress ) +{ + // load stored IP addresses in combo box + for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) + { + if ( !vstrIPAddress[iLEIdx].isEmpty() ) + { + LineEditServerAddr->addItem ( vstrIPAddress[iLEIdx] ); + } + } +} + +void CConnectDlg::showEvent ( QShowEvent* ) +{ + // reset flag (on opening the connect dialg, we always want to request a new + // updated server list per definition) + bServerListReceived = false; + + +// TEST +QString strNAddr = "llcon.dyndns.org:22122"; + + + // get the IP address of the central server (using the ParseNetworAddress + // function) when the connect dialog is opened, this seems to be the correct + // time to do it + if ( LlconNetwUtil().ParseNetworkAddress ( strNAddr, + CentralServerAddress ) ) + { + // send the request for the server list + emit ReqServerListQuery ( CentralServerAddress ); + + // start timer, if this message did not get any respond to retransmit + // the server list request message + TimerReRequestServList.start ( SERV_LIST_REQ_UPDATE_TIME_MS ); + } +} + +void CConnectDlg::hideEvent ( QHideEvent* ) +{ + // if window is closed, stop timers + TimerPing.stop(); + TimerReRequestServList.stop(); + +// TODO +/* +// store IP addresses +for ( int iLEIdx = 0; iLEIdx < LineEditServerAddr->count(); iLEIdx++ ) +{ + pClient->vstrIPAddress[iLEIdx] = + LineEditServerAddr->itemText ( iLEIdx ); +} +*/ + +} + +void CConnectDlg::OnTimerReRequestServList() +{ + // if the server list is not yet received, retransmit the request for the + // server list + if ( !bServerListReceived ) + { + emit ReqServerListQuery ( CentralServerAddress ); + } +} + +void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, + const CVector& vecServerInfo ) +{ + // set flag + bServerListReceived = true; + + // first clear list + ListViewServers->clear(); + + // add list item for each server in the server list + const int iServerInfoLen = vecServerInfo.Size(); + + for ( int iIdx = 0; iIdx < iServerInfoLen; iIdx++ ) + { + QTreeWidgetItem* pNewListViewItem = + new QTreeWidgetItem ( ListViewServers ); + + // make the entry invisible (will be set to visible on successful ping + // result) + pNewListViewItem->setHidden ( true ); + + // server name + pNewListViewItem->setText ( 0, vecServerInfo[iIdx].strName ); + + // server country + pNewListViewItem->setText ( 1, + QLocale::countryToString ( vecServerInfo[iIdx].eCountry ) ); + + // number of clients + pNewListViewItem->setText ( 2, + QString().setNum ( vecServerInfo[iIdx].iNumClients ) ); + + // the ping time shall be shown in bold font + QFont CurPingTimeFont = pNewListViewItem->font( 3 ); + CurPingTimeFont.setBold ( true ); + pNewListViewItem->setFont ( 3, CurPingTimeFont ); + + // get the host address, note that for the very first entry which is + // the central server, we have to use the receive host address + // instead + CHostAddress CurHostAddress; + if ( iIdx > 0 ) + { + CurHostAddress = vecServerInfo[iIdx].HostAddr; + } + else + { + // substitude the receive host address for central server + CurHostAddress = InetAddr; + } + + // IP address and port (use IP number without last byte) + // Definition: If the port number is the default port number, we do not + // show it. + if ( vecServerInfo[iIdx].HostAddr.iPort == LLCON_DEFAULT_PORT_NUMBER ) + { + // only show IP number, no port number + pNewListViewItem->setText ( 4, CurHostAddress. + toString ( CHostAddress::SM_IP_NO_LAST_BYTE ) ); + } + else + { + // show IP number and port + pNewListViewItem->setText ( 4, CurHostAddress. + toString ( CHostAddress::SM_IP_NO_LAST_BYTE_PORT ) ); + } + + // store host address + pNewListViewItem->setData ( 0, Qt::UserRole, + CurHostAddress.toString() ); + } + + // immediately issue the ping measurements and start the ping timer since + // the server list is filled now + OnTimerPing(); + TimerPing.start ( PING_UPDATE_TIME_SERVER_LIST_MS ); +} + +void CConnectDlg::OnLineEditServerAddrTextChanged ( const QString ) +{ + // if the maximum number of items in the combo box is reached, + // delete the last item so that the new item can be added (first + // in - first out) + if ( LineEditServerAddr->count() == MAX_NUM_SERVER_ADDR_ITEMS ) + { + LineEditServerAddr->removeItem ( MAX_NUM_SERVER_ADDR_ITEMS - 1 ); + } +} + +void CConnectDlg::OnLineEditServerAddrActivated ( int index ) +{ + // move activated list item to the top + const QString strCurIPAddress = LineEditServerAddr->itemText ( index ); + LineEditServerAddr->removeItem ( index ); + LineEditServerAddr->insertItem ( 0, strCurIPAddress ); + LineEditServerAddr->setCurrentIndex ( 0 ); +} + +void CConnectDlg::OnTimerPing() +{ + // send ping messages to the servers in the list + const int iServerListLen = ListViewServers->topLevelItemCount(); + + for ( int iIdx = 0; iIdx < iServerListLen; iIdx++ ) + { + CHostAddress CurServerAddress; + + // try to parse host address string which is stored as user data + // in the server list item GUI control element + if ( LlconNetwUtil().ParseNetworkAddress ( + ListViewServers->topLevelItem ( iIdx )-> + data ( 0, Qt::UserRole ).toString(), + CurServerAddress ) ) + { + // if address is valid, send ping + emit CreateCLPingMes ( CurServerAddress ); + } + } +} + +void CConnectDlg::SetPingTimeResult ( CHostAddress& InetAddr, + const int iPingTime, + const int iPingTimeLEDColor ) +{ + // apply the received ping time to the correct server list entry + const int iServerListLen = ListViewServers->topLevelItemCount(); + + for ( int iIdx = 0; iIdx < iServerListLen; iIdx++ ) + { + // compare the received address with the user data string of the + // host address by a string compare + if ( !ListViewServers->topLevelItem ( iIdx )-> + data ( 0, Qt::UserRole ).toString(). + compare ( InetAddr.toString() ) ) + { + // update the color of the ping time font + switch ( iPingTimeLEDColor ) + { + case MUL_COL_LED_GREEN: + ListViewServers-> + topLevelItem ( iIdx )->setTextColor ( 3, Qt::darkGreen ); + break; + + case MUL_COL_LED_YELLOW: + ListViewServers-> + topLevelItem ( iIdx )->setTextColor ( 3, Qt::darkYellow ); + break; + + case MUL_COL_LED_RED: + ListViewServers-> + topLevelItem ( iIdx )->setTextColor ( 3, Qt::red ); + break; + } + + // update ping text + ListViewServers->topLevelItem ( iIdx )-> + setText ( 3, QString().setNum ( iPingTime ) + " ms" ); + + // a ping time was received, set item to visible + ListViewServers->topLevelItem ( iIdx )->setHidden ( false ); + } + } +} diff --git a/src/connectdlg.h b/src/connectdlg.h index 58d8c5f8..960e825d 100755 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -1,84 +1,86 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "global.h" -#include "client.h" -#ifdef _WIN32 -# include "../windows/moc/connectdlgbase.h" -#else -# ifdef _IS_QMAKE_CONFIG -# include "ui_connectdlgbase.h" -# else -# include "moc/connectdlgbase.h" -# endif -#endif - - -/* Definitions ****************************************************************/ -// defines the time interval at which the request server list message is re- -// transmitted until it is received -#define SERV_LIST_REQ_UPDATE_TIME_MS 2000 // ms - - -/* Classes ********************************************************************/ -class CConnectDlg : public QDialog, private Ui_CConnectDlgBase -{ - Q_OBJECT - -public: - CConnectDlg ( QWidget* parent = 0, Qt::WindowFlags f = 0 ); - - void SetServerList ( const CHostAddress& InetAddr, - const CVector& vecServerInfo ); - - void SetPingTimeResult ( CHostAddress& InetAddr, - const int iPingTime, - const int iPingTimeLEDColor ); - -protected: - virtual void showEvent ( QShowEvent* ); - virtual void hideEvent ( QHideEvent* ); - - QTimer TimerPing; - QTimer TimerReRequestServList; - CHostAddress CentralServerAddress; - bool bServerListReceived; - -public slots: - void OnLineEditServerAddrTextChanged ( const QString ); - void OnLineEditServerAddrActivated ( int index ); - void OnTimerPing(); - void OnTimerReRequestServList(); - -signals: - void ReqServerListQuery ( CHostAddress InetAddr ); - void CreateCLPingMes ( CHostAddress InetAddr ); -}; +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include "global.h" +#include "client.h" +#ifdef _WIN32 +# include "../windows/moc/connectdlgbase.h" +#else +# ifdef _IS_QMAKE_CONFIG +# include "ui_connectdlgbase.h" +# else +# include "moc/connectdlgbase.h" +# endif +#endif + + +/* Definitions ****************************************************************/ +// defines the time interval at which the request server list message is re- +// transmitted until it is received +#define SERV_LIST_REQ_UPDATE_TIME_MS 2000 // ms + + +/* Classes ********************************************************************/ +class CConnectDlg : public QDialog, private Ui_CConnectDlgBase +{ + Q_OBJECT + +public: + CConnectDlg ( QWidget* parent = 0, Qt::WindowFlags f = 0 ); + + void SetServerList ( const CHostAddress& InetAddr, + const CVector& vecServerInfo ); + + void LoadStoredServers ( const CVector& vstrIPAddress ); + + void SetPingTimeResult ( CHostAddress& InetAddr, + const int iPingTime, + const int iPingTimeLEDColor ); + +protected: + virtual void showEvent ( QShowEvent* ); + virtual void hideEvent ( QHideEvent* ); + + QTimer TimerPing; + QTimer TimerReRequestServList; + CHostAddress CentralServerAddress; + bool bServerListReceived; + +public slots: + void OnLineEditServerAddrTextChanged ( const QString ); + void OnLineEditServerAddrActivated ( int index ); + void OnTimerPing(); + void OnTimerReRequestServList(); + +signals: + void ReqServerListQuery ( CHostAddress InetAddr ); + void CreateCLPingMes ( CHostAddress InetAddr ); +}; diff --git a/src/llconclientdlg.cpp b/src/llconclientdlg.cpp index 9f80f0b5..65b5a34a 100755 --- a/src/llconclientdlg.cpp +++ b/src/llconclientdlg.cpp @@ -1,985 +1,986 @@ -/******************************************************************************\ - * Copyright (c) 2004-2011 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "llconclientdlg.h" - - -/* Implementation *************************************************************/ -CLlconClientDlg::CLlconClientDlg ( CClient* pNCliP, - const bool bNewConnectOnStartup, - const bool bNewDisalbeLEDs, - QWidget* parent, - Qt::WindowFlags f ) : - QDialog ( parent, f ), - pClient ( pNCliP ), - bUnreadChatMessage ( false ), - ClientSettingsDlg ( pNCliP, parent -#ifdef _WIN32 - // this somehow only works reliable on Windows - , Qt::WindowMinMaxButtonsHint -#endif - ), - ChatDlg ( parent -#ifdef _WIN32 - // this somehow only works reliable on Windows - , Qt::WindowMinMaxButtonsHint -#endif - ), - ConnectDlg ( parent -#ifdef _WIN32 - // this somehow only works reliable on Windows - , Qt::WindowMinMaxButtonsHint -#endif - ) -{ - setupUi ( this ); - - // Add help text to controls ----------------------------------------------- - // input level meter - QString strInpLevH = tr ( "Input Level Meter: The input level " - "indicators show the current input level of the two stereo channels " - "of the current selected audio input. The upper level display belongs " - "to the left channel and the lower level display to the right channel " - "of the audio input.
" - "Make sure not to clip the input signal to avoid distortions of the " - "audio signal." ); - - QString strInpLevHTT = tr ( "If the llcon software is connected and " - "you play your instrument/sing in the microphone, the LED level " - "meter should flicker. If this is not the case, you have " - "probably selected the wrong input channel (e.g. line in instead " - "of the microphone input) or set the input gain too low in the " - "(Windows) audio mixer.
For a proper usage of the llcon software, " - "you should not hear your singing/instrument in the loudspeaker or " - "your headphone when the llcon software is not connected. This can " - "be achieved by muting your input audio channel in the Playback " - "mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; - - QString strInpLevHAccText = tr ( "Input level meter" ); - QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." ); - - TextInputLEDMeter->setWhatsThis ( strInpLevH ); - TextLevelMeterLeft->setWhatsThis ( strInpLevH ); - TextLevelMeterRight->setWhatsThis ( strInpLevH ); - MultiColorLEDBarInputLevelL->setWhatsThis ( strInpLevH ); - MultiColorLEDBarInputLevelL->setAccessibleName ( strInpLevHAccText ); - MultiColorLEDBarInputLevelL->setAccessibleDescription ( strInpLevHAccDescr ); - MultiColorLEDBarInputLevelL->setToolTip ( strInpLevHTT ); - MultiColorLEDBarInputLevelR->setWhatsThis ( strInpLevH ); - MultiColorLEDBarInputLevelR->setAccessibleName ( strInpLevHAccText ); - MultiColorLEDBarInputLevelR->setAccessibleDescription ( strInpLevHAccDescr ); - MultiColorLEDBarInputLevelR->setToolTip ( strInpLevHTT ); - - // connect/disconnect button - PushButtonConnect->setWhatsThis ( tr ( "Connect / Disconnect Button:" - " Push this button to connect the server. A valid IP address has " - "to be specified before. If the client is connected, pressing this " - "button will disconnect the connection." ) ); - - PushButtonConnect->setAccessibleName ( - tr ( "Connect and disconnect toggle button" ) ); - - PushButtonConnect->setAccessibleDescription ( tr ( "Clicking on this " - "button changes the caption of the button from Connect to " - "Disconnect, i.e., it implements a toggle functionality for connecting " - "and disconnecting the llcon software." ) ); - - // server address - QString strServAddrH = tr ( "Server Address: The IP address or URL " - "of the server running the llcon server software must be set here. " - "A list of the most recent used server URLs is available for " - "selection. If an invalid address was chosen, an error message is " - "shown in the status bar." ); - - TextLabelServerAddr->setWhatsThis ( strServAddrH ); - LineEditServerAddr->setWhatsThis ( strServAddrH ); - - LineEditServerAddr->setAccessibleName ( tr ( "Server address edit box" ) ); - LineEditServerAddr->setAccessibleDescription ( tr ( "Holds the current server " - "URL. It also stores old URLs in the combo box list." ) ); - - // fader tag - QString strFaderTag = tr ( "Fader Tag: The fader tag of the local " - "client is set in the fader tag edit box. This tag will appear " - "at your fader on the mixer board when you are connected to a llcon " - "server. This tag will also show up at each client which is connected " - "to the same server as the local client. If the fader tag is empty, " - "the IP address of the client is displayed instead." ); - - QString strFaderTagTT = tr ( "Set your name and/or instrument and/or " - "pseoudonym here so that the other musicians can identify you." ) + - TOOLTIP_COM_END_TEXT; - - TextLabelServerTag->setWhatsThis ( strFaderTag ); - TextLabelServerTag->setToolTip ( strFaderTagTT ); - LineEditFaderTag->setWhatsThis ( strFaderTag ); - LineEditFaderTag->setToolTip ( strFaderTagTT ); - - LineEditFaderTag->setAccessibleName ( tr ( "Fader tag edit box" ) ); - - // local audio input fader - QString strAudFader = tr ( "Local Audio Input Fader: With the " - "audio fader, the relative levels of the left and right local audio " - "channels can be changed. It acts like a panning between the two " - "channels. If, e.g., a microphone is connected to the right input " - "channel and an instrument is connected to the left input channel " - "which is much louder than the microphone, move the audio fader in " - "a direction where the label above the fader shows L -x, where " - "x is the current attenuation indication. " ); - - TextAudInFader->setWhatsThis ( strAudFader ); - SliderAudInFader->setWhatsThis ( strAudFader ); - - SliderAudInFader->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); - - // reverberation level - QString strAudReverb = tr ( "Reverberation Level: A reverberation " - "effect can be applied to one local audio channel. The channel " - "selection and the reverberation level can be modified. If, e.g., " - "the microphone signal is fed into the right audio channel of the " - "sound card and a reverberation effect shall be applied, set the " - "channel selector to right and move the fader upwards until the " - "desired reverberation level is reached.
" - "The reverberation effect requires significant CPU so that it should " - "only be used on fast PCs. If the reverberation level fader is set to " - "minimum (which is the default setting), the reverberation effect is " - "switched off and does not cause any additional CPU usage." ); - - TextLabelAudReverb->setWhatsThis ( strAudReverb ); - SliderAudReverb->setWhatsThis ( strAudReverb ); - - SliderAudReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) ); - - // reverberation channel selection - QString strRevChanSel = tr ( "Reverberation Channel Selection: " - "With these radio buttons the audio input channel on which the " - "reverberation effect is applied can be chosen. Either the left " - "or right input channel can be selected." ); - - RadioButtonRevSelL->setWhatsThis ( strRevChanSel ); - RadioButtonRevSelL->setAccessibleName ( tr ( "Left channel selection for reverberation" ) ); - RadioButtonRevSelR->setWhatsThis ( strRevChanSel ); - RadioButtonRevSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) ); - - // connection LED - QString strLEDConnection = tr ( "Connection Status LED: " - "The connection status LED indicator shows the current connection " - "status. If the light is green, a successful connection to the " - "server is established. If the light blinks red right after " - "pressing the connect button, the server address is invalid. " - "If the light turns red and stays red, the connection to the " - "server is lost." ); - - TextLabelConnection->setWhatsThis ( strLEDConnection ); - LEDConnection->setWhatsThis ( strLEDConnection ); - - LEDConnection->setAccessibleName ( tr ( "Connection status LED indicator" ) ); - - // delay LED - QString strLEDDelay = tr ( "Delay Status LED: " - "The delay status LED indicator shows the current audio delay " - "status. If the light is green, the delay is perfect for a jam " - "session. If the ligth is yellow, a session is still possible but " - "it may be harder to play. If the light is red, the delay is too " - "large for jamming." ); - - TextLabelDelay->setWhatsThis ( strLEDDelay ); - LEDDelay->setWhatsThis ( strLEDDelay ); - LEDDelay->setToolTip ( tr ( "If this LED indicator turns red, " - "you will not have much fun using the llcon software." ) + - TOOLTIP_COM_END_TEXT ); - - LEDDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) ); - - // buffers LED - QString strLEDBuffers = tr ( "Buffers Status LED: " - "The buffers status LED indicator shows the current audio/streaming " - "status. If the light is green, there are no buffer overruns/underruns " - "and the audio stream is not interrupted. If the light is red, the " - "audio stream is interrupted caused by one of the following problems:" - "
    " - "
  • The network jitter buffer is not large enough for the current " - "network/audio interface jitter.
  • " - "
  • The sound card buffer delay (buffer size) is set to a too small " - "value.
  • " - "
  • The upload or download stream rate is too high for the current " - "available internet bandwidth.
  • " - "
  • The CPU of the client or server is at 100%.
  • " - "
" ); - - TextLabelBuffers->setWhatsThis ( strLEDBuffers ); - LEDBuffers->setWhatsThis ( strLEDBuffers ); - - LEDBuffers->setAccessibleName ( tr ( "Buffers status LED indicator" ) ); - - // chat LED - QString strLEDChat = tr ( "Chat Status LED: " - "If the option Open Chat on New Message is not activated, this " - "status LED will turn green on a new received chat message." ); - - TextLabelChat->setWhatsThis ( strLEDChat ); - LEDChat->setWhatsThis ( strLEDChat ); - - LEDBuffers->setAccessibleName ( tr ( "Chat status LED indicator" ) ); - - - // init GUI design - SetGUIDesign ( pClient->GetGUIDesign() ); - - // reset mixer board - MainMixerBoard->HideAll(); - - // init fader tag line edit - LineEditFaderTag->setText ( pClient->strName ); - - // init server address combo box (max MAX_NUM_SERVER_ADDR_ITEMS entries) - LineEditServerAddr->setMaxCount ( MAX_NUM_SERVER_ADDR_ITEMS ); - LineEditServerAddr->setInsertPolicy ( QComboBox::InsertAtTop ); - - // load data from ini file - for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) - { - if ( !pClient->vstrIPAddress[iLEIdx].isEmpty() ) - { - LineEditServerAddr->addItem ( pClient->vstrIPAddress[iLEIdx] ); - } - } - - // we want the cursor to be at IP address line edit at startup - LineEditServerAddr->setFocus(); - - // init status label - OnTimerStatus(); - - // init connection button text - PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); - - // init input level meter bars - MultiColorLEDBarInputLevelL->setValue ( 0 ); - MultiColorLEDBarInputLevelR->setValue ( 0 ); - - // init status LEDs - LEDConnection->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); - LEDChat->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); - LEDDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS ); - LEDDelay->Reset(); - - - // init slider controls --- - // audio in fader - SliderAudInFader->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); - SliderAudInFader->setTickInterval ( AUD_FADER_IN_MAX / 5 ); - UpdateAudioFaderSlider(); - - // audio reverberation - SliderAudReverb->setRange ( 0, AUD_REVERB_MAX ); - const int iCurAudReverb = pClient->GetReverbLevel(); - SliderAudReverb->setValue ( iCurAudReverb ); - SliderAudReverb->setTickInterval ( AUD_REVERB_MAX / 5 ); - - // init reverb channel - UpdateRevSelection(); - - - // connect on startup --- - if ( bNewConnectOnStartup ) - { - // since the software starts up right now, the previous state was - // "not connected" so that a call to "OnConnectDisconBut()" will - // start the connection - OnConnectDisconBut(); - } - - - // disable controls on request --- - // disable LEDs in main window if requested - if ( bNewDisalbeLEDs ) - { - MultiColorLEDBarInputLevelL->setEnabled ( false ); - MultiColorLEDBarInputLevelR->setEnabled ( false ); - LEDConnection->setEnabled ( false ); - LEDBuffers->setEnabled ( false ); - LEDDelay->setEnabled ( false ); - LEDChat->setEnabled ( false ); - PushButtonConnect->setFocus(); - } - - - // Mac Workaround: - // If the connect button is the default button, on Mac it is highlighted - // by fading in and out a blue backgroud color. This operation consumes so - // much CPU that we get audio interruptions. - // Better solution: increase thread priority of worker thread (since the - // user can always highlight the button manually, too) -> TODO -#if defined ( __APPLE__ ) || defined ( __MACOSX ) - PushButtonConnect->setDefault ( false ); -#endif - - - // View menu -------------------------------------------------------------- - pViewMenu = new QMenu ( "&View", this ); - - pViewMenu->addAction ( tr ( "&Chat..." ), this, - SLOT ( OnOpenChatDialog() ) ); - - pViewMenu->addAction ( tr ( "&General Settings..." ), this, - SLOT ( OnOpenGeneralSettings() ) ); - - pViewMenu->addSeparator(); - - pViewMenu->addAction ( tr ( "E&xit" ), this, - SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); - - - // Main menu bar ----------------------------------------------------------- - pMenu = new QMenuBar ( this ); - - pMenu->addMenu ( pViewMenu ); - pMenu->addMenu ( new CLlconHelpMenu ( this ) ); - - // Now tell the layout about the menu - layout()->setMenuBar ( pMenu ); - - - // Connections ------------------------------------------------------------- - // push-buttons - QObject::connect ( PushButtonConnect, SIGNAL ( clicked() ), - this, SLOT ( OnConnectDisconBut() ) ); - - // timers - QObject::connect ( &TimerSigMet, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSigMet() ) ); - - QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), - this, SLOT ( OnTimerStatus() ) ); - - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); - - // sliders - QObject::connect ( SliderAudInFader, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnSliderAudInFader ( int ) ) ); - - QObject::connect ( SliderAudReverb, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnSliderAudReverb ( int ) ) ); - - // radio buttons - QObject::connect ( RadioButtonRevSelL, SIGNAL ( clicked() ), - this, SLOT ( OnRevSelL() ) ); - - QObject::connect ( RadioButtonRevSelR, SIGNAL ( clicked() ), - this, SLOT ( OnRevSelR() ) ); - - // line edits - QObject::connect ( LineEditFaderTag, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnFaderTagTextChanged ( const QString& ) ) ); - - QObject::connect ( LineEditServerAddr, SIGNAL ( editTextChanged ( const QString ) ), - this, SLOT ( OnLineEditServerAddrTextChanged ( const QString ) ) ); - - QObject::connect ( LineEditServerAddr, SIGNAL ( activated ( int ) ), - this, SLOT ( OnLineEditServerAddrActivated ( int ) ) ); - - // other - QObject::connect ( pClient, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - this, SLOT ( OnConClientListMesReceived ( CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( Disconnected() ), - this, SLOT ( OnDisconnected() ) ); - - QObject::connect ( pClient, - SIGNAL ( Stopped() ), - this, SLOT ( OnStopped() ) ); - - QObject::connect ( pClient, - SIGNAL ( ChatTextReceived ( QString ) ), - this, SLOT ( OnChatTextReceived ( QString ) ) ); - - QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), - this, SLOT ( OnPingTimeResult ( int ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLServerListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( pClient, SIGNAL ( CLPingTimeReceived ( CHostAddress, int ) ), - this, SLOT ( OnCLPingTimeResult ( CHostAddress, int ) ) ); - - QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ), - this, SLOT ( OnGUIDesignChanged() ) ); - - QObject::connect ( &ClientSettingsDlg, SIGNAL ( StereoCheckBoxChanged() ), - this, SLOT ( OnStereoCheckBoxChanged() ) ); - - QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanGain ( int, double ) ), - this, SLOT ( OnChangeChanGain ( int, double ) ) ); - - QObject::connect ( MainMixerBoard, SIGNAL ( NumClientsChanged ( int ) ), - this, SLOT ( OnNumClientsChanged ( int ) ) ); - - QObject::connect ( &ChatDlg, SIGNAL ( NewLocalInputText ( QString ) ), - this, SLOT ( OnNewLocalInputText ( QString ) ) ); - - QObject::connect ( &ConnectDlg, SIGNAL ( ReqServerListQuery ( CHostAddress ) ), - this, SLOT ( OnReqServerListQuery ( CHostAddress ) ) ); - - QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLPingMes ( CHostAddress ) ), - this, SLOT ( OnCreateCLPingMes ( CHostAddress ) ) ); - - - // Timers ------------------------------------------------------------------ - // start timer for status bar - TimerStatus.start ( LED_BAR_UPDATE_TIME_MS ); - - -// TEST -ConnectDlg.show(); - -} - -void CLlconClientDlg::closeEvent ( QCloseEvent* Event ) -{ - // if settings/connect dialog or chat dialog is open, close it - ClientSettingsDlg.close(); - ConnectDlg.close(); - ChatDlg.close(); - - // if connected, terminate connection - if ( pClient->IsRunning() ) - { - pClient->Stop(); - } - - // store IP addresses - for ( int iLEIdx = 0; iLEIdx < LineEditServerAddr->count(); iLEIdx++ ) - { - pClient->vstrIPAddress[iLEIdx] = - LineEditServerAddr->itemText ( iLEIdx ); - } - - // store fader tag - pClient->strName = LineEditFaderTag->text(); - - // default implementation of this event handler routine - Event->accept(); -} - -void CLlconClientDlg::UpdateAudioFaderSlider() -{ - // update slider and label of audio fader - const int iCurAudInFader = pClient->GetAudioInFader(); - SliderAudInFader->setValue ( iCurAudInFader ); - - // show in label the center position and what channel is - // attenuated - if ( iCurAudInFader == AUD_FADER_IN_MIDDLE ) - { - TextLabelAudFader->setText ( "Center" ); - } - else - { - if ( iCurAudInFader > AUD_FADER_IN_MIDDLE ) - { - // attenuation on right channel - TextLabelAudFader->setText ( "R -" + - QString().setNum ( iCurAudInFader - AUD_FADER_IN_MIDDLE ) ); - } - else - { - // attenuation on left channel - TextLabelAudFader->setText ( "L -" + - QString().setNum ( AUD_FADER_IN_MIDDLE - iCurAudInFader ) ); - } - } -} - -void CLlconClientDlg::UpdateRevSelection() -{ - if ( pClient->GetUseStereo() ) - { - // for stereo make channel selection invisible since - // reverberation effect is always applied to both channels - RadioButtonRevSelL->setVisible ( false ); - RadioButtonRevSelR->setVisible ( false ); - } - else - { - // make radio buttons visible - RadioButtonRevSelL->setVisible ( true ); - RadioButtonRevSelR->setVisible ( true ); - - // update value - if ( pClient->IsReverbOnLeftChan() ) - { - RadioButtonRevSelL->setChecked ( true ); - } - else - { - RadioButtonRevSelR->setChecked ( true ); - } - } -} - -void CLlconClientDlg::OnSliderAudInFader ( int value ) -{ - pClient->SetAudioInFader ( value ); - UpdateAudioFaderSlider(); -} - -void CLlconClientDlg::OnLineEditServerAddrTextChanged ( const QString ) -{ - // if the maximum number of items in the combo box is reached, - // delete the last item so that the new item can be added (first - // in - first out) - if ( LineEditServerAddr->count() == MAX_NUM_SERVER_ADDR_ITEMS ) - { - LineEditServerAddr->removeItem ( MAX_NUM_SERVER_ADDR_ITEMS - 1 ); - } -} - -void CLlconClientDlg::OnLineEditServerAddrActivated ( int index ) -{ - // move activated list item to the top - const QString strCurIPAddress = LineEditServerAddr->itemText ( index ); - LineEditServerAddr->removeItem ( index ); - LineEditServerAddr->insertItem ( 0, strCurIPAddress ); - LineEditServerAddr->setCurrentIndex ( 0 ); -} - -void CLlconClientDlg::OnConnectDisconBut() -{ - // the connect/disconnect button implements a toggle functionality - // -> apply inverted running state - ConnectDisconnect ( !pClient->IsRunning() ); -} - -void CLlconClientDlg::OnStopped() -{ - ConnectDisconnect ( false ); -} - -void CLlconClientDlg::OnOpenGeneralSettings() -{ - // open general settings dialog - ClientSettingsDlg.show(); - - // make sure dialog is upfront and has focus - ClientSettingsDlg.raise(); - ClientSettingsDlg.activateWindow(); -} - -void CLlconClientDlg::OnChatTextReceived ( QString strChatText ) -{ - // init flag (will maybe overwritten later in this function) - bUnreadChatMessage = false; - - ChatDlg.AddChatText ( strChatText ); - - // if requested, open window - if ( pClient->GetOpenChatOnNewMessage() ) - { - ShowChatWindow(); - } - else - { - if ( !ChatDlg.isVisible() ) - { - bUnreadChatMessage = true; - } - } - - UpdateDisplay(); -} - -void CLlconClientDlg::OnDisconnected() -{ - // channel is now disconnected, clear mixer board (remove all faders) - MainMixerBoard->HideAll(); - - UpdateDisplay(); -} - -void CLlconClientDlg::OnConClientListMesReceived ( CVector vecChanInfo ) -{ - // update mixer board - MainMixerBoard->ApplyNewConClientList ( vecChanInfo ); -} - -void CLlconClientDlg::OnNumClientsChanged ( int iNewNumClients ) -{ - // update window title - SetMyWindowTitle ( iNewNumClients ); -} - -void CLlconClientDlg::SetMyWindowTitle ( const int iNumClients ) -{ - // show number of connected clients in window title (and therefore also in - // the task bar of the OS) - if ( iNumClients == 0 ) - { - // only application name - setWindowTitle ( APP_NAME ); - } - else - { - if ( iNumClients == 1 ) - { - setWindowTitle ( QString ( APP_NAME ) + " (1 user)" ); - } - else - { - setWindowTitle ( QString ( APP_NAME ) + - QString ( " (%1 users)" ).arg ( iNumClients ) ); - } - } -} - -void CLlconClientDlg::ShowChatWindow() -{ - // open chat dialog - ChatDlg.show(); - - // make sure dialog is upfront and has focus - ChatDlg.raise(); - ChatDlg.activateWindow(); - - // chat dialog is opened, reset unread message flag - bUnreadChatMessage = false; - - UpdateDisplay(); -} - -void CLlconClientDlg::OnFaderTagTextChanged ( const QString& strNewName ) -{ - // check length - if ( strNewName.length() <= MAX_LEN_FADER_TAG ) - { - // refresh internal name parameter - pClient->strName = strNewName; - - // update name at server - pClient->SetRemoteName(); - } - else - { - // text is too long, update control with shortend text - LineEditFaderTag->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) ); - } -} - -void CLlconClientDlg::OnTimerSigMet() -{ - // get current input levels - double dCurSigLevelL = pClient->MicLevelL(); - double dCurSigLevelR = pClient->MicLevelR(); - - // linear transformation of the input level range to the progress-bar - // range - dCurSigLevelL -= LOW_BOUND_SIG_METER; - dCurSigLevelL *= NUM_STEPS_INP_LEV_METER / - ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - - // lower bound the signal - if ( dCurSigLevelL < 0 ) - { - dCurSigLevelL = 0; - } - - dCurSigLevelR -= LOW_BOUND_SIG_METER; - dCurSigLevelR *= NUM_STEPS_INP_LEV_METER / - ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - - // lower bound the signal - if ( dCurSigLevelR < 0 ) - { - dCurSigLevelR = 0; - } - - // show current level - MultiColorLEDBarInputLevelL->setValue ( (int) ceil ( dCurSigLevelL ) ); - MultiColorLEDBarInputLevelR->setValue ( (int) ceil ( dCurSigLevelR ) ); -} - -void CLlconClientDlg::OnTimerPing() -{ - // send ping message to server - pClient->CreatePingMes(); -} - -void CLlconClientDlg::OnPingTimeResult ( int iPingTime ) -{ - // calculate overall delay - const int iOverallDelayMs = pClient->EstimatedOverallDelay ( iPingTime ); - - // color definition: <= 40 ms green, <= 65 ms yellow, otherwise red - int iOverallDelayLEDColor; - if ( iOverallDelayMs <= 40 ) - { - iOverallDelayLEDColor = MUL_COL_LED_GREEN; - } - else - { - if ( iOverallDelayMs <= 65 ) - { - iOverallDelayLEDColor = MUL_COL_LED_YELLOW; - } - else - { - iOverallDelayLEDColor = MUL_COL_LED_RED; - } - } - - // only update delay information on settings dialog if it is visible to - // avoid CPU load on working thread if not necessary - if ( ClientSettingsDlg.isVisible() ) - { - // set ping time result to general settings dialog - ClientSettingsDlg.SetPingTimeResult ( iPingTime, - iOverallDelayMs, - iOverallDelayLEDColor ); - } - - // update delay LED on the main window - LEDDelay->SetLight ( iOverallDelayLEDColor ); -} - -void CLlconClientDlg::OnCLPingTimeResult ( CHostAddress InetAddr, - int iPingTime ) -{ - // color definition: <= 25 ms green, <= 50 ms yellow, otherwise red - int iPingTimeLEDColor; - if ( iPingTime <= 25 ) - { - iPingTimeLEDColor = MUL_COL_LED_GREEN; - } - else - { - if ( iPingTime <= 50 ) - { - iPingTimeLEDColor = MUL_COL_LED_YELLOW; - } - else - { - iPingTimeLEDColor = MUL_COL_LED_RED; - } - } - - // update connection dialog server list - ConnectDlg.SetPingTimeResult ( InetAddr, - iPingTime, - iPingTimeLEDColor ); -} - -void CLlconClientDlg::ConnectDisconnect ( const bool bDoStart ) -{ - // start/stop client, set button text - if ( bDoStart ) - { - // set address and check if address is valid - if ( pClient->SetServerAddr ( LineEditServerAddr->currentText() ) ) - { - bool bStartOk = true; - - // try to start client, if error occurred, do not go in - // running state but show error message - try - { - pClient->Start(); - } - - catch ( CGenErr generr ) - { - QMessageBox::critical ( - this, APP_NAME, generr.GetErrorText(), "Close", 0 ); - - bStartOk = false; - } - - if ( bStartOk ) - { - PushButtonConnect->setText ( CON_BUT_DISCONNECTTEXT ); - - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - } - } - else - { - // show the error as red light - LEDConnection->SetLight ( MUL_COL_LED_RED ); - } - } - else - { - // only stop client if currently running, in case we received - // the stopped message, the client is already stopped but the - // connect/disconnect button and other GUI controls must be - // updated - if ( pClient->IsRunning() ) - { - pClient->Stop(); - } - - PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); - - // stop timer for level meter bars and reset them - TimerSigMet.stop(); - MultiColorLEDBarInputLevelL->setValue ( 0 ); - MultiColorLEDBarInputLevelR->setValue ( 0 ); - - // stop ping time measurement timer - TimerPing.stop(); - - -// TODO is this still required??? -// immediately update status bar -OnTimerStatus(); - -// TODO this seems not to work, LEDs are still updated afterwards... -// reset LEDs -LEDConnection->Reset(); -LEDBuffers->Reset(); -LEDDelay->Reset(); -LEDChat->Reset(); - - - // clear mixer board (remove all faders) - MainMixerBoard->HideAll(); - } -} - -void CLlconClientDlg::UpdateDisplay() -{ - // update status LEDs - if ( pClient->IsRunning() ) - { - if ( pClient->IsConnected() ) - { - // chat LED - if ( bUnreadChatMessage ) - { - LEDChat->SetLight ( MUL_COL_LED_GREEN ); - } - else - { - LEDChat->Reset(); - } - - // connection LED - LEDConnection->SetLight ( MUL_COL_LED_GREEN ); - } - else - { - // connection LED - LEDConnection->SetLight ( MUL_COL_LED_RED ); - } - } -} - -void CLlconClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) -{ - // apply GUI design to current window - switch ( eNewDesign ) - { - case GD_ORIGINAL: - backgroundFrame->setStyleSheet ( - "QFrame#backgroundFrame { border-image: url(:/png/fader/res/mixerboardbackground.png) 34px 30px 40px 40px;" - " border-top: 34px transparent;" - " border-bottom: 40px transparent;" - " border-left: 30px transparent;" - " border-right: 40px transparent;" - " padding: -5px;" - " margin: -5px, -5px, 0px, 0px; }" - "QLabel { color: rgb(148, 148, 148);" - " font: bold; }" - "QRadioButton { color: rgb(148, 148, 148);" - " font: bold; }" - "QGroupBox::title { color: rgb(148, 148, 148); }" ); - -#ifdef _WIN32 -// Workaround QT-Windows problem: This should not be necessary since in the -// background frame the style sheet for QRadioButton was already set. But it -// seems that it is only applied if the style was set to default and then back -// to GD_ORIGINAL. This seems to be a QT related issue... -RadioButtonRevSelL->setStyleSheet ( "color: rgb(148, 148, 148);" - "font: bold;" ); -RadioButtonRevSelR->setStyleSheet ( "color: rgb(148, 148, 148);" - "font: bold;" ); -#endif - - break; - - default: - // reset style sheet and set original paramters - backgroundFrame->setStyleSheet ( "" ); - -#ifdef _WIN32 -// Workaround QT-Windows problem: See above description -RadioButtonRevSelL->setStyleSheet ( "" ); -RadioButtonRevSelR->setStyleSheet ( "" ); -#endif - - break; - } - - // also apply GUI design to child GUI controls - MainMixerBoard->SetGUIDesign ( eNewDesign ); -} - -void CLlconClientDlg::customEvent ( QEvent* Event ) -{ - if ( Event->type() == QEvent::User + 11 ) - { - const int iMessType = ( (CLlconEvent*) Event )->iMessType; - const int iStatus = ( (CLlconEvent*) Event )->iStatus; - - switch ( iMessType ) - { - case MS_SOUND_IN: - case MS_SOUND_OUT: - case MS_JIT_BUF_PUT: - case MS_JIT_BUF_GET: - // buffer status -> if any buffer goes red, this LED will go red - LEDBuffers->SetLight ( iStatus ); - break; - - case MS_RESET_ALL: - LEDConnection->Reset(); - LEDBuffers->Reset(); - LEDDelay->Reset(); - LEDChat->Reset(); - break; - - case MS_SET_JIT_BUF_SIZE: - pClient->SetSockBufNumFrames ( iStatus ); - break; - } - - // update general settings dialog, too - ClientSettingsDlg.SetStatus ( iMessType, iStatus ); - } -} +/******************************************************************************\ + * Copyright (c) 2004-2011 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "llconclientdlg.h" + + +/* Implementation *************************************************************/ +CLlconClientDlg::CLlconClientDlg ( CClient* pNCliP, + const bool bNewConnectOnStartup, + const bool bNewDisalbeLEDs, + QWidget* parent, + Qt::WindowFlags f ) : + QDialog ( parent, f ), + pClient ( pNCliP ), + bUnreadChatMessage ( false ), + ClientSettingsDlg ( pNCliP, parent +#ifdef _WIN32 + // this somehow only works reliable on Windows + , Qt::WindowMinMaxButtonsHint +#endif + ), + ChatDlg ( parent +#ifdef _WIN32 + // this somehow only works reliable on Windows + , Qt::WindowMinMaxButtonsHint +#endif + ), + ConnectDlg ( parent +#ifdef _WIN32 + // this somehow only works reliable on Windows + , Qt::WindowMinMaxButtonsHint +#endif + ) +{ + setupUi ( this ); + + // Add help text to controls ----------------------------------------------- + // input level meter + QString strInpLevH = tr ( "Input Level Meter: The input level " + "indicators show the current input level of the two stereo channels " + "of the current selected audio input. The upper level display belongs " + "to the left channel and the lower level display to the right channel " + "of the audio input.
" + "Make sure not to clip the input signal to avoid distortions of the " + "audio signal." ); + + QString strInpLevHTT = tr ( "If the llcon software is connected and " + "you play your instrument/sing in the microphone, the LED level " + "meter should flicker. If this is not the case, you have " + "probably selected the wrong input channel (e.g. line in instead " + "of the microphone input) or set the input gain too low in the " + "(Windows) audio mixer.
For a proper usage of the llcon software, " + "you should not hear your singing/instrument in the loudspeaker or " + "your headphone when the llcon software is not connected. This can " + "be achieved by muting your input audio channel in the Playback " + "mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; + + QString strInpLevHAccText = tr ( "Input level meter" ); + QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." ); + + TextInputLEDMeter->setWhatsThis ( strInpLevH ); + TextLevelMeterLeft->setWhatsThis ( strInpLevH ); + TextLevelMeterRight->setWhatsThis ( strInpLevH ); + MultiColorLEDBarInputLevelL->setWhatsThis ( strInpLevH ); + MultiColorLEDBarInputLevelL->setAccessibleName ( strInpLevHAccText ); + MultiColorLEDBarInputLevelL->setAccessibleDescription ( strInpLevHAccDescr ); + MultiColorLEDBarInputLevelL->setToolTip ( strInpLevHTT ); + MultiColorLEDBarInputLevelR->setWhatsThis ( strInpLevH ); + MultiColorLEDBarInputLevelR->setAccessibleName ( strInpLevHAccText ); + MultiColorLEDBarInputLevelR->setAccessibleDescription ( strInpLevHAccDescr ); + MultiColorLEDBarInputLevelR->setToolTip ( strInpLevHTT ); + + // connect/disconnect button + PushButtonConnect->setWhatsThis ( tr ( "Connect / Disconnect Button:" + " Push this button to connect the server. A valid IP address has " + "to be specified before. If the client is connected, pressing this " + "button will disconnect the connection." ) ); + + PushButtonConnect->setAccessibleName ( + tr ( "Connect and disconnect toggle button" ) ); + + PushButtonConnect->setAccessibleDescription ( tr ( "Clicking on this " + "button changes the caption of the button from Connect to " + "Disconnect, i.e., it implements a toggle functionality for connecting " + "and disconnecting the llcon software." ) ); + + // server address + QString strServAddrH = tr ( "Server Address: The IP address or URL " + "of the server running the llcon server software must be set here. " + "A list of the most recent used server URLs is available for " + "selection. If an invalid address was chosen, an error message is " + "shown in the status bar." ); + + TextLabelServerAddr->setWhatsThis ( strServAddrH ); + LineEditServerAddr->setWhatsThis ( strServAddrH ); + + LineEditServerAddr->setAccessibleName ( tr ( "Server address edit box" ) ); + LineEditServerAddr->setAccessibleDescription ( tr ( "Holds the current server " + "URL. It also stores old URLs in the combo box list." ) ); + + // fader tag + QString strFaderTag = tr ( "Fader Tag: The fader tag of the local " + "client is set in the fader tag edit box. This tag will appear " + "at your fader on the mixer board when you are connected to a llcon " + "server. This tag will also show up at each client which is connected " + "to the same server as the local client. If the fader tag is empty, " + "the IP address of the client is displayed instead." ); + + QString strFaderTagTT = tr ( "Set your name and/or instrument and/or " + "pseoudonym here so that the other musicians can identify you." ) + + TOOLTIP_COM_END_TEXT; + + TextLabelServerTag->setWhatsThis ( strFaderTag ); + TextLabelServerTag->setToolTip ( strFaderTagTT ); + LineEditFaderTag->setWhatsThis ( strFaderTag ); + LineEditFaderTag->setToolTip ( strFaderTagTT ); + + LineEditFaderTag->setAccessibleName ( tr ( "Fader tag edit box" ) ); + + // local audio input fader + QString strAudFader = tr ( "Local Audio Input Fader: With the " + "audio fader, the relative levels of the left and right local audio " + "channels can be changed. It acts like a panning between the two " + "channels. If, e.g., a microphone is connected to the right input " + "channel and an instrument is connected to the left input channel " + "which is much louder than the microphone, move the audio fader in " + "a direction where the label above the fader shows L -x, where " + "x is the current attenuation indication. " ); + + TextAudInFader->setWhatsThis ( strAudFader ); + SliderAudInFader->setWhatsThis ( strAudFader ); + + SliderAudInFader->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); + + // reverberation level + QString strAudReverb = tr ( "Reverberation Level: A reverberation " + "effect can be applied to one local audio channel. The channel " + "selection and the reverberation level can be modified. If, e.g., " + "the microphone signal is fed into the right audio channel of the " + "sound card and a reverberation effect shall be applied, set the " + "channel selector to right and move the fader upwards until the " + "desired reverberation level is reached.
" + "The reverberation effect requires significant CPU so that it should " + "only be used on fast PCs. If the reverberation level fader is set to " + "minimum (which is the default setting), the reverberation effect is " + "switched off and does not cause any additional CPU usage." ); + + TextLabelAudReverb->setWhatsThis ( strAudReverb ); + SliderAudReverb->setWhatsThis ( strAudReverb ); + + SliderAudReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) ); + + // reverberation channel selection + QString strRevChanSel = tr ( "Reverberation Channel Selection: " + "With these radio buttons the audio input channel on which the " + "reverberation effect is applied can be chosen. Either the left " + "or right input channel can be selected." ); + + RadioButtonRevSelL->setWhatsThis ( strRevChanSel ); + RadioButtonRevSelL->setAccessibleName ( tr ( "Left channel selection for reverberation" ) ); + RadioButtonRevSelR->setWhatsThis ( strRevChanSel ); + RadioButtonRevSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) ); + + // connection LED + QString strLEDConnection = tr ( "Connection Status LED: " + "The connection status LED indicator shows the current connection " + "status. If the light is green, a successful connection to the " + "server is established. If the light blinks red right after " + "pressing the connect button, the server address is invalid. " + "If the light turns red and stays red, the connection to the " + "server is lost." ); + + TextLabelConnection->setWhatsThis ( strLEDConnection ); + LEDConnection->setWhatsThis ( strLEDConnection ); + + LEDConnection->setAccessibleName ( tr ( "Connection status LED indicator" ) ); + + // delay LED + QString strLEDDelay = tr ( "Delay Status LED: " + "The delay status LED indicator shows the current audio delay " + "status. If the light is green, the delay is perfect for a jam " + "session. If the ligth is yellow, a session is still possible but " + "it may be harder to play. If the light is red, the delay is too " + "large for jamming." ); + + TextLabelDelay->setWhatsThis ( strLEDDelay ); + LEDDelay->setWhatsThis ( strLEDDelay ); + LEDDelay->setToolTip ( tr ( "If this LED indicator turns red, " + "you will not have much fun using the llcon software." ) + + TOOLTIP_COM_END_TEXT ); + + LEDDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) ); + + // buffers LED + QString strLEDBuffers = tr ( "Buffers Status LED: " + "The buffers status LED indicator shows the current audio/streaming " + "status. If the light is green, there are no buffer overruns/underruns " + "and the audio stream is not interrupted. If the light is red, the " + "audio stream is interrupted caused by one of the following problems:" + "
    " + "
  • The network jitter buffer is not large enough for the current " + "network/audio interface jitter.
  • " + "
  • The sound card buffer delay (buffer size) is set to a too small " + "value.
  • " + "
  • The upload or download stream rate is too high for the current " + "available internet bandwidth.
  • " + "
  • The CPU of the client or server is at 100%.
  • " + "
" ); + + TextLabelBuffers->setWhatsThis ( strLEDBuffers ); + LEDBuffers->setWhatsThis ( strLEDBuffers ); + + LEDBuffers->setAccessibleName ( tr ( "Buffers status LED indicator" ) ); + + // chat LED + QString strLEDChat = tr ( "Chat Status LED: " + "If the option Open Chat on New Message is not activated, this " + "status LED will turn green on a new received chat message." ); + + TextLabelChat->setWhatsThis ( strLEDChat ); + LEDChat->setWhatsThis ( strLEDChat ); + + LEDBuffers->setAccessibleName ( tr ( "Chat status LED indicator" ) ); + + + // init GUI design + SetGUIDesign ( pClient->GetGUIDesign() ); + + // reset mixer board + MainMixerBoard->HideAll(); + + // init fader tag line edit + LineEditFaderTag->setText ( pClient->strName ); + + // init server address combo box (max MAX_NUM_SERVER_ADDR_ITEMS entries) + LineEditServerAddr->setMaxCount ( MAX_NUM_SERVER_ADDR_ITEMS ); + LineEditServerAddr->setInsertPolicy ( QComboBox::InsertAtTop ); + + // load data from ini file + for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) + { + if ( !pClient->vstrIPAddress[iLEIdx].isEmpty() ) + { + LineEditServerAddr->addItem ( pClient->vstrIPAddress[iLEIdx] ); + } + } + + // we want the cursor to be at IP address line edit at startup + LineEditServerAddr->setFocus(); + + // init status label + OnTimerStatus(); + + // init connection button text + PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); + + // init input level meter bars + MultiColorLEDBarInputLevelL->setValue ( 0 ); + MultiColorLEDBarInputLevelR->setValue ( 0 ); + + // init status LEDs + LEDConnection->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); + LEDChat->SetUpdateTime ( 2 * LED_BAR_UPDATE_TIME_MS ); + LEDDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME_MS ); + LEDDelay->Reset(); + + + // init slider controls --- + // audio in fader + SliderAudInFader->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); + SliderAudInFader->setTickInterval ( AUD_FADER_IN_MAX / 5 ); + UpdateAudioFaderSlider(); + + // audio reverberation + SliderAudReverb->setRange ( 0, AUD_REVERB_MAX ); + const int iCurAudReverb = pClient->GetReverbLevel(); + SliderAudReverb->setValue ( iCurAudReverb ); + SliderAudReverb->setTickInterval ( AUD_REVERB_MAX / 5 ); + + // init reverb channel + UpdateRevSelection(); + + + // connect on startup --- + if ( bNewConnectOnStartup ) + { + // since the software starts up right now, the previous state was + // "not connected" so that a call to "OnConnectDisconBut()" will + // start the connection + OnConnectDisconBut(); + } + + + // disable controls on request --- + // disable LEDs in main window if requested + if ( bNewDisalbeLEDs ) + { + MultiColorLEDBarInputLevelL->setEnabled ( false ); + MultiColorLEDBarInputLevelR->setEnabled ( false ); + LEDConnection->setEnabled ( false ); + LEDBuffers->setEnabled ( false ); + LEDDelay->setEnabled ( false ); + LEDChat->setEnabled ( false ); + PushButtonConnect->setFocus(); + } + + + // Mac Workaround: + // If the connect button is the default button, on Mac it is highlighted + // by fading in and out a blue backgroud color. This operation consumes so + // much CPU that we get audio interruptions. + // Better solution: increase thread priority of worker thread (since the + // user can always highlight the button manually, too) -> TODO +#if defined ( __APPLE__ ) || defined ( __MACOSX ) + PushButtonConnect->setDefault ( false ); +#endif + + + // View menu -------------------------------------------------------------- + pViewMenu = new QMenu ( "&View", this ); + + pViewMenu->addAction ( tr ( "&Chat..." ), this, + SLOT ( OnOpenChatDialog() ) ); + + pViewMenu->addAction ( tr ( "&General Settings..." ), this, + SLOT ( OnOpenGeneralSettings() ) ); + + pViewMenu->addSeparator(); + + pViewMenu->addAction ( tr ( "E&xit" ), this, + SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); + + + // Main menu bar ----------------------------------------------------------- + pMenu = new QMenuBar ( this ); + + pMenu->addMenu ( pViewMenu ); + pMenu->addMenu ( new CLlconHelpMenu ( this ) ); + + // Now tell the layout about the menu + layout()->setMenuBar ( pMenu ); + + + // Connections ------------------------------------------------------------- + // push-buttons + QObject::connect ( PushButtonConnect, SIGNAL ( clicked() ), + this, SLOT ( OnConnectDisconBut() ) ); + + // timers + QObject::connect ( &TimerSigMet, SIGNAL ( timeout() ), + this, SLOT ( OnTimerSigMet() ) ); + + QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), + this, SLOT ( OnTimerStatus() ) ); + + QObject::connect ( &TimerPing, SIGNAL ( timeout() ), + this, SLOT ( OnTimerPing() ) ); + + // sliders + QObject::connect ( SliderAudInFader, SIGNAL ( valueChanged ( int ) ), + this, SLOT ( OnSliderAudInFader ( int ) ) ); + + QObject::connect ( SliderAudReverb, SIGNAL ( valueChanged ( int ) ), + this, SLOT ( OnSliderAudReverb ( int ) ) ); + + // radio buttons + QObject::connect ( RadioButtonRevSelL, SIGNAL ( clicked() ), + this, SLOT ( OnRevSelL() ) ); + + QObject::connect ( RadioButtonRevSelR, SIGNAL ( clicked() ), + this, SLOT ( OnRevSelR() ) ); + + // line edits + QObject::connect ( LineEditFaderTag, SIGNAL ( textChanged ( const QString& ) ), + this, SLOT ( OnFaderTagTextChanged ( const QString& ) ) ); + + QObject::connect ( LineEditServerAddr, SIGNAL ( editTextChanged ( const QString ) ), + this, SLOT ( OnLineEditServerAddrTextChanged ( const QString ) ) ); + + QObject::connect ( LineEditServerAddr, SIGNAL ( activated ( int ) ), + this, SLOT ( OnLineEditServerAddrActivated ( int ) ) ); + + // other + QObject::connect ( pClient, + SIGNAL ( ConClientListMesReceived ( CVector ) ), + this, SLOT ( OnConClientListMesReceived ( CVector ) ) ); + + QObject::connect ( pClient, + SIGNAL ( Disconnected() ), + this, SLOT ( OnDisconnected() ) ); + + QObject::connect ( pClient, + SIGNAL ( Stopped() ), + this, SLOT ( OnStopped() ) ); + + QObject::connect ( pClient, + SIGNAL ( ChatTextReceived ( QString ) ), + this, SLOT ( OnChatTextReceived ( QString ) ) ); + + QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), + this, SLOT ( OnPingTimeResult ( int ) ) ); + + QObject::connect ( pClient, + SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), + this, SLOT ( OnCLServerListReceived ( CHostAddress, CVector ) ) ); + + QObject::connect ( pClient, SIGNAL ( CLPingTimeReceived ( CHostAddress, int ) ), + this, SLOT ( OnCLPingTimeResult ( CHostAddress, int ) ) ); + + QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ), + this, SLOT ( OnGUIDesignChanged() ) ); + + QObject::connect ( &ClientSettingsDlg, SIGNAL ( StereoCheckBoxChanged() ), + this, SLOT ( OnStereoCheckBoxChanged() ) ); + + QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanGain ( int, double ) ), + this, SLOT ( OnChangeChanGain ( int, double ) ) ); + + QObject::connect ( MainMixerBoard, SIGNAL ( NumClientsChanged ( int ) ), + this, SLOT ( OnNumClientsChanged ( int ) ) ); + + QObject::connect ( &ChatDlg, SIGNAL ( NewLocalInputText ( QString ) ), + this, SLOT ( OnNewLocalInputText ( QString ) ) ); + + QObject::connect ( &ConnectDlg, SIGNAL ( ReqServerListQuery ( CHostAddress ) ), + this, SLOT ( OnReqServerListQuery ( CHostAddress ) ) ); + + QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLPingMes ( CHostAddress ) ), + this, SLOT ( OnCreateCLPingMes ( CHostAddress ) ) ); + + + // Timers ------------------------------------------------------------------ + // start timer for status bar + TimerStatus.start ( LED_BAR_UPDATE_TIME_MS ); + + +// TEST +ConnectDlg.LoadStoredServers ( pClient->vstrIPAddress ); +ConnectDlg.show(); + +} + +void CLlconClientDlg::closeEvent ( QCloseEvent* Event ) +{ + // if settings/connect dialog or chat dialog is open, close it + ClientSettingsDlg.close(); + ConnectDlg.close(); + ChatDlg.close(); + + // if connected, terminate connection + if ( pClient->IsRunning() ) + { + pClient->Stop(); + } + + // store IP addresses + for ( int iLEIdx = 0; iLEIdx < LineEditServerAddr->count(); iLEIdx++ ) + { + pClient->vstrIPAddress[iLEIdx] = + LineEditServerAddr->itemText ( iLEIdx ); + } + + // store fader tag + pClient->strName = LineEditFaderTag->text(); + + // default implementation of this event handler routine + Event->accept(); +} + +void CLlconClientDlg::UpdateAudioFaderSlider() +{ + // update slider and label of audio fader + const int iCurAudInFader = pClient->GetAudioInFader(); + SliderAudInFader->setValue ( iCurAudInFader ); + + // show in label the center position and what channel is + // attenuated + if ( iCurAudInFader == AUD_FADER_IN_MIDDLE ) + { + TextLabelAudFader->setText ( "Center" ); + } + else + { + if ( iCurAudInFader > AUD_FADER_IN_MIDDLE ) + { + // attenuation on right channel + TextLabelAudFader->setText ( "R -" + + QString().setNum ( iCurAudInFader - AUD_FADER_IN_MIDDLE ) ); + } + else + { + // attenuation on left channel + TextLabelAudFader->setText ( "L -" + + QString().setNum ( AUD_FADER_IN_MIDDLE - iCurAudInFader ) ); + } + } +} + +void CLlconClientDlg::UpdateRevSelection() +{ + if ( pClient->GetUseStereo() ) + { + // for stereo make channel selection invisible since + // reverberation effect is always applied to both channels + RadioButtonRevSelL->setVisible ( false ); + RadioButtonRevSelR->setVisible ( false ); + } + else + { + // make radio buttons visible + RadioButtonRevSelL->setVisible ( true ); + RadioButtonRevSelR->setVisible ( true ); + + // update value + if ( pClient->IsReverbOnLeftChan() ) + { + RadioButtonRevSelL->setChecked ( true ); + } + else + { + RadioButtonRevSelR->setChecked ( true ); + } + } +} + +void CLlconClientDlg::OnSliderAudInFader ( int value ) +{ + pClient->SetAudioInFader ( value ); + UpdateAudioFaderSlider(); +} + +void CLlconClientDlg::OnLineEditServerAddrTextChanged ( const QString ) +{ + // if the maximum number of items in the combo box is reached, + // delete the last item so that the new item can be added (first + // in - first out) + if ( LineEditServerAddr->count() == MAX_NUM_SERVER_ADDR_ITEMS ) + { + LineEditServerAddr->removeItem ( MAX_NUM_SERVER_ADDR_ITEMS - 1 ); + } +} + +void CLlconClientDlg::OnLineEditServerAddrActivated ( int index ) +{ + // move activated list item to the top + const QString strCurIPAddress = LineEditServerAddr->itemText ( index ); + LineEditServerAddr->removeItem ( index ); + LineEditServerAddr->insertItem ( 0, strCurIPAddress ); + LineEditServerAddr->setCurrentIndex ( 0 ); +} + +void CLlconClientDlg::OnConnectDisconBut() +{ + // the connect/disconnect button implements a toggle functionality + // -> apply inverted running state + ConnectDisconnect ( !pClient->IsRunning() ); +} + +void CLlconClientDlg::OnStopped() +{ + ConnectDisconnect ( false ); +} + +void CLlconClientDlg::OnOpenGeneralSettings() +{ + // open general settings dialog + ClientSettingsDlg.show(); + + // make sure dialog is upfront and has focus + ClientSettingsDlg.raise(); + ClientSettingsDlg.activateWindow(); +} + +void CLlconClientDlg::OnChatTextReceived ( QString strChatText ) +{ + // init flag (will maybe overwritten later in this function) + bUnreadChatMessage = false; + + ChatDlg.AddChatText ( strChatText ); + + // if requested, open window + if ( pClient->GetOpenChatOnNewMessage() ) + { + ShowChatWindow(); + } + else + { + if ( !ChatDlg.isVisible() ) + { + bUnreadChatMessage = true; + } + } + + UpdateDisplay(); +} + +void CLlconClientDlg::OnDisconnected() +{ + // channel is now disconnected, clear mixer board (remove all faders) + MainMixerBoard->HideAll(); + + UpdateDisplay(); +} + +void CLlconClientDlg::OnConClientListMesReceived ( CVector vecChanInfo ) +{ + // update mixer board + MainMixerBoard->ApplyNewConClientList ( vecChanInfo ); +} + +void CLlconClientDlg::OnNumClientsChanged ( int iNewNumClients ) +{ + // update window title + SetMyWindowTitle ( iNewNumClients ); +} + +void CLlconClientDlg::SetMyWindowTitle ( const int iNumClients ) +{ + // show number of connected clients in window title (and therefore also in + // the task bar of the OS) + if ( iNumClients == 0 ) + { + // only application name + setWindowTitle ( APP_NAME ); + } + else + { + if ( iNumClients == 1 ) + { + setWindowTitle ( QString ( APP_NAME ) + " (1 user)" ); + } + else + { + setWindowTitle ( QString ( APP_NAME ) + + QString ( " (%1 users)" ).arg ( iNumClients ) ); + } + } +} + +void CLlconClientDlg::ShowChatWindow() +{ + // open chat dialog + ChatDlg.show(); + + // make sure dialog is upfront and has focus + ChatDlg.raise(); + ChatDlg.activateWindow(); + + // chat dialog is opened, reset unread message flag + bUnreadChatMessage = false; + + UpdateDisplay(); +} + +void CLlconClientDlg::OnFaderTagTextChanged ( const QString& strNewName ) +{ + // check length + if ( strNewName.length() <= MAX_LEN_FADER_TAG ) + { + // refresh internal name parameter + pClient->strName = strNewName; + + // update name at server + pClient->SetRemoteName(); + } + else + { + // text is too long, update control with shortend text + LineEditFaderTag->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) ); + } +} + +void CLlconClientDlg::OnTimerSigMet() +{ + // get current input levels + double dCurSigLevelL = pClient->MicLevelL(); + double dCurSigLevelR = pClient->MicLevelR(); + + // linear transformation of the input level range to the progress-bar + // range + dCurSigLevelL -= LOW_BOUND_SIG_METER; + dCurSigLevelL *= NUM_STEPS_INP_LEV_METER / + ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + + // lower bound the signal + if ( dCurSigLevelL < 0 ) + { + dCurSigLevelL = 0; + } + + dCurSigLevelR -= LOW_BOUND_SIG_METER; + dCurSigLevelR *= NUM_STEPS_INP_LEV_METER / + ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + + // lower bound the signal + if ( dCurSigLevelR < 0 ) + { + dCurSigLevelR = 0; + } + + // show current level + MultiColorLEDBarInputLevelL->setValue ( (int) ceil ( dCurSigLevelL ) ); + MultiColorLEDBarInputLevelR->setValue ( (int) ceil ( dCurSigLevelR ) ); +} + +void CLlconClientDlg::OnTimerPing() +{ + // send ping message to server + pClient->CreatePingMes(); +} + +void CLlconClientDlg::OnPingTimeResult ( int iPingTime ) +{ + // calculate overall delay + const int iOverallDelayMs = pClient->EstimatedOverallDelay ( iPingTime ); + + // color definition: <= 40 ms green, <= 65 ms yellow, otherwise red + int iOverallDelayLEDColor; + if ( iOverallDelayMs <= 40 ) + { + iOverallDelayLEDColor = MUL_COL_LED_GREEN; + } + else + { + if ( iOverallDelayMs <= 65 ) + { + iOverallDelayLEDColor = MUL_COL_LED_YELLOW; + } + else + { + iOverallDelayLEDColor = MUL_COL_LED_RED; + } + } + + // only update delay information on settings dialog if it is visible to + // avoid CPU load on working thread if not necessary + if ( ClientSettingsDlg.isVisible() ) + { + // set ping time result to general settings dialog + ClientSettingsDlg.SetPingTimeResult ( iPingTime, + iOverallDelayMs, + iOverallDelayLEDColor ); + } + + // update delay LED on the main window + LEDDelay->SetLight ( iOverallDelayLEDColor ); +} + +void CLlconClientDlg::OnCLPingTimeResult ( CHostAddress InetAddr, + int iPingTime ) +{ + // color definition: <= 25 ms green, <= 50 ms yellow, otherwise red + int iPingTimeLEDColor; + if ( iPingTime <= 25 ) + { + iPingTimeLEDColor = MUL_COL_LED_GREEN; + } + else + { + if ( iPingTime <= 50 ) + { + iPingTimeLEDColor = MUL_COL_LED_YELLOW; + } + else + { + iPingTimeLEDColor = MUL_COL_LED_RED; + } + } + + // update connection dialog server list + ConnectDlg.SetPingTimeResult ( InetAddr, + iPingTime, + iPingTimeLEDColor ); +} + +void CLlconClientDlg::ConnectDisconnect ( const bool bDoStart ) +{ + // start/stop client, set button text + if ( bDoStart ) + { + // set address and check if address is valid + if ( pClient->SetServerAddr ( LineEditServerAddr->currentText() ) ) + { + bool bStartOk = true; + + // try to start client, if error occurred, do not go in + // running state but show error message + try + { + pClient->Start(); + } + + catch ( CGenErr generr ) + { + QMessageBox::critical ( + this, APP_NAME, generr.GetErrorText(), "Close", 0 ); + + bStartOk = false; + } + + if ( bStartOk ) + { + PushButtonConnect->setText ( CON_BUT_DISCONNECTTEXT ); + + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + } + } + else + { + // show the error as red light + LEDConnection->SetLight ( MUL_COL_LED_RED ); + } + } + else + { + // only stop client if currently running, in case we received + // the stopped message, the client is already stopped but the + // connect/disconnect button and other GUI controls must be + // updated + if ( pClient->IsRunning() ) + { + pClient->Stop(); + } + + PushButtonConnect->setText ( CON_BUT_CONNECTTEXT ); + + // stop timer for level meter bars and reset them + TimerSigMet.stop(); + MultiColorLEDBarInputLevelL->setValue ( 0 ); + MultiColorLEDBarInputLevelR->setValue ( 0 ); + + // stop ping time measurement timer + TimerPing.stop(); + + +// TODO is this still required??? +// immediately update status bar +OnTimerStatus(); + +// TODO this seems not to work, LEDs are still updated afterwards... +// reset LEDs +LEDConnection->Reset(); +LEDBuffers->Reset(); +LEDDelay->Reset(); +LEDChat->Reset(); + + + // clear mixer board (remove all faders) + MainMixerBoard->HideAll(); + } +} + +void CLlconClientDlg::UpdateDisplay() +{ + // update status LEDs + if ( pClient->IsRunning() ) + { + if ( pClient->IsConnected() ) + { + // chat LED + if ( bUnreadChatMessage ) + { + LEDChat->SetLight ( MUL_COL_LED_GREEN ); + } + else + { + LEDChat->Reset(); + } + + // connection LED + LEDConnection->SetLight ( MUL_COL_LED_GREEN ); + } + else + { + // connection LED + LEDConnection->SetLight ( MUL_COL_LED_RED ); + } + } +} + +void CLlconClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) +{ + // apply GUI design to current window + switch ( eNewDesign ) + { + case GD_ORIGINAL: + backgroundFrame->setStyleSheet ( + "QFrame#backgroundFrame { border-image: url(:/png/fader/res/mixerboardbackground.png) 34px 30px 40px 40px;" + " border-top: 34px transparent;" + " border-bottom: 40px transparent;" + " border-left: 30px transparent;" + " border-right: 40px transparent;" + " padding: -5px;" + " margin: -5px, -5px, 0px, 0px; }" + "QLabel { color: rgb(148, 148, 148);" + " font: bold; }" + "QRadioButton { color: rgb(148, 148, 148);" + " font: bold; }" + "QGroupBox::title { color: rgb(148, 148, 148); }" ); + +#ifdef _WIN32 +// Workaround QT-Windows problem: This should not be necessary since in the +// background frame the style sheet for QRadioButton was already set. But it +// seems that it is only applied if the style was set to default and then back +// to GD_ORIGINAL. This seems to be a QT related issue... +RadioButtonRevSelL->setStyleSheet ( "color: rgb(148, 148, 148);" + "font: bold;" ); +RadioButtonRevSelR->setStyleSheet ( "color: rgb(148, 148, 148);" + "font: bold;" ); +#endif + + break; + + default: + // reset style sheet and set original paramters + backgroundFrame->setStyleSheet ( "" ); + +#ifdef _WIN32 +// Workaround QT-Windows problem: See above description +RadioButtonRevSelL->setStyleSheet ( "" ); +RadioButtonRevSelR->setStyleSheet ( "" ); +#endif + + break; + } + + // also apply GUI design to child GUI controls + MainMixerBoard->SetGUIDesign ( eNewDesign ); +} + +void CLlconClientDlg::customEvent ( QEvent* Event ) +{ + if ( Event->type() == QEvent::User + 11 ) + { + const int iMessType = ( (CLlconEvent*) Event )->iMessType; + const int iStatus = ( (CLlconEvent*) Event )->iStatus; + + switch ( iMessType ) + { + case MS_SOUND_IN: + case MS_SOUND_OUT: + case MS_JIT_BUF_PUT: + case MS_JIT_BUF_GET: + // buffer status -> if any buffer goes red, this LED will go red + LEDBuffers->SetLight ( iStatus ); + break; + + case MS_RESET_ALL: + LEDConnection->Reset(); + LEDBuffers->Reset(); + LEDDelay->Reset(); + LEDChat->Reset(); + break; + + case MS_SET_JIT_BUF_SIZE: + pClient->SetSockBufNumFrames ( iStatus ); + break; + } + + // update general settings dialog, too + ClientSettingsDlg.SetStatus ( iMessType, iStatus ); + } +}