1 | /* |
2 | * Copyright (c) 2010 Ni Hui <shuizhuyuanluo@126.com> |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation; either version 2 of the License, or |
7 | * (at your option) any later version. |
8 | |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | */ |
18 | |
19 | #include "mainwindow.h" |
20 | |
21 | #include "bgselector.h" |
22 | #include "customgame.h" |
23 | #include "gameconfig.h" |
24 | #include "gamescene.h" |
25 | #include "gameview.h" |
26 | #include "settings.h" |
27 | |
28 | #include <KAction> |
29 | #include <KActionCollection> |
30 | #include <KApplication> |
31 | #include <KConfigDialog> |
32 | #include <KFileDialog> |
33 | #include <KGameClock> |
34 | #include <KgDifficulty> |
35 | #include <KgThemeSelector> |
36 | #include <KInputDialog> |
37 | #include <KLocale> |
38 | #include <KMessageBox> |
39 | #include <KNotifyConfigWidget> |
40 | #include <KScoreDialog> |
41 | #include <KStandardAction> |
42 | #include <KStandardGameAction> |
43 | #include <KStatusBar> |
44 | #include <KToggleAction> |
45 | |
46 | #include <QPointer> |
47 | |
48 | MainWindow::MainWindow( bool KSameMode, QWidget* parent ) |
49 | : KXmlGuiWindow(parent), |
50 | m_KSameMode(KSameMode), |
51 | m_gameClock(NULL), |
52 | m_gameScore(0), |
53 | m_lastRemainCount(0) |
54 | { |
55 | m_scene = new GameScene; |
56 | GameView* view = new GameView( m_scene ); |
57 | view->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); |
58 | view->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); |
59 | view->setFrameStyle( QFrame::NoFrame ); |
60 | view->setCacheMode( QGraphicsView::CacheBackground ); |
61 | setCentralWidget( view ); |
62 | |
63 | if ( m_KSameMode ) { |
64 | // statusBar()->insertItem( i18n( "Colors: XX" ), 1 ); |
65 | // statusBar()->insertItem( i18n( "Board: XXXXXX" ), 2 ); |
66 | statusBar()->insertItem( i18n( "Marked: 0" ), 3 ); |
67 | statusBar()->insertItem( i18n( "Score: 0" ), 4 ); |
68 | connect( m_scene, SIGNAL(remainCountChanged(int)), this, SLOT(changeScore(int)) ); |
69 | connect( m_scene, SIGNAL(markedCountChanged(int)), this, SLOT(changeMarkedCount(int)) ); |
70 | } |
71 | else { |
72 | m_gameClock = new KGameClock( this, KGameClock::MinSecOnly ); |
73 | statusBar()->insertItem( i18n( "Pieces: 0" ), 0 ); |
74 | statusBar()->insertItem( i18n( "Time: 00:00" ), 1 ); |
75 | connect( m_scene, SIGNAL(remainCountChanged(int)), this, SLOT(changeRemainCount(int)) ); |
76 | connect( m_gameClock, SIGNAL(timeChanged(QString)), this, SLOT(changeTime(QString)) ); |
77 | } |
78 | |
79 | connect( m_scene, SIGNAL(gameFinished(int)), this, SLOT(onGameOver(int)) ); |
80 | |
81 | setupActions(); |
82 | |
83 | loadSettings(); |
84 | |
85 | newGame( qrand() ); |
86 | } |
87 | |
88 | MainWindow::~MainWindow() |
89 | { |
90 | } |
91 | |
92 | void MainWindow::configureNotifications() |
93 | { |
94 | KNotifyConfigWidget::configure( this ); |
95 | } |
96 | |
97 | void MainWindow::configureSettings() |
98 | { |
99 | if ( KConfigDialog::showDialog( QLatin1String( "settings" ) ) ) |
100 | return; |
101 | |
102 | KConfigDialog* dialog = new KConfigDialog( this, QLatin1String( "settings" ), Settings::self() ); |
103 | dialog->addPage( new GameConfig( dialog ), i18n( "General" ), QLatin1String( "games-config-options" ) ); |
104 | dialog->addPage( new KgThemeSelector( m_scene->themeProvider() ), i18n( "Theme" ), QLatin1String( "games-config-theme" ) ); |
105 | dialog->addPage( new BackgroundSelector( dialog ), i18n( "Background" ), QLatin1String( "games-config-background" ) ); |
106 | if ( !m_KSameMode ) |
107 | dialog->addPage( new CustomGameConfig( dialog ), i18n( "Custom Game" ), QLatin1String( "games-config-custom" ) ); |
108 | connect(m_scene->themeProvider(), SIGNAL(currentThemeChanged(const KgTheme*)), SLOT(loadSettings())); //setBackgroundType! |
109 | connect( dialog, SIGNAL(settingsChanged(QString)), this, SLOT(loadSettings()) ); |
110 | dialog->setHelp( QString(), QLatin1String( "klickety" ) ); |
111 | dialog->show(); |
112 | } |
113 | |
114 | void MainWindow::loadSettings() |
115 | { |
116 | m_scene->setShowBoundLines( Settings::showBoundLines() ); |
117 | m_scene->setEnableAnimation( Settings::enableAnimation() ); |
118 | m_scene->setEnableHighlight( Settings::enableHighlight() ); |
119 | m_scene->setBackgroundType( Settings::bgType() ); |
120 | } |
121 | |
122 | void MainWindow::newGame( int gameId ) |
123 | { |
124 | if ( !confirmAbort() ) |
125 | return; |
126 | |
127 | m_pauseAction->setChecked( false ); |
128 | m_pauseAction->setEnabled( true ); |
129 | |
130 | if ( m_KSameMode ) { |
131 | m_gameScore = 0; |
132 | m_lastRemainCount = 15 * 10; |
133 | m_scene->startNewGame( 15, 10, 3, gameId ); |
134 | return; |
135 | } |
136 | |
137 | m_gameClock->restart(); |
138 | |
139 | switch ( Kg::difficultyLevel() ) { |
140 | case KgDifficultyLevel::VeryEasy: |
141 | m_scene->startNewGame( 10, 16, 3, gameId ); |
142 | break; |
143 | case KgDifficultyLevel::Easy: |
144 | m_scene->startNewGame( 10, 16, 4, gameId ); |
145 | break; |
146 | case KgDifficultyLevel::Medium: |
147 | m_scene->startNewGame( 10, 16, 5, gameId ); |
148 | break; |
149 | case KgDifficultyLevel::Hard: |
150 | m_scene->startNewGame( 10, 16, 6, gameId ); |
151 | break; |
152 | case KgDifficultyLevel::Custom: |
153 | m_scene->startNewGame( Settings::customWidth(), |
154 | Settings::customHeight(), |
155 | Settings::customColorCount(), gameId ); |
156 | break; |
157 | default: |
158 | break; |
159 | } |
160 | } |
161 | |
162 | void MainWindow::newNumGame() |
163 | { |
164 | if ( !confirmAbort() ) |
165 | return; |
166 | |
167 | bool ok = false; |
168 | int userGameId = KInputDialog::getInteger( i18n( "Select Board" ), |
169 | i18n( "Select a board number:" ), |
170 | qrand(), 1, RAND_MAX, 1, |
171 | &ok, this ); |
172 | if ( ok ) |
173 | newGame( userGameId ); |
174 | } |
175 | |
176 | void MainWindow::pauseGame( bool isPaused ) |
177 | { |
178 | m_scene->setPaused( isPaused ); |
179 | if ( !m_KSameMode ) { |
180 | if ( isPaused ) |
181 | m_gameClock->pause(); |
182 | else |
183 | m_gameClock->resume(); |
184 | } |
185 | } |
186 | |
187 | void MainWindow::restartGame() |
188 | { |
189 | if ( confirmAbort() ) { |
190 | m_pauseAction->setChecked( false ); |
191 | m_pauseAction->setEnabled( true ); |
192 | if ( m_KSameMode ) { |
193 | m_gameScore = 0; |
194 | m_lastRemainCount = 0; |
195 | } |
196 | else { |
197 | m_gameClock->restart(); |
198 | } |
199 | m_scene->restartGame(); |
200 | } |
201 | } |
202 | |
203 | void MainWindow::loadGame() |
204 | { |
205 | QString fileName = KFileDialog::getOpenFileName( KUrl(), QLatin1String( "*.klickety" ), this ); |
206 | if ( fileName.isEmpty() || !confirmAbort() ) |
207 | return; |
208 | |
209 | m_pauseAction->setChecked( false ); |
210 | m_pauseAction->setEnabled( true ); |
211 | KConfig config( fileName, KConfig::SimpleConfig ); |
212 | KConfigGroup group = config.group( "Savegame" ); |
213 | m_scene->loadGame( group ); |
214 | } |
215 | |
216 | void MainWindow::saveGame() |
217 | { |
218 | QString fileName = KFileDialog::getSaveFileName( KUrl(), QLatin1String( "*.klickety" ), this ); |
219 | if ( fileName.isEmpty() ) |
220 | return; |
221 | KConfig config( fileName, KConfig::SimpleConfig ); |
222 | KConfigGroup group = config.group( "Savegame" ); |
223 | m_scene->saveGame( group ); |
224 | } |
225 | |
226 | void MainWindow::changeMarkedCount( int markedCount ) |
227 | { |
228 | int markedScore = ( markedCount < 2 ) ? 0 : ( ( markedCount - 2 ) * ( markedCount - 2 ) ); |
229 | statusBar()->changeItem( i18np( "Marked: %2 (1 Point)" , "Marked: %2 (%1 Points)" , markedScore, markedCount ), 3 ); |
230 | } |
231 | |
232 | void MainWindow::changeScore( int remainCount ) |
233 | { |
234 | if ( m_lastRemainCount == 0 ) { |
235 | // new game or restart |
236 | m_lastRemainCount = remainCount; |
237 | statusBar()->changeItem( i18n( "Score: 0" ), 4 ); |
238 | return; |
239 | } |
240 | int removedCount = m_lastRemainCount - remainCount; |
241 | if ( removedCount > 0 ) { |
242 | // normal move |
243 | int score = ( removedCount < 2 ) ? 0 : ( ( removedCount - 2 ) * ( removedCount - 2 ) ); |
244 | m_gameScore += score; |
245 | } |
246 | else { |
247 | // undo action, minus the score |
248 | int score = ( removedCount > -2 ) ? 0 : ( ( removedCount + 2 ) * ( removedCount + 2 ) ); |
249 | m_gameScore -= score; |
250 | } |
251 | statusBar()->changeItem( i18n( "Score: %1" , m_gameScore ), 4 ); |
252 | m_lastRemainCount = remainCount; |
253 | } |
254 | |
255 | void MainWindow::changeRemainCount( int remainCount ) |
256 | { |
257 | statusBar()->changeItem( i18n( "Pieces: %1" , remainCount ), 0 ); |
258 | } |
259 | |
260 | void MainWindow::changeTime( const QString& newTime ) |
261 | { |
262 | statusBar()->changeItem( i18n( "Time: %1" , newTime ), 1 ); |
263 | } |
264 | |
265 | void MainWindow::showHighscores() |
266 | { |
267 | if ( m_KSameMode ) { |
268 | QPointer<KScoreDialog> d = new KScoreDialog( KScoreDialog::Name | KScoreDialog::Score, this ); |
269 | d->initFromDifficulty(Kg::difficulty(), /*setConfigGroup=*/ false); |
270 | d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) ); |
271 | d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" ); |
272 | d->exec(); |
273 | delete d; |
274 | return; |
275 | } |
276 | |
277 | QPointer<KScoreDialog> d = new KScoreDialog( KScoreDialog::Name, this ); |
278 | d->addField( KScoreDialog::Custom1, i18n( "Remaining pieces" ), QLatin1String( "remains" ) ); |
279 | d->addField( KScoreDialog::Custom2, i18n( "Time" ), QLatin1String( "time" ) ); |
280 | d->initFromDifficulty(Kg::difficulty(), /*setConfigGroup=*/ true); |
281 | d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" ); |
282 | d->hideField( KScoreDialog::Score ); |
283 | d->exec(); |
284 | delete d; |
285 | } |
286 | |
287 | void MainWindow::onGameOver( int remainCount ) |
288 | { |
289 | m_pauseAction->setEnabled( false ); |
290 | |
291 | if ( m_KSameMode ) { |
292 | if ( remainCount == 0 ) { |
293 | // if the board is empty, give a bonus |
294 | m_gameScore += 1000; |
295 | } |
296 | |
297 | QPointer<KScoreDialog> d = new KScoreDialog( KScoreDialog::Name | KScoreDialog::Score, this ); |
298 | d->initFromDifficulty(Kg::difficulty(), /*setConfigGroup=*/ false); |
299 | d->setConfigGroup( qMakePair( QByteArray( "KSame" ), i18n( "High Scores" ) ) ); |
300 | d->setHiddenConfigGroups( QList<QByteArray>() << "Very Easy" << "Easy" << "Medium" << "Hard" << "Custom" ); |
301 | |
302 | KScoreDialog::FieldInfo scoreInfo; |
303 | scoreInfo[ KScoreDialog::Score ].setNum( m_gameScore ); |
304 | |
305 | if ( d->addScore( scoreInfo ) ) |
306 | d->exec(); |
307 | delete d; |
308 | return; |
309 | } |
310 | |
311 | m_gameClock->pause(); |
312 | |
313 | QPointer<KScoreDialog> d = new KScoreDialog( KScoreDialog::Name, this ); |
314 | d->addField( KScoreDialog::Custom1, i18n( "Remaining pieces" ), QLatin1String( "remains" ) ); |
315 | d->addField( KScoreDialog::Custom2, i18n( "Time" ), QLatin1String( "time" ) ); |
316 | d->initFromDifficulty(Kg::difficulty(), /*setConfigGroup=*/ true); |
317 | d->setHiddenConfigGroups( QList<QByteArray>() << "KSame" ); |
318 | d->hideField( KScoreDialog::Score ); |
319 | |
320 | KScoreDialog::FieldInfo scoreInfo; |
321 | scoreInfo[KScoreDialog::Custom1].setNum( remainCount ); |
322 | scoreInfo[KScoreDialog::Custom2] = m_gameClock->timeString(); |
323 | // remainCount*10000000 is much bigger than a usual time seconds |
324 | scoreInfo[KScoreDialog::Score].setNum( remainCount*10000000 + m_gameClock->seconds() ); |
325 | if ( d->addScore( scoreInfo, KScoreDialog::LessIsMore ) != 0 ) |
326 | d->exec(); |
327 | delete d; |
328 | } |
329 | |
330 | bool MainWindow::confirmAbort() |
331 | { |
332 | return m_scene->isGameFinished() || ( KMessageBox::questionYesNo( this, i18n( "Do you want to resign?" ), |
333 | i18n( "New Game" ),KGuiItem( i18n( "Resign" ) ), KStandardGuiItem::cancel() ) == KMessageBox::Yes ); |
334 | } |
335 | |
336 | void MainWindow::setupActions() |
337 | { |
338 | // game menu |
339 | KStandardGameAction::gameNew( this, SLOT(newGame()), actionCollection() ); |
340 | if ( !m_KSameMode ) { |
341 | KStandardGameAction::load( this, SLOT(loadGame()), actionCollection() ); |
342 | KStandardGameAction::save( this, SLOT(saveGame()), actionCollection() ); |
343 | } |
344 | KStandardGameAction::restart( this, SLOT(restartGame()), actionCollection() ); |
345 | KStandardGameAction::highscores( this, SLOT(showHighscores()), actionCollection() ); |
346 | m_pauseAction = KStandardGameAction::pause( this, SLOT(pauseGame(bool)), actionCollection() ); |
347 | KStandardGameAction::quit( this, SLOT(close()), actionCollection() ); |
348 | KAction* m_newNumGameAction = new KAction( i18n( "New Numbered Game..." ), actionCollection() ); |
349 | actionCollection()->addAction( QLatin1String( "game_new_numeric" ), m_newNumGameAction ); |
350 | connect( m_newNumGameAction, SIGNAL(triggered(bool)), this, SLOT(newNumGame()) ); |
351 | |
352 | // move menu |
353 | KAction* undoAction = KStandardGameAction::undo( m_scene, SLOT(undoMove()), actionCollection() ); |
354 | undoAction->setEnabled( false ); |
355 | connect( m_scene, SIGNAL(canUndoChanged(bool)), undoAction, SLOT(setEnabled(bool)) ); |
356 | KAction* redoAction = KStandardGameAction::redo( m_scene, SLOT(redoMove()), actionCollection() ); |
357 | redoAction->setEnabled( false ); |
358 | connect( m_scene, SIGNAL(canRedoChanged(bool)), redoAction, SLOT(setEnabled(bool)) ); |
359 | |
360 | KAction* undoAllAction = actionCollection()->addAction( QLatin1String( "move_undo_all" ) ); |
361 | undoAllAction->setIcon( KIcon( QLatin1String( "media-skip-backward" ) ) ); |
362 | undoAllAction->setText( i18n( "Undo All" ) ); |
363 | undoAllAction->setEnabled( false ); |
364 | connect( m_scene, SIGNAL(canUndoChanged(bool)), undoAllAction, SLOT(setEnabled(bool)) ); |
365 | connect( undoAllAction, SIGNAL(triggered(bool)), m_scene, SLOT(undoAllMove()) ); |
366 | KAction* redoAllAction = actionCollection()->addAction( QLatin1String( "move_redo_all" ) ); |
367 | redoAllAction->setIcon( KIcon( QLatin1String( "media-skip-forward" ) ) ); |
368 | redoAllAction->setText( i18n( "Redo All" ) ); |
369 | redoAllAction->setEnabled( false ); |
370 | connect( m_scene, SIGNAL(canRedoChanged(bool)), redoAllAction, SLOT(setEnabled(bool)) ); |
371 | connect( redoAllAction, SIGNAL(triggered(bool)), m_scene, SLOT(redoAllMove()) ); |
372 | |
373 | // settings menu |
374 | KStandardAction::preferences( this, SLOT(configureSettings()), actionCollection() ); |
375 | KStandardAction::configureNotifications( this, SLOT(configureNotifications()), actionCollection() ); |
376 | |
377 | if ( m_KSameMode ) { |
378 | setupGUI( QSize( 576, 384 ) ); |
379 | return; |
380 | } |
381 | |
382 | Kg::difficulty()->addStandardLevelRange( |
383 | KgDifficultyLevel::VeryEasy, KgDifficultyLevel::Hard, |
384 | KgDifficultyLevel::Easy //default |
385 | ); |
386 | Kg::difficulty()->addLevel(new KgDifficultyLevel(1000, |
387 | QByteArray( "Custom" ), i18n( "Custom" ) |
388 | )); |
389 | KgDifficultyGUI::init(this); |
390 | connect(Kg::difficulty(), SIGNAL(currentLevelChanged(const KgDifficultyLevel*)), SLOT(newGame())); |
391 | |
392 | setupGUI( QSize( 340, 510 ) ); |
393 | } |
394 | |