File indexing completed on 2024-04-28 04:02:11

0001 /*
0002     SPDX-FileCopyrightText: 2002 Marco Krüger <grisuji@gmx.de>
0003     SPDX-FileCopyrightText: 2002 Ian Wadham <iandw.au@gmail.com>
0004     SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "kgoldrunner.h"
0010 
0011 #include <QAction>
0012 #include <QActionGroup>
0013 #include <QIcon>
0014 #include <QKeyEvent>
0015 #include <QKeySequence>
0016 #include <QScreen>
0017 #include <QShortcut>
0018 
0019 #include <KActionCollection>
0020 #include <KConfig>
0021 #include <KConfigGroup>
0022 #include <KIO/MkpathJob>
0023 #include <KLocalizedString>
0024 #include <KSharedConfig>
0025 #include <KShortcutsDialog>
0026 #include <KStandardAction>
0027 #include <KGameStandardAction>
0028 #include <KToggleAction>
0029 #include <KToggleFullScreenAction>
0030 #include <KToolBar>
0031 
0032 #include "kgoldrunner_debug.h"
0033 
0034 
0035 #include <libkdegames_capabilities.h> //defines KGAUDIO_BACKEND_OPENAL (or not)
0036 
0037 #include "kgrgame.h"
0038 #include "kgrview.h"
0039 #include "kgrscene.h"
0040 #include "kgrrenderer.h"
0041 
0042 // Shorthand for references to actions.
0043 #define ACTION(x)   (actionCollection()->action(x))
0044 
0045 KGoldrunner::KGoldrunner()
0046     :
0047     KXmlGuiWindow (nullptr)
0048 {
0049 /******************************************************************************/
0050 /*************  FIND WHERE THE GAMES DATA AND HANDBOOK SHOULD BE  *************/
0051 /******************************************************************************/
0052 
0053     setObjectName ( QStringLiteral("KGoldrunner" ));
0054 
0055     // Avoid "saveOK()" check if an error-exit occurs during the file checks.
0056     startupOK = true;
0057 
0058     // Get directory paths for the system levels, user levels and manual.
0059     if (! getDirectories()) {
0060         fprintf (stderr, "getDirectories() FAILED\n");
0061         startupOK = false;
0062         return;             // If games directory not found, abort.
0063     }
0064 
0065     // This message is to help diagnose distribution or installation problems.
0066     qCDebug(KGOLDRUNNER_LOG, "The games data should be in the following locations:\n"
0067             "System games: %s\nUser data:    %s",
0068             qPrintable(systemDataDir), qPrintable(userDataDir));
0069 
0070 /******************************************************************************/
0071 /************************  SET PLAYFIELD AND GAME DATA  ***********************/
0072 /******************************************************************************/
0073 
0074     const QSize maxSize = screen()->availableGeometry().size();
0075     // Base the size of playing-area and widgets on the monitor resolution.
0076     int dw = maxSize.width();
0077 
0078     // Need to consider the height, for widescreen displays (eg. 1280x768).
0079     int dh = maxSize.height();
0080 
0081     dw = qMin ((4 * dh + 1) / 3, dw);   // KGoldrunner aspect ratio is 4:3.
0082     dh = (3 * dw + 2) / 4;
0083 
0084     view = new KGrView (this);
0085     view->setMinimumSize ((dw + 1) / 2, (dh + 1) / 2);
0086 
0087     game = new KGrGame (view, systemDataDir, userDataDir);
0088 
0089     // Initialise the lists of games (i.e. collections of levels).
0090     if (! game->initGameLists()) {
0091         startupOK = false;
0092         return;             // If no game files, abort.
0093     }
0094 
0095 /******************************************************************************/
0096 /*************************  SET UP THE USER INTERFACE  ************************/
0097 /******************************************************************************/
0098 
0099     // Tell the KMainWindow that the KGrView object is the main widget.
0100     setCentralWidget (view);
0101 
0102     scene       = view->gameScene ();
0103     renderer    = scene->renderer ();
0104 
0105     // Set up our actions (menu, toolbar and keystrokes) ...
0106     setupActions();
0107 
0108     // Do NOT put show/hide actions for the statusbar and toolbar in the GUI.
0109     // We do not have a statusbar any more and the toolbar is relevant only
0110     // when using the game editor and then it appears automatically.  Maybe 1%
0111     // of players would use the game editor for 5% of their time.  Also we have
0112     // our own action to configure shortcut keys, so disable the KXmlGui one.
0113     setupGUI (static_cast<StandardWindowOption> (Default &
0114                         (~StatusBar) & (~ToolBar) & (~Keys)));
0115 
0116     // Initialize text-item lengths in the scene, before the first resize.
0117     scene->showLives (0);
0118     scene->showScore (0);
0119     adjustHintAction (false);
0120     gameFreeze (false);
0121 
0122     // Connect the game actions to the menu and toolbar displays.
0123     connect (game, &KGrGame::quitGame, this, &KGoldrunner::close);
0124     connect (game, &KGrGame::setEditMenu, this, &KGoldrunner::setEditMenu);
0125     connect (game, &KGrGame::showLives, scene, &KGrScene::showLives);
0126     connect (game, &KGrGame::showScore, scene, &KGrScene::showScore);
0127     connect (game, &KGrGame::hintAvailable, this, &KGoldrunner::adjustHintAction);
0128     connect (game, &KGrGame::gameFreeze, this, &KGoldrunner::gameFreeze);
0129 
0130     connect (game, &KGrGame::setAvail, this, &KGoldrunner::setAvail);
0131     connect (game, &KGrGame::setToggle, this, &KGoldrunner::setToggle);
0132 
0133     connect (scene, &KGrScene::redrawEditToolbar, this, &KGoldrunner::redrawEditToolbar);
0134 
0135     // Apply the saved mainwindow settings, if any, and ask the mainwindow
0136     // to automatically save settings if changed: window size, toolbar
0137     // position, icon size, etc.
0138     setAutoSaveSettings();
0139 
0140     // Explicitly hide the edit toolbar - we need it in edit mode only and we
0141     // always start in play mode, even if the last session ended in edit mode.
0142     // Besides, we cannot render it until after the initial resize event (s).
0143     toolBar (QStringLiteral("editToolbar"))->hide();
0144 
0145     // This is needed to make the arrow keys control the hero properly.
0146     setUpKeyboardControl();
0147 
0148     // Do NOT paint main widget yet (title bar, menu, blank playfield).
0149     // Instead, queue a call to the "KGoldrunner_2" constructor extension.
0150     QMetaObject::invokeMethod (this, "KGoldrunner_2", Qt::QueuedConnection);
0151     //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (this, \"KGoldrunner_2\") done ... ";
0152     //qCDebug(KGOLDRUNNER_LOG) << "1st scan of event-queue ...";
0153 }
0154 
0155 void KGoldrunner::KGoldrunner_2()
0156 {
0157     //qCDebug(KGOLDRUNNER_LOG) << "Entered constructor extension ...";
0158 
0159     // Queue a call to the "initGame" method. This renders and paints the
0160     // initial graphics, but only AFTER the initial main-window resize events
0161     // have been seen and the final SVG scale is known.
0162     QMetaObject::invokeMethod (game, "initGame", Qt::QueuedConnection);
0163     //qCDebug(KGOLDRUNNER_LOG) << "QMetaObject::invokeMethod (game, \"initGame\") done ... ";
0164     //qCDebug(KGOLDRUNNER_LOG) << "2nd scan of event-queue ...";
0165 }
0166 
0167 KGoldrunner::~KGoldrunner()
0168 {
0169 }
0170 
0171 void KGoldrunner::setupActions()
0172 {
0173     /**************************************************************************/
0174     /******************************   GAME MENU  ******************************/
0175     /**************************************************************************/
0176 
0177     // New Game...
0178     // Load Saved Game...
0179     // --------------------------
0180 
0181     QAction * a = KGameStandardAction::gameNew (this, nullptr, nullptr);
0182     actionCollection()->addAction (a->objectName(), a);
0183     connect (a, &QAction::triggered, this, [this] { game->gameActions(NEW); });
0184     a->setText (i18n ("&New Game..."));
0185 
0186     a        = gameAction (QStringLiteral("next_level"), NEXT_LEVEL,
0187                            i18n ("Pla&y Next Level"),
0188                            i18n ("Play next level."),
0189                            i18n ("Try the next level in the game "
0190                                  "you are playing."),
0191                            Qt::Key_Y);
0192 
0193     a = KGameStandardAction::load (this, nullptr, nullptr);
0194     actionCollection()->addAction (a->objectName(), a);
0195     connect (a, &QAction::triggered, this, [this] { game->gameActions(LOAD); });
0196     a->setText (i18n ("&Load Saved Game..."));
0197 
0198     // Save Game...
0199     // Save Solution...
0200     // --------------------------
0201 
0202     saveGame = KGameStandardAction::save (this, nullptr, nullptr);
0203     actionCollection()->addAction (saveGame->objectName(), saveGame);
0204     connect (saveGame, &QAction::triggered, this, [this] { game->gameActions(SAVE_GAME); });
0205     saveGame->setText (i18n ("&Save Game..."));
0206     KActionCollection::setDefaultShortcut(saveGame, Qt::Key_S); // Alternate key.
0207 
0208     // The name of the solution-file is 'sol_<prefix>.txt', where <prefix> is
0209     // the unique prefix belonging to the game involved (eg. plws, tute, etc.).
0210     a        = gameAction (QStringLiteral("save_solution"), SAVE_SOLUTION,
0211                            i18n ("Save A Solution..."),
0212                            i18n ("Save A Solution..."),
0213                            i18n ("Save a solution for a level into a file "
0214                                  "called 'sol_&lt;prefix&gt;.txt' in your "
0215                  "user's data directory..."),
0216                            {Qt::ShiftModifier | Qt::Key_S});
0217 
0218     // Pause
0219     // Show High Scores
0220     // Get a Hint
0221     // Kill the Hero
0222     // --------------------------
0223 
0224     myPause = KGameStandardAction::pause (this, nullptr, nullptr);
0225     actionCollection()->addAction (myPause->objectName(), myPause);
0226     connect (myPause, &QAction::triggered, this, [this] { game->gameActions(PAUSE); });
0227     // QAction * myPause gets QAction::shortcut(), returning 1 OR 2 shortcuts.
0228     QList<QKeySequence> pauseShortcut = { myPause->shortcut(), Qt::Key_Escape };
0229     myPause->setShortcuts (pauseShortcut);
0230 
0231     highScore = KGameStandardAction::highscores (this, nullptr, nullptr);
0232     actionCollection()->addAction (highScore->objectName(), highScore);
0233     connect (highScore, &QAction::triggered, this, [this] { game->gameActions(HIGH_SCORE); });
0234 
0235     hintAction = KGameStandardAction::hint (this, nullptr, nullptr);
0236     actionCollection()->addAction (hintAction->objectName(), hintAction);
0237     connect (hintAction, &QAction::triggered, this, [this] { game->gameActions(HINT); });
0238 
0239     a = KGameStandardAction::demo (this, nullptr, nullptr);
0240     actionCollection()->addAction (a->objectName(), a);
0241     connect (a, &QAction::triggered, this, [this] { game->gameActions(DEMO); });
0242 
0243     a = KGameStandardAction::solve (this, nullptr, nullptr);
0244     actionCollection()->addAction (a->objectName(), a);
0245     connect (a, &QAction::triggered, this, [this] { game->gameActions(SOLVE); });
0246     a->setText      (i18n ("&Show a Solution"));
0247     a->setToolTip   (i18n ("Show how to win this level."));
0248     a->setWhatsThis (i18n ("Play a recording of how to win this level, if "
0249                            "there is one available."));
0250 
0251     a        = gameAction (QStringLiteral("instant_replay"), INSTANT_REPLAY,
0252                            i18n ("&Instant Replay"),
0253                            i18n ("Instant replay."),
0254                            i18n ("Show a recording of the level "
0255                                  "you are currently playing."),
0256                            Qt::Key_R);
0257 
0258     a        = gameAction (QStringLiteral("replay_last"), REPLAY_LAST,
0259                            i18n ("Replay &Last Level"),
0260                            i18n ("Replay last level."),
0261                            i18n ("Show a recording of the last level you "
0262                                  "played and finished, regardless of whether "
0263                                  "you won or lost."),
0264                            Qt::Key_A);
0265 
0266     a        = gameAction (QStringLiteral("replay_any"), REPLAY_ANY,
0267                            i18n ("&Replay Any Level"),
0268                            i18n ("Replay any level."),
0269                            i18n ("Show a recording of any level you have "
0270                                  "played so far."),
0271                            QKeySequence()); // No key assigned.
0272 
0273     killHero = gameAction (QStringLiteral("kill_hero"), KILL_HERO,
0274                            i18n ("&Kill Hero"),
0275                            i18n ("Kill Hero."),
0276                            i18n ("Kill the hero, in case he finds himself in "
0277                                  "a situation from which he cannot escape."),
0278                            Qt::Key_Q);
0279 
0280     // Quit
0281     // --------------------------
0282 
0283     KGameStandardAction::quit (this, &QWidget::close, actionCollection());
0284 
0285     /**************************************************************************/
0286     /***************************   GAME EDITOR MENU  **************************/
0287     /**************************************************************************/
0288     // Create a Level
0289     // Edit a Level...
0290     // --------------------------
0291 
0292     QAction * ed = editAction (QStringLiteral("create_level"), CREATE_LEVEL,
0293                                i18n ("&Create Level"),
0294                                i18n ("Create level."),
0295                                i18n ("Create a completely new level."));
0296     ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-new" )));
0297     ed->setIconText (i18n ("Create"));
0298 
0299     ed           = editAction (QStringLiteral("edit_any"), EDIT_ANY,
0300                                i18n ("&Edit Level..."),
0301                                i18n ("Edit level..."),
0302                                i18n ("Edit any level..."));
0303     ed->setIcon (QIcon::fromTheme( QStringLiteral( "document-open" )));
0304     ed->setIconText (i18n ("Edit"));
0305 
0306     // Save Edits...
0307     // Move Level...
0308     // Delete Level...
0309     // --------------------------
0310 
0311     saveEdits    = editAction (QStringLiteral("save_edits"), SAVE_EDITS,
0312                                i18n ("&Save Edits..."),
0313                                i18n ("Save edits..."),
0314                                i18n ("Save your level after editing..."));
0315     saveEdits->setIcon (QIcon::fromTheme( QStringLiteral( "document-save" )));
0316     saveEdits->setIconText (i18n ("Save"));
0317     saveEdits->setEnabled (false);      // Nothing to save, yet.
0318 
0319     ed           = editAction (QStringLiteral("move_level"), MOVE_LEVEL,
0320                                i18n ("&Move Level..."),
0321                                i18n ("Move level..."),
0322                                i18n ("Change a level's number or move "
0323                                      "it to another game..."));
0324 
0325     ed           = editAction (QStringLiteral("delete_level"), DELETE_LEVEL,
0326                                i18n ("&Delete Level..."),
0327                                i18n ("Delete level..."),
0328                                i18n ("Delete a level..."));
0329 
0330     // Create a Game
0331     // Edit Game Info...
0332     // --------------------------
0333 
0334     ed           = editAction (QStringLiteral("create_game"), CREATE_GAME,
0335                                i18n ("Create &Game..."),
0336                                i18n ("Create game..."),
0337                                i18n ("Create a completely new game..."));
0338 
0339     ed           = editAction (QStringLiteral("edit_game"), EDIT_GAME,
0340                                i18n ("Edit Game &Info..."),
0341                                i18n ("Edit game info..."),
0342                                i18n ("Change the name, rules or description "
0343                                      "of a game..."));
0344 
0345     /**************************************************************************/
0346     /****************************   SETTINGS MENU  ****************************/
0347     /**************************************************************************/
0348 
0349     // Theme settings are handled by this class and KGrRenderer.
0350     QAction * themes = actionCollection()->addAction (QStringLiteral("select_theme"));
0351     themes->setText      (i18n ("Change &Theme..."));
0352     themes->setToolTip   (i18n ("Change the graphics theme..."));
0353     themes->setWhatsThis (i18n ("Alter the visual appearance of the runners "
0354                                 "and background scene..."));
0355     connect (themes, &QAction::triggered, this, &KGoldrunner::changeTheme);
0356 
0357     // Show/Exit Full Screen Mode
0358     KToggleFullScreenAction * fullScreen = KStandardAction::fullScreen
0359                         (this, &KGoldrunner::viewFullScreen, this, this);
0360     actionCollection()->addAction (fullScreen->objectName(), fullScreen);
0361 
0362     // Other settings are handled by KGrGame.
0363 
0364 #ifdef KGAUDIO_BACKEND_OPENAL
0365     // Sound effects on/off
0366                                   settingAction (QStringLiteral("options_sounds"), PLAY_SOUNDS,
0367                                   i18n ("&Play Sounds"),
0368                                   i18n ("Play sound effects."),
0369                                   i18n ("Play sound effects during the game."));
0370 
0371                                   settingAction (QStringLiteral("options_steps"), PLAY_STEPS,
0372                                   i18n ("Play &Footstep Sounds"),
0373                                   i18n ("Make sounds of player's footsteps."),
0374                                   i18n ("Make sounds of player's footsteps."));
0375 #endif
0376 
0377     // Demo at start on/off.
0378                                   settingAction (QStringLiteral("options_demo"), STARTUP_DEMO,
0379                                   i18n ("&Demo At Start"),
0380                                   i18n ("Run a demo when the game starts."),
0381                                   i18n ("Run a demo when the game starts."));
0382 
0383     // Mouse Controls Hero
0384     // Keyboard Controls Hero
0385     // Laptop Hybrid
0386     // --------------------------
0387 
0388     KToggleAction * setMouse    = settingAction (QStringLiteral("mouse_mode"), MOUSE,
0389                                   i18n ("&Mouse Controls Hero"),
0390                                   i18n ("Mouse controls hero."),
0391                                   i18n ("Use the mouse to control "
0392                                         "the hero's moves."));
0393 
0394     KToggleAction * setKeyboard = settingAction (QStringLiteral("keyboard_mode"), KEYBOARD,
0395                                   i18n ("&Keyboard Controls Hero"),
0396                                   i18n ("Keyboard controls hero."),
0397                                   i18n ("Use the keyboard to control "
0398                                         "the hero's moves."));
0399 
0400     KToggleAction * setLaptop   = settingAction (QStringLiteral("laptop_mode"), LAPTOP,
0401                                   i18n ("Hybrid Control (&Laptop)"),
0402                                   i18n ("Pointer controls hero; dig "
0403                                         "using keyboard."),
0404                                   i18n ("Use the laptop's pointer device "
0405                                         "to control the hero's moves, and use "
0406                                         "the keyboard to dig left and right."));
0407 
0408     QActionGroup* controlGrp = new QActionGroup (this);
0409     controlGrp->addAction (setMouse);
0410     controlGrp->addAction (setKeyboard);
0411     controlGrp->addAction (setLaptop);
0412     controlGrp->setExclusive (true);
0413 
0414     // Options within keyboard mode.
0415     // Click key to begin moving and continue moving indefinitely.
0416     // Click and hold key to begin moving: release key to stop.
0417 
0418     KToggleAction * clickKey    = settingAction (QStringLiteral("click_key"), CLICK_KEY,
0419                                   i18n ("&Click Key To Move"),
0420                                   i18n ("Click Key To Move."),
0421                                   i18n ("In keyboard mode, click a "
0422                                         "direction-key to start moving "
0423                                         "and keep on going until you "
0424                                         "click another key."));
0425 
0426     KToggleAction * holdKey     = settingAction (QStringLiteral("hold_key"), HOLD_KEY,
0427                                   i18n ("&Hold Key To Move"),
0428                                   i18n ("Hold Key To Move."),
0429                                   i18n ("In keyboard mode, hold down a "
0430                                         "direction-key to move "
0431                                         "and release it to stop."));
0432 
0433     QActionGroup * keyGrp = new QActionGroup (this);
0434     keyGrp->addAction (clickKey);
0435     keyGrp->addAction (holdKey);
0436     keyGrp->setExclusive (true);
0437 
0438     // Normal Speed
0439     // Beginner Speed
0440     // Champion Speed
0441     // Increase Speed
0442     // Decrease Speed
0443     // --------------------------
0444 
0445     KToggleAction * nSpeed      = settingAction (QStringLiteral("normal_speed"), NORMAL_SPEED,
0446                                   i18n ("Normal Speed"),
0447                                   i18n ("Set normal speed."),
0448                                   i18n ("Set normal game speed."));
0449 
0450     KToggleAction * bSpeed      = settingAction (QStringLiteral("beginner_speed"),
0451                                   BEGINNER_SPEED,
0452                                   i18n ("Beginner Speed"),
0453                                   i18n ("Set beginners' speed."),
0454                                   i18n ("Set beginners' game speed "
0455                                         "(0.5 times normal)."));
0456 
0457     KToggleAction * cSpeed      = settingAction (QStringLiteral("champion_speed"),
0458                                   CHAMPION_SPEED,
0459                                   i18n ("Champion Speed"),
0460                                   i18n ("Set champions' speed."),
0461                                   i18n ("Set champions' game speed "
0462                                         "(1.5 times normal)."));
0463 
0464     a                           = gameAction (QStringLiteral("increase_speed"), INC_SPEED,
0465                                   i18n ("Increase Speed"),
0466                                   i18n ("Increase speed."),
0467                                   i18n ("Increase the game speed by 0.1 "
0468                                         "(maximum is 2.0 times normal)."),
0469                                   Qt::Key_Plus);
0470 
0471     a                           = gameAction (QStringLiteral("decrease_speed"), DEC_SPEED,
0472                                   i18n ("Decrease Speed"),
0473                                   i18n ("Decrease speed."),
0474                                   i18n ("Decrease the game speed by 0.1 "
0475                                         "(minimum is 0.2 times normal)."),
0476                                   Qt::Key_Minus);
0477 
0478     QActionGroup* speedGrp = new QActionGroup (this);
0479     speedGrp->addAction (nSpeed);
0480     speedGrp->addAction (bSpeed);
0481     speedGrp->addAction (cSpeed);
0482     speedGrp->setExclusive (true);
0483 
0484     // Configure Shortcuts...
0485     // --------------------------
0486 
0487     KStandardAction::keyBindings (
0488                                 this, &KGoldrunner::optionsConfigureKeys,
0489                                 actionCollection());
0490 
0491     /**************************************************************************/
0492     /**************************   KEYSTROKE ACTIONS  **************************/
0493     /**************************************************************************/
0494 
0495     // Two-handed KB controls and alternate one-handed controls for the hero.
0496 
0497     // The actions for the movement keys are created but disabled.  This lets
0498     // keyPressEvent() come through, instead of a signal, while still allowing
0499     // Settings->Configure Keys to change the key mappings.  The keyPressEvent()
0500     // call is needed so that the key can be identified and matched to the
0501     // corresponding keyReleaseEvent() call and make the hold-key option
0502     // work correctly when two keys are held down simultaneously.
0503 
0504     keyControl (QStringLiteral("stop"),       i18n ("Stop"),       Qt::Key_Space, STAND);
0505     keyControl (QStringLiteral("move_right"), i18n ("Move Right"), Qt::Key_Right, RIGHT, true);
0506     keyControl (QStringLiteral("move_left"),  i18n ("Move Left"),  Qt::Key_Left,  LEFT,  true);
0507     keyControl (QStringLiteral("move_up"),    i18n ("Move Up"),    Qt::Key_Up,    UP,    true);
0508     keyControl (QStringLiteral("move_down"),  i18n ("Move Down"),  Qt::Key_Down,  DOWN,  true);
0509     keyControl (QStringLiteral("dig_right"),  i18n ("Dig Right"),  Qt::Key_C,     DIG_RIGHT);
0510     keyControl (QStringLiteral("dig_left"),   i18n ("Dig Left"),   Qt::Key_Z,     DIG_LEFT);
0511 
0512     // Alternate one-handed controls.  Set up in "kgoldrunnerui.rc".
0513 
0514     // Key_I, "move_up"
0515     // Key_L, "move_right"
0516     // Key_K, "move_down"
0517     // Key_J, "move_left"
0518     // Key_Space, "stop" (as above)
0519     // Key_O, "dig_right"
0520     // Key_U, "dig_left"
0521 
0522     setupEditToolbarActions();      // Uses pixmaps from "renderer".
0523 
0524     // Authors' debugging aids, effective when Pause is hit.  Options include
0525     // stepping through the animation, toggling a debug patch or log messages
0526     // on or off during gameplay and printing the states of runners or tiles.
0527 
0528     KConfigGroup debugGroup (KSharedConfig::openConfig(), QStringLiteral("Debugging"));
0529     bool addDebuggingShortcuts = debugGroup.readEntry
0530                         ("DebuggingShortcuts", false);  // Get debug option.
0531     if (! addDebuggingShortcuts)
0532         return;
0533 
0534     keyControlDebug (QStringLiteral("do_step"),      i18n ("Do a Step"), Qt::Key_Period, DO_STEP);
0535     keyControlDebug (QStringLiteral("bug_fix"),      i18n ("Test Bug Fix"), Qt::Key_B, BUG_FIX);
0536     keyControlDebug (QStringLiteral("show_positions"), i18n ("Show Positions"), Qt::Key_W, S_POSNS);
0537     keyControlDebug (QStringLiteral("logging"),      i18n ("Start Logging"), Qt::Key_G, LOGGING);
0538     keyControlDebug (QStringLiteral("show_hero"),    i18n ("Show Hero"), Qt::Key_E, S_HERO);
0539     keyControlDebug (QStringLiteral("show_obj"),     i18n ("Show Object"), Qt::Key_Slash, S_OBJ);
0540 
0541     keyControlDebug (QStringLiteral("show_enemy_0"), i18n ("Show Enemy") + QLatin1Char('0'), Qt::Key_0, ENEMY_0);
0542     keyControlDebug (QStringLiteral("show_enemy_1"), i18n ("Show Enemy") + QLatin1Char('1'), Qt::Key_1, ENEMY_1);
0543     keyControlDebug (QStringLiteral("show_enemy_2"), i18n ("Show Enemy") + QLatin1Char('2'), Qt::Key_2, ENEMY_2);
0544     keyControlDebug (QStringLiteral("show_enemy_3"), i18n ("Show Enemy") + QLatin1Char('3'), Qt::Key_3, ENEMY_3);
0545     keyControlDebug (QStringLiteral("show_enemy_4"), i18n ("Show Enemy") + QLatin1Char('4'), Qt::Key_4, ENEMY_4);
0546     keyControlDebug (QStringLiteral("show_enemy_5"), i18n ("Show Enemy") + QLatin1Char('5'), Qt::Key_5, ENEMY_5);
0547     keyControlDebug (QStringLiteral("show_enemy_6"), i18n ("Show Enemy") + QLatin1Char('6'), Qt::Key_6, ENEMY_6);
0548 }
0549 
0550 QAction * KGoldrunner::gameAction (const QString & name,
0551                                    const int       code,
0552                                    const QString & text,
0553                                    const QString & toolTip,
0554                                    const QString & whatsThis,
0555                                    const QKeySequence & key)
0556 {
0557     QAction * ga = actionCollection()->addAction (name);
0558     ga->setText (text);
0559     ga->setToolTip (toolTip);
0560     ga->setWhatsThis (whatsThis);
0561     if (! key.isEmpty()) {
0562         KActionCollection::setDefaultShortcut(ga, key);
0563     }
0564     connect (ga, &QAction::triggered, this, [this, code] { game->gameActions(code); });
0565     return ga;
0566 }
0567 
0568 QAction * KGoldrunner::editAction (const QString & name,
0569                                    const int       code,
0570                                    const QString & text,
0571                                    const QString & toolTip,
0572                                    const QString & whatsThis)
0573 {
0574     QAction * ed = actionCollection()->addAction (name);
0575     ed->setText (text);
0576     ed->setToolTip (toolTip);
0577     ed->setWhatsThis (whatsThis);
0578     connect (ed, &QAction::triggered, this, [this, code] { game->editActions(code); });
0579     return ed;
0580 }
0581 
0582 KToggleAction * KGoldrunner::settingAction (const QString & name,
0583                                             const int       code,
0584                                             const QString & text,
0585                                             const QString & toolTip,
0586                                             const QString & whatsThis)
0587 {
0588     KToggleAction * s = new KToggleAction (text, this);
0589     actionCollection()->addAction (name, s);
0590     s->setToolTip (toolTip);
0591     s->setWhatsThis (whatsThis);
0592     connect (s, &QAction::triggered, this, [this, code] { game->settings(code); });
0593     return s;
0594 }
0595 
0596 KToggleAction * KGoldrunner::editToolbarAction (const QString & name,
0597                                                 const char      code,
0598                                                 const QString & shortText,
0599                                                 const QString & text,
0600                                                 const QString & toolTip,
0601                                                 const QString & whatsThis)
0602 {
0603     KToggleAction * ed = new KToggleAction (text, this);
0604     actionCollection()->addAction (name, ed);
0605     ed->setIconText (shortText);
0606     ed->setToolTip (toolTip);
0607     ed->setWhatsThis (whatsThis);
0608     connect (ed, &QAction::triggered, this, [this, code] { game->editToolbarActions(code); });
0609     return ed;
0610 }
0611 
0612 void KGoldrunner::keyControl (const QString & name, const QString & text,
0613                               const QKeySequence & shortcut, const int code,
0614                               const bool mover)
0615 {
0616     QAction * a = actionCollection()->addAction (name);
0617     a->setText (text);
0618     KActionCollection::setDefaultShortcut(a, shortcut);
0619     a->setAutoRepeat (false);       // Avoid repeats of signals by QAction.
0620 
0621     // If this is a move-key, let keyPressEvent() through, instead of signal.
0622     if (mover) {
0623         a->setEnabled (false);
0624     addAction (a);
0625     return;
0626     }
0627 
0628     connect (a, &QAction::triggered, this, [this, code] { game->kbControl(code); });
0629     addAction (a);
0630 }
0631 
0632 void KGoldrunner::keyControlDebug (const QString & name, const QString & text,
0633                               const QKeySequence & shortcut, const int code,
0634                               const bool mover)
0635 {
0636     QAction * a = actionCollection()->addAction (name);
0637     a->setText (text);
0638     KActionCollection::setDefaultShortcut(a, shortcut);
0639     a->setAutoRepeat (false);       // Avoid repeats of signals by QAction.
0640 
0641     // If this is a move-key, let keyPressEvent() through, instead of signal.
0642     if (mover) {
0643         a->setEnabled (false);
0644     addAction (a);
0645     return;
0646     }
0647 
0648     connect (a, &QAction::triggered, this, [this, code] { game->dbgControl(code); });
0649     addAction (a);
0650 }
0651 
0652 void KGoldrunner::setUpKeyboardControl()
0653 {
0654     // This is needed to ensure that the press and release of Up, Down, Left and
0655     // Right keys (arrow-keys) are all received as required.
0656     //
0657     // If the KGoldrunner widget does not have the keyboard focus, arrow-keys
0658     // provide only key-release events, which do not control the hero properly.
0659     // Other keys provide both press and release events, regardless of focus.
0660 
0661     this->setFocusPolicy (Qt::StrongFocus); // Tab or click gets the focus.
0662     view->setFocusProxy (this);         // So does a click on the play-area.
0663     this->setFocus (Qt::OtherFocusReason);  // And we start by having the focus.
0664 }
0665 
0666 void KGoldrunner::keyPressEvent (QKeyEvent * event)
0667 {
0668     // For movement keys, all presses and releases are processed, thus allowing
0669     // the hold-key option to work correctly when two keys are held down.
0670 
0671     if (! identifyMoveAction (event, true)) {
0672         QWidget::keyPressEvent (event);
0673     }
0674 }
0675 
0676 void KGoldrunner::keyReleaseEvent (QKeyEvent * event)
0677 {
0678     if (! identifyMoveAction (event, false)) {
0679         QWidget::keyReleaseEvent (event);
0680     }
0681 }
0682 
0683 bool KGoldrunner::identifyMoveAction (QKeyEvent * event, bool pressed)
0684 {
0685     if (event->isAutoRepeat()) {
0686         return false;       // Use only the release and the initial press.
0687     }
0688     Direction dirn = STAND;
0689     // The arrow keys show the "Keypad" modifier as being set, even if the
0690     // computer does NOT have a keypad (see Qt::KeypadModifier doco). It is
0691     // OK to ignore the Keypad modifier (see the code for "qevent.cpp" at
0692     // "bool QKeyEvent::matches(QKeySequence::StandardKey matchKey)"). The
0693     // keys on the keypad usually have equivalents on the main keyboard.
0694     QKeySequence keystroke (~(Qt::KeypadModifier) &
0695                              (event->key() | event->modifiers()));
0696     if ((ACTION (QStringLiteral("move_left")))->shortcuts().contains(keystroke)) {
0697         dirn = LEFT;
0698     }
0699     else if ((ACTION (QStringLiteral("move_right")))->shortcuts().contains(keystroke)) {
0700         dirn = RIGHT;
0701     }
0702     else if ((ACTION (QStringLiteral("move_up")))->shortcuts().contains(keystroke)) {
0703         dirn = UP;
0704     }
0705     else if ((ACTION (QStringLiteral("move_down")))->shortcuts().contains(keystroke)) {
0706         dirn = DOWN;
0707     }
0708     else {
0709         return false;
0710     }
0711     // Use this event to control the hero, if KEYBOARD mode is selected.
0712     game->kbControl (dirn, pressed);
0713     return true;
0714 }
0715 
0716 void KGoldrunner::viewFullScreen (bool activation)
0717 {
0718     KToggleFullScreenAction::setFullScreen (this, activation);
0719 }
0720 
0721 void KGoldrunner::gameFreeze (bool on_off)
0722 {
0723     myPause->setChecked (on_off);
0724     frozen = on_off;    // Remember the state (for the configure-keys case).
0725     QStringList pauseKeys;
0726 
0727     const auto keyBindings = myPause->shortcuts();
0728     for (const QKeySequence &s : keyBindings) {
0729         pauseKeys.append(s.toString(QKeySequence::NativeText));
0730     }
0731 
0732     QString msg;
0733     if (on_off) {
0734         if (pauseKeys.isEmpty()) {
0735             msg = i18n("The game is paused");
0736         } else if (pauseKeys.size() == 1) {
0737             msg = i18n("Press \"%1\" to RESUME", pauseKeys.at(0));
0738         } else {
0739             msg = i18n("Press \"%1\" or \"%2\" to RESUME", pauseKeys.at(0),
0740                                                            pauseKeys.at(1));
0741         }
0742     } else {
0743         if (pauseKeys.isEmpty()) {
0744             msg = QString();
0745         } else if (pauseKeys.size() == 1) {
0746             msg = i18n("Press \"%1\" to PAUSE", pauseKeys.at(0));
0747         } else {
0748             msg = i18n("Press \"%1\" or \"%2\" to PAUSE", pauseKeys.at(0),
0749                                                           pauseKeys.at(1));
0750         }
0751     }
0752     scene->setPauseResumeText (msg);
0753 }
0754 
0755 void KGoldrunner::adjustHintAction (bool hintAvailable)
0756 {
0757     hintAction->setEnabled (hintAvailable);
0758 
0759     QString msg;
0760     msg = hintAvailable ? i18n("Has hint") : i18n("No hint");
0761     scene->setHasHintText (msg);
0762 }
0763 
0764 void KGoldrunner::setToggle (const QString &actionName, const bool onOff)
0765 {
0766     ((KToggleAction *) ACTION (actionName))->setChecked (onOff);
0767 }
0768 
0769 void KGoldrunner::setAvail (const QString &actionName, const bool onOff)
0770 {
0771     ((QAction *) ACTION (actionName))->setEnabled (onOff);
0772 }
0773 
0774 void KGoldrunner::setEditMenu (bool on_off)
0775 {
0776     saveEdits->setEnabled  (on_off);
0777 
0778     saveGame->setEnabled   (! on_off);
0779     hintAction->setEnabled (! on_off);
0780     killHero->setEnabled   (! on_off);
0781     highScore->setEnabled  (! on_off);
0782     setAvail (QStringLiteral("instant_replay"), (! on_off));
0783     setAvail (QStringLiteral("game_pause"),     (! on_off));
0784 
0785     if (on_off){
0786         // Set the editToolbar icons to the current tile-size.
0787         //qCDebug(KGOLDRUNNER_LOG) << "ToolBar icon size:" << scene->tileSize ();
0788         toolBar (QStringLiteral("editToolbar"))->setIconSize (scene->tileSize ());
0789 
0790         // Set the editToolbar icons up with pixmaps of the current theme.
0791         setEditIcon (QStringLiteral("brickbg"),   BRICK);
0792         setEditIcon (QStringLiteral("fbrickbg"),  FBRICK);
0793         setEditIcon (QStringLiteral("freebg"),    FREE);
0794         setEditIcon (QStringLiteral("nuggetbg"),  NUGGET);
0795         setEditIcon (QStringLiteral("polebg"),    BAR);
0796         setEditIcon (QStringLiteral("concretebg"), CONCRETE);
0797         setEditIcon (QStringLiteral("ladderbg"),  LADDER);
0798         setEditIcon (QStringLiteral("hladderbg"), HLADDER);
0799         setEditIcon (QStringLiteral("edherobg"),  HERO);
0800         setEditIcon (QStringLiteral("edenemybg"), ENEMY);
0801         setToggle   (QStringLiteral("brickbg"), true); // Default edit-object is BRICK.
0802 
0803         toolBar (QStringLiteral("editToolbar"))->show();
0804     }
0805     else {
0806         toolBar (QStringLiteral("editToolbar"))->hide();
0807     }
0808 }
0809 
0810 void KGoldrunner::setEditIcon (const QString & actionName, const char iconType)
0811 {
0812     ((KToggleAction *) (actionCollection()->action (actionName)))->
0813                 setIcon (QIcon(renderer->getPixmap (iconType)));
0814 }
0815 
0816 /******************************************************************************/
0817 /*******************   SLOTS FOR MENU AND KEYBOARD ACTIONS  *******************/
0818 /******************************************************************************/
0819 
0820 void KGoldrunner::changeTheme ()
0821 {
0822     renderer->selectTheme ();
0823 }
0824 
0825 void KGoldrunner::redrawEditToolbar ()
0826 {
0827     // Signalled by the scene after the theme or tile size has changed.
0828     if (game->inEditMode()) {
0829         setEditMenu (true);
0830     }
0831 }
0832 
0833 void KGoldrunner::saveProperties (KConfigGroup & /* config - unused */)
0834 {
0835     // The 'config' object points to the session managed
0836     // config file.  Anything you write here will be available
0837     // later when this app is restored.
0838 
0839     //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::saveProperties.";
0840 }
0841 
0842 void KGoldrunner::readProperties (const KConfigGroup & /* config - unused */)
0843 {
0844     // The 'config' object points to the session managed
0845     // config file.  This function is automatically called whenever
0846     // the app is being restored.  Read in here whatever you wrote
0847     // in 'saveProperties'
0848 
0849     //qCDebug(KGOLDRUNNER_LOG) << "I am in KGoldrunner::readProperties.";
0850 }
0851 
0852 void KGoldrunner::optionsConfigureKeys()
0853 {
0854     auto *dlg = new KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
0855     dlg->addCollection(actionCollection());
0856     dlg->setAttribute(Qt::WA_DeleteOnClose);
0857     connect(dlg, &KShortcutsDialog::saved, this, [this]() {
0858         gameFreeze(frozen);
0859     });
0860 
0861     dlg->configure(true /* save settings */);
0862 }
0863 
0864 bool KGoldrunner::getDirectories()
0865 {
0866     bool result = true;
0867 
0868     QString myDir = QStringLiteral("kgoldrunner");
0869     QStringList genericDataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0870     QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
0871 
0872     // Find the system collections in a directory of the required KDE type.
0873     systemDataDir = QStandardPaths::locate(QStandardPaths::AppDataLocation,
0874                                            QStringLiteral("system/")
0875                                            , QStandardPaths::LocateDirectory);
0876     if (systemDataDir.length() <= 0) {
0877         KGrMessage::information (this, i18n ("Get Folders"),
0878         i18n ("Cannot find system games sub-folder '/system/' "
0879         "in areas '%1'.",
0880         appDataLocations.join(QLatin1Char(';'))));
0881         result = false;         // ABORT if the games data is missing.
0882     }
0883 
0884     // Locate and optionally create directories for user collections and levels.
0885     userDataDir   = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/');
0886     QString levelDir = userDataDir + QStringLiteral("levels");
0887     KIO::mkpath(QUrl::fromUserInput(levelDir))->exec();
0888 
0889     return (result);
0890 }
0891 
0892 // This method is invoked when the main window is closed, whether by selecting
0893 // "Quit" from the menu or by clicking the "X" at the top right of the window.
0894 
0895 // If we return true, game-edits were saved or abandoned or there was no editing
0896 // in progress, so the main window will close.  If we return false, the user has
0897 // asked to go on editing, so the main window stays open.
0898 
0899 bool KGoldrunner::queryClose()
0900 {
0901     return (game->saveOK());
0902 }
0903 
0904 /******************************************************************************/
0905 /**********************  MAKE A TOOLBAR FOR THE EDITOR   **********************/
0906 /******************************************************************************/
0907 
0908 void KGoldrunner::setupEditToolbarActions()
0909 {
0910     QAction * ed = editAction (QStringLiteral("edit_hint"), EDIT_HINT,
0911                                i18n ("Edit Name/Hint"),
0912                                i18n ("Edit level name or hint"),
0913                                i18n ("Edit text for the name or hint "
0914                                      "of a level"));
0915     ed->setIcon (QIcon::fromTheme( QStringLiteral( "games-hint" )));
0916     ed->setIconText (i18n ("Name/Hint"));
0917 
0918     KToggleAction * free    = editToolbarAction (QStringLiteral("freebg"), FREE,
0919                               i18n ("Erase"), i18n ("Space/Erase"),
0920                               i18n ("Paint empty squares or erase"),
0921                               i18n ("Erase objects or paint empty squares"));
0922 
0923     KToggleAction * edhero  = editToolbarAction (QStringLiteral("edherobg"), HERO,
0924                               i18n("Hero"), i18n ("Hero"),
0925                               i18n ("Move hero"),
0926                               i18n ("Change the hero's starting position"));
0927 
0928     KToggleAction * edenemy = editToolbarAction (QStringLiteral("edenemybg"), ENEMY,
0929                               i18n ("Enemy"), i18n ("Enemy"),
0930                               i18n ("Paint enemies"),
0931                               i18n ("Paint enemies at their starting positions")
0932                               );
0933 
0934     KToggleAction * brick   = editToolbarAction (QStringLiteral("brickbg"), BRICK,
0935                               i18n ("Brick"), i18n ("Brick"),
0936                               i18n ("Paint bricks (can dig)"),
0937                               i18n ("Paint bricks (diggable objects)"));
0938 
0939     KToggleAction* concrete = editToolbarAction (QStringLiteral("concretebg"), CONCRETE,
0940                               i18n ("Concrete"), i18n ("Concrete"),
0941                               i18n ("Paint concrete (cannot dig)"),
0942                               i18n ("Paint concrete objects (not diggable)"));
0943 
0944     KToggleAction * fbrick  = editToolbarAction (QStringLiteral("fbrickbg"), FBRICK,
0945                               i18n ("Trap"), i18n ("Trap/False Brick"),
0946                               i18n ("Paint traps or false bricks "
0947                                     "(can fall through)"),
0948                               i18n ("Paint traps or false bricks "
0949                                     "(can fall through)"));
0950 
0951     KToggleAction * ladder  = editToolbarAction (QStringLiteral("ladderbg"), LADDER,
0952                               i18n ("Ladder"), i18n ("Ladder"),
0953                               i18n ("Paint ladders"),
0954                               i18n ("Paint ladders (ways to go up or down)"));
0955 
0956     KToggleAction * hladder = editToolbarAction (QStringLiteral("hladderbg"), HLADDER,
0957                               i18n ("H Ladder"), i18n ("Hidden Ladder"),
0958                               i18n ("Paint hidden ladders"),
0959                               i18n ("Paint hidden ladders, which appear "
0960                                     "when all the gold is gone"));
0961 
0962     KToggleAction * bar     = editToolbarAction (QStringLiteral("polebg"), BAR,
0963                               i18n ("Bar"), i18n ("Bar/Pole"),
0964                               i18n ("Paint bars or poles"),
0965                               i18n ("Paint bars or poles (can fall from these)")
0966                               );
0967 
0968     KToggleAction * nugget  = editToolbarAction (QStringLiteral("nuggetbg"), NUGGET,
0969                               i18n ("Gold"), i18n ("Gold/Treasure"),
0970                               i18n ("Paint gold (or other treasure)"),
0971                               i18n ("Paint gold pieces (or other treasure)"));
0972 
0973     QActionGroup* editButtons = new QActionGroup (this);
0974     editButtons->setExclusive (true);
0975     editButtons->addAction (free);
0976     editButtons->addAction (edhero);
0977     editButtons->addAction (edenemy);
0978     editButtons->addAction (brick);
0979     editButtons->addAction (concrete);
0980     editButtons->addAction (fbrick);
0981     editButtons->addAction (ladder);
0982     editButtons->addAction (hladder);
0983     editButtons->addAction (bar);
0984     editButtons->addAction (nugget);
0985 
0986     brick->setChecked (true);
0987 }
0988 
0989 QSize KGoldrunner::sizeHint() const
0990 {
0991     return QSize (640, 600);
0992 }
0993 
0994 #include "moc_kgoldrunner.cpp"