kgoldrunner.cpp 43.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/****************************************************************************
 *    Copyright 2002  Marco Krüger <grisuji@gmx.de>                         *
 *    Copyright 2002  Ian Wadham <iandw.au@gmail.com>                       *
 *    Copyright 2009  Ian Wadham <iandw.au@gmail.com>                       *
 *                                                                          *
 *    This program 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 of        *
 *    the License, or (at your option) any later version.                   *
 *                                                                          *
 *    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. *
 ****************************************************************************/
Ian Wadham's avatar
Ian Wadham committed
19

20
21
#include "kgoldrunner.h"

22
#include <QAction>
23
#include <QApplication>
Laurent Montel's avatar
Laurent Montel committed
24
#include <QDesktopWidget>
25
26
27
#include <QIcon>
#include <QKeyEvent>
#include <QKeySequence>
28
#include <QShortcut>
Laurent Montel's avatar
Laurent Montel committed
29

30
31
32
33
34
35
36
37
38
39
40
41
#include <KActionCollection>
#include <KConfig>
#include <KConfigGroup>
#include <KIO/MkpathJob>
#include <KLocalizedString>
#include <KSharedConfig>
#include <KShortcutsDialog>
#include <KStandardAction>
#include <KStandardGameAction>
#include <KToggleAction>
#include <KToggleFullScreenAction>
#include <KToolBar>
42

Laurent Montel's avatar
Laurent Montel committed
43
#include "kgoldrunner_debug.h"
44

45

46
#include <libkdegames_capabilities.h> //defines KGAUDIO_BACKEND_OPENAL (or not)
47

48
#include "kgrgame.h"
49
50
51
#include "kgrview.h"
#include "kgrscene.h"
#include "kgrrenderer.h"
Ian Wadham's avatar
Ian Wadham committed
52

53
54
55
// Shorthand for references to actions.
#define ACTION(x)   (actionCollection()->action(x))

56
KGoldrunner::KGoldrunner()
57
    :
58
    KXmlGuiWindow (nullptr)
59
60
61
62
{
/******************************************************************************/
/*************  FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE  *************/
/******************************************************************************/
Ian Wadham's avatar
Ian Wadham committed
63

Laurent Montel's avatar
Laurent Montel committed
64
    setObjectName ( QStringLiteral("KGoldrunner" ));
65

66
    // Avoid "saveOK()" check if an error-exit occurs during the file checks.
Jaison Lee's avatar
Jaison Lee committed
67
    startupOK = true;
Ian Wadham's avatar
Ian Wadham committed
68

69
70
    // Get directory paths for the system levels, user levels and manual.
    if (! getDirectories()) {
71
72
73
        fprintf (stderr, "getDirectories() FAILED\n");
        startupOK = false;
        return;				// If games directory not found, abort.
Ian Wadham's avatar
Ian Wadham committed
74
75
    }

76
    // This message is to help diagnose distribution or installation problems.
77
78
79
    qCDebug(KGOLDRUNNER_LOG, "The games data should be in the following locations:\n"
            "System games: %s\nUser data:    %s",
            qPrintable(systemDataDir), qPrintable(userDataDir));
Ian Wadham's avatar
Ian Wadham committed
80
81

/******************************************************************************/
82
/************************  SET PLAYFIELD AND GAME DATA  ***********************/
Ian Wadham's avatar
Ian Wadham committed
83
84
/******************************************************************************/

Ian Wadham's avatar
Ian Wadham committed
85
    // Base the size of playing-area and widgets on the monitor resolution.
86
    int dw = QApplication::desktop()->width();
Ian Wadham's avatar
Ian Wadham committed
87
88

    // Need to consider the height, for widescreen displays (eg. 1280x768).
89
    int dh = QApplication::desktop()->height();
Ian Wadham's avatar
Ian Wadham committed
90

91
92
    dw = qMin ((4 * dh + 1) / 3, dw);	// KGoldrunner aspect ratio is 4:3.
    dh = (3 * dw + 2) / 4;
93
94

    view = new KGrView (this);
95
96
    view->setMinimumSize ((dw + 1) / 2, (dh + 1) / 2);

97
    game = new KGrGame (view, systemDataDir, userDataDir);
Ian Wadham's avatar
Ian Wadham committed
98

99
100
    // Initialise the lists of games (i.e. collections of levels).
    if (! game->initGameLists()) {
101
102
        startupOK = false;
        return;				// If no game files, abort.
Ian Wadham's avatar
Ian Wadham committed
103
104
    }

105
106
107
/******************************************************************************/
/*************************  SET UP THE USER INTERFACE  ************************/
/******************************************************************************/
Ian Wadham's avatar
Ian Wadham committed
108

109
    // Tell the KMainWindow that the KGrView object is the main widget.
110
    setCentralWidget (view);
111

112
113
    scene       = view->gameScene ();
    renderer    = scene->renderer ();
114

115
116
    // Set up our actions (menu, toolbar and keystrokes) ...
    setupActions();
117

Ian Wadham's avatar
Ian Wadham committed
118
119
    // Do NOT put show/hide actions for the statusbar and toolbar in the GUI.
    // We do not have a statusbar any more and the toolbar is relevant only
120
    // when using the game editor and then it appears automatically.  Maybe 1%
121
    // of players would use the game editor for 5% of their time.  Also we have
122
    // our own action to configure shortcut keys, so disable the KXmlGui one.
123
    setupGUI (static_cast<StandardWindowOption> (Default &
124
                        (~StatusBar) & (~ToolBar) & (~Keys)));
125

126
127
128
    // Initialize text-item lengths in the scene, before the first resize.
    scene->showLives (0);
    scene->showScore (0);
Ian Wadham's avatar
Ian Wadham committed
129
130
    adjustHintAction (false);
    gameFreeze (false);
131

132
    // Connect the game actions to the menu and toolbar displays.
Andrius Štikonas's avatar
Andrius Štikonas committed
133
134
135
136
137
138
    connect (game, &KGrGame::quitGame, this, &KGoldrunner::close);
    connect (game, &KGrGame::setEditMenu, this, &KGoldrunner::setEditMenu);
    connect (game, &KGrGame::showLives, scene, &KGrScene::showLives);
    connect (game, &KGrGame::showScore, scene, &KGrScene::showScore);
    connect (game, &KGrGame::hintAvailable, this, &KGoldrunner::adjustHintAction);
    connect (game, &KGrGame::gameFreeze, this, &KGoldrunner::gameFreeze);
139

Andrius Štikonas's avatar
Andrius Štikonas committed
140
141
    connect (game, &KGrGame::setAvail, this, &KGoldrunner::setAvail);
    connect (game, &KGrGame::setToggle, this, &KGoldrunner::setToggle);
142

Andrius Štikonas's avatar
Andrius Štikonas committed
143
    connect (scene, &KGrScene::redrawEditToolbar, this, &KGoldrunner::redrawEditToolbar);
144

145
146
147
148
    // Apply the saved mainwindow settings, if any, and ask the mainwindow
    // to automatically save settings if changed: window size, toolbar
    // position, icon size, etc.
    setAutoSaveSettings();
149

150
    // Explicitly hide the edit toolbar - we need it in edit mode only and we
151
    // always start in play mode, even if the last session ended in edit mode.
152
    // Besides, we cannot render it until after the initial resize event (s).
153
    toolBar (QStringLiteral("editToolbar"))->hide();
154

155
156
157
    // This is needed to make the arrow keys control the hero properly.
    setUpKeyboardControl();

Ian Wadham's avatar
Ian Wadham committed
158
    // Do NOT paint main widget yet (title bar, menu, blank playfield).
159
    // Instead, queue a call to the "KGoldrunner_2" constructor extension.
160
    QMetaObject::invokeMethod (this, "KGoldrunner_2", Qt::QueuedConnection);
Laurent Montel's avatar
Laurent Montel committed
161
162
    //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (this, \"KGoldrunner_2\") done ... ";
    //qCDebug(KGOLDRUNNER_LOG) << "1st scan of event-queue ...";
163
164
165
166
}

