mainwindow.cpp 15.7 KB
Newer Older
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
1
2
3
/*******************************************************************
 *
 * Copyright 2006 Dmitry Suzdalev <dimsuz@gmail.com>
4
 * Copyright 2010 Brian Croom <brian.s.croom@gmail.com>
5
 * Copyright 2013 Denis Kuplyakov <dener.kup@gmail.com>
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 *
 * This file is part of the KDE project "KReversi"
 *
 * KReversi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * KReversi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with KReversi; see the file COPYING.  If not, write to
 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 ********************************************************************/
Denis Kuplyakov's avatar
Denis Kuplyakov committed
25
26
27
28
29
30
31
32
33
34
35
36
#include <mainwindow.h>

#include <KDebug>
#include <KIcon>
#include <KLocale>
#include <KMessageBox>
#include <KStandardDirs>
#include <KStatusBar>
#include <KActionCollection>
#include <KStandardGameAction>
#include <KExtHighscore>

37
#include <commondefs.h>
Denis Kuplyakov's avatar
Denis Kuplyakov committed
38
39
#include <kreversihumanplayer.h>
#include <kreversicomputerplayer.h>
40

41
42
static const int BLACK_STATUSBAR_ID = 1;
static const int WHITE_STATUSBAR_ID = 2;
43
static const int COMMON_STATUSBAR_ID = 0;
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
44

45
46
KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo)
    : KXmlGuiWindow(parent), m_view(0), m_game(0),
47
      m_historyDock(0), m_historyView(0),
48
49
      m_firstShow(true), m_startInDemoMode(startDemo),
      m_undoAct(0), m_hintAct(0), m_startDialog(0)
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
50
{
51
    memset(m_player, 0, sizeof(m_player));
52

53
54
55
    m_provider = new KgThemeProvider();
    m_provider->discoverThemes("appdata", QLatin1String("pics"));

56
57
58
59
    statusBar()->insertItem(i18n("Press start game!"), COMMON_STATUSBAR_ID);
    statusBar()->insertItem("", BLACK_STATUSBAR_ID);
    statusBar()->insertItem("", WHITE_STATUSBAR_ID);

60
    // initialize difficulty stuff
61
62
63
64
    Kg::difficulty()->addStandardLevelRange(
        KgDifficultyLevel::VeryEasy, KgDifficultyLevel::Impossible,
        KgDifficultyLevel::Easy //default
    );
65
66

    KgDifficultyGUI::init(this);
67
    connect(Kg::difficulty(), SIGNAL(currentLevelChanged(const KgDifficultyLevel*)), SLOT(levelChanged()));
68
    Kg::difficulty()->setEditable(false);
69

70
    // initialize history dock
71
72
73
74
75
    m_historyView = new QListWidget(this);
    m_historyView->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
    m_historyDock = new QDockWidget(i18n("Move History"));
    m_historyDock->setWidget(m_historyView);
    m_historyDock->setObjectName("history_dock");
76

77
78
    m_historyDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
    addDockWidget(Qt::RightDockWidgetArea, m_historyDock);
79

80
    // create main game view
81
    m_view = new KReversiView(m_game, this, m_provider);
82
83
    setCentralWidget(m_view);

84
85
86
87
    // initialise dialog handler
    m_startDialog = new StartGameDialog(this, m_provider);
    connect(m_startDialog, SIGNAL(startGame()), this, SLOT(slotDialogReady()));

88
89
    // initialise actions
    setupActionsInit();
90

91
    // load saved settings
92
93
    loadSettings();

94
    setupGUI(qApp->desktop()->availableGeometry().size() * 0.7);
95

96
    m_historyDock->hide();
97
98
99
100
101
}

KReversiMainWindow::~KReversiMainWindow()
{
    delete m_provider;
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
102
103
}

