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"