SVN_SILENT made messages (.desktop file)
[kdegames.git] / kblackbox / kbbmainwindow.cpp
blob85bfd24096bbab4c29ddfb3d29109f3ca9ee9c6f
1 //
2 // KBlackBox
3 //
4 // A simple game inspired by an emacs module
5 //
6 /***************************************************************************
7 * Copyright (c) 1999-2000, Robert Cimrman *
8 * cimrman3@students.zcu.cz *
9 * *
10 * Copyright (c) 2007, Nicolas Roffet *
11 * nicolas-kde@roffet.com *
12 * *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *
28 ***************************************************************************/
30 #include "kbbmainwindow.h"
34 #include <QFile>
35 #include <QHBoxLayout>
36 #include <QWidget>
39 #include <KActionCollection>
40 #include <KConfigDialog>
41 #include <KGameClock>
42 #include <KGameDifficulty>
43 #include <KGamePopupItem>
44 #include <KGlobal>
45 #include <KLocale>
46 #include <KMessageBox>
47 #include <KScoreDialog>
48 #include <KStandardDirs>
49 #include <KStandardGameAction>
50 #include <KStatusBar>
51 #include <KToggleAction>
54 #include "kbbgamedoc.h"
55 #include "kbbgraphicsitemtutorialmarker.h"
56 #include "kbblevelconfigurationwidget.h"
57 #include "kbbprefs.h"
58 #include "kbbscalablegraphicwidget.h"
59 #include "kbbthememanager.h"
60 #include "kbbtutorial.h"
65 // Constructor / Destructor
68 KBBMainWindow::KBBMainWindow()
70 m_boardEnabled = false;
72 //Read configuration options
73 m_customBallNumber = KBBPrefs::balls();
74 m_customColumns = KBBPrefs::columns();
75 m_customRows = KBBPrefs::rows();
78 // Status bar
79 statusBar()->insertPermanentItem("", SRUN, 1);
80 statusBar()->insertPermanentItem(i18n("Time: 00:00"), STIME, 1);
81 statusBar()->insertPermanentItem(i18n("Size: 00 x 00"), SSIZE);
82 statusBar()->setItemAlignment(SRUN, Qt::AlignLeft | Qt::AlignVCenter);
85 // Difficulty
86 KGameDifficulty::init(this, this, SLOT(levelChanged(KGameDifficulty::standardLevel)));
87 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryEasy);
88 KGameDifficulty::addStandardLevel(KGameDifficulty::Easy);
89 KGameDifficulty::addStandardLevel(KGameDifficulty::Medium);
90 KGameDifficulty::addStandardLevel(KGameDifficulty::Hard);
91 KGameDifficulty::addStandardLevel(KGameDifficulty::VeryHard);
92 KGameDifficulty::addStandardLevel(KGameDifficulty::ExtremelyHard);
93 KGameDifficulty::addStandardLevel(KGameDifficulty::Configurable);
96 // Menu "Game"
97 KStandardGameAction::gameNew(this, SLOT(newGame()), actionCollection());
98 m_pauseAction = KStandardGameAction::pause(this, SLOT(pause(bool)), actionCollection());
99 QAction* tutorial = actionCollection()->addAction("game_tutorial");
100 tutorial->setText(i18n("Start Tutorial"));
101 tutorial->setIcon(KIcon("footprint"));
102 tutorial->setToolTip(i18n("Start tutorial"));
103 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>"));
104 connect(tutorial, SIGNAL(triggered(bool)), SLOT(startTutorial()));
105 KStandardGameAction::quit(this, SLOT(close()), actionCollection());
106 QAction* sandbox = actionCollection()->addAction("game_sandbox");
107 sandbox->setText(i18n("New Sandbox Game"));
108 sandbox->setToolTip(i18n("Start a new sandbox game"));
109 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>"));
110 connect(sandbox, SIGNAL(triggered(bool)), SLOT(startSandbox()));
111 KStandardGameAction::highscores(this, SLOT(showHighscores()), actionCollection());
113 // Menu "Move"
114 m_doneAction = actionCollection()->addAction("move_done");
115 m_doneAction->setText(i18nc("This is the last action of a game to check the result, when the user is done.", "Done!"));
116 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>"));
117 m_doneAction->setIcon(KIcon("dialog-ok"));
118 connect(m_doneAction, SIGNAL(triggered(bool)), SLOT(done()));
119 m_solveAction = KStandardGameAction::solve(this, SLOT(solve()), actionCollection());
120 m_solveAction->setToolTip(i18n("Give up the game"));
121 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>"));
123 // Menu "Settings"
124 KStandardAction::preferences(this, SLOT(settingsDialog()), actionCollection());
127 // Theme manager
128 QString svgzFile = KBBPrefs::theme();
129 if (!QFile(svgzFile).exists())
130 svgzFile = KStandardDirs::locate("appdata", "pics/kblackbox.svgz");
131 m_themeManager = new KBBThemeManager(svgzFile);
134 // Tutorial widget
135 m_tutorial = new KBBTutorial(this);
138 // Board
139 m_gameDoc = new KBBGameDoc(this, m_tutorial);
140 connect(m_gameDoc, SIGNAL(updateStats()), this, SLOT(updateStats()) );
141 connect(m_gameDoc, SIGNAL(isRunning(bool)), SLOT(setRunning(bool)));
144 // Game widget
145 m_gameWidget = new KBBScalableGraphicWidget(m_gameDoc, m_themeManager, m_doneAction);
146 m_tutorial->setGameWidget(m_gameWidget, new KBBGraphicsItemTutorialMarker(m_gameWidget, m_themeManager, KBBTutorial::COLUMNS, KBBTutorial::ROWS));
149 // Central Widget
150 m_centralWidget = new QWidget(this);
151 QHBoxLayout *widgetLayout = new QHBoxLayout();
152 widgetLayout->setMargin(0);
153 m_centralWidget->setLayout(widgetLayout);
154 widgetLayout->addWidget(m_gameWidget);
155 widgetLayout->addWidget(m_tutorial);
156 setCentralWidget(m_centralWidget);
159 // Keyboard only
160 KAction* action = actionCollection()->addAction( "move_down" );
161 action->setText( i18n("Move Down") );
162 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveDown()));
163 action->setShortcut(Qt::Key_Down);
164 addAction(action);
166 action = actionCollection()->addAction( "move_up" );
167 action->setText( i18n("Move Up") );
168 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveUp()));
169 action->setShortcut(Qt::Key_Up);
170 addAction(action);
172 action = actionCollection()->addAction( "move_left" );
173 action->setText( i18n("Move Left") );
174 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveLeft()));
175 action->setShortcut(Qt::Key_Left);
176 addAction(action);
178 action = actionCollection()->addAction( "move_right" );
179 action->setText( i18n("Move Right") );
180 connect(action, SIGNAL(triggered(bool) ), m_gameWidget, SLOT(keyboardMoveRight()));
181 action->setShortcut(Qt::Key_Right);
182 addAction(action);
184 action = actionCollection()->addAction("switch_ball");
185 action->setText(i18n("Switch Ball or Shoot Laser"));
186 connect(action, SIGNAL(triggered(bool)), m_gameWidget, SLOT(keyboardEnter()));
187 action->setShortcut(Qt::Key_Return);
188 addAction(action);
190 action = actionCollection()->addAction("switch_marker");
191 action->setText(i18n("Switch Marker"));
192 connect(action, SIGNAL(triggered(bool)), m_gameWidget, SLOT(keyboardSpace()));
193 action->setShortcut(Qt::Key_Space);
194 addAction(action);
197 m_gameClock = new KGameClock(this, KGameClock::MinSecOnly);
198 connect(m_gameClock, SIGNAL(timeChanged(const QString&)), SLOT(updateStats()));
199 connect(m_gameClock, SIGNAL(timeChanged(const QString&)), m_gameDoc, SLOT(timeChanged()));
202 levelChanged((KGameDifficulty::standardLevel) (KBBPrefs::level()));
203 KGameDifficulty::setLevel(m_level);
206 setupGUI();
208 // start a new game
209 startGame(false);
213 KBBMainWindow::~KBBMainWindow()
215 KBBPrefs::self()->writeConfig();
217 delete m_gameWidget;
218 delete m_themeManager;
224 // Public slots
227 void KBBMainWindow::levelChanged(KGameDifficulty::standardLevel level)
229 switch(level) {
230 case KGameDifficulty::VeryEasy:
231 m_ballNumber = 2;
232 m_columns = 6;
233 m_rows = 6;
234 break;
235 case KGameDifficulty::Easy:
236 default:
237 m_ballNumber = 4;
238 m_columns = 8;
239 m_rows = 8;
240 level = KGameDifficulty::Medium;
241 break;
242 case KGameDifficulty::Medium:
243 m_ballNumber = 6;
244 m_columns = 10;
245 m_rows = 10;
246 break;
247 case KGameDifficulty::Hard:
248 m_ballNumber = 8;
249 m_columns = 12;
250 m_rows = 12;
251 break;
252 case KGameDifficulty::VeryHard:
253 m_ballNumber = 11;
254 m_columns = 14;
255 m_rows = 10;
256 break;
257 case KGameDifficulty::ExtremelyHard:
258 m_ballNumber = 15;
259 m_columns = 20;
260 m_rows = 12;
261 break;
262 case KGameDifficulty::Configurable:
263 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."));
264 break;
267 m_level = level;
268 KBBPrefs::setLevel((int)(m_level));
269 startGame(m_sandboxMode);
273 void KBBMainWindow::setRunning(bool r)
275 // Difficulty
276 KGameDifficulty::setRunning(r);
278 // Clock
279 if (r) {
280 m_gameClock->resume();
281 m_gameDoc->timeChanged(); // It's important to end the current seconde before pausing so that the player cannot cheat with pause.
282 } else
283 m_gameClock->pause();
285 // Pause
286 m_pauseAction->setEnabled(r);
290 void KBBMainWindow::updateStats()
292 int ballsLeftToPlace = m_gameDoc->numberOfBallsToPlace() - m_gameDoc->numberOfBallsPlaced();
294 m_doneAction->setEnabled(m_solveAction->isEnabled() && (ballsLeftToPlace==0));
296 if (ballsLeftToPlace<0)
297 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));
298 else if (ballsLeftToPlace==0) {
299 m_doneAction->setToolTip(i18n("To check if you successfully guessed the ball positions, click here!"));
300 } else if (ballsLeftToPlace>0) {
301 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));
304 if (!m_boardEnabled)
305 m_doneAction->setToolTip(i18n("Game over."));
306 if (m_pauseAction->isChecked())
307 m_doneAction->setToolTip(i18n("Game paused."));
310 // Status bar
311 if (m_tutorial->isVisible())
312 statusBar()->changeItem(i18n("Tutorial"), SRUN );
313 if (!m_tutorial->isVisible()) {
314 if (m_boardEnabled) {
315 if (ballsLeftToPlace<0) {
316 statusBar()->changeItem((i18np("1 ball too many!", "%1 balls too many!", -ballsLeftToPlace)), SRUN);
317 } else if (ballsLeftToPlace==0) {
318 statusBar()->changeItem(i18n("No more balls to place"), SRUN);
319 } else if (ballsLeftToPlace>0) {
320 statusBar()->changeItem(i18np("1 ball to place", "%1 balls to place", ballsLeftToPlace), SRUN);
322 } else
323 statusBar()->changeItem(i18n("Game over"), SRUN );
326 statusBar()->changeItem(i18n("Time: %1", m_gameClock->timeString()), STIME);
328 statusBar()->changeItem( i18n("Size: %1 x %2", m_gameDoc->columns(), m_gameDoc->rows()), SSIZE );
331 // 2. Info Widget
332 m_gameWidget->setScore(m_gameDoc->score());
338 // Private slots
341 void KBBMainWindow::done()
343 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
344 KMessageBox::sorry(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"));
345 } else {
346 solving();
348 const int score = m_gameDoc->score();
349 QString s;
350 if (score <= (m_ballNumber*35)) {
351 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);
352 if (m_sandboxMode)
353 s += QString("<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>");
354 } else
355 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 practise.", score);
357 if ((!m_tutorial->isVisible()) && (!m_sandboxMode) && (KGameDifficulty::level() != KGameDifficulty::Configurable) && (score<KBBGameDoc::SCORE_LOST)) {
358 KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
359 scoreDialog.addLocalizedConfigGroupNames(KGameDifficulty::localizedLevelStrings());
360 scoreDialog.setConfigGroupWeights(KGameDifficulty::levelWeights());
361 scoreDialog.setConfigGroup(KGameDifficulty::localizedLevelString());
363 KScoreDialog::FieldInfo scoreInfo;
364 scoreInfo[KScoreDialog::Score].setNum(score);
365 if(scoreDialog.addScore(scoreInfo, KScoreDialog::LessIsMore) != 0)
366 scoreDialog.exec();
369 m_gameWidget->popupText(s);
374 void KBBMainWindow::newGame()
376 if (mayAbortGame())
377 startGame(false);
381 void KBBMainWindow::pause(bool state)
383 if (state) {
384 m_gameClock->pause();
385 m_gameWidget->popupText(i18n("Game paused.<br />Press \"%1\" to resume.", m_pauseAction->shortcut().toString(QKeySequence::NativeText)), 0);
386 } else {
387 m_gameClock->resume();
388 m_gameWidget->popupText("");
390 m_solveAction->setEnabled(!state);
392 updateStats();
393 m_gameWidget->setPause(state);
397 void KBBMainWindow::settingsChanged()
399 m_customBallNumber = m_levelConfig->balls();
400 m_customColumns = m_levelConfig->columns();
401 m_customRows = m_levelConfig->rows();
403 if (m_level==KGameDifficulty::Configurable) {
404 bool mayRestart = true;
405 if (m_gameDoc->gameReallyStarted())
406 if (KMessageBox::questionYesNo(this, i18n("Do you want to cancel the current custom game and start a new one with the new parameters?"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::No)
407 mayRestart = false;
409 if (mayRestart)
410 startGame(m_sandboxMode);
415 void KBBMainWindow::settingsDialog()
417 if (!KConfigDialog::showDialog("settings")) {
418 KConfigDialog *dialog = new KConfigDialog(this, "settings", KBBPrefs::self());
419 m_levelConfig = new KBBLevelConfigurationWidget(dialog, m_customBallNumber, m_customColumns, m_customRows, m_themeManager);
420 dialog->addPage(m_levelConfig, i18n("Custom Game"), "games-config-custom");
421 connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(settingsChanged()));
422 dialog->setHelp(QString(), "kblackbox");
423 dialog->show();
428 void KBBMainWindow::showHighscores()
430 KScoreDialog scoreDialog(KScoreDialog::Score | KScoreDialog::Name, this);
431 scoreDialog.addLocalizedConfigGroupNames(KGameDifficulty::localizedLevelStrings());
432 scoreDialog.setConfigGroupWeights(KGameDifficulty::levelWeights());
433 scoreDialog.setConfigGroup( KGameDifficulty::localizedLevelString() );
434 scoreDialog.exec();
438 void KBBMainWindow::solve()
440 if (m_tutorial->isVisible() && !m_tutorial->maySolve()) {
441 KMessageBox::sorry(this, i18n("Sorry, you may not give up during the tutorial."), i18n("Solve"));
442 } else {
443 if (m_gameDoc->numberOfBallsToPlace()==m_gameDoc->numberOfBallsPlaced()) {
444 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)
445 solving();
446 } 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)
447 solving();
452 void KBBMainWindow::startSandbox()
454 if (mayAbortGame()) {
455 startGame(true);
456 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!"));
461 void KBBMainWindow::startTutorial()
463 if (mayAbortGame()) {
464 m_gameDoc->startTutorial();
465 m_solveAction->setEnabled(true);
466 m_pauseAction->setChecked(false);
467 KGameDifficulty::setEnabled(false);
469 // Reset clock but don't start it yet.
470 m_gameClock->restart();
471 m_gameClock->pause();
473 updateStats();
480 // Private
483 bool KBBMainWindow::mayAbortGame()
485 bool mayAbort = true;
487 if (m_gameDoc->gameReallyStarted())
488 mayAbort = ( KMessageBox::warningContinueCancel(0, i18n("This will be the end of the current game!"), QString(), KGuiItem(i18n("Start new game"))) == KMessageBox::Continue );
490 return mayAbort;
494 void KBBMainWindow::solving()
496 m_boardEnabled = false;
497 m_solveAction->setEnabled(false);
498 m_doneAction->setEnabled(false);
499 m_gameDoc->gameOver();
500 m_gameWidget->solve(false);
501 updateStats();
505 void KBBMainWindow::startGame(bool sandboxMode)
507 if (m_level==KGameDifficulty::Configurable) {
508 m_ballNumber = m_customBallNumber;
509 m_columns = m_customColumns;
510 m_rows = m_customRows;
513 m_boardEnabled = true;
514 m_sandboxMode = sandboxMode;
516 m_solveAction->setEnabled(true);
517 m_pauseAction->setChecked(false);
518 KGameDifficulty::setEnabled(true);
519 m_tutorial->hide();
520 m_gameDoc->newGame(m_ballNumber, m_columns, m_rows);
521 m_gameWidget->newGame(m_columns, m_rows, m_ballNumber);
522 if (m_sandboxMode)
523 m_gameWidget->solve(true);
525 // Reset clock but don't start it yet.
526 m_gameClock->restart();
527 m_gameClock->pause();
529 updateStats();
534 #include "kbbmainwindow.moc"