104
void KReversiMainWindow::setupActionsInit()
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
105
{
106
    // Common actions
107
    KStandardGameAction::gameNew(this, SLOT(slotNewGame()), actionCollection());
Denis Kuplyakov's avatar
Denis Kuplyakov committed
108
    KStandardGameAction::highscores(this, SLOT(slotHighscores()), actionCollection());
109
    KStandardGameAction::quit(this, SLOT(close()), actionCollection());
110

Denis Kuplyakov's avatar
Denis Kuplyakov committed
111
112
113
    // Undo
    m_undoAct = KStandardGameAction::undo(this, SLOT(slotUndo()), actionCollection());
    m_undoAct->setEnabled(false);   // nothing to undo at the start of the game
114

Denis Kuplyakov's avatar
Denis Kuplyakov committed
115
    // Hint
Denis Kuplyakov's avatar
Denis Kuplyakov committed
116
117
    m_hintAct = KStandardGameAction::hint(m_view, SLOT(slotHint()), actionCollection());
    m_hintAct->setEnabled(false);
118

119
120
121
122
123
124
125
126
127
    // Last move
    m_showLast = new KToggleAction(KIcon(QLatin1String("lastmoves")), i18n("Show Last Move"), this);
    actionCollection()->addAction(QLatin1String("show_last_move"), m_showLast);
    connect(m_showLast, SIGNAL(triggered(bool)), m_view, SLOT(setShowLastMove(bool)));

    // Legal moves
    m_showLegal = new KToggleAction(KIcon(QLatin1String("legalmoves")), i18n("Show Legal Moves"), this);
    actionCollection()->addAction(QLatin1String("show_legal_moves"), m_showLegal);
    connect(m_showLegal, SIGNAL(triggered(bool)), m_view, SLOT(setShowLegalMoves(bool)));
128

129
130
131
    // Animation speed
    m_animSpeedAct = new KSelectAction(i18n("Animation Speed"), this);
    actionCollection()->addAction(QLatin1String("anim_speed"), m_animSpeedAct);
132

133
134
135
136
    QStringList acts;
    acts << i18n("Slow") << i18n("Normal") << i18n("Fast");
    m_animSpeedAct->setItems(acts);
    connect(m_animSpeedAct, SIGNAL(triggered(int)), SLOT(slotAnimSpeedChanged(int)));
137

138
139
140
141
    // Chip's color
    m_coloredChipsAct = new KToggleAction(i18n("Use Colored Chips"), this);
    actionCollection()->addAction(QLatin1String("use_colored_chips"), m_coloredChipsAct);
    connect(m_coloredChipsAct, SIGNAL(triggered(bool)), SLOT(slotUseColoredChips(bool)));
142

143
144
145
146
147
    // Move history
    // NOTE: read/write this from/to config file? Or not necessary?
    m_showMovesAct = new KToggleAction(KIcon(QLatin1String("view-history")), i18n("Show Move History"), this);
    actionCollection()->addAction(QLatin1String("show_moves"), m_showMovesAct);
    connect(m_showMovesAct, SIGNAL(triggered(bool)), SLOT(slotShowMovesHistory(bool)));
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
148
}
149

150
151
void KReversiMainWindow::loadSettings()
{
152
153
154
    // Animation speed
    m_animSpeedAct->setCurrentItem(Preferences::animationSpeed());
    m_view->setAnimationSpeed(Preferences::animationSpeed());
155

156
157
158
    // Chip's color
    m_coloredChipsAct->setChecked(Preferences::useColoredChips());
    m_view->setChipsPrefix(Preferences::useColoredChips() ?
159
160
161
                           Colored : BlackWhite);
    m_startDialog->setChipsPrefix(Preferences::useColoredChips() ?
                                       Colored : BlackWhite);
162
163
}

164
void KReversiMainWindow::levelChanged()
165
{
166
167
168
    // we are assuming that level can be changed here only when it is
    // USER-AI or AI-USER match

169
    int skill = Utils::difficultyLevelToInt();
170
171
172
173
174

    if (m_nowPlayingInfo.type[White] == GameStartInformation::AI)
        ((KReversiComputerPlayer *)(m_player[White]))->setSkill(skill);
    else if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human)
        ((KReversiComputerPlayer *)(m_player[Black]))->setSkill(skill);
175
176
}

177
178
void KReversiMainWindow::slotAnimSpeedChanged(int speed)
{
179
180
181
    m_view->setAnimationSpeed(speed);
    Preferences::setAnimationSpeed(speed);
    Preferences::self()->writeConfig();
182
183
}

184
185
void KReversiMainWindow::slotUseColoredChips(bool toggled)
{
186
187
188
    ChipsPrefix chipsPrefix = m_coloredChipsAct->isChecked() ?
                                            Colored :
                                            BlackWhite;
189
    m_view->setChipsPrefix(chipsPrefix);
190
    m_startDialog->setChipsPrefix(chipsPrefix);
191
192
    Preferences::setUseColoredChips(toggled);
    Preferences::self()->writeConfig();
193
194
}

