Warning, file /games/kblackbox/src/kbbmainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     KBlackBox - A simple game inspired by an emacs module
0003 
0004     SPDX-FileCopyrightText: 1999-2000 Robert Cimrman <cimrman3@students.zcu.cz>
0005     SPDX-FileCopyrightText: 2007 Nicolas Roffet <nicolas-kde@roffet.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "kbbmainwindow.h"
0011 
0012 
0013 
0014 #include <QFile>
0015 #include <QHBoxLayout>
0016 #include <QPointer>
0017 #include <QWidget>
0018 
0019 
0020 #include <kwidgetsaddons_version.h>
0021 #include <KActionCollection>
0022 #include <KConfigDialog>
0023 #include <KGameClock>
0024 #include <KLocalizedString>
0025 #include <KMessageBox>
0026 #include <KScoreDialog>
0027 
0028 #include <KStandardGameAction>
0029 #include <QStatusBar>
0030 #include <QIcon>
0031 #include <QStandardPaths>
0032 
0033 
0034 #include "kbbgamedoc.h"
0035 #include "kbbgraphicsitemtutorialmarker.h"
0036 #include "kbblevelconfigurationwidget.h"
0037 #include "kbbprefs.h"
0038 #include "kbbscalablegraphicwidget.h"
0039 #include "kbbthememanager.h"
0040 #include "kbbtutorial.h"
0041 
0042 
0043 
0044 //
0045 // Constructor / Destructor
0046 //
0047 
0048 KBBMainWindow::KBBMainWindow()
0049 {
0050     m_boardEnabled = false;
0051 
0052     //Read configuration options
0053     m_customBallNumber = KBBPrefs::balls();
0054     m_customColumns = KBBPrefs::columns();
0055     m_customRows = KBBPrefs::rows();
0056 
0057 
0058     // Status bar
0059     //QT5 statusBar()->insertPermanentItem("", SRUN, 1);
0060     //QT5 statusBar()->insertPermanentItem(i18n("Time: 00:00"), STIME, 1);
0061     //QT5 statusBar()->insertPermanentItem(i18n("Size: 00 x 00"), SSIZE);
0062     //QT5 statusBar()->setItemAlignment(SRUN, Qt::AlignLeft | Qt::AlignVCenter);
0063 
0064 
0065     // Difficulty
0066     Kg::difficulty()->addStandardLevelRange(
0067         KgDifficultyLevel::VeryEasy, KgDifficultyLevel::ExtremelyHard,
0068         KgDifficultyLevel::Medium //default
0069     );
0070     Kg::difficulty()->addLevel(new KgDifficultyLevel(
0071         1000, QByteArray("config"), i18nc("Difficulty level title", "Configurable")
0072     ));
0073     KgDifficultyGUI::init(this);
0074     connect(Kg::difficulty(), &KgDifficulty::currentLevelChanged, this, &KBBMainWindow::levelChanged);
0075 
0076 
0077     // Menu "Game"
0078     KStandardGameAction::gameNew(this, &KBBMainWindow::newGame, actionCollection());
0079     m_pauseAction = KStandardGameAction::pause(this, &KBBMainWindow::pause, actionCollection());
0080     QAction* tutorial = actionCollection()->addAction( QStringLiteral( "game_tutorial" ));
0081     tutorial->setText(i18n("Start Tutorial"));
0082     tutorial->setIcon(QIcon::fromTheme( QStringLiteral( "footprint" )));
0083     tutorial->setToolTip(i18n("Start tutorial"));
0084     tutorial->setWhatsThis(i18n("<qt>The <b>tutorial</b> is a fast, user friendly and interactive way to learn the rules of the game. Start it if you do not know them!</qt>"));
0085     connect(tutorial, &QAction::triggered, this, &KBBMainWindow::startTutorial);
0086     KStandardGameAction::quit(this, &QWidget::close, actionCollection());
0087     QAction* sandbox = actionCollection()->addAction( QStringLiteral( "game_sandbox" ));
0088     sandbox->setText(i18n("New Sandbox Game"));
0089     sandbox->setToolTip(i18n("Start a new sandbox game"));
0090     sandbox->setWhatsThis(i18n("<qt><p>In a <b>sandbox game</b>, the solution is displayed at the beginning of the game. This is useful to understand the game principles.</p><p>However: after a while, it is not really fun and you should try to start a real game!</p></qt>"));
0091     connect(sandbox, &QAction::triggered, this, &KBBMainWindow::startSandbox);
0092     KStandardGameAction::highscores(this, &KBBMainWindow::showHighscores, actionCollection());
0093 
0094     // Menu "Move"
0095     m_doneAction = actionCollection()->addAction( QStringLiteral( "move_done" ));
0096     m_doneAction->setText(i18nc("This is the last action of a game to check the result, when the user is done.", "Done!"));
0097     m_doneAction->setWhatsThis(i18n("<qt><ul><li>First, you have to place all the balls on the black box. To guess the correct positions of the balls and see how they interact with laser beams, you should use the lasers that are positioned around the black box.</li><li><b>When you think you are done</b>, you should click here.</li></ul><p>Note that it is only possible to click here if you have placed the correct number of balls.</p></qt>"));
0098     m_doneAction->setIcon(QIcon::fromTheme( QStringLiteral( "dialog-ok" )));
0099     connect(m_doneAction, &QAction::triggered, this, &KBBMainWindow::done);
0100     m_solveAction = KStandardGameAction::solve(this, &KBBMainWindow::solve, actionCollection());
0101     m_solveAction->setToolTip(i18n("Give up the game"));
0102     m_solveAction->setWhatsThis(i18n("<qt><p>Choose \"<b>Solve</b>\" if you want to give up the current game. The solution will be displayed.</p><p>If you placed all the balls and do not want to give up, choose \"Done!\".</p></qt>"));
0103 
0104     // Menu "Settings"
0105     KStandardAction::preferences(this, &KBBMainWindow::settingsDialog, actionCollection());
0106     m_toggleCursorAction = actionCollection()->addAction( QStringLiteral( "toggle_cursor" ));
0107     m_toggleCursorAction->setText(i18n("Enable highlight under mouse"));
0108     m_toggleCursorAction->setCheckable(true);
0109     const KConfigGroup group = KSharedConfig::openConfig()->group("default");
0110     m_toggleCursorAction->setChecked(group.readEntry<bool>("highlight_enabled", true));
0111     connect(m_toggleCursorAction, &QAction::triggered, this, &KBBMainWindow::toggleCursor);
0112 
0113     // Theme manager
0114     QString svgzFile = KBBPrefs::theme();
0115     if (!QFile(svgzFile).exists())
0116         svgzFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("pics/kblackbox.svgz"));
0117     m_themeManager = new KBBThemeManager(svgzFile);
0118 
0119 
0120     // Tutorial widget
0121     m_tutorial = new KBBTutorial(this);
0122 
0123 
0124     // Board
0125     m_gameDoc = new KBBGameDoc(this, m_tutorial);
0126     connect(m_gameDoc, &KBBGameDoc::updateStats, this, &KBBMainWindow::updateStats);
0127     connect(m_gameDoc, &KBBGameDoc::isRunning, this, &KBBMainWindow::setRunning);
0128 
0129 
0130     // Game widget
0131     m_gameWidget = new KBBScalableGraphicWidget(m_gameDoc, m_themeManager, m_doneAction);
0132     m_tutorial->setGameWidget(m_gameWidget, new KBBGraphicsItemTutorialMarker(m_gameWidget, m_themeManager, KBBTutorial::COLUMNS, KBBTutorial::ROWS));
0133 
0134 
0135     // Central Widget
0136     m_centralWidget = new QWidget(this);
0137     QHBoxLayout *widgetLayout = new QHBoxLayout();
0138     widgetLayout->setContentsMargins(0, 0, 0, 0);
0139     m_centralWidget->setLayout(widgetLayout);
0140     widgetLayout->addWidget(m_gameWidget);
0141     widgetLayout->addWidget(m_tutorial);
0142     setCentralWidget(m_centralWidget);
0143 
0144 
0145     // Keyboard only
0146     QAction * action = actionCollection()->addAction( QStringLiteral(  "move_down" ) );
0147     action->setText( i18n("Move Down") );
0148     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveDown);
0149     actionCollection()->setDefaultShortcut(action, Qt::Key_Down);
0150     addAction(action);
0151 
0152     action = actionCollection()->addAction( QStringLiteral(  "move_up" ) );
0153     action->setText( i18n("Move Up") );
0154     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveUp);
0155     actionCollection()->setDefaultShortcut(action,Qt::Key_Up);
0156     addAction(action);
0157 
0158     action = actionCollection()->addAction( QStringLiteral(  "move_left" ) );
0159     action->setText( i18n("Move Left") );
0160     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveLeft);
0161     actionCollection()->setDefaultShortcut(action, Qt::Key_Left);
0162     addAction(action);
0163 
0164     action = actionCollection()->addAction( QStringLiteral(  "move_right" ) );
0165     action->setText( i18n("Move Right") );
0166     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardMoveRight);
0167     actionCollection()->setDefaultShortcut(action, Qt::Key_Right);
0168     addAction(action);
0169 
0170     action = actionCollection()->addAction( QStringLiteral( "switch_ball" ));
0171     action->setText(i18n("Switch Ball or Shoot Laser"));
0172     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardEnter);
0173     actionCollection()->setDefaultShortcut(action, Qt::Key_Return);
0174     addAction(action);
0175 
0176     action = actionCollection()->addAction( QStringLiteral( "switch_marker" ));
0177     action->setText(i18n("Switch Marker"));
0178     connect(action, &QAction::triggered, m_gameWidget, &KBBScalableGraphicWidget::keyboardSpace);
0179     actionCollection()->setDefaultShortcut(action, Qt::Key_Space);
0180     addAction(action);
0181 
0182 
0183     m_gameClock = new KGameClock(this, KGameClock::MinSecOnly);
0184     connect(m_gameClock, &KGameClock::timeChanged, this, &KBBMainWindow::updateStats);
0185     connect(m_gameClock, &KGameClock::timeChanged, m_gameDoc, &KBBGameDoc::timeChanged);
0186 
0187 
0188     levelChanged();
0189 
0190     setupGUI();
0191 
0192     if (m_toggleCursorAction->isChecked())
0193         toggleCursor();
0194 
0195     // start a new game
0196     startGame(false);
0197 }
0198 
0199 
0200 KBBMainWindow::~KBBMainWindow()
0201 {
0202     KBBPrefs::self()->save();
0203 
0204     delete m_gameWidget;
0205     delete m_themeManager;
0206 }
0207 
0208 
0209 
0210 //
0211 // Public slots
0212 //
0213 
0214 void KBBMainWindow::levelChanged()
0215 {
0216     KgDifficultyLevel::StandardLevel level = Kg::difficultyLevel();
0217     switch(level) {
0218         case KgDifficultyLevel::VeryEasy:
0219             m_ballNumber = 2;
0220             m_columns = 6;
0221             m_rows = 6;
0222             break;
0223         case KgDifficultyLevel::Easy:
0224         default:
0225             m_ballNumber = 4;
0226             m_columns = 8;
0227             m_rows = 8;
0228             level = KgDifficultyLevel::Medium;
0229             break;
0230         case KgDifficultyLevel::Medium:
0231             m_ballNumber = 6;
0232             m_columns = 10;
0233             m_rows = 10;
0234             break;
0235         case KgDifficultyLevel::Hard:
0236             m_ballNumber = 8;
0237             m_columns = 12;
0238             m_rows = 12;
0239             break;
0240         case KgDifficultyLevel::VeryHard:
0241             m_ballNumber = 11;
0242             m_columns = 14;
0243             m_rows = 10;
0244             break;
0245         case KgDifficultyLevel::ExtremelyHard:
0246             m_ballNumber = 15;
0247             m_columns = 20;
0248             m_rows = 12;
0249             break;
0250         case KgDifficultyLevel::Custom:
0251             m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: You can change<br />the parameters of<br />custom games in the<br />Settings dialog."));
0252             break;
0253     }
0254 
0255     m_level = level;
0256     startGame(m_sandboxMode);
0257 }
0258 
0259 
0260 void KBBMainWindow::setRunning(bool r)
0261 {
0262     // Difficulty
0263     Kg::difficulty()->setGameRunning(r);
0264 
0265     // Clock
0266     if (r) {
0267         m_gameClock->resume();
0268         m_gameDoc->timeChanged(); // It's important to end the current seconde before pausing so that the player cannot cheat with pause.
0269     } else
0270         m_gameClock->pause();
0271 
0272     // Pause
0273     m_pauseAction->setEnabled(r);
0274 }
0275 
0276 
0277 void KBBMainWindow::updateStats()
0278 {
0279     int ballsLeftToPlace = m_gameDoc->numberOfBallsToPlace() - m_gameDoc->numberOfBallsPlaced();
0280 
0281     m_doneAction->setEnabled(m_solveAction->isEnabled() && (ballsLeftToPlace==0));
0282 
0283     if (ballsLeftToPlace<0)
0284         m_doneAction->setToolTip(i18np("First, you need to remove 1 ball from the black box.", "First, you need to remove %1 balls from the black box.", -ballsLeftToPlace));
0285     else if (ballsLeftToPlace==0) {
0286         m_doneAction->setToolTip(i18n("To check if you successfully guessed the ball positions, click here!"));
0287     } else if (ballsLeftToPlace>0) {
0288         m_doneAction->setToolTip(i18np("You need to place 1 more ball on the black box.", "You need to place %1 more balls on the black box.", ballsLeftToPlace));
0289     }
0290 
0291     if (!m_boardEnabled)
0292         m_doneAction->setToolTip(i18n("Game over."));
0293     if (m_pauseAction->isChecked())
0294         m_doneAction->setToolTip(i18n("Game paused."));
0295 
0296 
0297     // Status bar
0298     if (m_tutorial->isVisible()) {
0299         //QT5 statusBar()->changeItem(i18n("Tutorial"), SRUN );
0300     }
0301     if (!m_tutorial->isVisible()) {
0302         if (m_boardEnabled) {
0303             if (ballsLeftToPlace<0) {
0304                 //QT5 statusBar()->changeItem((i18np("1 ball too many!", "%1 balls too many!", -ballsLeftToPlace)), SRUN);
0305             } else if (ballsLeftToPlace==0) {
0306                 //QT5 statusBar()->changeItem(i18n("No more balls to place"), SRUN);
0307             } else if (ballsLeftToPlace>0) {
0308                 //QT5 statusBar()->changeItem(i18np("1 ball to place", "%1 balls to place", ballsLeftToPlace), SRUN);
0309             }
0310         } else {
0311             //QT5 statusBar()->changeItem(i18n("Game over"), SRUN );
0312         }
0313     }
0314 
0315     //QT5 statusBar()->changeItem(i18n("Time: %1", m_gameClock->timeString()), STIME);
0316 
0317     //QT5 statusBar()->changeItem( i18n("Size: %1 x %2", m_gameDoc->columns(), m_gameDoc->rows()), SSIZE );
0318 
0319 
0320     // 2. Info Widget
0321     m_gameWidget->setScore(m_gameDoc->score());
0322 }
0323 
0324 
0325 
0326 //
0327 // Private slots
0328 //
0329 
0330 void KBBMainWindow::done()
0331 {
0332     if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
0333         KMessageBox::information(this, i18n("Clicking on \"Done!\" is the normal way to check the positions of the balls at the end of the game. However, it is not possible in the tutorial to end the game before you reached the last step.\nPlease first finish the tutorial."), i18n("Check positions"));
0334     } else {
0335         solving();
0336 
0337         const int score = m_gameDoc->score();
0338         QString s;
0339         if (score <= (m_ballNumber*35)) {
0340             s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />You did really well!", score);
0341             if (m_sandboxMode)
0342                 s += QLatin1String("<br /><br />") + i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "But it does not count<br />because <b>it is the sandbox!</b>");
0343         } else
0344             s = i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Your final score is: %1.<br />I guess you need more practice.", score);
0345 
0346         if ((!m_tutorial->isVisible()) && (!m_sandboxMode) && (Kg::difficultyLevel() != KgDifficultyLevel::Custom) && (score<KBBGameDoc::SCORE_LOST)) {
0347             QPointer<KScoreDialog> scoreDialog = new KScoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
0348             scoreDialog->initFromDifficulty(Kg::difficulty());
0349 
0350             KScoreDialog::FieldInfo scoreInfo;
0351             scoreInfo[KScoreDialog::Score].setNum(score);
0352             if(scoreDialog->addScore(scoreInfo, KScoreDialog::LessIsMore) != 0)
0353                 scoreDialog->exec();
0354             delete scoreDialog;
0355         }
0356 
0357         m_gameWidget->popupText(s);
0358     }
0359 }
0360 
0361 
0362 void KBBMainWindow::newGame()
0363 {
0364     if (mayAbortGame())
0365         startGame(false);
0366 }
0367 
0368 
0369 void KBBMainWindow::pause(bool state)
0370 {
0371     if (state) {
0372         m_gameClock->pause();
0373         m_gameWidget->popupText(i18n("Game paused.<br />Press \"%1\" to resume.", m_pauseAction->shortcut().toString(QKeySequence::NativeText)), 0);
0374     } else {
0375         m_gameClock->resume();
0376         m_gameWidget->popupText(QString());
0377     }
0378     m_solveAction->setEnabled(!state);
0379 
0380     updateStats();
0381     m_gameWidget->setPause(state);
0382 }
0383 
0384 
0385 void KBBMainWindow::settingsChanged()
0386 {
0387     m_customBallNumber = m_levelConfig->balls();
0388     m_customColumns = m_levelConfig->columns();
0389     m_customRows = m_levelConfig->rows();
0390 
0391     if (m_level==KgDifficultyLevel::Custom) {
0392         bool mayRestart = true;
0393         if (m_gameDoc->gameReallyStarted())
0394 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0395             if (KMessageBox::questionTwoActions(this,
0396 #else
0397             if (KMessageBox::questionYesNo(this,
0398 #endif
0399                 i18n("Do you want to cancel the current custom game and start a new one with the new parameters?"), QString(),
0400                 KGuiItem(i18nc("@action:button", "Start New Game"), QStringLiteral("view-refresh")),
0401                 KStandardGuiItem::cont())
0402 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0403                 == KMessageBox::SecondaryAction)
0404 #else
0405                 == KMessageBox::No)
0406 #endif
0407                 mayRestart = false;
0408 
0409         if (mayRestart)
0410             startGame(m_sandboxMode);
0411     }
0412 }
0413 
0414 
0415 void KBBMainWindow::settingsDialog()
0416 {
0417     if (!KConfigDialog::showDialog(QStringLiteral("settings"))) {
0418         KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), KBBPrefs::self());
0419         m_levelConfig = new KBBLevelConfigurationWidget(dialog, m_customBallNumber, m_customColumns, m_customRows, m_themeManager);
0420         dialog->addPage(m_levelConfig, i18n("Custom Game"), QStringLiteral("games-config-custom"));
0421         connect(dialog, &KConfigDialog::settingsChanged, this, &KBBMainWindow::settingsChanged);
0422                 //QT5 dialog->setHelp(QString(), "kblackbox");
0423         dialog->show();
0424     }
0425 }
0426 
0427 
0428 void KBBMainWindow::showHighscores()
0429 {
0430     KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
0431     scoreDialog.initFromDifficulty(Kg::difficulty());
0432     scoreDialog.exec();
0433 }
0434 
0435 
0436 void KBBMainWindow::solve()
0437 {
0438     if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
0439         KMessageBox::information(this, i18n("Sorry, you may not give up during the tutorial."), i18n("Solve"));
0440     } else {
0441         if (m_gameDoc->numberOfBallsToPlace()==m_gameDoc->numberOfBallsPlaced()) {
0442             if (KMessageBox::warningContinueCancel(this, i18n("You placed all the balls. Great!\nYou should now click on \"Done!\" to end the game and check if you guessed correctly.\nSo, do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue)
0443                 solving();
0444         } else if (KMessageBox::warningContinueCancel(this, i18np("You should place %1 ball!\n", "You should place %1 balls!\n", m_gameDoc->numberOfBallsToPlace()) + i18np("You have placed %1.\n", "You have placed %1.\n", m_gameDoc->numberOfBallsPlaced()) + i18n("Do you really want to give up this game?"), QString(), KGuiItem(i18n("Give up"))) == KMessageBox::Continue)
0445             solving();
0446     }
0447 }
0448 
0449 
0450 void KBBMainWindow::startSandbox()
0451 {
0452     if (mayAbortGame()) {
0453         startGame(true);
0454         m_gameWidget->popupText(i18nc("The text may not be too wide. So please use some HTML-BR-tags to have something more or less as wide as in english. Thanks!", "Note: In the sandbox mode,<br />the solution is already displayed.<br />Have fun!"));
0455     }
0456 }
0457 
0458 
0459 void KBBMainWindow::startTutorial()
0460 {
0461     if (mayAbortGame()) {
0462         m_gameDoc->startTutorial();
0463         m_solveAction->setEnabled(true);
0464         m_pauseAction->setChecked(false);
0465         Kg::difficulty()->setEditable(false);
0466 
0467         // Reset clock but don't start it yet.
0468         m_gameClock->restart();
0469         m_gameClock->pause();
0470 
0471         updateStats();
0472     }
0473 }
0474 
0475 void KBBMainWindow::toggleCursor()
0476 {
0477     m_gameWidget->toggleCursor();
0478 
0479     KConfigGroup group = KSharedConfig::openConfig()->group("default");
0480     group.writeEntry<bool>("highlight_enabled", m_toggleCursorAction->isChecked());
0481 }
0482 
0483 
0484 //
0485 // Private
0486 //
0487 
0488 bool KBBMainWindow::mayAbortGame()
0489 {
0490     bool mayAbort = true;
0491 
0492     if (m_gameDoc->gameReallyStarted())
0493         mayAbort = ( KMessageBox::warningContinueCancel(nullptr, i18n("This will be the end of the current game!"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::Continue );
0494 
0495     return mayAbort;
0496 }
0497 
0498 
0499 void KBBMainWindow::solving()
0500 {
0501     m_boardEnabled = false;
0502     m_solveAction->setEnabled(false);
0503     m_doneAction->setEnabled(false);
0504     m_gameDoc->gameOver();
0505     m_gameWidget->solve(false);
0506     updateStats();
0507 }
0508 
0509 
0510 void KBBMainWindow::startGame(bool sandboxMode)
0511 {
0512     if (m_level==KgDifficultyLevel::Custom) {
0513         m_ballNumber = m_customBallNumber;
0514         m_columns = m_customColumns;
0515         m_rows = m_customRows;
0516     }
0517 
0518     m_boardEnabled = true;
0519     m_sandboxMode = sandboxMode;
0520 
0521     m_solveAction->setEnabled(true);
0522     m_pauseAction->setChecked(false);
0523     Kg::difficulty()->setEditable(true);
0524     m_tutorial->hide();
0525     m_gameDoc->newGame(m_ballNumber, m_columns, m_rows);
0526     m_gameWidget->newGame(m_columns, m_rows, m_ballNumber);
0527     if (m_sandboxMode)
0528         m_gameWidget->solve(true);
0529 
0530     // Reset clock but don't start it yet.
0531     m_gameClock->restart();
0532     m_gameClock->pause();
0533 
0534     updateStats();
0535 }
0536 
0537 
0538 
0539