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

0001 /***************************************************************************
0002     File                 : knights.cpp
0003     Project              : Knights
0004     Description          : Main window of the application
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2016-1018 Alexander Semke (alexander.semke@web.de)
0007     SPDX-FileCopyrightText: 2010-2012 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 "knights.h"
0028 #include "proto/localprotocol.h"
0029 #include "gamemanager.h"
0030 #include "knightsview.h"
0031 #include "knightsdebug.h"
0032 #include "settings.h"
0033 #include "gamedialog.h"
0034 #include "clockwidget.h"
0035 #include "historywidget.h"
0036 #include "enginesettings.h"
0037 
0038 // KDEGames
0039 #include <KGameThemeSelector>
0040 #include <KGameStandardAction>
0041 
0042 #include <KConfigDialog>
0043 #include <KActionCollection>
0044 #include <KStandardAction>
0045 #include <KToggleAction>
0046 #include <KLocalizedString>
0047 #include <KMessageBox>
0048 
0049 #include <QCloseEvent>
0050 #include <QDockWidget>
0051 #include <QFileDialog>
0052 #include <QTimer>
0053 #include <QStatusBar>
0054 
0055 const char* DontAskDiscard = "dontAskInternal";
0056 
0057 using namespace Knights;
0058 
0059 /**
0060 * This class serves as the main window for Knights.  It handles the
0061 * menus, toolbars, and status bars.
0062 *
0063 * @short Main window class
0064 * @author %{AUTHOR} <%{EMAIL}>
0065 * @version %{VERSION}
0066 */
0067 MainWindow::MainWindow() : KXmlGuiWindow(),
0068     m_view(new KnightsView(this)),
0069     m_themeProvider(new KGameThemeProvider("Theme", this)) {
0070     // accept dnd
0071     setAcceptDrops(true);
0072 
0073     // tell the KXmlGuiWindow that this is indeed the main widget
0074     setCentralWidget(m_view);
0075 
0076     // initial creation/setup of the docks
0077     setDockNestingEnabled(true);
0078     setupDocks();
0079 
0080     // setup actions and GUI
0081     setupActions();
0082     setupGUI();
0083 
0084     //protocol features
0085     m_protocolFeatures = {
0086         {KGameStandardAction::name(KGameStandardAction::Pause), Protocol::Pause},
0087         {QStringLiteral("propose_draw"), Protocol::Draw},
0088         {QStringLiteral("adjourn"), Protocol::Adjourn},
0089         {QStringLiteral("resign"), Protocol::Resign},
0090         {QStringLiteral("abort"), Protocol::Abort},
0091     };
0092 
0093     // setup difficulty management
0094     connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, Manager::self(), &Manager::levelChanged);
0095     KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(0, "custom", i18n("Custom"), false));
0096     KGameDifficulty::global()->addStandardLevelRange(KGameDifficultyLevel::VeryEasy, KGameDifficultyLevel::VeryHard, KGameDifficultyLevel::Medium);
0097     KGameDifficultyGUI::init(this);
0098     KGameDifficulty::global()->setEditable(false);
0099 
0100     // make all the docks invisible.
0101     // Show required docks after the game protocols are selected
0102     m_clockDock->hide();
0103     m_bconsoleDock->hide();
0104     m_wconsoleDock->hide();
0105     m_chatDock->hide();
0106     m_historyDock->hide();
0107 
0108     connect(Manager::self(), &Manager::initComplete, this, &MainWindow::protocolInitSuccesful);
0109     connect(Manager::self(), &Manager::playerNameChanged, this, &MainWindow::updateCaption);
0110     connect(Manager::self(), &Manager::activePlayerChanged, this, &MainWindow::activePlayerChanged);
0111     connect(Manager::self(), &Manager::pieceMoved, this, &MainWindow::gameChanged);
0112     connect(Manager::self(), &Manager::winnerNotify, this, &MainWindow::gameOver);
0113     connect(qApp, &QGuiApplication::lastWindowClosed, this, &MainWindow::exitKnights);
0114 
0115     m_themeProvider->discoverThemes(QStringLiteral("themes"));
0116     m_view->drawBoard(m_themeProvider);
0117 }
0118 
0119 void MainWindow::activePlayerChanged() {
0120     statusBar()->clearMessage();
0121     Knights::Color color = Manager::self()->activePlayer();
0122 
0123     //show the notification in the status bar, delay it by one second
0124     QTimer::singleShot(1000, this, [=] () {
0125         //the current player has changed within one second,
0126         //don't need to show the notification
0127         if (Manager::self()->activePlayer() != color)
0128             return;
0129 
0130         QString name;
0131         if (color == White)
0132             name = Protocol::white()->playerName();
0133         else
0134             name = Protocol::black()->playerName();
0135         statusBar()->showMessage(i18n("%1 is thinking...", name));
0136     });
0137 
0138 }
0139 
0140 void MainWindow::setupDocks() {
0141     // clock dock
0142     m_clockDock = new QDockWidget(i18n("Clock"), this);
0143     m_clockDock->setObjectName(QStringLiteral("ClockDockWidget"));       // for QMainWindow::saveState()
0144     m_playerClock = new ClockWidget(this);
0145     m_clockDock->setWidget(m_playerClock);
0146     m_clockDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0147     connect(m_view, &KnightsView::displayedPlayerChanged, m_playerClock, &ClockWidget::setDisplayedPlayer);
0148     connect(Manager::self(), &Manager::timeChanged, m_playerClock, &ClockWidget::setCurrentTime);
0149     addDockWidget(Qt::RightDockWidgetArea, m_clockDock);
0150 
0151     // console dock for black
0152     m_bconsoleDock = new QDockWidget();
0153     m_bconsoleDock->setObjectName(QStringLiteral("BlackConsoleDockWidget"));
0154     m_bconsoleDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0155     addDockWidget(Qt::LeftDockWidgetArea, m_bconsoleDock);
0156 
0157     // console dock for white
0158     m_wconsoleDock = new QDockWidget();
0159     m_wconsoleDock->setObjectName(QStringLiteral("WhiteConsoleDockWidget"));
0160     m_wconsoleDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0161     addDockWidget(Qt::LeftDockWidgetArea, m_wconsoleDock);
0162 
0163     // chat dock
0164     m_chatDock = new QDockWidget();
0165     m_chatDock->setObjectName(QStringLiteral("ChatDockWidget"));
0166     m_chatDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0167     addDockWidget(Qt::LeftDockWidgetArea, m_chatDock);
0168 
0169     // history dock
0170     m_historyDock = new QDockWidget();
0171     m_historyDock->setObjectName(QStringLiteral("HistoryDockWidget"));
0172     m_historyDock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
0173     m_historyDock->setWindowTitle(i18nc("@title:window Title of the list of all the moves made in the game", "Move History"));
0174     m_historyDock->setWidget(new HistoryWidget);
0175     addDockWidget(Qt::LeftDockWidgetArea, m_historyDock);
0176 }
0177 
0178 void MainWindow::setupActions() {
0179     KGameStandardAction::gameNew(this, &MainWindow::fileNew, actionCollection());
0180     KGameStandardAction::quit(qApp, &QApplication::closeAllWindows, actionCollection());
0181     m_pauseAction = KGameStandardAction::pause(Manager::self(), &Manager::pause, actionCollection());
0182     m_pauseAction->setEnabled(false);
0183     KStandardAction::preferences(this, &MainWindow::optionsPreferences, actionCollection());
0184 
0185     m_saveAction = KGameStandardAction::save(this, &MainWindow::fileSave, actionCollection());
0186     m_saveAction->setEnabled(false);
0187     m_saveAsAction = KGameStandardAction::saveAs(this, &MainWindow::fileSaveAs, actionCollection());
0188     m_saveAsAction->setEnabled(false);
0189     KGameStandardAction::load(this, &MainWindow::fileLoad, actionCollection());
0190 
0191     m_resignAction = actionCollection()->addAction(QStringLiteral("resign"), this, &MainWindow::resign);
0192     m_resignAction->setText(i18n("Resign"));
0193     m_resignAction->setToolTip(i18n("Admit your inevitable defeat"));
0194     m_resignAction->setIcon(QIcon::fromTheme(QStringLiteral("flag-red")));
0195     m_resignAction->setEnabled(false);
0196 
0197     m_undoAction = actionCollection()->addAction(QStringLiteral("move_undo"), this, &MainWindow::undo);
0198     m_undoAction->setText(i18n("Undo"));
0199     m_undoAction->setToolTip(i18n("Take back your last move"));
0200     m_undoAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
0201     connect(Manager::self(), &Manager::undoPossible, m_undoAction, &QAction::setEnabled);
0202     m_undoAction->setEnabled(false);
0203 
0204     m_redoAction = actionCollection()->addAction(QStringLiteral("move_redo"), this, &MainWindow::redo);
0205     m_redoAction->setText(i18n("Redo"));
0206     m_redoAction->setToolTip(i18n("Repeat your last move"));
0207     m_redoAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-redo")));
0208     connect(Manager::self(), &Manager::redoPossible, m_redoAction, &QAction::setEnabled);
0209     m_redoAction->setEnabled(false);
0210     m_redoAction->setVisible(false);
0211 
0212     m_drawAction = actionCollection()->addAction(QStringLiteral("propose_draw"), Manager::self(), &Manager::offerDraw);
0213     m_drawAction->setText(i18n("Offer &Draw"));
0214     m_drawAction->setToolTip(i18n("Offer a draw to your opponent"));
0215     m_drawAction->setIcon(QIcon::fromTheme(QStringLiteral("flag-blue")));
0216     m_drawAction->setEnabled(false);
0217 
0218     m_adjournAction = actionCollection()->addAction(QStringLiteral("adjourn"), Manager::self(), &Manager::adjourn);
0219     m_adjournAction->setText(i18n("Adjourn"));
0220     m_adjournAction->setToolTip(i18n("Continue this game at a later time"));
0221     m_adjournAction->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0222     m_adjournAction->setEnabled(false);
0223 
0224     QAction* abortAction = actionCollection()->addAction(QStringLiteral("abort"), Manager::self(), &Manager::abort);
0225     abortAction->setText(i18n("Abort"));
0226     abortAction->setToolTip(i18n("End the game immediately"));
0227     abortAction->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
0228     abortAction->setEnabled(false);
0229 
0230     KToggleAction* clockAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("clock")), i18n("Show Clock"), actionCollection());
0231     actionCollection()->addAction(QStringLiteral("show_clock"), clockAction);
0232     connect(clockAction, &KToggleAction::triggered, m_clockDock, &QDockWidget::setVisible);
0233     connect(clockAction, &KToggleAction::triggered, this, &MainWindow::setShowClockSetting);
0234     clockAction->setVisible(false);
0235 
0236     KToggleAction* historyAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-history")), i18n("Show History"), actionCollection());
0237     actionCollection()->addAction(QStringLiteral("show_history"), historyAction);
0238     connect(historyAction, &KToggleAction::triggered, m_historyDock, &QDockWidget::setVisible);
0239     connect(historyAction, &KToggleAction::triggered, this, &MainWindow::setShowHistorySetting);
0240     historyAction->setVisible(true);
0241     historyAction->setChecked(Settings::showHistory());
0242 
0243     KToggleAction* wconsoleAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Show White Console"), actionCollection());
0244     actionCollection()->addAction(QStringLiteral("show_console_white"), wconsoleAction);
0245     connect(wconsoleAction, &KToggleAction::triggered, m_wconsoleDock, &QDockWidget::setVisible);
0246     connect(wconsoleAction, &KToggleAction::triggered, this, &MainWindow::setShowConsoleSetting);
0247     wconsoleAction->setVisible(false);
0248 
0249     KToggleAction* bconsoleAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Show Black Console"), actionCollection());
0250     actionCollection()->addAction(QStringLiteral("show_console_black"), bconsoleAction);
0251     connect(bconsoleAction, &KToggleAction::triggered, m_bconsoleDock, &QDockWidget::setVisible);
0252     connect(bconsoleAction, &KToggleAction::triggered, this, &MainWindow::setShowConsoleSetting);
0253     bconsoleAction->setVisible(false);
0254 
0255     KToggleAction* chatAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("meeting-attending")), i18n("Show Chat"), actionCollection());
0256     actionCollection()->addAction(QStringLiteral("show_chat"), chatAction);
0257     connect(chatAction, &KToggleAction::triggered, m_chatDock, &QDockWidget::setVisible);
0258     connect(chatAction, &KToggleAction::triggered, this, &MainWindow::setShowChatSetting);
0259     chatAction->setVisible(false);
0260 }
0261 
0262 void MainWindow::fileNew() {
0263     if(!maybeSave())
0264         return;
0265 
0266     QPointer<GameDialog> gameNewDialog = new GameDialog(this);
0267     if(gameNewDialog->exec() == QDialog::Accepted) {
0268         Manager::self()->reset();
0269         gameNewDialog->setupProtocols();
0270         connect(Protocol::white(), &Protocol::error, this, &MainWindow::protocolError);
0271         connect(Protocol::black(), &Protocol::error, this, &MainWindow::protocolError);
0272         gameNewDialog->save();
0273 
0274         m_pauseAction->setChecked(false);
0275         Manager::self()->initialize();
0276 
0277         bool difficulty = (Protocol::white()->supportedFeatures()& Protocol::AdjustDifficulty)
0278                           || (Protocol::black()->supportedFeatures()& Protocol::AdjustDifficulty);
0279         KGameDifficulty::global()->setEditable(difficulty);
0280     }
0281     delete gameNewDialog;
0282 
0283     m_saveAction->setEnabled(false);
0284     m_saveAsAction->setEnabled(false);
0285     m_fileName = QString();
0286 }
0287 
0288 void MainWindow::fileLoad() {
0289     if(!maybeSave())
0290         return;
0291 
0292     KConfigGroup conf(KSharedConfig::openConfig(), QStringLiteral("MainWindow"));
0293     QString dir = conf.readEntry("LastOpenDir", "");
0294     const QString&  fileName = QFileDialog::getOpenFileName(this, i18n("Open File"), dir,
0295                                i18n("Portable game notation (*.pgn)"));
0296     if(fileName.isEmpty())
0297         return;
0298 
0299     Manager::self()->reset();
0300     Protocol::setWhiteProtocol(new LocalProtocol());
0301     Protocol::setBlackProtocol(new LocalProtocol());
0302 
0303     connect(Protocol::white(), &Protocol::error, this, &MainWindow::protocolError);
0304     connect(Protocol::black(), &Protocol::error, this, &MainWindow::protocolError);
0305 
0306     m_loadFileName = fileName;
0307     Manager::self()->initialize();
0308     m_saveAction->setEnabled(false);
0309 
0310     //save new "last open directory"
0311     int pos = fileName.lastIndexOf(QDir::separator());
0312     if (pos != -1) {
0313         const QString& newDir = fileName.left(pos);
0314         if (newDir != dir)
0315             conf.writeEntry("LastOpenDir", newDir);
0316     }
0317 }
0318 
0319 void MainWindow::protocolInitSuccesful() {
0320     qCDebug(LOG_KNIGHTS) << "Show Clock:" << Settings::showClock() << "Show Console:" << Settings::showConsole();
0321     updateCaption();
0322 
0323     // show clock action button if timer active
0324     // show clock dock widget if timer active and configuration file entry has visible = true
0325     bool showClock = false;
0326     if(Manager::self()->timeControlEnabled(White) || Manager::self()->timeControlEnabled(Black)) {
0327         actionCollection()->action(QStringLiteral("show_clock"))->setVisible(true);
0328         m_playerClock->setPlayerName(White, Protocol::white()->playerName());
0329         m_playerClock->setPlayerName(Black, Protocol::black()->playerName());
0330         m_playerClock->setTimeLimit(White, Manager::self()->timeLimit(White));
0331         m_playerClock->setTimeLimit(Black, Manager::self()->timeLimit(Black));
0332         showClock = Settings::showClock();
0333     }
0334     m_clockDock->setVisible(showClock);
0335     actionCollection()->action(QStringLiteral("show_clock"))->setChecked(showClock);
0336 
0337     //history dock
0338     bool showHistory = Settings::showHistory();
0339     m_historyDock->setVisible(showHistory);
0340     actionCollection()->action(QStringLiteral("show_history"))->setChecked(showHistory);
0341 
0342     if ( !(Protocol::white()->supportedFeatures() & Protocol::Undo &&
0343         Protocol::black()->supportedFeatures() & Protocol::Undo) ) {
0344         m_undoAction->setVisible(false);
0345         m_redoAction->setVisible(false);
0346     } else {
0347         m_undoAction->setVisible(true);
0348         m_redoAction->setVisible(true);
0349     }
0350 
0351     Protocol::Features f = Protocol::NoFeatures;
0352     if(Protocol::white()->isLocal() && !(Protocol::black()->isLocal()))
0353         f = Protocol::black()->supportedFeatures();
0354     else if(Protocol::black()->isLocal() && !(Protocol::white()->isLocal()))
0355         f = Protocol::white()->supportedFeatures();
0356     else if(!(Protocol::black()->isLocal()) && !(Protocol::white()->isLocal())) {
0357         // These protocol features make sense when neither player is local
0358         f = Protocol::Pause | Protocol::Adjourn | Protocol::Abort;
0359         f &= Protocol::white()->supportedFeatures();
0360         f &= Protocol::black()->supportedFeatures();
0361     }
0362 
0363     for (auto it = m_protocolFeatures.constBegin(); it != m_protocolFeatures.constEnd(); ++it)
0364         actionCollection()->action(it.key())->setEnabled(f & it.value());
0365 
0366     // show console action button if protocol allows a console
0367     // show console dock widget if protocol allows and configuration file entry has visible = true
0368     // finally, hide any dock widget not needed - in case it is still active from previous game
0369     actionCollection()->action(QStringLiteral("show_console_white"))->setVisible(false);
0370     actionCollection()->action(QStringLiteral("show_console_black"))->setVisible(false);
0371     actionCollection()->action(QStringLiteral("show_chat"))->setVisible(false);
0372     QList<Protocol::ToolWidgetData> list;
0373     list << Protocol::black()->toolWidgets();
0374     list << Protocol::white()->toolWidgets();
0375     for (const auto& data : std::as_const(list)) {
0376         switch(data.type) {
0377         case Protocol::ConsoleToolWidget:
0378             if(data.owner == White) {
0379                 m_wconsoleDock->setWindowTitle(data.title);
0380                 m_wconsoleDock->setWidget(data.widget);
0381                 actionCollection()->action(QStringLiteral("show_console_white"))->setVisible(true);
0382                 if(Settings::showConsole()) {
0383                     m_wconsoleDock->setVisible(true);
0384                     actionCollection()->action(QStringLiteral("show_console_white"))->setChecked(true);
0385                 } else {
0386                     m_wconsoleDock->setVisible(false);
0387                     actionCollection()->action(QStringLiteral("show_console_white"))->setChecked(false);
0388                 }
0389             } else {
0390                 m_bconsoleDock->setWindowTitle(data.title);
0391                 m_bconsoleDock->setWidget(data.widget);
0392                 actionCollection()->action(QStringLiteral("show_console_black"))->setVisible(true);
0393                 if(Settings::showConsole()) {
0394                     m_bconsoleDock->setVisible(true);
0395                     actionCollection()->action(QStringLiteral("show_console_black"))->setChecked(true);
0396                 } else {
0397                     m_bconsoleDock->setVisible(false);
0398                     actionCollection()->action(QStringLiteral("show_console_black"))->setChecked(false);
0399                 }
0400             }
0401             break;
0402 
0403         case Protocol::ChatToolWidget:
0404             m_chatDock->setWindowTitle(data.title);
0405             m_chatDock->setWidget(data.widget);
0406             actionCollection()->action(QStringLiteral("show_chat"))->setVisible(true);
0407             if(Settings::showChat()) {
0408                 m_chatDock->setVisible(true);
0409                 actionCollection()->action(QStringLiteral("show_chat"))->setChecked(true);
0410             } else {
0411                 m_chatDock->setVisible(false);
0412                 actionCollection()->action(QStringLiteral("show_chat"))->setChecked(false);
0413             }
0414             break;
0415 
0416         default:
0417             break;
0418         }
0419     }
0420     if(!actionCollection()->action(QStringLiteral("show_console_white"))->isVisible())
0421         m_wconsoleDock->hide();
0422     if(!actionCollection()->action(QStringLiteral("show_console_black"))->isVisible())
0423         m_bconsoleDock->hide();
0424     if(!actionCollection()->action(QStringLiteral("show_chat"))->isVisible())
0425         m_chatDock->hide();
0426 
0427     Manager::self()->startGame();
0428 
0429     if(m_loadFileName.isEmpty())
0430         m_view->setupBoard(m_themeProvider);
0431     else {
0432         int speed = Settings::animationSpeed();
0433         Settings::setAnimationSpeed(Settings::EnumAnimationSpeed::Instant);
0434         m_view->setupBoard(m_themeProvider);
0435 
0436         Manager::self()->loadGameHistoryFrom(m_loadFileName);
0437         setCaption(m_loadFileName);
0438 
0439         m_loadFileName.clear();
0440         Settings::setAnimationSpeed(speed);
0441     }
0442 }
0443 
0444 void MainWindow::protocolError(Protocol::ErrorCode errorCode, const QString& errorString) {
0445     if(errorCode != Protocol::UserCancelled)
0446         KMessageBox::error(this, errorString, Protocol::stringFromErrorCode(errorCode));
0447     Protocol::white()->deleteLater();
0448     Protocol::black()->deleteLater();
0449 }
0450 
0451 void MainWindow::optionsPreferences() {
0452     if(KConfigDialog::showDialog(QStringLiteral("settings")))
0453         return;
0454     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), Settings::self());
0455     QWidget *generalSettingsDlg = new QWidget;
0456     ui_prefs_base.setupUi(generalSettingsDlg);
0457 
0458     dialog->addPage(generalSettingsDlg, i18n("General"), QStringLiteral("games-config-options"));
0459     connect(dialog, &KConfigDialog::settingsChanged, m_view, &KnightsView::settingsChanged);
0460 
0461     EngineSettings* engineSettings = new EngineSettings(this);
0462     dialog->addPage(engineSettings, i18n("Computer Engines"), QStringLiteral("computer"));
0463     connect(dialog, &KConfigDialog::accepted, engineSettings, &EngineSettings::save);
0464 
0465     //FIXME: the accessibility page doesn't seem to be used at the moment.
0466     //Furthermore, the option "Speak opponent's moves" has to be behind HAVE_SPEECH
0467 //  QWidget* accessDlg = new QWidget;
0468 //  ui_prefs_access.setupUi(accessDlg);
0469 //  dialog->addPage(accessDlg, i18n("Accessibility"), QLatin1String("preferences-desktop-accessibility"));
0470 
0471     KGameThemeSelector* themeDlg = new KGameThemeSelector(m_themeProvider, KGameThemeSelector::EnableNewStuffDownload, dialog);
0472     themeDlg->setNewStuffConfigFileName(QStringLiteral("knights.knsrc"));
0473     dialog->addPage(themeDlg, i18n("Theme"), QStringLiteral("games-config-theme"));
0474     dialog->setAttribute(Qt::WA_DeleteOnClose);
0475 
0476     dialog->show();
0477 }
0478 
0479 void MainWindow::resign() {
0480     int rc = KMessageBox::questionTwoActions(this,
0481                         i18n("Do you really want to resign?"), i18n("Resign"),
0482                         KGuiItem(i18nc("@action:button", "Resign"), QStringLiteral("flag-red")),
0483                         KStandardGuiItem::cancel());
0484     if (rc == KMessageBox::PrimaryAction)
0485         Manager::self()->resign();
0486 }
0487 
0488 void MainWindow::undo() {
0489     if(!Protocol::white()->isLocal() && !Protocol::black()->isLocal()) {
0490         // No need to pause the game if both players are local
0491         QAction* pa = actionCollection()->action(KGameStandardAction::name(KGameStandardAction::Pause));
0492         if(pa)
0493             pa->setChecked(true);
0494     }
0495     Manager::self()->undo();
0496 }
0497 
0498 void MainWindow::redo() {
0499     Manager::self()->redo();
0500     if(!Protocol::white()->isLocal() && !Protocol::black()->isLocal()) {
0501         // No need to pause the game if both players are local
0502         QAction* pa = actionCollection()->action(KGameStandardAction::name(KGameStandardAction::Pause));
0503         if(pa && !Manager::self()->canRedo())
0504             pa->setChecked(false);
0505     }
0506 }
0507 
0508 void MainWindow::gameChanged() {
0509     m_saveAction->setEnabled(true);
0510     m_saveAsAction->setEnabled(true);
0511 }
0512 
0513 void MainWindow::gameOver(Color winner) {
0514     qCDebug(LOG_KNIGHTS) << colorName (winner);
0515 
0516     statusBar()->clearMessage();
0517 
0518     //game is over -> disable game actions
0519     m_pauseAction->setEnabled(false);
0520     m_drawAction->setEnabled(false);
0521     m_resignAction->setEnabled(false);
0522     m_adjournAction->setEnabled(false);
0523 
0524     //show the dialog to ask to save the current game or to start a new one
0525     QPointer<QDialog> dlg = new QDialog ( this );
0526     QVBoxLayout *mainLayout = new QVBoxLayout;
0527     QWidget *mainWidget = new QWidget(this);
0528     dlg->setLayout(mainLayout);
0529     dlg->setWindowTitle (i18nc("@title:window", "Game over"));
0530     mainLayout->addWidget(mainWidget);
0531 
0532     QDialogButtonBox *bBox = new QDialogButtonBox( QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Apply );
0533     QMap<QDialogButtonBox::StandardButton, QString> buttonsMap;
0534     buttonsMap[QDialogButtonBox::Ok] = KGameStandardAction::name ( KGameStandardAction::New );
0535     buttonsMap[QDialogButtonBox::Apply] = KGameStandardAction::name ( KGameStandardAction::Save );
0536 
0537     for ( auto it = buttonsMap.constBegin(); it != buttonsMap.constEnd(); ++it ) {
0538         QAction* a = actionCollection()->action ( it.value() );
0539         Q_ASSERT(a);
0540 
0541         bBox->button ( it.key() )->setText ( a->text() );
0542         bBox->button ( it.key() )->setIcon ( QIcon ( a->icon() ) );
0543         bBox->button ( it.key() )->setToolTip ( a->toolTip() );
0544     }
0545 
0546     connect( bBox, &QDialogButtonBox::accepted, dlg.data(), &QDialog::accept );
0547     connect( bBox, &QDialogButtonBox::rejected, dlg.data(), &QDialog::reject );
0548     connect( bBox->button (QDialogButtonBox::Apply), &QPushButton::clicked,
0549              static_cast<MainWindow *> (window()), &MainWindow::fileSave );
0550 
0551     QLabel* label = new QLabel(this);
0552     if ( winner == NoColor )
0553         label->setText ( i18n ( "The game ended in a draw" ) );
0554     else {
0555         QString winnerName = Protocol::byColor ( winner )->playerName();
0556         if ( winnerName == colorName(winner) ) {
0557             if ( winner == White ) {
0558                 label->setText ( i18nc("White as in the player with white pieces",
0559                                        "The game ended with a victory for <em>White</em>") );
0560             } else {
0561                 label->setText ( i18nc("Black as in the player with black pieces",
0562                                        "The game ended with a victory for <em>Black</em>") );
0563             }
0564         } else {
0565             if ( winner == White ) {
0566                 label->setText ( i18nc("Player name, then <White as in the player with white pieces",
0567                                        "The game ended with a victory for <em>%1</em>, playing White", winnerName) );
0568             } else {
0569                 label->setText ( i18nc("Player name, then Black as in the player with black pieces",
0570                                        "The game ended with a victory for <em>%1</em>, playing Black", winnerName) );
0571             }
0572         }
0573     }
0574     mainLayout->addWidget(label);
0575     mainLayout->addWidget(bBox);
0576 
0577     int rc = dlg->exec();
0578 
0579     qCDebug(LOG_KNIGHTS) << Protocol::white();
0580     qCDebug(LOG_KNIGHTS) << Protocol::black();
0581     delete dlg;
0582 
0583     if (rc == QDialog::Accepted)
0584         fileNew();
0585 }
0586 
0587 void MainWindow::setShowClockSetting(bool value) {
0588     Settings::setShowClock(value);
0589 }
0590 
0591 void MainWindow::setShowHistorySetting(bool value) {
0592     Settings::setShowHistory(value);
0593 }
0594 
0595 void MainWindow::setShowConsoleSetting() {
0596     if((actionCollection()->action(QStringLiteral("show_console_white"))->isChecked()) && (actionCollection()->action(QStringLiteral("show_console_white"))->isVisible()))
0597         Settings::setShowConsole(true);
0598     else if((actionCollection()->action(QStringLiteral("show_console_black"))->isChecked()) && (actionCollection()->action(QStringLiteral("show_console_black"))->isVisible()))
0599         Settings::setShowConsole(true);
0600     else
0601         Settings::setShowConsole(false);
0602 }
0603 
0604 void MainWindow::setShowChatSetting(bool value) {
0605     Settings::setShowChat(value);
0606 }
0607 
0608 void MainWindow::exitKnights() {
0609     //This will close the gnuchess/crafty/whatever process if it's running.
0610     Manager::self()->reset();
0611     Settings::self()->save();
0612 }
0613 
0614 void MainWindow::updateCaption() {
0615     if(Protocol::white() && Protocol::black())
0616         setCaption(i18n("%1 vs. %2", Protocol::white()->playerName(), Protocol::black()->playerName()));
0617 }
0618 
0619 bool MainWindow::maybeSave() {
0620     if(!Manager::self()->isGameActive())
0621         return true;
0622 
0623     if(!Settings::askDiscard())
0624         return true;
0625 
0626     Settings::setDontAskInternal(QString());
0627 
0628     int result = KMessageBox::warningTwoActionsCancel(QApplication::activeWindow(),
0629                  i18n("This will end your game.\nWould you like to save the move history?"),
0630                  QString(),
0631                  KStandardGuiItem::save(),
0632                  KStandardGuiItem::discard(),
0633                  KStandardGuiItem::cancel(),
0634                  QLatin1String(DontAskDiscard));
0635 
0636     KMessageBox::ButtonCode res;
0637     Settings::setAskDiscard(KMessageBox::shouldBeShownTwoActions(QLatin1String(DontAskDiscard), res));
0638 
0639     if(result == KMessageBox::PrimaryAction)
0640         fileSave();
0641 
0642     return result != KMessageBox::Cancel;
0643 }
0644 
0645 void MainWindow::fileSave() {
0646     if(m_fileName.isEmpty()) {
0647         KConfigGroup conf(KSharedConfig::openConfig(), QStringLiteral("MainWindow"));
0648         QString dir = conf.readEntry("LastOpenDir", "");
0649         m_fileName = QFileDialog::getSaveFileName(this, i18n("Save"), dir,
0650                      i18n("Portable game notation (*.pgn)"));
0651 
0652         if (m_fileName.isEmpty())// "Cancel" was clicked
0653             return;
0654 
0655         //save new "last open directory"
0656         int pos = m_fileName.lastIndexOf(QDir::separator());
0657         if (pos != -1) {
0658             const QString& newDir = m_fileName.left(pos);
0659             if (newDir != dir)
0660                 conf.writeEntry("LastOpenDir", newDir);
0661         }
0662     }
0663 
0664     Manager::self()->saveGameHistoryAs(m_fileName);
0665     setCaption(m_fileName);
0666     m_saveAction->setEnabled(false);
0667 }
0668 
0669 void MainWindow::fileSaveAs() {
0670     KConfigGroup conf(KSharedConfig::openConfig(), QStringLiteral("MainWindow"));
0671     QString dir = conf.readEntry("LastOpenDir", "");
0672     QString fileName = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save As"), dir,
0673                        i18n("Portable game notation (*.pgn)"));
0674 
0675     if (fileName.isEmpty())// "Cancel" was clicked
0676         return;
0677 
0678     if (fileName.contains(QLatin1String(".lml"), Qt::CaseInsensitive) == false)
0679         fileName.append(QLatin1String(".lml"));
0680 
0681     //save new "last open directory"
0682     int pos = fileName.lastIndexOf(QDir::separator());
0683     if (pos != -1) {
0684         const QString& newDir = fileName.left(pos);
0685         if (newDir != dir)
0686             conf.writeEntry("LastOpenDir", newDir);
0687     }
0688 
0689     m_fileName = fileName;
0690     Manager::self()->saveGameHistoryAs(m_fileName);
0691     setCaption(m_fileName);
0692 }
0693 
0694 void MainWindow::closeEvent(QCloseEvent* event) {
0695     if (!maybeSave())
0696         event->ignore();
0697 }
0698 
0699 #include "moc_knights.cpp"