195
196
void KReversiMainWindow::slotShowMovesHistory(bool toggled)
{
197
198
    m_historyDock->setVisible(toggled);
    m_view->setShowBoardLabels(toggled);
199
200
}

Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
201
void KReversiMainWindow::slotNewGame()
202
{
203
    m_startDialog->exec();
Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
204
205
}

206
207
void KReversiMainWindow::slotGameOver()
{
Denis Kuplyakov's avatar
Denis Kuplyakov committed
208
    m_hintAct->setEnabled(false);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
209
    m_undoAct->setEnabled(m_game->canUndo());
Denis Kuplyakov's avatar
Denis Kuplyakov committed
210

211
212
    int blackScore = m_game->playerScore(Black);
    int whiteScore = m_game->playerScore(White);
213

Denis Kuplyakov's avatar
Denis Kuplyakov committed
214
215
    bool storeScore = false;
    KExtHighscore::Score score;
216

217
218
219
    QString res;
    if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human
            && m_nowPlayingInfo.type[White] == GameStartInformation::AI) { // we are playing black
Denis Kuplyakov's avatar
Denis Kuplyakov committed
220
221
222
        storeScore = true;
        KExtHighscore::setGameType(((KReversiComputerPlayer *)m_player[White])->lowestSkill());
        score.setScore(blackScore);
223
224
        if (blackScore == whiteScore) {
            res = i18n("Game is drawn!");
Denis Kuplyakov's avatar
Denis Kuplyakov committed
225
            score.setType(KExtHighscore::Draw);
226
227
        } else if (blackScore > whiteScore) {
            res = i18n("You win!");
Denis Kuplyakov's avatar
Denis Kuplyakov committed
228
            score.setType(KExtHighscore::Won);
229
230
        } else {
            res = i18n("You have lost!");
Denis Kuplyakov's avatar
Denis Kuplyakov committed
231
            score.setType(KExtHighscore::Lost);
232
233
        }
    } else if (m_nowPlayingInfo.type[White] == GameStartInformation::Human
Denis Kuplyakov's avatar
Denis Kuplyakov committed
234
               && m_nowPlayingInfo.type[Black] == GameStartInformation::AI) { // we are playing white
Denis Kuplyakov's avatar
Denis Kuplyakov committed
235
236
237
238
        storeScore = true;
        KExtHighscore::setGameType(((KReversiComputerPlayer *)m_player[Black])->lowestSkill());
        score.setScore(whiteScore);
        if (blackScore == whiteScore) {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
239
240
            res = i18n("Game is drawn!");
            score.setType(KExtHighscore::Draw);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
241
        } else if (blackScore < whiteScore) {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
242
243
            res = i18n("You win!");
            score.setType(KExtHighscore::Won);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
244
        } else {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
245
246
            res = i18n("You have lost!");
            score.setType(KExtHighscore::Lost);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
247
        }
248

249
250
    } else if (m_nowPlayingInfo.type[Black] == GameStartInformation::Human
               && m_nowPlayingInfo.type[White] == GameStartInformation::Human) { // friends match
Denis Kuplyakov's avatar
Denis Kuplyakov committed
251
        if (blackScore == whiteScore) {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
252
            res = i18n("Game is drawn!");
Denis Kuplyakov's avatar
Denis Kuplyakov committed
253
        } else if (blackScore > whiteScore) {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
254
            res = i18n("%1 has won!", m_nowPlayingInfo.name[Black]);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
255
        } else {
Denis Kuplyakov's avatar
Denis Kuplyakov committed
256
            res = i18n("%1 has won!", m_nowPlayingInfo.name[White]);
Denis Kuplyakov's avatar
Denis Kuplyakov committed
257
        }
258
259
260
261
    } else { // using Black White names in other cases
        if (blackScore == whiteScore) {
            res = i18n("Game is drawn!");
        } else if (blackScore > whiteScore) {
262
            res = i18n("%1 has won!", Utils::colorToString(Black));
263
        } else {
264
            res = i18n("%1 has won!", Utils::colorToString(White));
265
266
        }
    }
267