void KGoldrunner::KGoldrunner_2()
{
Laurent Montel's avatar
Laurent Montel committed
167
    //qCDebug(KGOLDRUNNER_LOG) << "Entered constructor extension ...";
168
169
170
171

    // Queue a call to the "initGame" method. This renders and paints the
    // initial graphics, but only AFTER the initial main-window resize events
    // have been seen and the final SVG scale is known.
172
    QMetaObject::invokeMethod (game, "initGame", Qt::QueuedConnection);
Laurent Montel's avatar
Laurent Montel committed
173
174
    //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (game, \"initGame\") done ... ";
    //qCDebug(KGOLDRUNNER_LOG) << "2nd scan of event-queue ...";
Ian Wadham's avatar
Ian Wadham committed
175
176
}

177
KGoldrunner::~KGoldrunner()
Ian Wadham's avatar
Ian Wadham committed
178
{
179
180
181
182
183
184
185
186
187
188
189
190
}

void KGoldrunner::setupActions()
{
    /**************************************************************************/
    /******************************   GAME MENU  ******************************/
    /**************************************************************************/

    // New Game...
    // Load Saved Game...
    // --------------------------

191
    QAction * a = KStandardGameAction::gameNew (this, nullptr, nullptr);
192
    actionCollection()->addAction (a->objectName(), a);
193
    connect (a, &QAction::triggered, this, [this] { game->gameActions(NEW); });
194
195
    a->setText (i18n ("&New Game..."));

196
    a        = gameAction (QStringLiteral("next_level"), NEXT_LEVEL,
197
198
199
200
201
202
                           i18n ("Pla&y Next Level"),
                           i18n ("Play next level."),
                           i18n ("Try the next level in the game "
                                 "you are playing."),
                           Qt::Key_Y);

203
    a =	KStandardGameAction::load (this, nullptr, nullptr);
204
    actionCollection()->addAction (a->objectName(), a);
205
    connect (a, &QAction::triggered, this, [this] { game->gameActions(LOAD); });
206
    a->setText (i18n ("&Load Saved Game..."));
207

208
    // Save Game...
209
    // Save Solution...
210
211
    // --------------------------

212
    saveGame = KStandardGameAction::save (this, nullptr, nullptr);
213
    actionCollection()->addAction (saveGame->objectName(), saveGame);
214
    connect (saveGame, &QAction::triggered, this, [this] { game->gameActions(SAVE_GAME); });
215
    saveGame->setText (i18n ("&Save Game..."));
Laurent Montel's avatar
Laurent Montel committed
216
    actionCollection()->setDefaultShortcut(saveGame, Qt::Key_S); // Alternate key.
217

218
219
    // The name of the solution-file is 'sol_<prefix>.txt', where <prefix> is
    // the unique prefix belonging to the game involved (eg. plws, tute, etc.).
220
    a        = gameAction (QStringLiteral("save_solution"), SAVE_SOLUTION,
221
222
223
                           i18n ("Save A Solution..."),
                           i18n ("Save A Solution..."),
                           i18n ("Save a solution for a level into a file "
224
225
                                 "called 'sol_&lt;prefix&gt;.txt' in your "
				 "user's data directory..."),
226
                           Qt::ShiftModifier | Qt::Key_S);
227

228
229
230
231
232
233
    // Pause
    // Show High Scores
    // Get a Hint
    // Kill the Hero
    // --------------------------

234
    myPause = KStandardGameAction::pause (this, nullptr, nullptr);
235
    actionCollection()->addAction (myPause->objectName(), myPause);
236
    connect (myPause, &QAction::triggered, this, [this] { game->gameActions(PAUSE); });
Laurent Montel's avatar
Laurent Montel committed
237
    // QAction * myPause gets QAction::shortcut(), returning 1 OR 2 shortcuts.
238
239
    QList<QKeySequence> pauseShortcut = { myPause->shortcut(), Qt::Key_Escape };
    myPause->setShortcuts (pauseShortcut);
240

241
    highScore = KStandardGameAction::highscores (this, nullptr, nullptr);
242
    actionCollection()->addAction (highScore->objectName(), highScore);
243
    connect (highScore, &QAction::triggered, this, [this] { game->gameActions(HIGH_SCORE); });
244

245
    hintAction = KStandardGameAction::hint (this, nullptr, nullptr);
246
    actionCollection()->addAction (hintAction->objectName(), hintAction);
247
    connect (hintAction, &QAction::triggered, this, [this] { game->gameActions(HINT); });
248

249
    a = KStandardGameAction::demo (this, nullptr, nullptr);
250
    actionCollection()->addAction (a->objectName(), a);
251
    connect (a, &QAction::triggered, this, [this] { game->gameActions(DEMO); });
252

253
    a = KStandardGameAction::solve (this, nullptr, nullptr);
254
    actionCollection()->addAction (a->objectName(), a);
255
    connect (a, &QAction::triggered, this, [this] { game->gameActions(SOLVE); });
256
257
    a->setText      (i18n ("&Show A Solution"));
    a->setToolTip   (i18n ("Show how to win this level."));
258
    a->setWhatsThis (i18n ("Play a recording of how to win this level, if "
259
                           "there is one available."));
260

261
    a        = gameAction (QStringLiteral("instant_replay"), INSTANT_REPLAY,
262
                           i18n ("&Instant Replay"),
263
264
265
266
267
                           i18n ("Instant replay."),
                           i18n ("Show a recording of the level "
                                 "you are currently playing."),
                           Qt::Key_R);

268
    a        = gameAction (QStringLiteral("replay_last"), REPLAY_LAST,
269
270
271
272
273
274
                           i18n ("Replay &Last Level"),
                           i18n ("Replay last level."),
                           i18n ("Show a recording of the last level you "
                                 "played and finished, regardless of whether "
                                 "you won or lost."),
                           Qt::Key_A);
275

276
    a        = gameAction (QStringLiteral("replay_any"), REPLAY_ANY,
277
                           i18n ("&Replay Any Level"),
278
279
280
                           i18n ("Replay any level."),
                           i18n ("Show a recording of any level you have "
                                 "played so far."),
281
282
                           QKeySequence());	// No key assigned.

283
    killHero = gameAction (QStringLiteral("kill_hero"), KILL_HERO,
284
                           i18n ("&Kill Hero"),
285
                           i18n ("Kill Hero."),
286
                           i18n ("Kill the hero, in case he finds himself in "
287
                                 "a situation from which he cannot escape."),
288
                           Qt::Key_Q);
289
290
291
292

    // Quit
    // --------------------------

293
    KStandardGameAction::quit (this, SLOT (close()), actionCollection());
294
295
296
297
298

    /**************************************************************************/
    /***************************   GAME EDITOR MENU  **************************/
    /**************************************************************************/
    // Create a Level
299
    // Edit a Level...
300
301
    // --------------------------

302
    QAction * ed = editAction (QStringLiteral("create_level"), CREATE_LEVEL,
303
                               i18n ("&Create Level"),
304
305
                               i18n ("Create level."),
                               i18n ("Create a completely new level."));
Laurent Montel's avatar
Laurent Montel committed
306
    ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-new" )));
307
    ed->setIconText (i18n ("Create"));
308

309
    ed           = editAction (QStringLiteral("edit_any"), EDIT_ANY,
310
311
312
                               i18n ("&Edit Level..."),
                               i18n ("Edit level..."),
                               i18n ("Edit any level..."));
Laurent Montel's avatar
Laurent Montel committed
313
    ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-open" )));
314
    ed->setIconText (i18n ("Edit"));
315

316
317
318
319
320
    // Save Edits...
    // Move Level...
    // Delete Level...
    // --------------------------

321
    saveEdits    = editAction (QStringLiteral("save_edits"), SAVE_EDITS,
322
323
324
                               i18n ("&Save Edits..."),
                               i18n ("Save edits..."),
                               i18n ("Save your level after editing..."));
Laurent Montel's avatar
Laurent Montel committed
325
    saveEdits->setIcon (QIcon::fromTheme( QStringLiteral( "document-save" )));
326
    saveEdits->setIconText (i18n ("Save"));
327
328
    saveEdits->setEnabled (false);		// Nothing to save, yet.

329
    ed           = editAction (QStringLiteral("move_level"), MOVE_LEVEL,
330
331
332
333
334
                               i18n ("&Move Level..."),
                               i18n ("Move level..."),
                               i18n ("Change a level's number or move "
                                     "it to another game..."));

335
    ed           = editAction (QStringLiteral("delete_level"), DELETE_LEVEL,
336
337
338
                               i18n ("&Delete Level..."),
                               i18n ("Delete level..."),
                               i18n ("Delete a level..."));
339
340
341
342
343

    // Create a Game
    // Edit Game Info...
    // --------------------------

344
    ed           = editAction (QStringLiteral("create_game"), CREATE_GAME,
345
346
347
                               i18n ("Create &Game..."),
                               i18n ("Create game..."),
                               i18n ("Create a completely new game..."));
348

349
    ed           = editAction (QStringLiteral("edit_game"), EDIT_GAME,
350
351
352
353
                               i18n ("Edit Game &Info..."),
                               i18n ("Edit game info..."),
                               i18n ("Change the name, rules or description "
                                     "of a game..."));
354

355
    /**************************************************************************/
356
    /****************************   SETTINGS MENU  ****************************/
357
358
    /**************************************************************************/

359
    // Theme settings are handled by this class and KGrRenderer.
360
    QAction * themes = actionCollection()->addAction (QStringLiteral("select_theme"));
361
362
363
364
    themes->setText      (i18n ("Change &Theme..."));
    themes->setToolTip   (i18n ("Change the graphics theme..."));
    themes->setWhatsThis (i18n ("Alter the visual appearance of the runners "
                                "and background scene..."));
Andrius Štikonas's avatar
Andrius Štikonas committed
365
    connect (themes, &QAction::triggered, this, &KGoldrunner::changeTheme);
366

367
368
    // Show/Exit Full Screen Mode
    KToggleFullScreenAction * fullScreen = KStandardAction::fullScreen
Andrius Štikonas's avatar
Andrius Štikonas committed
369
                        (this, &KGoldrunner::viewFullScreen, this, this);
370
    actionCollection()->addAction (fullScreen->objectName(), fullScreen);
371

372
    // Other settings are handled by KGrGame.
373

374
#ifdef KGAUDIO_BACKEND_OPENAL
375
    // Sound effects on/off
376
                                  settingAction (QStringLiteral("options_sounds"), PLAY_SOUNDS,
377
                                  i18n ("&Play Sounds"),
378
379
                                  i18n ("Play sound effects."),
                                  i18n ("Play sound effects during the game."));
380

381
                                  settingAction (QStringLiteral("options_steps"), PLAY_STEPS,
382
383
384
                                  i18n ("Play &Footstep Sounds"),
                                  i18n ("Make sounds of player's footsteps."),
                                  i18n ("Make sounds of player's footsteps."));
385
#endif
386

387
    // Demo at start on/off.
388
                                  settingAction (QStringLiteral("options_demo"), STARTUP_DEMO,
389
                                  i18n ("&Demo At Start"),
390
391
                                  i18n ("Run a demo when the game starts."),
                                  i18n ("Run a demo when the game starts."));
392

393
394
    // Mouse Controls Hero
    // Keyboard Controls Hero
Ian Wadham's avatar
Ian Wadham committed
395
    // Laptop Hybrid
396
397
    // --------------------------

398
    KToggleAction * setMouse    = settingAction (QStringLiteral("mouse_mode"), MOUSE,
399
                                  i18n ("&Mouse Controls Hero"),
400
                                  i18n ("Mouse controls hero."),
401
                                  i18n ("Use the mouse to control "
402
                                        "the hero's moves."));
403

404
    KToggleAction * setKeyboard = settingAction (QStringLiteral("keyboard_mode"), KEYBOARD,
405
                                  i18n ("&Keyboard Controls Hero"),
406
                                  i18n ("Keyboard controls hero."),
407
                                  i18n ("Use the keyboard to control "
408
                                        "the hero's moves."));
409

410
    KToggleAction * setLaptop   = settingAction (QStringLiteral("laptop_mode"), LAPTOP,
411
                                  i18n ("Hybrid Control (&Laptop)"),
Andrew Coles's avatar
Andrew Coles committed
412
413
                                  i18n ("Pointer controls hero; dig "
                                        "using keyboard."),
Burkhard Lück's avatar
Burkhard Lück committed
414
                                  i18n ("Use the laptop's pointer device "
415
416
                                        "to control the hero's moves, and use "
                                        "the keyboard to dig left and right."));
Ian Wadham's avatar
Ian Wadham committed
417

418
419
420
    QActionGroup* controlGrp = new QActionGroup (this);
    controlGrp->addAction (setMouse);
    controlGrp->addAction (setKeyboard);
Ian Wadham's avatar
Ian Wadham committed
421
    controlGrp->addAction (setLaptop);
422
    controlGrp->setExclusive (true);
423
424
425
426
427

    // Options within keyboard mode.
    // Click key to begin moving and continue moving indefinitely.
    // Click and hold key to begin moving: release key to stop.

428
    KToggleAction * clickKey    = settingAction (QStringLiteral("click_key"), CLICK_KEY,
429
430
431
432
433
434
435
                                  i18n ("&Click Key To Move"),
                                  i18n ("Click Key To Move."),
                                  i18n ("In keyboard mode, click a "
                                        "direction-key to start moving "
                                        "and keep on going until you "
                                        "click another key."));

436
    KToggleAction * holdKey     = settingAction (QStringLiteral("hold_key"), HOLD_KEY,
437
438
439
440
441
442
443
444
445
446
                                  i18n ("&Hold Key To Move"),
                                  i18n ("Hold Key To Move."),
                                  i18n ("In keyboard mode, hold down a "
                                        "direction-key to move "
                                        "and release it to stop."));

    QActionGroup * keyGrp = new QActionGroup (this);
    keyGrp->addAction (clickKey);
    keyGrp->addAction (holdKey);
    keyGrp->setExclusive (true);
447
448
449
450
451
452
453
454

    // Normal Speed
    // Beginner Speed
    // Champion Speed
    // Increase Speed
    // Decrease Speed
    // --------------------------

455
    KToggleAction * nSpeed      = settingAction (QStringLiteral("normal_speed"), NORMAL_SPEED,
456
                                  i18n ("Normal Speed"),
457
                                  i18n ("Set normal speed."),
458
                                  i18n ("Set normal game speed."));
459

460
    KToggleAction * bSpeed      = settingAction (QStringLiteral("beginner_speed"),
461
462
                                  BEGINNER_SPEED,
                                  i18n ("Beginner Speed"),
463
                                  i18n ("Set beginners' speed."),
464
465
                                  i18n ("Set beginners' game speed "
                                        "(0.5 times normal)."));
466

467
    KToggleAction * cSpeed      = settingAction (QStringLiteral("champion_speed"),
468
469
                                  CHAMPION_SPEED,
                                  i18n ("Champion Speed"),
470
                                  i18n ("Set champions' speed."),
471
472
                                  i18n ("Set champions' game speed "
                                        "(1.5 times normal)."));
473

474
    a                           = gameAction (QStringLiteral("increase_speed"), INC_SPEED,
475
                                  i18n ("Increase Speed"),
476
                                  i18n ("Increase speed."),
477
478
                                  i18n ("Increase the game speed by 0.1 "
                                        "(maximum is 2.0 times normal)."),
479
480
                                  Qt::Key_Plus);

481
    a                           = gameAction (QStringLiteral("decrease_speed"), DEC_SPEED,
482
                                  i18n ("Decrease Speed"),
483
                                  i18n ("Decrease speed."),
484
485
                                  i18n ("Decrease the game speed by 0.1 "
                                        "(minimum is 0.2 times normal)."),
486
                                  Qt::Key_Minus);
487
488
489
490
491

    QActionGroup* speedGrp = new QActionGroup (this);
    speedGrp->addAction (nSpeed);
    speedGrp->addAction (bSpeed);
    speedGrp->addAction (cSpeed);
492
    speedGrp->setExclusive (true);
493
494
495
496

    // Configure Shortcuts...
    // --------------------------

Aaron J. Seigo's avatar
Aaron J. Seigo committed
497
    KStandardAction::keyBindings (
498
499
                                this, SLOT (optionsConfigureKeys()),
                                actionCollection());
500
501
502
503
504
505
506

    /**************************************************************************/
    /**************************   KEYSTROKE ACTIONS  **************************/
    /**************************************************************************/

    // Two-handed KB controls and alternate one-handed controls for the hero.

507
508
509
510
511
512
513
    // The actions for the movement keys are created but disabled.  This lets
    // keyPressEvent() come through, instead of a signal, while still allowing
    // Settings->Configure Keys to change the key mappings.  The keyPressEvent()
    // call is needed so that the key can be identified and matched to the
    // corresponding keyReleaseEvent() call and make the hold-key option
    // work correctly when two keys are held down simultaneously.

514
515
516
517
518
519
520
    keyControl (QStringLiteral("stop"),       i18n ("Stop"),       Qt::Key_Space, STAND);
    keyControl (QStringLiteral("move_right"), i18n ("Move Right"), Qt::Key_Right, RIGHT, true);
    keyControl (QStringLiteral("move_left"),  i18n ("Move Left"),  Qt::Key_Left,  LEFT,  true);
    keyControl (QStringLiteral("move_up"),    i18n ("Move Up"),    Qt::Key_Up,    UP,    true);
    keyControl (QStringLiteral("move_down"),  i18n ("Move Down"),  Qt::Key_Down,  DOWN,  true);
    keyControl (QStringLiteral("dig_right"),  i18n ("Dig Right"),  Qt::Key_C,     DIG_RIGHT);
    keyControl (QStringLiteral("dig_left"),   i18n ("Dig Left"),   Qt::Key_Z,     DIG_LEFT);
521

522
523
524
525
526
527
528
529
530
531
    // Alternate one-handed controls.  Set up in "kgoldrunnerui.rc".

    // Key_I, "move_up"
    // Key_L, "move_right"
    // Key_K, "move_down"
    // Key_J, "move_left"
    // Key_Space, "stop" (as above)
    // Key_O, "dig_right"
    // Key_U, "dig_left"

532
    setupEditToolbarActions();		// Uses pixmaps from "renderer".
533

534
535
536
537
    // Authors' debugging aids, effective when Pause is hit.  Options include
    // stepping through the animation, toggling a debug patch or log messages
    // on or off during gameplay and printing the states of runners or tiles.

Laurent Montel's avatar
Laurent Montel committed
538
    KConfigGroup debugGroup (KSharedConfig::openConfig(), "Debugging");
539
    bool addDebuggingShortcuts = debugGroup.readEntry
540
                        ("DebuggingShortcuts", false);	// Get debug option.
541
    if (! addDebuggingShortcuts)
542
543
        return;

544
545
546
547
548
549
550
551
552
553
554
555
556
557
    keyControlDebug (QStringLiteral("do_step"),      i18n ("Do a Step"), Qt::Key_Period, DO_STEP);
    keyControlDebug (QStringLiteral("bug_fix"),      i18n ("Test Bug Fix"), Qt::Key_B, BUG_FIX);
    keyControlDebug (QStringLiteral("show_positions"), i18n ("Show Positions"), Qt::Key_W, S_POSNS);
    keyControlDebug (QStringLiteral("logging"),      i18n ("Start Logging"), Qt::Key_G, LOGGING);
    keyControlDebug (QStringLiteral("show_hero"),    i18n ("Show Hero"), Qt::Key_E, S_HERO);
    keyControlDebug (QStringLiteral("show_obj"),     i18n ("Show Object"), Qt::Key_Slash, S_OBJ);

    keyControlDebug (QStringLiteral("show_enemy_0"), i18n ("Show Enemy") + QLatin1Char('0'), Qt::Key_0, ENEMY_0);
    keyControlDebug (QStringLiteral("show_enemy_1"), i18n ("Show Enemy") + QLatin1Char('1'), Qt::Key_1, ENEMY_1);
    keyControlDebug (QStringLiteral("show_enemy_2"), i18n ("Show Enemy") + QLatin1Char('2'), Qt::Key_2, ENEMY_2);
    keyControlDebug (QStringLiteral("show_enemy_3"), i18n ("Show Enemy") + QLatin1Char('3'), Qt::Key_3, ENEMY_3);
    keyControlDebug (QStringLiteral("show_enemy_4"), i18n ("Show Enemy") + QLatin1Char('4'), Qt::Key_4, ENEMY_4);
    keyControlDebug (QStringLiteral("show_enemy_5"), i18n ("Show Enemy") + QLatin1Char('5'), Qt::Key_5, ENEMY_5);
    keyControlDebug (QStringLiteral("show_enemy_6"), i18n ("Show Enemy") + QLatin1Char('6'), Qt::Key_6, ENEMY_6);
558
}
Ian Wadham's avatar
Ian Wadham committed
559

