File indexing completed on 2024-05-05 04:02:56
0001 /* 0002 SPDX-FileCopyrightText: 2010 Ni Hui <shuizhuyuanluo@126.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mainwindow.h" 0008 0009 #include "bgselectorconfig.h" 0010 #include "customgameconfig.h" 0011 #include "gameconfig.h" 0012 #include "gamescene.h" 0013 #include "gameview.h" 0014 #include "settings.h" 0015 0016 #include <KGameHighScoreDialog> 0017 0018 #include <QAction> 0019 #include <KActionCollection> 0020 #include <KConfigDialog> 0021 #include <QFileDialog> 0022 #include <KGameClock> 0023 #include <KGameDifficulty> 0024 #include <KGameThemeSelector> 0025 #include <QInputDialog> 0026 #include <KLocalizedString> 0027 #include <KMessageBox> 0028 #include <KStandardAction> 0029 #include <KGameStandardAction> 0030 #include <QStatusBar> 0031 #include <KToggleAction> 0032 #include <QIcon> 0033 #include <QPointer> 0034 #include <QRandomGenerator> 0035 0036 MainWindow::MainWindow( bool kSameMode, QWidget* parent ) 0037 : KXmlGuiWindow(parent), 0038 m_kSameMode(kSameMode), 0039 m_gameClock(nullptr), 0040 m_gameScore(0), 0041 m_lastRemainCount(0), 0042 m_statusBarLabelLeft(new QLabel(this)), 0043 m_statusBarLabelRight(new QLabel(this)) 0044 { 0045 m_scene = new GameScene(this); 0046 GameView* view = new GameView( m_scene ); 0047 view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 0048 view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 0049 view->setFrameStyle( QFrame::NoFrame ); 0050 view->setCacheMode( QGraphicsView::CacheBackground ); 0051 setCentralWidget( view ); 0052 0053 statusBar()->addPermanentWidget( m_statusBarLabelLeft, 1 ); 0054 statusBar()->addPermanentWidget( m_statusBarLabelRight, 1 ); 0055 0056 if ( m_kSameMode ) { 0057 // statusBar()->insertItem( i18n( "Colors: XX" ), 1 ); 0058 // statusBar()->insertItem( i18n( "Board: XXXXXX" ), 2 ); 0059 m_statusBarLabelLeft->setText( i18n( "Marked: 0" ) ); 0060 m_statusBarLabelRight->setText( i18n( "Score: 0" ) ); 0061 connect(m_scene, &GameScene::remainCountChanged, this, &MainWindow::changeScore); 0062 connect(m_scene, &GameScene::markedCountChanged, this, &MainWindow::changeMarkedCount); 0063 } 0064 else { 0065 m_gameClock = new KGameClock( this, KGameClock::MinSecOnly ); 0066 m_statusBarLabelLeft->setText( i18n( "Pieces: 0" ) ); 0067 m_statusBarLabelRight->setText( i18n( "Time: 00:00" ) ); 0068 connect(m_scene, &GameScene::remainCountChanged, this, &MainWindow::changeRemainCount); 0069 connect(m_gameClock, &KGameClock::timeChanged, this, &MainWindow::changeTime); 0070 } 0071 0072 connect(m_scene, &GameScene::gameFinished, this, &MainWindow::onGameOver); 0073 0074 setupActions(); 0075 0076 loadSettings(); 0077 0078 newGame(); 0079 } 0080 0081 MainWindow::~MainWindow() 0082 { 0083 } 0084 0085 void MainWindow::configureSettings() 0086 { 0087 if ( KConfigDialog::showDialog( QStringLiteral( "settings" ) ) ) { 0088 return; 0089 } 0090 0091 KConfigDialog* dialog = new KConfigDialog( this, QStringLiteral( "settings" ), Settings::self() ); 0092 dialog->addPage( new GameConfig( dialog ), i18n( "General" ), QStringLiteral( "games-config-options" ) ); 0093 dialog->addPage( new KGameThemeSelector( m_scene->themeProvider() ), i18n( "Theme" ), QStringLiteral( "games-config-theme" ) ); 0094 dialog->addPage( new BgSelectorConfig( dialog ), i18n( "Background" ), QStringLiteral( "games-config-background" ) ); 0095 if ( !m_kSameMode ) { 0096 dialog->addPage( new CustomGameConfig( dialog ), i18n( "Custom Game" ), QStringLiteral( "games-config-custom" ) ); 0097 } 0098 0099 connect(m_scene->themeProvider(), &KGameThemeProvider::currentThemeChanged, this, &MainWindow::loadSettings); //setBackgroundType! 0100 connect(dialog, &KConfigDialog::settingsChanged, this, &MainWindow::loadSettings); 0101 dialog->show(); 0102 } 0103 0104 void MainWindow::loadSettings() 0105 { 0106 m_scene->setShowBoundLines( Settings::showBoundLines() ); 0107 m_scene->setEnableAnimation( Settings::enableAnimation() ); 0108 m_scene->setEnableHighlight( Settings::enableHighlight() ); 0109 0110 int bgType = 0; // Theme Radio Button 0111 if ( Settings::radioColor() == true ) { 0112 bgType = 1; 0113 } else if ( Settings::radioImage() == true ) { 0114 bgType = 2; 0115 } 0116 m_scene->setBackgroundType( bgType ); 0117 } 0118 0119 void MainWindow::newGame() 0120 { 0121 newGameWithId(QRandomGenerator::global()->bounded(RAND_MAX)); 0122 } 0123 0124 void MainWindow::newGameWithId( int gameId ) 0125 { 0126 if ( !confirmAbort() ) { 0127 return; 0128 } 0129 0130 m_pauseAction->setChecked( false ); 0131 m_pauseAction->setEnabled( true ); 0132 0133 if ( m_kSameMode ) { 0134 m_gameScore = 0; 0135 m_lastRemainCount = 15 * 10; 0136 m_scene->startNewGame( 15, 10, 3, gameId ); 0137 return; 0138 } 0139 0140 m_gameClock->restart(); 0141 0142 switch ( KGameDifficulty::globalLevel() ) { 0143 case KGameDifficultyLevel::VeryEasy: 0144 m_scene->startNewGame( 10, 16, 3, gameId ); 0145 break; 0146 case KGameDifficultyLevel::Easy: 0147 m_scene->startNewGame( 10, 16, 4, gameId ); 0148 break; 0149 case KGameDifficultyLevel::Medium: 0150 m_scene->startNewGame( 10, 16, 5, gameId ); 0151 break; 0152 case KGameDifficultyLevel::Hard: 0153 m_scene->startNewGame( 10, 16, 6, gameId ); 0154 break; 0155 case KGameDifficultyLevel::Custom: 0156 m_scene->startNewGame( Settings::customWidth(), 0157 Settings::customHeight(), 0158 Settings::customColorCount(), gameId ); 0159 break; 0160 default: 0161 break; 0162 } 0163 } 0164 0165 void MainWindow::newNumGame() 0166 { 0167 if ( !confirmAbort() ) { 0168 return; 0169 } 0170 0171 bool ok = false; 0172 int random = QRandomGenerator::global()->bounded(1, RAND_MAX); 0173 int userGameId = QInputDialog::getInt( this, i18nc("@title:window", "Select Board" ), 0174 i18n( "Select a board number:" ), 0175 random, 1, RAND_MAX, 1, 0176 &ok ); 0177 if ( ok ) { 0178 newGameWithId(userGameId); 0179 } 0180 } 0181 0182 void MainWindow::pauseGame( bool isPaused ) 0183 { 0184 m_scene->setPaused( isPaused ); 0185 if ( !m_kSameMode ) { 0186 if ( isPaused ) { 0187 m_gameClock->pause(); 0188 } else { 0189 m_gameClock->resume(); 0190 } 0191 } 0192 } 0193 0194 void MainWindow::restartGame() 0195 { 0196 if ( confirmAbort() ) { 0197 m_pauseAction->setChecked( false ); 0198 m_pauseAction->setEnabled( true ); 0199 if ( m_kSameMode ) { 0200 m_gameScore = 0; 0201 m_lastRemainCount = 0; 0202 } 0203 else { 0204 m_gameClock->restart(); 0205 } 0206 m_scene->restartGame(); 0207 } 0208 } 0209 0210 void MainWindow::loadGame() 0211 { 0212 QString fileName = QFileDialog::getOpenFileName( this, QString(), QString() , i18n( "Klickety Game Files (*.klickety)" ) ); 0213 if ( fileName.isEmpty() || !confirmAbort() ) { 0214 return; 0215 } 0216 0217 m_pauseAction->setChecked( false ); 0218 m_pauseAction->setEnabled( true ); 0219 KConfig config( fileName, KConfig::SimpleConfig ); 0220 KConfigGroup group = config.group( QStringLiteral("Savegame") ); 0221 m_scene->loadGame( group ); 0222 } 0223 0224 void MainWindow::saveGame() 0225 { 0226 QString fileName = QFileDialog::getSaveFileName( this, QString(), QString(), i18n( "Klickety Game Files (*.klickety)" ) ); 0227 if ( fileName.isEmpty() ) { 0228 return; 0229 } 0230 KConfig config( fileName, KConfig::SimpleConfig ); 0231 KConfigGroup group = config.group( QStringLiteral("Savegame") ); 0232 m_scene->saveGame( group ); 0233 } 0234 0235 void MainWindow::changeMarkedCount( int markedCount ) 0236 { 0237 int markedScore = ( markedCount < 2 ) ? 0 : ( ( markedCount - 2 ) * ( markedCount - 2 ) ); 0238 m_statusBarLabelLeft->setText( i18np( "Marked: %2 (1 Point)", "Marked: %2 (%1 Points)", markedScore, markedCount ) ); 0239 } 0240 0241 void MainWindow::changeScore( int remainCount ) 0242 { 0243 if ( m_lastRemainCount == 0 ) { 0244 // new game or restart 0245 m_lastRemainCount = remainCount; 0246 m_statusBarLabelRight->setText( i18n( "Score: 0" ) ); 0247 return; 0248 } 0249 int removedCount = m_lastRemainCount - remainCount; 0250 if ( removedCount > 0 ) { 0251 // normal move 0252 int score = ( removedCount < 2 ) ? 0 : ( ( removedCount - 2 ) * ( removedCount - 2 ) ); 0253 m_gameScore += score; 0254 } 0255 else { 0256 // undo action, minus the score 0257 int score = ( removedCount > -2 ) ? 0 : ( ( removedCount + 2 ) * ( removedCount + 2 ) ); 0258 m_gameScore -= score; 0259 } 0260 m_statusBarLabelRight->setText( i18n( "Score: %1", m_gameScore ) ); 0261 m_lastRemainCount = remainCount; 0262 } 0263 0264 void MainWindow::changeRemainCount( int remainCount ) 0265 { 0266 m_statusBarLabelLeft->setText( i18n( "Pieces: %1", remainCount ) ); 0267 } 0268 0269 void MainWindow::changeTime( const QString& newTime ) 0270 { 0271 m_statusBarLabelRight->setText( i18n( "Time: %1", newTime ) ); 0272 } 0273 0274 void MainWindow::showHighscores() 0275 { 0276 if ( m_kSameMode ) { 0277 QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name | KGameHighScoreDialog::Score, this ); 0278 d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ false); 0279 d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) ); 0280 d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" ); 0281 d->exec(); 0282 delete d; 0283 return; 0284 } 0285 0286 QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name, this ); 0287 d->addField( KGameHighScoreDialog::Custom1, i18n( "Remaining pieces" ), QStringLiteral( "remains" ) ); 0288 d->addField( KGameHighScoreDialog::Custom2, i18n( "Time" ), QStringLiteral( "time" ) ); 0289 d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ true); 0290 d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" ); 0291 d->hideField( KGameHighScoreDialog::Score ); 0292 d->exec(); 0293 delete d; 0294 } 0295 0296 void MainWindow::onGameOver( int remainCount ) 0297 { 0298 m_pauseAction->setEnabled( false ); 0299 0300 if ( m_kSameMode ) { 0301 if ( remainCount == 0 ) { 0302 // if the board is empty, give a bonus 0303 m_gameScore += 1000; 0304 } 0305 0306 QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name | KGameHighScoreDialog::Score, this ); 0307 d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ false); 0308 d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) ); 0309 d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" ); 0310 0311 KGameHighScoreDialog::FieldInfo scoreInfo; 0312 scoreInfo[ KGameHighScoreDialog::Score ].setNum( m_gameScore ); 0313 0314 if ( d->addScore( scoreInfo ) != 0 ) { 0315 d->exec(); 0316 } 0317 delete d; 0318 return; 0319 } 0320 0321 m_gameClock->pause(); 0322 0323 QPointer<KGameHighScoreDialog> d = new KGameHighScoreDialog( KGameHighScoreDialog::Name, this ); 0324 d->addField( KGameHighScoreDialog::Custom1, i18n( "Remaining pieces" ), QStringLiteral( "remains" ) ); 0325 d->addField( KGameHighScoreDialog::Custom2, i18n( "Time" ), QStringLiteral( "time" ) ); 0326 d->initFromDifficulty(KGameDifficulty::global(), /*setConfigGroup=*/ true); 0327 d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" ); 0328 d->hideField( KGameHighScoreDialog::Score ); 0329 0330 KGameHighScoreDialog::FieldInfo scoreInfo; 0331 scoreInfo[KGameHighScoreDialog::Custom1].setNum( remainCount ); 0332 scoreInfo[KGameHighScoreDialog::Custom2] = m_gameClock->timeString(); 0333 // remainCount*10000000 is much bigger than a usual time seconds 0334 scoreInfo[KGameHighScoreDialog::Score].setNum( remainCount*10000000 + m_gameClock->seconds() ); 0335 if ( d->addScore( scoreInfo, KGameHighScoreDialog::LessIsMore ) != 0 ) { 0336 d->exec(); 0337 } 0338 delete d; 0339 } 0340 0341 bool MainWindow::confirmAbort() 0342 { 0343 return m_scene->isGameFinished() || 0344 ( KMessageBox::questionTwoActions( this, 0345 i18n( "Do you want to resign?" ), 0346 i18n( "New Game" ), 0347 KGuiItem( i18nc("@action:button", "Resign") ), 0348 KStandardGuiItem::cancel() ) 0349 == KMessageBox::PrimaryAction ); 0350 } 0351 0352 void MainWindow::setupActions() 0353 { 0354 // game menu 0355 KGameStandardAction::gameNew(this, &MainWindow::newGame, actionCollection()); 0356 if ( !m_kSameMode ) { 0357 KGameStandardAction::load(this, &MainWindow::loadGame, actionCollection()); 0358 KGameStandardAction::save(this, &MainWindow::saveGame, actionCollection()); 0359 } 0360 KGameStandardAction::restart(this, &MainWindow::restartGame, actionCollection()); 0361 KGameStandardAction::highscores(this, &MainWindow::showHighscores, actionCollection()); 0362 m_pauseAction = KGameStandardAction::pause(this, &MainWindow::pauseGame, actionCollection()); 0363 KGameStandardAction::quit( this, &MainWindow::close, actionCollection()); 0364 QAction * m_newNumGameAction = new QAction( i18n( "New Numbered Game..." ), actionCollection() ); 0365 actionCollection()->addAction( QStringLiteral( "game_new_numeric" ), m_newNumGameAction ); 0366 connect(m_newNumGameAction, &QAction::triggered, this, &MainWindow::newNumGame); 0367 0368 KToggleAction * soundAction = new KToggleAction(QIcon::fromTheme(QStringLiteral("speaker")), i18n("Play Sounds"), this); 0369 soundAction->setChecked(Settings::enableSounds()); 0370 actionCollection()->addAction(QStringLiteral("sounds"), soundAction); 0371 connect(soundAction, &KToggleAction::triggered, this, &MainWindow::setSoundsEnabled); 0372 0373 // move menu 0374 QAction * undoAction = KGameStandardAction::undo(m_scene, &GameScene::undoMove, actionCollection()); 0375 undoAction->setEnabled( false ); 0376 connect(m_scene, &GameScene::canUndoChanged, undoAction, &QAction::setEnabled); 0377 QAction * redoAction = KGameStandardAction::redo(m_scene, &GameScene::redoMove, actionCollection()); 0378 redoAction->setEnabled( false ); 0379 connect(m_scene, &GameScene::canRedoChanged, redoAction, &QAction::setEnabled); 0380 0381 QAction * undoAllAction = actionCollection()->addAction( QStringLiteral( "move_undo_all" ) ); 0382 undoAllAction->setIcon( QIcon::fromTheme( QStringLiteral( "media-skip-backward" ) ) ); 0383 undoAllAction->setText( i18n( "Undo All" ) ); 0384 undoAllAction->setEnabled( false ); 0385 connect(m_scene, &GameScene::canUndoChanged, undoAllAction, &QAction::setEnabled); 0386 connect(undoAllAction, &QAction::triggered, m_scene, &GameScene::undoAllMove); 0387 QAction * redoAllAction = actionCollection()->addAction( QStringLiteral( "move_redo_all" ) ); 0388 redoAllAction->setIcon( QIcon::fromTheme( QStringLiteral( "media-skip-forward" ) ) ); 0389 redoAllAction->setText( i18n( "Redo All" ) ); 0390 redoAllAction->setEnabled( false ); 0391 connect(m_scene, &GameScene::canRedoChanged, redoAllAction, &QAction::setEnabled); 0392 connect(redoAllAction, &QAction::triggered, m_scene, &GameScene::redoAllMove); 0393 0394 // settings menu 0395 KStandardAction::preferences(this, &MainWindow::configureSettings, actionCollection()); 0396 0397 if ( m_kSameMode ) { 0398 KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(0, 0399 QByteArray( "KSame" ), i18n( "High Scores" ) 0400 )); 0401 setupGUI( QSize( 576, 384 ) ); 0402 return; 0403 } 0404 0405 KGameDifficulty::global()->addStandardLevelRange( 0406 KGameDifficultyLevel::VeryEasy, KGameDifficultyLevel::Hard, 0407 KGameDifficultyLevel::Easy //default 0408 ); 0409 KGameDifficulty::global()->addLevel(new KGameDifficultyLevel(1000, 0410 QByteArray( "Custom" ), i18n( "Custom" ) 0411 )); 0412 KGameDifficultyGUI::init(this); 0413 connect(KGameDifficulty::global(), &KGameDifficulty::currentLevelChanged, 0414 this, &MainWindow::newGame); 0415 0416 setupGUI( QSize( 340, 510 ) ); 0417 } 0418 0419 void MainWindow::setSoundsEnabled( bool enabled ) 0420 { 0421 Settings::setEnableSounds( enabled ); 0422 Settings::self()->save(); 0423 } 0424 0425 #include "moc_mainwindow.cpp"