268
    if (m_nowPlayingInfo.type[Black] == GameStartInformation::AI
Denis Kuplyakov's avatar
Denis Kuplyakov committed
269
            && m_nowPlayingInfo.type[White] == GameStartInformation::AI) {
270
271
        res += i18n("\n%1: %2", Utils::colorToString(Black), blackScore);
        res += i18n("\n%1: %2", Utils::colorToString(White), whiteScore);
272
273
274
275
276
    } else {
        res += i18n("\n%1: %2", m_nowPlayingInfo.name[Black], blackScore);
        res += i18n("\n%1: %2", m_nowPlayingInfo.name[White], whiteScore);
    }

277
    KMessageBox::information(this, res, i18n("Game over"));
Denis Kuplyakov's avatar
Denis Kuplyakov committed
278
279
280

    if (storeScore)
        KExtHighscore::submitScore(score, this);
281
282
}

283
284
void KReversiMainWindow::slotMoveFinished()
{
285
    updateHistory();
286
    updateStatusBar();
Denis Kuplyakov's avatar
Denis Kuplyakov committed
287
288

    m_hintAct->setEnabled(m_game->isHintAllowed());
Denis Kuplyakov's avatar
Denis Kuplyakov committed
289
    m_undoAct->setEnabled(m_game->canUndo());
290
291
}

Denis Kuplyakov's avatar
Denis Kuplyakov committed
292
293
void KReversiMainWindow::updateHistory()
{
294
295
    MoveList history = m_game->getHistory();
    m_historyView->clear();
296

297
298
    for (int i = 0; i < history.size(); i++) {
        QString numStr = QString::number(i + 1) + QLatin1String(". ");
299
        m_historyView->addItem(numStr + Utils::moveToString(history.at(i)));
300
    }
301

302
303
304
    QListWidgetItem *last = m_historyView->item(m_historyView->count() - 1);
    m_historyView->setCurrentItem(last);
    m_historyView->scrollToItem(last);
305
306
}

Dmitry Suzdalev's avatar
Dmitry Suzdalev committed
307
308
void KReversiMainWindow::slotUndo()
{
Denis Kuplyakov's avatar
Denis Kuplyakov committed
309
310
    // scene will automatically notice that it needs to update
    m_game->undo();
311

Denis Kuplyakov's avatar
Denis Kuplyakov committed
312
313
    updateHistory();
    updateStatusBar();
314

Denis Kuplyakov's avatar
Denis Kuplyakov committed
315
316
    m_undoAct->setEnabled(m_game->canUndo());
    m_hintAct->setEnabled(m_game->isHintAllowed());
317
318
}

319
320
void KReversiMainWindow::slotHighscores()
{
Denis Kuplyakov's avatar
Denis Kuplyakov committed
321
    KExtHighscore::show(this);
322
323
324
325
}

void KReversiMainWindow::slotDialogReady()
{
326
    GameStartInformation info = m_startDialog->createGameStartInformation();
327
    receivedGameStartInformation(info);
328
329
}

330
void KReversiMainWindow::showEvent(QShowEvent*)
331
{
332
    if (m_firstShow && m_startInDemoMode) {
333
        kDebug() << "starting demo...";
334
        startDemo();
Denis Kuplyakov's avatar
Denis Kuplyakov committed
335
    } else if (m_firstShow) {
336
        QTimer::singleShot(0, this, SLOT(slotNewGame()));
337
338
339
340
    }
    m_firstShow = false;
}

341
void KReversiMainWindow::updateStatusBar()
342
{
343
344
345
346
347
348
    if (m_game->isGameOver()) {
        statusBar()->changeItem(i18n("GAME OVER"), COMMON_STATUSBAR_ID);
    }

    if (m_nowPlayingInfo.type[Black] == GameStartInformation::AI
            && m_nowPlayingInfo.type[White] == GameStartInformation::AI) { // using Black White names
349
        statusBar()->changeItem(i18n("%1: %2",
350
                                     Utils::colorToString(Black),
351
                                     m_game->playerScore(Black)), BLACK_STATUSBAR_ID);
352
        statusBar()->changeItem(i18n("%1: %2",
353
                                     Utils::colorToString(White),
354
355
356
                                     m_game->playerScore(White)), WHITE_STATUSBAR_ID);

        if (!m_game->isGameOver()) {
357
            statusBar()->changeItem(i18n("%1 turn",
Denis Kuplyakov's avatar
Denis Kuplyakov committed
358
                                         Utils::colorToString(m_game->currentPlayer())),
359
                                    COMMON_STATUSBAR_ID);
360
361
362
363
364
365
366
        }
    } else { // using player's names
        statusBar()->changeItem(i18n("%1: %2", m_nowPlayingInfo.name[Black],
                                     m_game->playerScore(Black)), BLACK_STATUSBAR_ID);
        statusBar()->changeItem(i18n("%1: %2", m_nowPlayingInfo.name[White],
                                     m_game->playerScore(White)), WHITE_STATUSBAR_ID);

367
        if (!m_game->isGameOver() && m_game->currentPlayer() != NoColor) {
368
369
370
371
            statusBar()->changeItem(i18n("%1's turn",
                                         m_nowPlayingInfo.name[m_game->currentPlayer()]), COMMON_STATUSBAR_ID);
        }
    }
372
373
374
375
}