Laurent Montel's avatar
Laurent Montel committed
560
QAction * KGoldrunner::gameAction (const QString & name,
561
562
563
564
565
566
                                   const int       code,
                                   const QString & text,
                                   const QString & toolTip,
                                   const QString & whatsThis,
                                   const QKeySequence & key)
{
Laurent Montel's avatar
Laurent Montel committed
567
    QAction * ga = actionCollection()->addAction (name);
568
569
570
    ga->setText (text);
    ga->setToolTip (toolTip);
    ga->setWhatsThis (whatsThis);
571
    if (! key.isEmpty()) {
Laurent Montel's avatar
Laurent Montel committed
572
        actionCollection()->setDefaultShortcut(ga, key);
573
    }
574
    connect (ga, &QAction::triggered, this, [this, code] { game->gameActions(code); });
575
576
577
    return ga;
}

Laurent Montel's avatar
Laurent Montel committed
578
QAction * KGoldrunner::editAction (const QString & name,
579
580
581
582
583
                                   const int       code,
                                   const QString & text,
                                   const QString & toolTip,
                                   const QString & whatsThis)
{
Laurent Montel's avatar
Laurent Montel committed
584
    QAction * ed = actionCollection()->addAction (name);
585
586
587
    ed->setText (text);
    ed->setToolTip (toolTip);
    ed->setWhatsThis (whatsThis);
588
    connect (ed, &QAction::triggered, this, [this, code] { game->editActions(code); });
589
590
591
    return ed;
}

