Warning, file /games/kigo/src/gui/mainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2008 Sascha Peilicke <sasch.pe@gmx.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "mainwindow.h" 0008 #include "game/game.h" 0009 #include "game/score.h" 0010 #include "gui/config/generalconfig.h" 0011 #include "gui/graphicsview/gamescene.h" 0012 #include "gui/graphicsview/gameview.h" 0013 #include "gui/graphicsview/themerenderer.h" 0014 #include "gui/widgets/errorwidget.h" 0015 #include "gui/widgets/gamewidget.h" 0016 #include "gui/widgets/setupwidget.h" 0017 #include "preferences.h" 0018 0019 #include <KGameThemeSelector> 0020 #include <KGameStandardAction> 0021 0022 #include <QAction> 0023 #include <KActionCollection> 0024 #include <KConfigDialog> 0025 #include <KToggleAction> 0026 #include <QIcon> 0027 0028 #include <QDockWidget> 0029 #include <QFileDialog> 0030 #include <QTimer> 0031 #include <QUndoView> 0032 0033 namespace Kigo { 0034 0035 MainWindow::MainWindow(const QString &fileName, QWidget *parent) 0036 : KXmlGuiWindow(parent), m_game(new Game(this)) 0037 , m_gameScene(new GameScene(m_game)), m_gameView(new GameView(m_gameScene)) 0038 { 0039 setCentralWidget(m_gameView); 0040 0041 setupActions(); 0042 setupDockWindows(); 0043 setupGUI(QSize(800, 700), KXmlGuiWindow::ToolBar | KXmlGuiWindow::Keys | 0044 KXmlGuiWindow::Save | KXmlGuiWindow::Create); 0045 0046 connect(m_game, &Game::waiting, this, &MainWindow::showBusy); 0047 connect(m_game, &Game::consecutivePassMovesPlayed, this, &MainWindow::showFinishGameAction); 0048 connect(m_game, &Game::resigned, this, &MainWindow::finishGame); 0049 connect(m_game, &Game::passMovePlayed, this, &MainWindow::passMovePlayed); 0050 0051 if (isBackendWorking()) { 0052 if (!loadGame(fileName)) { 0053 newGame(); 0054 } 0055 } else { 0056 backendError(); 0057 } 0058 } 0059 0060 void MainWindow::newGame() 0061 { 0062 m_game->start(Preferences::engineCommand()); 0063 0064 m_gameScene->showTerritory(false); 0065 m_gameView->setInteractive(false); 0066 0067 m_newGameAction->setEnabled(true); 0068 m_loadGameAction->setEnabled(true); 0069 m_saveAction->setEnabled(false); 0070 m_startGameAction->setEnabled(true); 0071 m_finishGameAction->setEnabled(false); 0072 0073 m_undoMoveAction->setEnabled(false); 0074 m_redoMoveAction->setEnabled(false); 0075 m_passMoveAction->setEnabled(false); 0076 m_hintAction->setEnabled(false); 0077 0078 disconnect(m_game, &Game::canRedoChanged, m_redoMoveAction, &QAction::setEnabled); 0079 disconnect(m_game, &Game::canUndoChanged, m_undoMoveAction, &QAction::setEnabled); 0080 disconnect(m_game, &Game::currentPlayerChanged, this, &MainWindow::playerChanged); 0081 0082 m_gameDock->setVisible(false); 0083 m_gameDock->toggleViewAction()->setEnabled(false); 0084 m_movesDock->setVisible(false); 0085 m_movesDock->toggleViewAction()->setEnabled(false); 0086 m_setupDock->setVisible(true); 0087 m_errorDock->setVisible(false); 0088 0089 m_setupWidget->newGame(); 0090 QTimer::singleShot(0, this, [this] { m_gameScene->showMessage(i18n("Set up a new game...")); } ); 0091 } 0092 0093 void MainWindow::loadGame() 0094 { 0095 const QString folderName = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("games"), QStandardPaths::LocateDirectory); 0096 const QString fileName = QFileDialog::getOpenFileName(this, QString(), folderName, i18n("Kigo Game Files (*.sgf)")); 0097 if (!fileName.isEmpty()) { 0098 loadGame(fileName); 0099 } 0100 } 0101 0102 bool MainWindow::loadGame(const QString &fileName) 0103 { 0104 if (!fileName.isEmpty() && QFile(fileName).exists()) { 0105 m_game->start(Preferences::engineCommand()); 0106 0107 m_gameScene->showTerritory(false); 0108 m_gameView->setInteractive(false); 0109 0110 m_newGameAction->setEnabled(true); 0111 m_loadGameAction->setEnabled(true); 0112 m_saveAction->setEnabled(false); 0113 m_startGameAction->setEnabled(true); 0114 m_finishGameAction->setEnabled(false); 0115 0116 m_undoMoveAction->setEnabled(false); 0117 m_redoMoveAction->setEnabled(false); 0118 m_passMoveAction->setEnabled(false); 0119 m_hintAction->setEnabled(false); 0120 0121 disconnect(m_game, &Game::canRedoChanged, m_redoMoveAction, &QAction::setEnabled); 0122 disconnect(m_game, &Game::canUndoChanged, m_undoMoveAction, &QAction::setEnabled); 0123 disconnect(m_game, &Game::currentPlayerChanged, this, &MainWindow::playerChanged); 0124 0125 m_gameDock->setVisible(false); 0126 m_gameDock->toggleViewAction()->setEnabled(false); 0127 m_movesDock->setVisible(false); 0128 m_movesDock->toggleViewAction()->setEnabled(false); 0129 m_setupDock->setVisible(true); 0130 m_errorDock->setVisible(false); 0131 0132 m_setupWidget->loadedGame(fileName); 0133 m_gameScene->showMessage(i18n("Set up a loaded game...")); 0134 return true; 0135 } else { 0136 m_gameScene->showMessage(i18n("Unable to load game...")); 0137 //Note: New game implied here 0138 return false; 0139 } 0140 } 0141 0142 void MainWindow::backendError() 0143 { 0144 m_gameView->setInteractive(false); 0145 0146 m_newGameAction->setEnabled(false); 0147 m_loadGameAction->setEnabled(false); 0148 m_saveAction->setEnabled(false); 0149 m_startGameAction->setEnabled(false); 0150 m_finishGameAction->setEnabled(false); 0151 0152 m_undoMoveAction->setEnabled(false); 0153 m_redoMoveAction->setEnabled(false); 0154 m_passMoveAction->setEnabled(false); 0155 m_hintAction->setEnabled(false); 0156 0157 disconnect(m_game, &Game::canRedoChanged, m_redoMoveAction, &QAction::setEnabled); 0158 disconnect(m_game, &Game::canUndoChanged, m_undoMoveAction, &QAction::setEnabled); 0159 disconnect(m_game, &Game::currentPlayerChanged, this, &MainWindow::playerChanged); 0160 m_gameDock->setVisible(false); 0161 m_gameDock->toggleViewAction()->setEnabled(false); 0162 m_movesDock->setVisible(false); 0163 m_movesDock->toggleViewAction()->setEnabled(false); 0164 m_setupDock->setVisible(false); 0165 m_errorDock->setVisible(true); 0166 } 0167 0168 void MainWindow::saveGame() 0169 { 0170 const QString fileName = QFileDialog::getSaveFileName(this, QString(), QStandardPaths::writableLocation(QStandardPaths::HomeLocation), i18n("Kigo Game Files (*.sgf)")); 0171 0172 if (!fileName.isEmpty()) { 0173 if (m_game->save(fileName)) { 0174 m_gameScene->showMessage(i18n("Game saved...")); 0175 } else { 0176 m_gameScene->showMessage(i18n("Unable to save game.")); 0177 } 0178 } 0179 } 0180 0181 void MainWindow::startGame() 0182 { 0183 m_saveAction->setEnabled(true); 0184 m_startGameAction->setEnabled(false); 0185 m_finishGameAction->setEnabled(false); 0186 0187 m_setupWidget->commit(); 0188 0189 //Decide on players how to display the UI 0190 if (m_game->whitePlayer().isHuman() || m_game->blackPlayer().isHuman()) { 0191 connect(m_game, &Game::canRedoChanged, m_redoMoveAction, &QAction::setEnabled); 0192 connect(m_game, &Game::canUndoChanged, m_undoMoveAction, &QAction::setEnabled); 0193 0194 m_passMoveAction->setEnabled(true); 0195 m_hintAction->setEnabled(true); 0196 0197 m_gameView->setInteractive(true); 0198 m_undoView->setEnabled(true); 0199 0200 m_gameScene->showPlacementMarker(true); 0201 } else { 0202 m_passMoveAction->setEnabled(false); 0203 m_hintAction->setEnabled(false); 0204 0205 m_gameView->setInteractive(false); 0206 m_undoView->setEnabled(false); 0207 0208 m_gameScene->showPlacementMarker(false); 0209 } 0210 0211 connect(m_game, &Game::currentPlayerChanged, this, &MainWindow::playerChanged); 0212 // Trigger the slot once to make a move if the starting player 0213 // (black) is a computer player. 0214 playerChanged(); 0215 0216 m_setupDock->setVisible(false); 0217 m_errorDock->setVisible(false); 0218 m_gameDock->setVisible(true); 0219 m_gameDock->toggleViewAction()->setEnabled(true); 0220 m_movesDock->setVisible(true); 0221 m_movesDock->toggleViewAction()->setEnabled(true); 0222 0223 m_gameScene->showMessage(i18n("Game started...")); 0224 0225 //TODO: Fix undo view clicking somehow 0226 m_undoView->setEnabled(false); 0227 } 0228 0229 void MainWindow::finishGame() 0230 { 0231 m_gameView->setInteractive(false); 0232 m_gameScene->showHint(false); 0233 m_gameScene->showTerritory(true); 0234 0235 m_undoMoveAction->setEnabled(false); 0236 m_redoMoveAction->setEnabled(false); 0237 m_passMoveAction->setEnabled(false); 0238 m_hintAction->setEnabled(false); 0239 m_startGameAction->setEnabled(false); 0240 m_finishGameAction->setEnabled(false); 0241 0242 m_undoView->setEnabled(false); 0243 0244 //qCDebug(KIGO_LOG) << "Fetching final score from engine ..."; 0245 Score score = m_game->estimatedScore(); 0246 QString name; 0247 if (score.color() == QLatin1Char('W')) { 0248 name = i18n("%1 (White)", m_game->whitePlayer().name()); 0249 } else { 0250 name = i18n("%1 (Black)", m_game->blackPlayer().name()); 0251 } 0252 // Show a score message that stays visible until the next 0253 // popup message arrives 0254 if (score.upperBound() == score.lowerBound()) { 0255 m_gameScene->showMessage(i18n("%1 won with a score of %2.", 0256 name, score.score()), 0); 0257 } else { 0258 m_gameScene->showMessage(i18n("%1 won with a score of %2 (bounds: %3 and %4).", 0259 name, score.score(), score.upperBound(), score.lowerBound()), 0); 0260 } 0261 } 0262 0263 void MainWindow::undo() 0264 { 0265 if (m_game->undoMove()) { 0266 m_gameScene->showMessage(i18n("Undone move")); 0267 m_gameScene->showHint(false); 0268 } 0269 } 0270 0271 void MainWindow::redo() 0272 { 0273 if (m_game->redoMove()) { 0274 m_gameScene->showMessage(i18n("Redone move")); 0275 m_gameScene->showHint(false); 0276 } 0277 } 0278 0279 void MainWindow::pass() 0280 { 0281 if (m_game->playMove(m_game->currentPlayer())) { // E.g. Pass move 0282 m_gameScene->showHint(false); 0283 } 0284 } 0285 0286 void MainWindow::hint() 0287 { 0288 m_gameScene->showHint(true); 0289 } 0290 0291 void MainWindow::showPreferences() 0292 { 0293 if (KConfigDialog::showDialog(QStringLiteral("settings"))) { 0294 return; 0295 } 0296 0297 KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), Preferences::self()); 0298 dialog->addPage(new GeneralConfig(), i18n("General"), QStringLiteral("preferences-other")); 0299 dialog->addPage(new KGameThemeSelector(ThemeRenderer::self()->themeProvider(), KGameThemeSelector::EnableNewStuffDownload), 0300 i18n("Themes"), QStringLiteral("games-config-theme")); 0301 if (QPushButton *restore = dialog->button(QDialogButtonBox::RestoreDefaults)) { 0302 restore->hide(); 0303 } 0304 connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::applyPreferences); 0305 dialog->show(); 0306 } 0307 0308 void MainWindow::applyPreferences() 0309 { 0310 //qCDebug(KIGO_LOG) << "Update settings based on changed configuration..."; 0311 m_gameScene->showLabels(Preferences::showBoardLabels()); 0312 0313 if (!isBackendWorking()) { 0314 backendError(); 0315 } else if (m_game->engineCommand() != Preferences::engineCommand()) { 0316 // Restart the Go game if the game command was changed by the user. 0317 m_gameScene->showMessage(i18n("Backend was changed, restart necessary...")); 0318 newGame(); 0319 } 0320 } 0321 0322 void MainWindow::showBusy(bool busy) 0323 { 0324 //Decide on players how to display the UI 0325 if (m_game->whitePlayer().isHuman() || m_game->blackPlayer().isHuman()) { 0326 if (busy) { 0327 m_undoMoveAction->setDisabled(true); 0328 m_redoMoveAction->setDisabled(true); 0329 } else { 0330 // Only re-enable undo/redo if it actually makes sense 0331 if (m_game->canUndo()) { 0332 m_undoMoveAction->setDisabled(false); 0333 } 0334 if (m_game->canRedo()) { 0335 m_redoMoveAction->setDisabled(false); 0336 } 0337 } 0338 m_passMoveAction->setDisabled(busy); 0339 m_hintAction->setDisabled(busy); 0340 m_undoView->setEnabled(false); 0341 } 0342 0343 m_gameView->setInteractive(!busy); 0344 } 0345 0346 void MainWindow::showFinishGameAction() 0347 { 0348 m_finishGameAction->setEnabled(true); 0349 } 0350 0351 void MainWindow::playerChanged() 0352 { 0353 if (!m_game->currentPlayer().isHuman() && !m_game->isFinished()) { 0354 QTimer::singleShot(200, this, &MainWindow::generateMove); 0355 } 0356 } 0357 0358 void MainWindow::generateMove() 0359 { 0360 m_game->generateMove(m_game->currentPlayer()); 0361 } 0362 0363 void MainWindow::passMovePlayed(const Player &player) 0364 { 0365 if (player.isComputer()) { 0366 if (player.isWhite()) { 0367 m_gameScene->showMessage(i18n("White passed")); 0368 } else { 0369 m_gameScene->showMessage(i18n("Black passed")); 0370 } 0371 } 0372 } 0373 0374 void MainWindow::setupActions() 0375 { 0376 // Game menu 0377 m_newGameAction = KGameStandardAction::gameNew(this, &MainWindow::newGame, actionCollection()); 0378 m_loadGameAction = KGameStandardAction::load( 0379 this, qOverload<>(&MainWindow::loadGame), actionCollection()); 0380 m_getMoreGamesAction = new KNSWidgets::Action(i18nc("@action", "Get More Games..." ), QStringLiteral("kigo-games.knsrc"), actionCollection()); 0381 KActionCollection::setDefaultShortcut(m_getMoreGamesAction, Qt::CTRL | Qt::Key_G); 0382 m_getMoreGamesAction->setToolTip(i18nc("@action", "Get More Games...")); 0383 actionCollection()->addAction( QStringLiteral( "get_more_games" ), m_getMoreGamesAction); 0384 m_saveAction = KGameStandardAction::save(this, &MainWindow::saveGame, actionCollection()); 0385 KGameStandardAction::quit(this, &QWidget::close, actionCollection()); 0386 0387 m_startGameAction = new QAction(QIcon::fromTheme( QStringLiteral( "media-playback-start") ), i18nc("@action", "Start Game" ), this); 0388 KActionCollection::setDefaultShortcut(m_startGameAction, Qt::Key_S); 0389 m_startGameAction->setToolTip(i18nc("@action", "Start Game")); 0390 connect(m_startGameAction, &QAction::triggered, this, &MainWindow::startGame); 0391 actionCollection()->addAction( QStringLiteral( "game_start" ), m_startGameAction); 0392 0393 m_finishGameAction = new QAction(QIcon::fromTheme( QStringLiteral( "media-playback-stop") ), i18nc("@action", "Finish Game" ), this); 0394 KActionCollection::setDefaultShortcut(m_finishGameAction, Qt::Key_F); 0395 m_finishGameAction->setToolTip(i18nc("@action", "Finish Game")); 0396 connect(m_finishGameAction, &QAction::triggered, this, &MainWindow::finishGame); 0397 actionCollection()->addAction( QStringLiteral( "game_finish" ), m_finishGameAction); 0398 0399 // Move menu 0400 m_undoMoveAction = KGameStandardAction::undo(this, &MainWindow::undo, actionCollection()); 0401 m_redoMoveAction = KGameStandardAction::redo(this, &MainWindow::redo, actionCollection()); 0402 m_passMoveAction = KGameStandardAction::endTurn(this, &MainWindow::pass, actionCollection()); 0403 m_passMoveAction->setText(i18nc("@action:inmenu Move", "Pass Move")); 0404 KActionCollection::setDefaultShortcut(m_passMoveAction, Qt::Key_P); 0405 m_hintAction = KGameStandardAction::hint(this, &MainWindow::hint, actionCollection()); 0406 0407 // View menu 0408 m_moveNumbersAction = new KToggleAction(QIcon::fromTheme( QStringLiteral( "pin") ), i18nc("@action:inmenu View", "Show Move &Numbers" ), this); 0409 KActionCollection::setDefaultShortcut(m_moveNumbersAction, Qt::Key_N); 0410 m_moveNumbersAction->setChecked(Preferences::showMoveNumbers()); 0411 connect(m_moveNumbersAction, &KToggleAction::toggled, m_gameScene, &GameScene::showMoveNumbers); 0412 actionCollection()->addAction( QStringLiteral( "move_numbers" ), m_moveNumbersAction); 0413 0414 // Settings menu 0415 KStandardAction::preferences(this, &MainWindow::showPreferences, actionCollection()); 0416 } 0417 0418 void MainWindow::setupDockWindows() 0419 { 0420 // Setup dock 0421 m_setupDock = new QDockWidget(i18nc("@title:window", "Game Setup"), this); 0422 m_setupDock->setObjectName( QStringLiteral("setupDock" )); 0423 m_setupDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); 0424 m_setupWidget = new SetupWidget(m_game, this); 0425 m_setupDock->setWidget(m_setupWidget); 0426 connect(m_setupWidget, &SetupWidget::startClicked, this, &MainWindow::startGame); 0427 addDockWidget(Qt::RightDockWidgetArea, m_setupDock); 0428 0429 // Game dock 0430 m_gameDock = new QDockWidget(i18nc("@title:window", "Information"), this); 0431 m_gameDock->setObjectName( QStringLiteral("gameDock" )); 0432 m_gameDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); 0433 auto gameWidget = new GameWidget(m_game, this); 0434 connect(gameWidget, &GameWidget::finishClicked, this, &MainWindow::finishGame); 0435 m_gameDock->setWidget(gameWidget); 0436 m_gameDock->toggleViewAction()->setText(i18nc("@title:window", "Information")); 0437 KActionCollection::setDefaultShortcut(m_gameDock->toggleViewAction(), Qt::Key_I); 0438 actionCollection()->addAction( QStringLiteral( "show_game_panel" ), m_gameDock->toggleViewAction()); 0439 addDockWidget(Qt::RightDockWidgetArea, m_gameDock); 0440 0441 // Move history dock 0442 m_movesDock = new QDockWidget(i18nc("@title:window", "Moves"), this); 0443 m_movesDock->setObjectName( QStringLiteral("movesDock" )); 0444 m_undoView = new QUndoView(m_game->undoStack()); 0445 m_undoView->setEmptyLabel(i18n("No move")); 0446 m_undoView->setAlternatingRowColors(true); 0447 m_undoView->setFocusPolicy(Qt::NoFocus); 0448 m_undoView->setEditTriggers(QAbstractItemView::NoEditTriggers); 0449 m_undoView->setSelectionMode(QAbstractItemView::NoSelection); 0450 m_movesDock->setWidget(m_undoView); 0451 m_movesDock->toggleViewAction()->setText(i18nc("@title:window", "Moves")); 0452 KActionCollection::setDefaultShortcut(m_movesDock->toggleViewAction(), Qt::Key_M); 0453 actionCollection()->addAction( QStringLiteral( "show_moves_panel" ), m_movesDock->toggleViewAction()); 0454 addDockWidget(Qt::RightDockWidgetArea, m_movesDock); 0455 0456 m_errorDock = new QDockWidget(i18nc("@title:window", "Error"), this); 0457 m_errorDock->setObjectName( QStringLiteral("errorDock" )); 0458 m_errorDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable); 0459 auto errorWidget = new ErrorWidget(this); 0460 connect(errorWidget, &ErrorWidget::configureClicked, this, &MainWindow::showPreferences); 0461 m_errorDock->setWidget(errorWidget); 0462 addDockWidget(Qt::BottomDockWidgetArea, m_errorDock); 0463 } 0464 0465 bool MainWindow::isBackendWorking() 0466 { 0467 Game game; 0468 return game.start(Preferences::engineCommand()); 0469 } 0470 0471 } // End of namespace Kigo 0472 0473 #include "moc_mainwindow.cpp"