// TODO: test it!!!
Denis Kuplyakov's avatar
Denis Kuplyakov committed
376
377
void KReversiMainWindow::startDemo()
{
378
    GameStartInformation info;
379
    info.name[0] = info.name[1] = i18n("Computer");
380
    info.type[0] = info.type[1] = GameStartInformation::AI;
381
    info.skill[0] = info.skill[1] = Utils::difficultyLevelToInt();
382
383

    receivedGameStartInformation(info);
384
385
}

Denis Kuplyakov's avatar
Denis Kuplyakov committed
386
387
void KReversiMainWindow::clearPlayers()
{
388
    for (int i = 0; i < 2; i++) // iterating through white to black
Denis Kuplyakov's avatar
Denis Kuplyakov committed
389
        if (m_player[i]) {
390
            m_player[i]->disconnect();
391
392
393
394
395
396
            delete m_player[i];
            m_player[i] = 0;
        }
}

void KReversiMainWindow::receivedGameStartInformation(GameStartInformation info)
397
{
398
399
400
401
    clearPlayers();
    m_nowPlayingInfo = info;

    for (int i = 0; i < 2; i++) // iterating through black and white
Denis Kuplyakov's avatar
Denis Kuplyakov committed
402
        if (info.type[i] == GameStartInformation::AI) {
403
404
405
            m_player[i] = new KReversiComputerPlayer(ChipColor(i), info.name[i]);
            ((KReversiComputerPlayer *)(m_player[i]))->setSkill(info.skill[i]);
            levelChanged();
Denis Kuplyakov's avatar
Denis Kuplyakov committed
406
        } else {
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
            m_player[i] = new KReversiHumanPlayer(ChipColor(i), info.name[i]);
        }

    m_game = new KReversiGame(m_player[Black], m_player[White]);

    m_view->setGame(m_game);

    connect(m_game, SIGNAL(gameOver()), SLOT(slotGameOver()));
    connect(m_game, SIGNAL(moveFinished()), SLOT(slotMoveFinished()));

    for (int i = 0; i < 2; i++) // iterating white to black
        if (info.type[i] == GameStartInformation::Human)
            connect(m_view, SIGNAL(userMove(KReversiPos)),
                    (KReversiHumanPlayer *)(m_player[i]), SLOT(onUICellClick(KReversiPos)));

422
    updateStatusBar();
423
    updateHistory();
Denis Kuplyakov's avatar
Denis Kuplyakov committed
424

425
    if (info.type[White] == GameStartInformation::AI
Denis Kuplyakov's avatar
Denis Kuplyakov committed
426
            && info.type[Black] == GameStartInformation::Human) {
427
        Kg::difficulty()->setEditable(true);
428
        Kg::difficulty()->select(Utils::intToDifficultyLevel(info.skill[White]));
Denis Kuplyakov's avatar
Denis Kuplyakov committed
429
430
    } else if (info.type[White] == GameStartInformation::Human
               && info.type[Black] == GameStartInformation::AI) {
431
        Kg::difficulty()->setEditable(true);
432
        Kg::difficulty()->select(Utils::intToDifficultyLevel(info.skill[Black]));
Denis Kuplyakov's avatar
Denis Kuplyakov committed
433
    } else
434
435
        Kg::difficulty()->setEditable(false);

Denis Kuplyakov's avatar
Denis Kuplyakov committed
436
    m_hintAct->setEnabled(m_game->isHintAllowed());
Denis Kuplyakov's avatar
Denis Kuplyakov committed
437
    m_undoAct->setEnabled(m_game->canUndo());
438
}