592
593
594
595
596
597
598
599
600
601
KToggleAction * KGoldrunner::settingAction (const QString & name,
                                            const int       code,
                                            const QString & text,
                                            const QString & toolTip,
                                            const QString & whatsThis)
{
    KToggleAction * s = new KToggleAction (text, this);
    actionCollection()->addAction (name, s);
    s->setToolTip (toolTip);
    s->setWhatsThis (whatsThis);
602
    connect (s, &QAction::triggered, this, [this, code] { game->settings(code); });
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    return s;
}

KToggleAction * KGoldrunner::editToolbarAction (const QString & name,
                                                const char      code,
                                                const QString & shortText,
                                                const QString & text,
                                                const QString & toolTip,
                                                const QString & whatsThis)
{
    KToggleAction * ed = new KToggleAction (text, this);
    actionCollection()->addAction (name, ed);
    ed->setIconText (shortText);
    ed->setToolTip (toolTip);
    ed->setWhatsThis (whatsThis);
618
    connect (ed, &QAction::triggered, this, [this, code] { game->editToolbarActions(code); });
619
620
621
    return ed;
}

622
void KGoldrunner::keyControl (const QString & name, const QString & text,
623
624
                              const QKeySequence & shortcut, const int code,
                              const bool mover)
625
{
Laurent Montel's avatar
Laurent Montel committed
626
    QAction * a = actionCollection()->addAction (name);
627
    a->setText (text);
Laurent Montel's avatar
Laurent Montel committed
628
    actionCollection()->setDefaultShortcut(a, shortcut);
629
    a->setAutoRepeat (false);		// Avoid repeats of signals by QAction.
630
631
632
633

    // If this is a move-key, let keyPressEvent() through, instead of signal.
    if (mover) {
        a->setEnabled (false);
634
635
	addAction (a);
	return;
636
637
    }

638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
    connect (a, &QAction::triggered, this, [this, code] { game->kbControl(code); });
    addAction (a);
}

