File indexing completed on 2024-04-28 04:03:11

0001 /***************************************************************************
0002     File                 : gamedialog.cpp
0003     Project              : Knights
0004     Description          : Game Dialog
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016-2018 Alexander Semke (alexander.semke@web.de)
0007     SPDX-FileCopyrightText: 2009-2011 Miha Čančula (miha@noughmad.eu)
0008 
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  SPDX-License-Identifier: GPL-2.0-or-later
0014  *                                                                         *
0015  *  This program is distributed in the hope that it will be useful,        *
0016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0018  *  GNU General Public License for more details.                           *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the Free Software           *
0022  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0023  *   Boston, MA  02110-1301  USA                                           *
0024  *                                                                         *
0025  ***************************************************************************/
0026 
0027 #include "gamedialog.h"
0028 
0029 #include "ui_gamedialog.h"
0030 #include "proto/localprotocol.h"
0031 #include "proto/xboardprotocol.h"
0032 #include "proto/ficsprotocol.h"
0033 #include "proto/uciprotocol.h"
0034 #include "gamemanager.h"
0035 #include "knightsdebug.h"
0036 #include "settings.h"
0037 #include "enginesettings.h"
0038 
0039 #include <QRandomGenerator>
0040 #include <QDialogButtonBox>
0041 
0042 #include <QNetworkInformation>
0043 
0044 using namespace Knights;
0045 
0046 GameDialog::GameDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f),
0047     ui(new Ui::GameDialog()),
0048     okButton(nullptr) {
0049 
0050     QFrame* mainFrame = new QFrame(this);
0051     ui->setupUi(mainFrame);
0052     ui->pbPlayer1Engine->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0053     ui->pbPlayer2Engine->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
0054     ui->cbTimeControl->setChecked(Settings::timeEnabled());
0055     ui->sbTimeLimit->setValue(Settings::timeLimit());
0056     ui->sbTimeIncrement->setValue(Settings::timeIncrement());
0057     ui->sbNumberOfMoves->setValue(Settings::numberOfMoves());
0058 
0059     QDialogButtonBox* bBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
0060     okButton = bBox->button(QDialogButtonBox::Ok);
0061 
0062     QVBoxLayout* layout = new QVBoxLayout(this);
0063     layout->addWidget(mainFrame);
0064     layout->addWidget(bBox);
0065 
0066     setLayout(layout);
0067     setWindowTitle(i18nc("@title:window", "New Game"));
0068 
0069     connect(bBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
0070     connect(bBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0071 
0072     switch(Settings::player1Protocol()) {
0073     case Settings::EnumPlayer1Protocol::Local:
0074         ui->rbPlayer1Human->setChecked(true);
0075         break;
0076     case Settings::EnumPlayer1Protocol::XBoard:
0077         ui->rbPlayer1Engine->setChecked(true);
0078         break;
0079     }
0080 
0081     switch(Settings::color()) {
0082     case Settings::EnumColor::NoColor:
0083         ui->colorRandom->setChecked(true);
0084         break;
0085     case Settings::EnumColor::White:
0086         ui->colorWhite->setChecked(true);
0087         break;
0088     case Settings::EnumColor::Black:
0089         ui->colorBlack->setChecked(true);
0090         break;
0091     }
0092 
0093     switch(Settings::player2Protocol()) {
0094     case Settings::EnumPlayer2Protocol::Local:
0095         ui->rbPlayer2Human->setChecked(true);
0096         break;
0097     case Settings::EnumPlayer2Protocol::XBoard:
0098         ui->rbPlayer2Engine->setChecked(true);
0099         break;
0100     case Settings::EnumPlayer2Protocol::Fics:
0101         ui->rbPlayer2Server->setChecked(true);
0102         break;
0103     }
0104 
0105     loadEngines();
0106 
0107     ui->cbPlayer2Server->setHistoryItems(Settings::servers());
0108     ui->cbPlayer2Server->setEditText(Settings::currentServer());
0109 
0110     //SIGNALs/SLOTs
0111     //player 1
0112     connect(ui->rbPlayer1Human, &QRadioButton::clicked, this, &GameDialog::player1SettingsChanged);
0113     connect(ui->rbPlayer1Engine, &QRadioButton::clicked, this, &GameDialog::player1SettingsChanged);
0114     connect(ui->pbPlayer1Engine, &QPushButton::clicked, this, &GameDialog::showEngineConfigDialog);
0115     connect(ui->cbPlayer1Engine, &QComboBox::currentIndexChanged, this, &GameDialog::checkOkButton);
0116 
0117     //player 2
0118     connect(ui->rbPlayer2Human, &QRadioButton::clicked, this, &GameDialog::player2SettingsChanged);
0119     connect(ui->rbPlayer2Engine, &QRadioButton::clicked, this, &GameDialog::player2SettingsChanged);
0120     connect(ui->rbPlayer2Server, &QRadioButton::clicked, this, &GameDialog::player2SettingsChanged);
0121     connect(ui->pbPlayer2Engine, &QPushButton::clicked, this, &GameDialog::showEngineConfigDialog);
0122     connect(ui->cbPlayer2Engine, &QComboBox::currentIndexChanged, this, &GameDialog::checkOkButton);
0123 
0124     //time control
0125     connect(ui->cbTimeControl, &QCheckBox::toggled, this, &GameDialog::timeControlChanged);
0126     connect(ui->sbTimeLimit, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged), this, &GameDialog::updateTimeEdits);
0127     connect(ui->sbTimeIncrement, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged), this, &GameDialog::updateTimeEdits);
0128     connect(ui->sbNumberOfMoves, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged), this, &GameDialog::updateTimeEdits);
0129 
0130     bool hasNetworkInfo = QNetworkInformation::loadBackendByFeatures(QNetworkInformation::Feature::Reachability);
0131 
0132     if (hasNetworkInfo) {
0133         connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, [this](QNetworkInformation::Reachability newReachability) {
0134             networkStatusChanged(newReachability == QNetworkInformation::Reachability::Online);
0135         });
0136 
0137         networkStatusChanged(QNetworkInformation::instance()->reachability() == QNetworkInformation::Reachability::Online);
0138     }
0139 
0140     updateTimeEdits();
0141     player1SettingsChanged();
0142     player2SettingsChanged();
0143     timeControlChanged();
0144 }
0145 
0146 GameDialog::~GameDialog() {
0147     delete ui;
0148 }
0149 
0150 void GameDialog::setupProtocols() {
0151     TimeControl tc;
0152     tc.baseTime = ui->cbTimeControl->isChecked() ? QTime(0, ui->sbTimeLimit->value(), 0, 0) : QTime();
0153     tc.moves = ui->rbPlayer2Server->isChecked() ? 0 : ui->sbNumberOfMoves->value();
0154     tc.increment = ui->sbTimeIncrement->value();
0155     Manager::self()->setTimeControl(NoColor, tc);
0156 
0157     QList<EngineConfiguration> configs;
0158     for (const QString& s : Settings::engineConfigurations())
0159         configs << EngineConfiguration(s);
0160 
0161     Protocol* p1 = nullptr;
0162     Protocol* p2 = nullptr;
0163     Color c1 = NoColor;
0164     if(ui->rbPlayer1Human->isChecked())
0165         p1 = new LocalProtocol;
0166     else {
0167         if(ui->cbPlayer2Engine->currentIndex() != -1) {
0168             EngineConfiguration c = configs [ ui->cbPlayer1Engine->currentIndex() ];
0169             if(c.iface == EngineConfiguration::XBoard)
0170                 p1 = new XBoardProtocol;
0171             else
0172                 p1 = new UciProtocol;
0173             p1->setAttribute("program", c.commandLine);
0174             p1->setPlayerName(c.name);
0175         } else {
0176             //This happens when the user didn't select any game engine, so we are
0177             //creating below a dummy XBoardProtocol object with an invalid command
0178             //path (i.e. an empty QString), thus this object will send a signal
0179             //error which will end up in a notification of the error to the user,
0180             //raising a KMessageBox error or so.
0181             p1 = new XBoardProtocol;
0182             p1->setAttribute("program", QString());
0183         }
0184     }
0185 
0186     if(ui->colorWhite->isChecked())
0187         c1 = White;
0188     else if(ui->colorBlack->isChecked())
0189         c1 = Black;
0190 
0191     if(ui->rbPlayer2Human->isChecked())
0192         p2 = new LocalProtocol;
0193     else if(ui->rbPlayer2Engine->isChecked()) {
0194         if(ui->cbPlayer2Engine->currentIndex() != -1) {
0195             EngineConfiguration c = configs [ ui->cbPlayer2Engine->currentIndex() ];
0196             if(c.iface == EngineConfiguration::XBoard)
0197                 p2 = new XBoardProtocol;
0198             else
0199                 p2 = new UciProtocol;
0200             p2->setAttribute("program", c.commandLine);
0201             p2->setPlayerName(c.name);
0202         } else {
0203             //This happens when the user didn't select any game engine, so we are
0204             //creating below a dummy XBoardProtocol object with an invalid command
0205             //path (i.e. an empty QString), thus this object will send a signal
0206             //error which will end up in a notification of the error to the user,
0207             //raising a KMessageBox error or so.
0208             p2 = new XBoardProtocol;
0209             p2->setAttribute("program", QString());
0210         }
0211     } else {
0212         p2 = new FicsProtocol;
0213         p2->setAttribute("server", ui->cbPlayer2Server->currentText());
0214     }
0215     if(c1 == NoColor) {
0216         c1 = (QRandomGenerator::global()->bounded(2) == 1) ? White : Black;
0217     }
0218     // Color-changing by the FICS protocol happens later, so it doesn't matter what we do here.
0219     Protocol::setWhiteProtocol(c1 == White ? p1 : p2);
0220     Protocol::setBlackProtocol(c1 == White ? p2 : p1);
0221 }
0222 
0223 void GameDialog::save() {
0224     Settings::EnumPlayer1Protocol::type p1;
0225     if(ui->rbPlayer1Human->isChecked())
0226         p1 = Settings::EnumPlayer1Protocol::Local;
0227     else {
0228         p1 = Settings::EnumPlayer1Protocol::XBoard;
0229         Settings::setPlayer1Program(ui->cbPlayer1Engine->currentText());
0230     }
0231     Settings::setPlayer1Protocol(p1);
0232 
0233     Settings::EnumPlayer2Protocol::type p2;
0234     if(ui->rbPlayer2Human->isChecked())
0235         p2 = Settings::EnumPlayer2Protocol::Local;
0236     else if(ui->rbPlayer2Engine->isChecked()) {
0237         p2 = Settings::EnumPlayer2Protocol::XBoard;
0238         Settings::setPlayer2Program(ui->cbPlayer2Engine->currentText());
0239     } else {
0240         p2 = Settings::EnumPlayer2Protocol::Fics;
0241         Settings::setServers(ui->cbPlayer2Server->historyItems());
0242         Settings::setCurrentServer(ui->cbPlayer2Server->currentText());
0243     }
0244     Settings::setPlayer2Protocol(p2);
0245 
0246     Settings::EnumColor::type selectedColor = Settings::EnumColor::NoColor;
0247     if(ui->colorWhite->isChecked())
0248         selectedColor = Settings::EnumColor::White;
0249     else if(ui->colorBlack->isChecked())
0250         selectedColor = Settings::EnumColor::Black;
0251     Settings::setColor(selectedColor);
0252 
0253     bool timeLimitEnabled = ui->cbTimeControl->isChecked();
0254     Settings::setTimeEnabled(timeLimitEnabled);
0255     if(timeLimitEnabled) {
0256         Settings::setTimeLimit(ui->sbTimeLimit->value());
0257         Settings::setTimeIncrement(ui->sbTimeIncrement->value());
0258         if(p2 != Settings::EnumPlayer2Protocol::Fics)
0259             Settings::setNumberOfMoves(ui->sbNumberOfMoves->value());
0260     }
0261 
0262     Settings::self()->save();
0263 }
0264 
0265 void GameDialog::updateTimeEdits() {
0266     ui->sbTimeLimit->setSuffix(i18np(" minute", " minutes", ui->sbTimeLimit->value()));
0267     ui->sbTimeIncrement->setSuffix(i18np(" second", " seconds", ui->sbTimeIncrement->value()));
0268     ui->sbNumberOfMoves->setSuffix(i18np(" move", " moves", ui->sbNumberOfMoves->value()));
0269 }
0270 
0271 void GameDialog::player1SettingsChanged() {
0272     ui->cbPlayer1Engine->setEnabled(ui->rbPlayer1Engine->isChecked());
0273     ui->pbPlayer1Engine->setEnabled(ui->rbPlayer1Engine->isChecked());
0274     checkOkButton();
0275 }
0276 
0277 void GameDialog::player2SettingsChanged() {
0278     ui->cbPlayer2Engine->setEnabled(ui->rbPlayer2Engine->isChecked());
0279     ui->pbPlayer2Engine->setEnabled(ui->rbPlayer2Engine->isChecked());
0280     ui->cbPlayer2Server->setEnabled(ui->rbPlayer2Server->isChecked());
0281 
0282     bool server = ui->rbPlayer2Server->isChecked();
0283     if(server)
0284         ui->cbTimeControl->setChecked(true);
0285     ui->cbTimeControl->setEnabled(!server);
0286     ui->lNumberOfMoves->setVisible(!server);
0287     ui->sbNumberOfMoves->setVisible(!server);
0288     checkOkButton();
0289 }
0290 
0291 void GameDialog::timeControlChanged() {
0292     bool b = ui->cbTimeControl->isChecked();
0293     ui->sbNumberOfMoves->setEnabled(b);
0294     ui->sbTimeLimit->setEnabled(b);
0295     ui->sbTimeIncrement->setEnabled(b);
0296 }
0297 
0298 void GameDialog::networkStatusChanged(bool isOnline) {
0299     qCDebug(LOG_KNIGHTS) << isOnline;
0300     if(!isOnline && ui->rbPlayer2Server->isChecked())
0301         ui->rbPlayer2Engine->setChecked(true);
0302     ui->rbPlayer2Server->setEnabled(isOnline);
0303 }
0304 
0305 void GameDialog::showEngineConfigDialog() {
0306     QPointer<QDialog> dlg = new QDialog(this);
0307     dlg->setWindowTitle(i18nc("@title:window", "Chess Engines"));
0308 
0309     auto bBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0310     connect( bBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept );
0311     connect( bBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject );
0312 
0313     QVBoxLayout *layout = new QVBoxLayout;
0314     EngineSettings* ecd = new EngineSettings(dlg);
0315     layout->addWidget(ecd);
0316     layout->addWidget(bBox);
0317     dlg->setLayout(layout);
0318     dlg->setFocus();
0319 
0320     if (dlg->exec() == QDialog::Accepted) {
0321         ecd->save();
0322         loadEngines();
0323     }
0324     delete dlg;
0325 }
0326 
0327 void GameDialog::loadEngines() {
0328     QStringList programs;
0329     QList<EngineConfiguration> configs;
0330     for (const QString& s : Settings::engineConfigurations()) {
0331         const  QStringList l = s.split(QLatin1Char(':'), Qt::SkipEmptyParts);
0332         EngineConfiguration e = EngineConfiguration(s);
0333         if(!e.name.isEmpty()) {
0334             programs << e.name;
0335             configs << e;
0336         }
0337     }
0338 
0339     ui->cbPlayer1Engine->clear();
0340     ui->cbPlayer1Engine->addItems(programs);
0341     if(!Settings::player1Program().isEmpty())
0342         ui->cbPlayer1Engine->setCurrentItem(Settings::player1Program(), false);
0343     else
0344         ui->cbPlayer1Engine->setCurrentIndex(0);
0345 
0346     ui->cbPlayer2Engine->clear();
0347     ui->cbPlayer2Engine->addItems(programs);
0348     if(!Settings::player2Program().isEmpty())
0349         ui->cbPlayer2Engine->setCurrentItem(Settings::player2Program(), false);
0350     else
0351         ui->cbPlayer2Engine->setCurrentIndex(0);
0352 
0353     checkOkButton();
0354 }
0355 
0356 void GameDialog::checkOkButton() {
0357     if (ui->rbPlayer1Engine->isChecked() && ui->cbPlayer1Engine->currentIndex() == -1) {
0358         okButton->setEnabled(false);
0359         okButton->setToolTip(i18n("Select a chess engine for the first player."));
0360         return;
0361     }
0362 
0363     if (ui->rbPlayer2Engine->isChecked() && ui->cbPlayer2Engine->currentIndex() == -1) {
0364         okButton->setEnabled(false);
0365         okButton->setToolTip(i18n("Select a chess engine for the second player."));
0366         return;
0367     }
0368 
0369     okButton->setEnabled(true);
0370     okButton->setToolTip(i18n("Start the game."));
0371 }
0372 
0373 #include "moc_gamedialog.cpp"