File indexing completed on 2024-04-28 04:05:20
0001 /* 0002 SPDX-FileCopyrightText: 2015 Jakob Gruber <jakob.gruber@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mainwindow.h" 0008 0009 #include <KActionCollection> 0010 #include <KLocalizedString> 0011 #include <KMessageBox> 0012 #include <KGameStandardAction> 0013 #include <KToggleFullScreenAction> 0014 #include <QCoreApplication> 0015 #include <QGraphicsSimpleTextItem> 0016 #include <QPointer> 0017 #include <QPushButton> 0018 #include <QStatusBar> 0019 0020 #include "src/constants.h" 0021 #include "src/logic/levelloader.h" 0022 #include "src/logic/picmi.h" 0023 #include "src/logic/settings.h" 0024 #include "selectboardwindow.h" 0025 #include "settingswindow.h" 0026 #include "scene.h" 0027 0028 MainWindow::MainWindow(QWidget *parent) : 0029 KXmlGuiWindow(parent), 0030 m_key_pos(QStringLiteral("window/position")), m_in_progress(false), m_mode(Random) 0031 { 0032 QCoreApplication::setApplicationName(QStringLiteral("picmi")); 0033 QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); 0034 0035 m_timer.setInterval(500); 0036 0037 setCentralWidget(&m_view); 0038 0039 setupActions(); 0040 restoreWindowState(); 0041 0042 startRandomGame(); 0043 } 0044 0045 void MainWindow::setupActions() { 0046 KGameStandardAction::gameNew(this, &MainWindow::startRandomGame, actionCollection()); 0047 KGameStandardAction::load(this, &MainWindow::loadBoard, actionCollection()); 0048 KGameStandardAction::restart(this, &MainWindow::restartGame, actionCollection()); 0049 KGameStandardAction::highscores(this, &MainWindow::highscores, actionCollection()); 0050 KGameStandardAction::quit(this, &MainWindow::close, actionCollection()); 0051 KStandardAction::preferences(this, &MainWindow::settings, actionCollection()); 0052 KStandardAction::fullScreen(this, &MainWindow::toggleFullscreen, this, actionCollection()); 0053 m_action_pause = KGameStandardAction::pause(this, &MainWindow::togglePaused, actionCollection()); 0054 m_action_undo = KGameStandardAction::undo(this, &MainWindow::undo, actionCollection()); 0055 m_action_hint = KGameStandardAction::hint(this, &MainWindow::hint, actionCollection()); 0056 m_action_solve = KGameStandardAction::solve(this, &MainWindow::solve, actionCollection()); 0057 0058 /* Prevent the default hint shortcut from overwriting our HJKL vim-like control mapping. */ 0059 KActionCollection::setDefaultShortcut(m_action_hint, QKeySequence(Qt::CTRL | Qt::Key_I)); 0060 0061 m_action_save_state = actionCollection()->addAction(QStringLiteral("save-position")); 0062 m_action_save_state->setText(i18n("Save Position")); 0063 m_action_save_state->setIcon(QIcon::fromTheme(QStringLiteral("list-add"))); 0064 KActionCollection::setDefaultShortcut(m_action_save_state, QKeySequence(Qt::CTRL | Qt::Key_S)); 0065 connect(m_action_save_state, &QAction::triggered, this, &MainWindow::saveState); 0066 0067 m_action_load_state = actionCollection()->addAction(QStringLiteral("load-position")); 0068 m_action_load_state->setText(i18n("Load Position")); 0069 m_action_load_state->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); 0070 KActionCollection::setDefaultShortcut(m_action_load_state, QKeySequence(Qt::CTRL | Qt::Key_L)); 0071 connect(m_action_load_state, &QAction::triggered, this, &MainWindow::loadState); 0072 0073 m_status_time = new QLabel; 0074 m_status_time->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 0075 m_status_position = new QLabel; 0076 m_status_position->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); 0077 0078 m_new_game = new QPushButton; 0079 m_new_game->setText(i18n("New Game")); 0080 m_new_game->setVisible(false); 0081 connect(m_new_game, &QAbstractButton::clicked, this, &MainWindow::startRandomGame); 0082 0083 m_load_game = new QPushButton; 0084 m_load_game->setText(i18n("Load New Game")); 0085 m_load_game->setVisible(false); 0086 connect(m_load_game, &QAbstractButton::clicked, this, &MainWindow::loadBoard); 0087 0088 this->statusBar()->addWidget(m_new_game, 0); 0089 this->statusBar()->addWidget(m_load_game, 0); 0090 this->statusBar()->addWidget(m_status_position, 1); 0091 this->statusBar()->addWidget(m_status_time, 1); 0092 0093 KGameDifficulty::global()->addStandardLevel(KGameDifficultyLevel::Easy); 0094 KGameDifficulty::global()->addStandardLevel(KGameDifficultyLevel::Medium, true); 0095 KGameDifficulty::global()->addStandardLevel(KGameDifficultyLevel::Hard); 0096 0097 KGameDifficultyLevel *configurable = new KGameDifficultyLevel(90, QByteArray("Custom"), i18nc("custom difficulty", "Custom")); 0098 KGameDifficulty::global()->addLevel(configurable); 0099 0100 KGameDifficultyGUI::init(this); 0101 connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, this, 0102 &MainWindow::levelChanged); 0103 0104 /* Disable the toolbar configuration menu entry. 0105 * The default size is used at first start up. */ 0106 setupGUI(QSize(700, 560), Keys | StatusBar | Save | Create); 0107 } 0108 0109 void MainWindow::loadBoard() { 0110 QPointer<SelectBoardWindow> w(new SelectBoardWindow(this)); 0111 if (w->exec() == QDialog::Accepted) { 0112 startPresetGame(w->selectedBoard()); 0113 } 0114 delete w; 0115 } 0116 0117 void MainWindow::levelChanged(const KGameDifficultyLevel* level) { 0118 Settings::instance()->setLevel(level->standardLevel()); 0119 Settings::instance()->sync(); 0120 startRandomGame(); 0121 } 0122 0123 void MainWindow::toggleFullscreen(bool full_screen) { 0124 KToggleFullScreenAction::setFullScreen(this, full_screen); 0125 } 0126 0127 void MainWindow::undoStackSizeChanged(int size) 0128 { 0129 m_action_undo->setEnabled(size != 0); 0130 } 0131 0132 void MainWindow::saveStackSizeChanged(int size) 0133 { 0134 m_action_load_state->setEnabled(size != 0); 0135 } 0136 0137 void MainWindow::closeEvent(QCloseEvent *event) { 0138 saveWindowState(); 0139 KXmlGuiWindow::closeEvent(event); 0140 } 0141 0142 void MainWindow::saveWindowState() { 0143 QSharedPointer<QSettings> settings = Settings::instance()->qSettings(); 0144 settings->setValue(m_key_pos, pos()); 0145 settings->sync(); 0146 } 0147 0148 void MainWindow::restoreWindowState() { 0149 QSharedPointer<QSettings> settings = Settings::instance()->qSettings(); 0150 QPoint p = settings->value(m_key_pos, pos()).toPoint(); 0151 0152 move(p); 0153 } 0154 0155 void MainWindow::undo() { 0156 QPoint p = m_game->undo(); 0157 m_scene->refresh(p); 0158 } 0159 0160 void MainWindow::hint() 0161 { 0162 QPoint p = m_game->hint(); 0163 0164 if (m_in_progress) { 0165 m_scene->refresh(p); 0166 m_scene->hover(p.x(), p.y()); 0167 } 0168 } 0169 0170 void MainWindow::solve() 0171 { 0172 m_game->solve(); 0173 } 0174 0175 void MainWindow::saveState() { 0176 m_game->saveState(); 0177 updatePositions(); 0178 } 0179 0180 void MainWindow::loadState() { 0181 m_game->loadState(); 0182 m_scene->refresh(); 0183 } 0184 0185 void MainWindow::startRandomGame() { 0186 m_game = QSharedPointer<Picmi>(new Picmi()); 0187 m_mode = Random; 0188 0189 startGame(); 0190 } 0191 0192 void MainWindow::restartGame() 0193 { 0194 m_game = QSharedPointer<Picmi>(new Picmi(m_game->getBoardMap())); 0195 0196 startGame(); 0197 } 0198 0199 void MainWindow::startPresetGame(QSharedPointer<Level> board) { 0200 QSharedPointer<BoardMap> p(new BoardMap(board->width(), board->height(), board->map())); 0201 m_game = QSharedPointer<Picmi>(new Picmi(p)); 0202 m_mode = Preset; 0203 m_current_level = board; 0204 0205 startGame(); 0206 } 0207 0208 void MainWindow::startGame() { 0209 0210 if (m_scene) { 0211 disconnect(&m_timer, &QTimer::timeout, this, &MainWindow::updatePlayedTime); 0212 } 0213 0214 m_action_undo->setEnabled(false); 0215 m_action_hint->setEnabled(true); 0216 m_action_solve->setEnabled(true); 0217 m_action_save_state->setEnabled(true); 0218 m_action_load_state->setEnabled(false); 0219 m_action_pause->setEnabled(true); 0220 m_action_pause->setChecked(false); 0221 m_status_position->setVisible(true); 0222 m_load_game->setVisible(false); 0223 m_new_game->setVisible(false); 0224 KGameDifficulty::global()->setGameRunning(true); 0225 0226 m_timer.start(); 0227 m_scene = m_view.createScene(m_game); 0228 updatePlayedTime(); 0229 updatePositions(); 0230 0231 m_view.setEnabled(true); 0232 m_view.setFocus(); 0233 m_view.setPaused(false); 0234 0235 connect(&m_timer, &QTimer::timeout, this, &MainWindow::updatePlayedTime); 0236 connect(m_game.data(), &Picmi::stateChanged, this, &MainWindow::updatePositions); 0237 connect(m_game.data(), &Picmi::gameCompleted, this, &MainWindow::gameCompleted); 0238 connect(m_game.data(), &Picmi::gameWon, this, &MainWindow::gameWon); 0239 connect(m_game.data(), &Picmi::undoStackSizeChanged, this, &MainWindow::undoStackSizeChanged); 0240 connect(m_game.data(), &Picmi::saveStackSizeChanged, this, &MainWindow::saveStackSizeChanged); 0241 0242 m_in_progress = true; 0243 } 0244 0245 void MainWindow::updatePlayedTime() { 0246 m_status_time->setText(i18n("Elapsed time: %1", 0247 Time(m_game->elapsedSecs()).toString())); 0248 } 0249 0250 void MainWindow::updatePositions() { 0251 m_status_position->setText(i18n("Actions since last saved position: %1", 0252 m_game->currentStateAge())); 0253 } 0254 0255 QSharedPointer<KGameHighScoreDialog> MainWindow::createScoreDialog() { 0256 QSharedPointer<KGameHighScoreDialog> p(new KGameHighScoreDialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Date | KGameHighScoreDialog::Time)); 0257 0258 p->initFromDifficulty(KGameDifficulty::global()); 0259 p->hideField(KGameHighScoreDialog::Score); 0260 0261 return p; 0262 } 0263 0264 void MainWindow::gameWon() { 0265 KGameHighScoreDialog::FieldInfo score = m_game->endGame(); 0266 m_status_position->setVisible(false); 0267 if (m_mode == Random) { 0268 bool notified = false; 0269 m_new_game->setVisible(true); 0270 if (KGameDifficulty::globalLevel() != KGameDifficultyLevel::Custom) { 0271 QSharedPointer<KGameHighScoreDialog> scoreDialog = createScoreDialog(); 0272 if (scoreDialog->addScore(score, KGameHighScoreDialog::LessIsMore | KGameHighScoreDialog::AskName) != 0) { 0273 scoreDialog->exec(); 0274 notified = true; 0275 } 0276 } 0277 0278 /* Ensure that the user gets some kind of feedback about solving the board. */ 0279 if (!notified) { 0280 KMessageBox::information(this, i18n("Congratulations, you have solved this board!"), 0281 i18n("Board Solved!")); 0282 } 0283 } else if (m_mode == Preset) { 0284 m_load_game->setVisible(true); 0285 m_current_level->setSolved(m_game->elapsedSecs()); 0286 KMessageBox::information(this, i18n("Congratulations, you have solved board '%1'!", 0287 m_current_level->name()), 0288 i18n("Board Solved!")); 0289 } 0290 0291 m_view.setFocus(); 0292 } 0293 0294 void MainWindow::gameCompleted() { 0295 m_view.setEnabled(false); 0296 m_action_pause->setEnabled(false); 0297 m_action_solve->setEnabled(false); 0298 m_action_hint->setEnabled(false); 0299 m_action_undo->setEnabled(false); 0300 m_action_save_state->setEnabled(false); 0301 m_action_load_state->setEnabled(false); 0302 KGameDifficulty::global()->setGameRunning(false); 0303 m_timer.stop(); 0304 updatePlayedTime(); 0305 m_in_progress = false; 0306 } 0307 0308 void MainWindow::highscores() { 0309 pauseGame(); 0310 0311 QSharedPointer<KGameHighScoreDialog> scoreDialog = createScoreDialog(); 0312 scoreDialog->exec(); 0313 0314 m_view.setFocus(); 0315 } 0316 0317 void MainWindow::togglePaused(bool paused) { 0318 m_view.setPaused(paused); 0319 m_action_undo->setEnabled(!paused); 0320 m_action_hint->setEnabled(!paused); 0321 m_action_solve->setEnabled(!paused); 0322 m_action_save_state->setEnabled(!paused); 0323 m_action_load_state->setEnabled(!paused); 0324 0325 if (paused) { 0326 m_timer.stop(); 0327 } else { 0328 m_timer.start(); 0329 } 0330 } 0331 0332 void MainWindow::pauseGame() { 0333 if (m_action_pause->isChecked() || !m_in_progress) { 0334 return; 0335 } 0336 0337 m_action_pause->setChecked(true); 0338 togglePaused(true); 0339 } 0340 0341 void MainWindow::settings() { 0342 pauseGame(); 0343 0344 QPointer<SettingsWindow> w(new SettingsWindow(this)); 0345 w->exec(); 0346 delete w; 0347 } 0348 0349 #include "moc_mainwindow.cpp"