void KGoldrunner::keyControlDebug (const QString & name, const QString & text,
                              const QKeySequence & shortcut, const int code,
                              const bool mover)
{
    QAction * a = actionCollection()->addAction (name);
    a->setText (text);
    actionCollection()->setDefaultShortcut(a, shortcut);
    a->setAutoRepeat (false);		// Avoid repeats of signals by QAction.

    // If this is a move-key, let keyPressEvent() through, instead of signal.
    if (mover) {
        a->setEnabled (false);
	addAction (a);
	return;
    }

    connect (a, &QAction::triggered, this, [this, code] { game->dbgControl(code); });
659
660
661
    addAction (a);
}

662
663
664
665
666
667
668
669
670
671
672
673
674
675
void KGoldrunner::setUpKeyboardControl()
{
    // This is needed to ensure that the press and release of Up, Down, Left and
    // Right keys (arrow-keys) are all received as required.
    //
    // If the KGoldrunner widget does not have the keyboard focus, arrow-keys
    // provide only key-release events, which do not control the hero properly.
    // Other keys provide both press and release events, regardless of focus.

    this->setFocusPolicy (Qt::StrongFocus); // Tab or click gets the focus.
    view->setFocusProxy (this);		    // So does a click on the play-area.
    this->setFocus (Qt::OtherFocusReason);  // And we start by having the focus.
}

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
void KGoldrunner::keyPressEvent (QKeyEvent * event)
{
    // For movement keys, all presses and releases are processed, thus allowing
    // the hold-key option to work correctly when two keys are held down.

    if (! identifyMoveAction (event, true)) {
        QWidget::keyPressEvent (event);
    }
}

void KGoldrunner::keyReleaseEvent (QKeyEvent * event)
{
    if (! identifyMoveAction (event, false)) {
        QWidget::keyReleaseEvent (event);
    }
}

bool KGoldrunner::identifyMoveAction (QKeyEvent * event, bool pressed)
{
695
696
697
698
699
700
701
702
703
704
705
    if (event->isAutoRepeat()) {
        return false;		// Use only the release and the initial press.
    }
    Direction dirn = STAND;
    // The arrow keys show the "Keypad" modifier as being set, even if the
    // computer does NOT have a keypad (see Qt::KeypadModifier doco). It is
    // OK to ignore the Keypad modifier (see the code for "qevent.cpp" at
    // "bool QKeyEvent::matches(QKeySequence::StandardKey matchKey)"). The
    // keys on the keypad usually have equivalents on the main keyboard.
    QKeySequence keystroke (~(Qt::KeypadModifier) &
                             (event->key() | event->modifiers()));
706
    if ((ACTION (QStringLiteral("move_left")))->shortcuts().contains(keystroke)) {
707
708
        dirn = LEFT;
    }
709
    else if ((ACTION (QStringLiteral("move_right")))->shortcuts().contains(keystroke)) {
710
711
        dirn = RIGHT;
    }
712
    else if ((ACTION (QStringLiteral("move_up")))->shortcuts().contains(keystroke)) {
713
714
        dirn = UP;
    }
715
    else if ((ACTION (QStringLiteral("move_down")))->shortcuts().contains(keystroke)) {
716
717
718
719
        dirn = DOWN;
    }
    else {
        return false;
720
    }
721
722
723
    // Use this event to control the hero, if KEYBOARD mode is selected.
    game->kbControl (dirn, pressed);
    return true;
724
725
}

726
void KGoldrunner::viewFullScreen (bool activation)
Luciano Montanaro's avatar
Luciano Montanaro committed
727
{
728
    KToggleFullScreenAction::setFullScreen (this, activation);
Luciano Montanaro's avatar
Luciano Montanaro committed
729
730
}

731
void KGoldrunner::gameFreeze (bool on_off)
Ian Wadham's avatar
Ian Wadham committed
732
{
733
    myPause->setChecked (on_off);
734
    frozen = on_off;	// Remember the state (for the configure-keys case).
735
    QStringList pauseKeys;
736

737
    const auto keyBindings = myPause->shortcuts();
738
    for (const QKeySequence &s : keyBindings) {
739
740
        pauseKeys.append(s.toString(QKeySequence::NativeText));
    }
741

742
    QString msg;
743
    if (on_off) {
744
        if (pauseKeys.isEmpty()) {
745
            msg = i18n("The game is paused");
746
        } else if (pauseKeys.size() == 1) {
747
            msg = i18n("Press \"%1\" to RESUME", pauseKeys.at(0));
748
        } else {
749
750
            msg = i18n("Press \"%1\" or \"%2\" to RESUME", pauseKeys.at(0),
                                                           pauseKeys.at(1));
751
752
        }
    } else {
753
754
        if (pauseKeys.isEmpty()) {
            msg = QString();
755
        } else if (pauseKeys.size() == 1) {
756
            msg = i18n("Press \"%1\" to PAUSE", pauseKeys.at(0));
757
        } else {
758
759
            msg = i18n("Press \"%1\" or \"%2\" to PAUSE", pauseKeys.at(0),
                                                          pauseKeys.at(1));
760
761
        }
    }
762
    scene->setPauseResumeText (msg);
Ian Wadham's avatar
Ian Wadham committed
763
764
}

765
void KGoldrunner::adjustHintAction (bool hintAvailable)
Ian Wadham's avatar
Ian Wadham committed
766
{
767
    hintAction->setEnabled (hintAvailable);
Ian Wadham's avatar
Ian Wadham committed
768

769
770
771
    QString msg;
    msg = hintAvailable ? i18n("Has hint") : i18n("No hint");
    scene->setHasHintText (msg);
Dirk Mueller's avatar
compile    
Dirk Mueller committed
772
}
Ian Wadham's avatar
Ian Wadham committed
773

774
775
void KGoldrunner::setToggle (const char * actionName, const bool onOff)
{
776
    ((KToggleAction *) ACTION (QLatin1String(actionName)))->setChecked (onOff);
777
778
779
780
}

void KGoldrunner::setAvail (const char * actionName, const bool onOff)
{
781
    ((QAction *) ACTION (QLatin1String(actionName)))->setEnabled (onOff);
782
783
}

784
void KGoldrunner::setEditMenu (bool on_off)
Ian Wadham's avatar
Ian Wadham committed
785
{
786
    saveEdits->setEnabled  (on_off);
Ian Wadham's avatar
Ian Wadham committed
787

788
789
790
791
    saveGame->setEnabled   (! on_off);
    hintAction->setEnabled (! on_off);
    killHero->setEnabled   (! on_off);
    highScore->setEnabled  (! on_off);
792
793
    setAvail ("instant_replay", (! on_off));
    setAvail ("game_pause",     (! on_off));
Ian Wadham's avatar
Ian Wadham committed
794

795
    if (on_off){
796
        // Set the editToolbar icons to the current tile-size.
Laurent Montel's avatar
Laurent Montel committed
797
        //qCDebug(KGOLDRUNNER_LOG) << "ToolBar icon size:" << scene->tileSize ();
798
        toolBar (QStringLiteral("editToolbar"))->setIconSize (scene->tileSize ());
799
800

        // Set the editToolbar icons up with pixmaps of the current theme.
801
802
803
804
805
806
807
808
809
810
        setEditIcon (QStringLiteral("brickbg"),   BRICK);
        setEditIcon (QStringLiteral("fbrickbg"),  FBRICK);
        setEditIcon (QStringLiteral("freebg"),    FREE);
        setEditIcon (QStringLiteral("nuggetbg"),  NUGGET);
        setEditIcon (QStringLiteral("polebg"),    BAR);
        setEditIcon (QStringLiteral("concretebg"), CONCRETE);
        setEditIcon (QStringLiteral("ladderbg"),  LADDER);
        setEditIcon (QStringLiteral("hladderbg"), HLADDER);
        setEditIcon (QStringLiteral("edherobg"),  HERO);
        setEditIcon (QStringLiteral("edenemybg"), ENEMY);
811
        setToggle   ("brickbg", true);		// Default edit-object is BRICK.
812

813
        toolBar (QStringLiteral("editToolbar"))->show();
Ian Wadham's avatar
Ian Wadham committed
814
    }
815
    else {
816
        toolBar (QStringLiteral("editToolbar"))->hide();
Ian Wadham's avatar
Ian Wadham committed
817
    }
818
}
Ian Wadham's avatar
Ian Wadham committed
819

820
void KGoldrunner::setEditIcon (const QString & actionName, const char iconType)
821
{
822
    ((KToggleAction *) (actionCollection()->action (actionName)))->
Laurent Montel's avatar
Laurent Montel committed
823
                setIcon (QIcon(renderer->getPixmap (iconType)));
824
}
825

Ian Wadham's avatar
Ian Wadham committed
826
/******************************************************************************/
827
/*******************   SLOTS FOR MENU AND KEYBOARD ACTIONS  *******************/
Ian Wadham's avatar
Ian Wadham committed
828
829
/******************************************************************************/

830
void KGoldrunner::changeTheme ()
831
{
832
    renderer->selectTheme ();
833
}
834

835
836
837
void KGoldrunner::redrawEditToolbar ()
{
    // Signalled by the scene after the theme or tile size has changed.
838
    if (game->inEditMode()) {
839
        setEditMenu (true);
840
    }
841
842
}

843
void KGoldrunner::saveProperties (KConfigGroup & /* config - unused */)
844
845
846
847
{
    // The 'config' object points to the session managed
    // config file.  Anything you write here will be available
    // later when this app is restored.
Ian Wadham's avatar
Ian Wadham committed
848

Laurent Montel's avatar
Laurent Montel committed
849
    //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::saveProperties.";
Ian Wadham's avatar
Ian Wadham committed
850
851
}

852
void KGoldrunner::readProperties (const KConfigGroup & /* config - unused */)
Ian Wadham's avatar
Ian Wadham committed
853
{
854
855
856
857
    // The 'config' object points to the session managed
    // config file.  This function is automatically called whenever
    // the app is being restored.  Read in here whatever you wrote
    // in 'saveProperties'
Ian Wadham's avatar
Ian Wadham committed
858

Laurent Montel's avatar
Laurent Montel committed
859
    //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::readProperties.";
Ian Wadham's avatar
Ian Wadham committed
860
861
}

862
void KGoldrunner::optionsConfigureKeys()
Ian Wadham's avatar
Ian Wadham committed
863
{
864
865
866
867
868
869
    // First run the standard KDE dialog for shortcut key settings.
    KShortcutsDialog::configure (actionCollection(),
	KShortcutsEditor::LetterShortcutsAllowed,	// Single letters OK.
	this,						// Parent widget.
	true);						// saveSettings value.

Ian Wadham's avatar
Ian Wadham committed
870
    gameFreeze (frozen);		// Update the pause/resume text.
Ian Wadham's avatar
Ian Wadham committed
871
872
}

873
bool KGoldrunner::getDirectories()
Ian Wadham's avatar
Ian Wadham committed
874
{
Jaison Lee's avatar
Jaison Lee committed
875
    bool result = true;
876

877
    QString myDir = QStringLiteral("kgoldrunner");
878
879
    QStringList genericDataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
    QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
Ian Wadham's avatar
Ian Wadham committed
880

881
    // Find the system collections in a directory of the required KDE type.
882
    systemDataDir = QStandardPaths::locate(QStandardPaths::AppDataLocation,
883
884
                                           QStringLiteral("system/")
                                           , QStandardPaths::LocateDirectory);
885
    if (systemDataDir.length() <= 0) {
886
        KGrMessage::information (this, i18n ("Get Folders"),
887
888
        i18n ("Cannot find system games sub-folder '/system/' "
        "in areas '%1'.",
889
        appDataLocations.join(QLatin1Char(';'))));
890
        result = false;			// ABORT if the games data is missing.
Ian Wadham's avatar
Ian Wadham committed
891
892
    }

893
    // Locate and optionally create directories for user collections and levels.
894
895
    userDataDir   = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/');
    QString levelDir = userDataDir + QStringLiteral("levels");
Andrius Štikonas's avatar