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

0001 /*
0002     SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kgrdebug.h"
0008 #include "kgreditor.h"
0009 #include "kgrscene.h"
0010 #include "kgrview.h"
0011 #include "kgrselector.h"
0012 #include "kgrdialog.h"
0013 #include "kgrgameio.h"
0014 #include <KLocalizedString>
0015 #include <ctype.h>
0016 #include <QTimer>
0017 
0018 #include "kgoldrunner_debug.h"
0019 
0020 KGrEditor::KGrEditor (KGrView * theView,
0021                       const QString & theSystemDir,
0022                       const QString & theUserDir,
0023                       QList<KGrGameData *> & pGameList)
0024     :
0025     QObject          (theView),     // Destroy Editor if view closes.
0026     view             (theView),
0027     scene            (view->gameScene()),
0028     io               (new KGrGameIO (view)),
0029     systemDataDir    (theSystemDir),
0030     userDataDir      (theUserDir),
0031     gameList         (pGameList),
0032     editObj          (BRICK),       // Default edit-object.
0033     shouldSave       (false),
0034     mouseDisabled    (true)
0035 {
0036     levelData.width  = FIELDWIDTH;  // Default values for a brand new game.
0037     levelData.height = FIELDHEIGHT;
0038 
0039     // Connect and start the timer.
0040     timer = new QTimer (this);
0041     connect(timer, &QTimer::timeout, this, &KGrEditor::tick);
0042     timer->start (TickTime);        // TickTime def in kgrglobals.h.
0043 
0044     // Connect edit-mode slots to signals from "view".
0045     connect(view, &KGrView::mouseClick, this, &KGrEditor::doEdit);
0046     connect(view, &KGrView::mouseLetGo, this, &KGrEditor::endEdit);
0047     connect(this, &KGrEditor::getMousePos, scene, &KGrScene::getMousePos);
0048 }
0049 
0050 KGrEditor::~KGrEditor()
0051 {
0052 }
0053 
0054 void KGrEditor::setEditObj (char newEditObj)
0055 {
0056     editObj = newEditObj;
0057 }
0058 
0059 bool KGrEditor::createLevel (int pGameIndex)
0060 {
0061     if (! saveOK ()) {              // Check unsaved work.
0062         return false;
0063     }
0064 
0065     if (! ownerOK (USER)) {
0066         KGrMessage::information (view, i18n ("Create Level"),
0067                 i18n ("You cannot create and save a level "
0068                 "until you have created a game to hold "
0069                 "it. Try menu item \"Create Game\"."));
0070         return false;
0071     }
0072 
0073     int i, j;
0074     gameIndex = pGameIndex;
0075     editLevel = 0;
0076 
0077     // Ignore player input from the mouse while the screen is set up.
0078     mouseDisabled = true;
0079 
0080     levelData.level = 0;
0081     levelData.name  = "";
0082     levelData.hint  = "";
0083     initEdit();
0084 
0085     // Clear the playfield.
0086     levelData.layout.resize (levelData.width * levelData.height);
0087     for (i = 1; i <= levelData.width; ++i) {
0088     for (j = 1; j <= levelData.height; ++j) {
0089             insertEditObj (i, j, FREE);
0090         }
0091     }
0092 
0093     // Add a hero.
0094     insertEditObj (1, 1, HERO);
0095 
0096     savedLevelData.layout = levelData.layout;       // Copy for "saveOK()".
0097     savedLevelData.name   = levelData.name;
0098     savedLevelData.hint   = levelData.hint;
0099 
0100     view->update();                 // Show the level name.
0101 
0102     // Re-enable player input.
0103     mouseDisabled = false;
0104     return true;
0105 }
0106 
0107 bool KGrEditor::updateLevel (int pGameIndex, int level)
0108 {
0109 
0110     if (! saveOK ()) {              // Check unsaved work.
0111         return false;
0112     }
0113 
0114     if (! ownerOK (USER)) {
0115         KGrMessage::information (view, i18n ("Edit Level"),
0116             i18n ("You cannot edit and save a level until you "
0117             "have created a game and a level. Try menu item \"Create Game\"."));
0118         return false;
0119     }
0120 
0121     gameIndex = pGameIndex;
0122 
0123     // Ignore player input from the mouse while the screen is set up.
0124     mouseDisabled = true;
0125 
0126     if (level < 0)
0127         level = 0;
0128     int selectedLevel = selectLevel (SL_UPDATE, level, gameIndex);
0129     qCDebug(KGOLDRUNNER_LOG) << "Selected" << gameList.at(gameIndex)->name
0130              << "level" << selectedLevel;
0131     if (selectedLevel == 0) {
0132         mouseDisabled = false;
0133         return false;
0134     }
0135 
0136     if (gameList.at(gameIndex)->owner == SYSTEM) {
0137         KGrMessage::information (view, i18n ("Edit Level"),
0138             i18n ("It is OK to edit a system level, but you MUST save "
0139             "the level in one of your own games. You are not just "
0140             "taking a peek at the hidden ladders "
0141             "and fall-through bricks, are you? :-)"));
0142     }
0143 
0144     loadEditLevel (selectedLevel);
0145     mouseDisabled = false;
0146     return true;
0147 }
0148 
0149 void KGrEditor::loadEditLevel (int lev)
0150 {
0151     KGrLevelData d;
0152 
0153     qCDebug(KGOLDRUNNER_LOG) << "gameIndex" << gameIndex;
0154     // If system game or ENDE screen, choose system dir, else choose user dir.
0155     const QString dir = ((gameList.at(gameIndex)->owner == SYSTEM) ||
0156                          (lev == 0)) ? systemDataDir : userDataDir;
0157     // Read the level data.
0158     if (! io->readLevelData (dir, gameList.at(gameIndex)->prefix, lev, d)) {
0159         return;     // If I/O failed, no load.
0160     }
0161 
0162     editLevel = lev;
0163     initEdit();
0164 
0165     int  i, j;
0166     char obj;
0167 
0168     // Load the level.
0169     for (i = 1; i <= levelData.width;  ++i) {
0170     for (j = 1; j <= levelData.height; ++j) {
0171             obj = d.layout [(j-1) * d.width + (i-1)];
0172             insertEditObj (i, j, obj);
0173     }
0174     }
0175     savedLevelData.layout = d.layout;       // Copy for "saveOK()".
0176 
0177     // Retain the original language of the name and hint when editing,
0178     // but convert non-ASCII, UTF-8 substrings to Unicode (eg. ü to ü).
0179     levelName = (d.name.size() > 0) ?
0180                 QString::fromUtf8 (d.name) : QString();
0181     levelHint = (d.hint.size() > 0) ?
0182                 QString::fromUtf8 (d.hint) : QString();
0183 
0184     scene->setTitle (getTitle());       // Show the level name.
0185 }
0186 
0187 void KGrEditor::editNameAndHint()
0188 {
0189     // Run a dialog box to create/edit the level name and hint.
0190     KGrNHDialog * nh = new KGrNHDialog (levelName, levelHint, view);
0191 
0192     if (nh->exec() == QDialog::Accepted) {
0193         levelName = nh->getName();
0194         levelHint = nh->getHint();
0195         shouldSave = true;
0196     }
0197 
0198     delete nh;
0199 }
0200 
0201 bool KGrEditor::saveLevelFile()
0202 {
0203     bool isNew;
0204     int action;
0205 
0206     int i, j;
0207     QString filePath;
0208 
0209     // Save the current game index.
0210     int N = gameIndex;
0211 
0212     if (editLevel == 0) {
0213         // New level: choose a number.
0214         action = SL_CREATE;
0215     }
0216     else {
0217         // Existing level: confirm the number or choose a new number.
0218         action = SL_SAVE;
0219     }
0220 
0221     // Pop up dialog box, which could change the game or level or both.
0222     int selectedLevel = selectLevel (action, editLevel, gameIndex);
0223     if (selectedLevel == 0) {
0224         return false;
0225     }
0226 
0227     // Get the new game (if changed).
0228     int n = gameIndex;
0229 
0230     // Set the name of the output file.
0231     filePath = getLevelFilePath (gameList.at(n), selectedLevel);
0232     QFile levelFile (filePath);
0233 
0234     if ((action == SL_SAVE) && (n == N) && (selectedLevel == editLevel)) {
0235         // This is a normal edit: the old file is to be re-written.
0236         isNew = false;
0237     }
0238     else {
0239         isNew = true;
0240         // Check if the file is to be inserted in or appended to the game.
0241         if (levelFile.exists()) {
0242             switch (KGrMessage::warning (view, i18n ("Save Level"),
0243                         i18n ("Do you want to insert a level and "
0244                         "move existing levels up by one?"),
0245                         i18n ("&Insert Level"), i18n ("&Cancel"))) {
0246 
0247             case 0: if (! reNumberLevels (n, selectedLevel,
0248                                             gameList.at (n)->nLevels, +1)) {
0249                             return false;
0250                         }
0251                         break;
0252             case 1: return false;
0253                         break;
0254             }
0255         }
0256     }
0257 
0258     // Open the output file.
0259     if (! levelFile.open (QIODevice::WriteOnly)) {
0260         KGrMessage::information (view, i18n ("Save Level"),
0261                 i18n ("Cannot open file '%1' for output.", filePath));
0262         return false;
0263     }
0264 
0265     // Save the level - row by row.
0266     for (j = 1; j <= levelData.height; ++j) {
0267         for (i = 1; i <= levelData.width; ++i) {
0268             levelFile.putChar (editableCell (i, j));
0269         }
0270     }
0271     savedLevelData.layout = levelData.layout;   // Copy for "saveOK()".
0272     levelFile.putChar ('\n');
0273 
0274     // Save the level name, changing non-ASCII chars to UTF-8 (eg. ü to ü).
0275     QByteArray levelNameC = levelName.toUtf8();
0276     int len1 = levelNameC.length();
0277     if (len1 > 0) {
0278         for (i = 0; i < len1; ++i)
0279             levelFile.putChar (levelNameC[i]);
0280         levelFile.putChar ('\n');           // Add a newline.
0281     }
0282 
0283     // Save the level hint, changing non-ASCII chars to UTF-8 (eg. ü to ü).
0284     QByteArray levelHintC = levelHint.toUtf8();
0285     int len2 = levelHintC.length();
0286     char ch = '\0';
0287 
0288     if (len2 > 0) {
0289         if (len1 <= 0)
0290             levelFile.putChar ('\n');       // Leave blank line for name.
0291         for (i = 0; i < len2; ++i) {
0292             ch = levelHintC[i];
0293             levelFile.putChar (ch);     // Copy the character.
0294         }
0295         if (ch != '\n')
0296             levelFile.putChar ('\n');       // Add a newline character.
0297     }
0298 
0299     levelFile.close();
0300     shouldSave = false;
0301 
0302     if (isNew) {
0303         gameList.at (n)->nLevels++;
0304         saveGameData (USER);
0305     }
0306 
0307     editLevel = selectedLevel;
0308     scene->setLevel (editLevel);        // Choose a background picture.
0309     scene->setTitle (getTitle());       // Display new title.
0310     return true;
0311 }
0312 
0313 bool KGrEditor::moveLevelFile (int pGameIndex, int level)
0314 {
0315     if (level <= 0) {
0316         KGrMessage::information (view, i18n ("Move Level"),
0317                 i18n ("You must first load a level to be moved. Use "
0318                      "the \"%1\" or \"%2\" menu.",
0319                      i18n ("Game"), i18n ("Editor")));
0320         return false;
0321     }
0322     gameIndex = pGameIndex;
0323 
0324     int action = SL_MOVE;
0325 
0326     int fromC = gameIndex;
0327     int fromL = level;
0328     int toC   = fromC;
0329     int toL   = fromL;
0330 
0331     if (! ownerOK (USER)) {
0332         KGrMessage::information (view, i18n ("Move Level"),
0333                 i18n ("You cannot move a level until you "
0334                 "have created a game and at least two levels. Try "
0335                 "menu item \"Create Game\"."));
0336         return false;
0337     }
0338 
0339     if (gameList.at (fromC)->owner != USER) {
0340         KGrMessage::information (view, i18n ("Move Level"),
0341                 i18n ("Sorry, you cannot move a system level."));
0342         return false;
0343     }
0344 
0345     // Pop up dialog box to get the game and level number to move to.
0346     while ((toC == fromC) && (toL == fromL)) {
0347         toL = selectLevel (action, toL, gameIndex);
0348         if (toL == 0) {
0349             return false;
0350         }
0351 
0352         toC = gameIndex;
0353 
0354         if ((toC == fromC) && (toL == fromL)) {
0355             KGrMessage::information (view, i18n ("Move Level"),
0356                     i18n ("You must change the level or the game or both."));
0357         }
0358     }
0359 
0360     QString filePath1;
0361     QString filePath2;
0362 
0363     // Save the "fromN" file under a temporary name.
0364     filePath1 = getLevelFilePath (gameList.at (fromC), fromL);
0365     filePath2 = filePath1;
0366     filePath2.append (QStringLiteral(".tmp"));
0367     if (! KGrGameIO::safeRename (view, filePath1, filePath2)) {
0368         return false;
0369     }
0370 
0371     if (toC == fromC) {                 // Same game.
0372         if (toL < fromL) {              // Decrease level.
0373             // Move "toL" to "fromL - 1" up by 1.
0374             if (! reNumberLevels (toC, toL, fromL-1, +1)) {
0375                 return false;
0376             }
0377         }
0378         else {                      // Increase level.
0379             // Move "fromL + 1" to "toL" down by 1.
0380             if (! reNumberLevels (toC, fromL+1, toL, -1)) {
0381                 return false;
0382             }
0383         }
0384     }
0385     else {                      // Different game.
0386         // In "fromC", move "fromL + 1" to "nLevels" down and update "nLevels".
0387         if (! reNumberLevels (fromC, fromL + 1,
0388                                     gameList.at (fromC)->nLevels, -1)) {
0389             return false;
0390         }
0391         gameList.at (fromC)->nLevels--;
0392 
0393         // In "toC", move "toL + 1" to "nLevels" up and update "nLevels".
0394         if (! reNumberLevels (toC, toL, gameList.at (toC)->nLevels, +1)) {
0395             return false;
0396         }
0397         gameList.at (toC)->nLevels++;
0398 
0399         saveGameData (USER);
0400     }
0401 
0402     // Rename the saved "fromL" file to become "toL".
0403     filePath1 = getLevelFilePath (gameList.at (toC), toL);
0404     KGrGameIO::safeRename (view, filePath2, filePath1);
0405 
0406     editLevel = toL;
0407     scene->setLevel (editLevel);        // Choose a background picture.
0408     scene->setTitle (getTitle());       // Re-write title.
0409     return true;
0410 }
0411 
0412 bool KGrEditor::deleteLevelFile (int pGameIndex, int level)
0413 {
0414     int action = SL_DELETE;
0415     gameIndex = pGameIndex;
0416 
0417     if (! ownerOK (USER)) {
0418         KGrMessage::information (view, i18n ("Delete Level"),
0419                 i18n ("You cannot delete a level until you "
0420                 "have created a game and a level. Try "
0421                 "menu item \"Create Game\"."));
0422         return false;
0423     }
0424 
0425     // Pop up dialog box to get the game and level number.
0426     int selectedLevel = selectLevel (action, level, gameIndex);
0427     if (selectedLevel == 0) {
0428         return false;
0429     }
0430 
0431     QString filePath;
0432 
0433     // Set the name of the file to be deleted.
0434     int n = gameIndex;
0435     filePath = getLevelFilePath (gameList.at (n), selectedLevel);
0436     QFile levelFile (filePath);
0437 
0438     // Delete the file for the selected game and level.
0439     if (levelFile.exists()) {
0440         if (selectedLevel < gameList.at (n)->nLevels) {
0441             switch (KGrMessage::warning (view, i18n ("Delete Level"),
0442                                 i18n ("Do you want to delete a level and "
0443                                 "move higher levels down by one?"),
0444                                 i18n ("&Delete Level"), i18n ("&Cancel"))) {
0445             case 0: break;
0446             case 1: return false; break;
0447             }
0448             levelFile.remove();
0449             if (! reNumberLevels (n, selectedLevel + 1,
0450                                   gameList.at(n)->nLevels, -1)) {
0451                 return false;
0452             }
0453         }
0454         else {
0455             levelFile.remove();
0456         }
0457     }
0458     else {
0459         KGrMessage::information (view, i18n ("Delete Level"),
0460                 i18n ("Cannot find file '%1' to be deleted.", filePath));
0461         return false;
0462     }
0463 
0464     gameList.at (n)->nLevels--;
0465     saveGameData (USER);
0466     if (selectedLevel <= gameList.at (n)->nLevels) {
0467         editLevel = selectedLevel;
0468     }
0469     else {
0470         editLevel = gameList.at (n)->nLevels;
0471     }
0472 
0473     // Repaint the screen with the level that now has the selected number.
0474     if (level > 0) {
0475         loadEditLevel (editLevel);  // Load level in edit mode.
0476     }
0477     else {
0478         createLevel (gameIndex);    // No levels left in game.
0479     }
0480     return true;
0481 }
0482 
0483 bool KGrEditor::editGame (int pGameIndex)
0484 {
0485     int n = -1;
0486     int action = (pGameIndex < 0) ? SL_CR_GAME : SL_UPD_GAME;
0487     gameIndex = pGameIndex;
0488 
0489     // If editing, choose a game.
0490     if (gameIndex >= 0) {
0491         int selectedLevel = selectLevel (SL_UPD_GAME, editLevel, gameIndex);
0492         if (selectedLevel == 0) {
0493             return false;
0494         }
0495         editLevel = selectedLevel;
0496         n = gameIndex;
0497     }
0498 
0499     bool result = false;
0500     KGrECDialog * ec = new KGrECDialog (action, n, gameList, view);
0501 
0502     while (ec->exec() == QDialog::Accepted) {   // Loop until valid.
0503 
0504         // Validate the game details.
0505         QString ecName = ec->getName();
0506         int len = ecName.length();
0507         if (len == 0) {
0508             KGrMessage::information (view, i18n ("Save Game Info"),
0509                 i18n ("You must enter a name for the game."));
0510             continue;
0511         }
0512 
0513         QString ecPrefix = ec->getPrefix();
0514         if ((action == SL_CR_GAME) || (gameList.at (n)->nLevels <= 0)) {
0515             // The filename prefix could have been entered, so validate it.
0516             len = ecPrefix.length();
0517         if (len == 0) {
0518                 KGrMessage::information (view, i18n ("Save Game Info"),
0519                     i18n ("You must enter a filename prefix for the game."));
0520                 continue;
0521             }
0522             if (len > 5) {
0523                 KGrMessage::information (view, i18n ("Save Game Info"),
0524                     i18n ("The filename prefix should not "
0525                     "be more than 5 characters."));
0526                 continue;
0527             }
0528 
0529             bool allAlpha = true;
0530             for (int i = 0; i < len; i++) {
0531                 if (! isalpha (ecPrefix.at (i).toLatin1())) {
0532                     allAlpha = false;
0533                     break;
0534                 }
0535             }
0536             if (! allAlpha) {
0537                 KGrMessage::information (view, i18n ("Save Game Info"),
0538                     i18n ("The filename prefix should be "
0539                     "all alphabetic characters."));
0540                 continue;
0541             }
0542 
0543             bool duplicatePrefix = false;
0544             KGrGameData * c;
0545             int imax = gameList.count();
0546             for (int i = 0; i < imax; i++) {
0547                 c = gameList.at (i);
0548                 if ((c->prefix == ecPrefix) && (i != n)) {
0549                     duplicatePrefix = true;
0550                     break;
0551                 }
0552             }
0553 
0554             if (duplicatePrefix) {
0555                 KGrMessage::information (view, i18n ("Save Game Info"),
0556                     i18n ("The filename prefix '%1' is already in use.",
0557                     ecPrefix));
0558                 continue;
0559             }
0560         }
0561 
0562         // Save the game details.
0563         char rules = 'K';
0564         if (ec->isTrad()) {
0565             rules = 'T';
0566         }
0567 
0568         KGrGameData * gameData = nullptr;
0569         if (action == SL_CR_GAME) {
0570             // Create empty game data and add it to the main list in KGrGame.
0571             gameData = new KGrGameData();
0572             gameList.append (gameData);
0573             gameIndex = gameList.count() - 1;
0574             editLevel = 1;
0575 
0576             // Set the initial values for a new game.
0577             gameData->owner   = USER;
0578             gameData->nLevels = 0;
0579             gameData->skill   = 'N';
0580             gameData->width   = FIELDWIDTH;
0581             gameData->height  = FIELDHEIGHT;
0582         }
0583         else {
0584             // Point to existing game data.
0585             gameData = gameList.at (gameIndex);
0586         }
0587 
0588         // Create or update the editable values.
0589         gameData->rules       = rules;
0590         gameData->prefix      = ecPrefix;
0591         gameData->name        = ecName;
0592         gameData->about       = ec->getAboutText().toUtf8();
0593 
0594         saveGameData (USER);
0595         result = true;          // Successful create/edit.
0596         break;              // All done now.
0597     }
0598 
0599     delete ec;
0600     return result;
0601 }
0602 
0603 /******************************************************************************/
0604 /************************    LEVEL SELECTION DIALOG    ************************/
0605 /******************************************************************************/
0606 
0607 int KGrEditor::selectLevel (int action, int requestedLevel, int & requestedGame)
0608 {
0609     int selectedLevel = 0;      // 0 = no selection (Cancel) or invalid.
0610     int selectedGame  = requestedGame;
0611 
0612     // Create and run a modal dialog box to select a game and level.
0613     KGrSLDialog * sl = new KGrSLDialog (action, requestedLevel, requestedGame,
0614                                         gameList, systemDataDir, userDataDir,
0615                                         view);
0616     connect(sl, &KGrSLDialog::editNameAndHint, this, &KGrEditor::editNameAndHint);
0617     bool selected = sl->selectLevel (selectedGame, selectedLevel);
0618     delete sl;
0619 
0620     if (selected) {
0621         requestedGame = selectedGame;
0622         return (selectedLevel);
0623     }
0624     else {
0625         return (0);         // 0 = cancelled or invalid.
0626     }
0627 }
0628 
0629 /******************************************************************************/
0630 /*********************  SUPPORTING GAME EDITOR FUNCTIONS  *********************/
0631 /******************************************************************************/
0632 
0633 bool KGrEditor::saveOK ()
0634 {
0635     bool result = true;
0636 
0637     if ((shouldSave) || (levelData.layout != savedLevelData.layout)) {
0638         // If shouldSave == true, level name or hint was edited.
0639         switch (KGrMessage::warning (view, i18n ("Editor"),
0640                     i18n ("You have not saved your work. Do "
0641                     "you want to save it now?"),
0642                     i18n ("&Save"), i18n ("&Do Not Save"),
0643                     i18n ("&Go on editing")))
0644         {
0645         case 0:
0646             result = saveLevelFile();   // Save, do next action: or more edits.
0647             break;
0648         case 1:
0649             shouldSave = false;     // Do not save, but do next action.
0650             break;
0651         case 2:
0652             result = false;     // Go back to editing.
0653             break;
0654         }
0655     }
0656 
0657     return (result);
0658 }
0659 
0660 void KGrEditor::initEdit()
0661 {
0662     paintEditObj = false;
0663     paintAltObj  = false;
0664 
0665     // Set the default object and button.
0666     editObj = BRICK;
0667 
0668     oldI = 0;
0669     oldJ = 0;
0670     heroCount = 0;
0671 
0672     scene->setLevel (editLevel);    // Choose a background picture.
0673     scene->setTitle (getTitle());   // Show title of level.
0674 
0675     shouldSave = false;     // Used to flag editing of name or hint.
0676 }
0677 
0678 void KGrEditor::insertEditObj (int i, int j, char obj)
0679 {
0680     dbk2 << i << j << obj;
0681     if ((i < 1) || (j < 1) || (i > levelData.width) || (j > levelData.height)) {
0682         return;     // Do nothing: mouse pointer is out of playfield.
0683     }
0684 
0685     if (editableCell (i, j) == HERO) {
0686         // The hero is in this cell: remove him.
0687         setEditableCell (i, j, FREE);
0688         heroCount = 0;
0689     }
0690 
0691     if (obj == HERO) {
0692         if (heroCount > 0) {
0693             // Can only have one hero: remove him from his previous position.
0694             for (int m = 1; m <= levelData.width; m++) {
0695         for (int n = 1; n <= levelData.height; n++) {
0696                     if (editableCell (m, n) == HERO) {
0697                         setEditableCell (m, n, FREE);
0698                     }
0699         }
0700             }
0701         }
0702         heroCount = 1;
0703     }
0704 
0705     setEditableCell (i, j, obj);
0706 }
0707 
0708 char KGrEditor::editableCell (int i, int j)
0709 {
0710     return (levelData.layout [(i - 1) + (j - 1) * levelData.width]);
0711 }
0712 
0713 void KGrEditor::setEditableCell (int i, int j, char type)
0714 {
0715     levelData.layout [(i - 1) + (j - 1) * levelData.width] = type;
0716     scene->paintCell (i, j, type);
0717 }
0718 
0719 bool KGrEditor::reNumberLevels (int cIndex, int first, int last, int inc)
0720 {
0721     int i, n, step;
0722     QString file1, file2;
0723 
0724     if (inc > 0) {
0725         i = last;
0726         n = first - 1;
0727         step = -1;
0728     }
0729     else {
0730         i = first;
0731         n = last + 1;
0732         step = +1;
0733     }
0734 
0735     while (i != n) {
0736         file1 = getLevelFilePath (gameList.at (cIndex), i);
0737         file2 = getLevelFilePath (gameList.at (cIndex), i - step);
0738         if (! KGrGameIO::safeRename (view, file1, file2)) {
0739             return (false);
0740         }
0741         i = i + step;
0742     }
0743 
0744     return (true);
0745 }
0746 
0747 bool KGrEditor::ownerOK (Owner o)
0748 {
0749     // Check that this owner has at least one set of game data.
0750     bool OK = false;
0751 
0752     for (KGrGameData * d : std::as_const(gameList)) {
0753         if (d->owner == o) {
0754             OK = true;
0755             break;
0756         }
0757     }
0758 
0759     return (OK);
0760 }
0761 
0762 bool KGrEditor::saveGameData (Owner o)
0763 {
0764     QString filePath;
0765 
0766     if (o != USER) {
0767         KGrMessage::information (view, i18n ("Save Game Info"),
0768             i18n ("You can only modify user games."));
0769         return (false);
0770     }
0771 
0772     filePath = userDataDir + QStringLiteral("games.dat");
0773 
0774     QFile c (filePath);
0775 
0776     // Open the output file.
0777     if (! c.open (QIODevice::WriteOnly)) {
0778         KGrMessage::information (view, i18n ("Save Game Info"),
0779                 i18n ("Cannot open file '%1' for output.", filePath));
0780         return (false);
0781     }
0782 
0783     // Save the game-data objects.
0784     QString             line;
0785     QByteArray          lineC;
0786     int         i, len;
0787     char        ch;
0788 
0789     for (KGrGameData * gData : std::as_const(gameList)) {
0790         if (gData->owner == o) {
0791             line = QStringLiteral ("%1 %2 %3 %4\n")
0792                             .arg (gData->nLevels, 3, 10, QLatin1Char('0')) // int 00n
0793                             .arg (gData->rules)                      // char
0794                             .arg (gData->prefix)                     // QString
0795                             .arg (gData->name);                      // QString
0796             lineC = line.toUtf8();
0797             len = lineC.length();
0798             for (i = 0; i < len; ++i) {
0799                 c.putChar (lineC.at (i));
0800             }
0801 
0802             len = gData->about.length();
0803             if (len > 0) {
0804                 QByteArray aboutC = gData->about;
0805                 len = aboutC.length();      // Might be longer now.
0806                 for (i = 0; i < len; ++i) {
0807                     ch = aboutC[i];
0808                     if (ch != '\n') {
0809                         c.putChar (ch);     // Copy the character.
0810                     }
0811                     else {
0812                         c.putChar ('\\');   // Change newline to \ and n.
0813                         c.putChar ('n');
0814                     }
0815                 }
0816                 c.putChar ('\n');       // Add a real newline.
0817             }
0818         }
0819     }
0820 
0821     c.close();
0822     return (true);
0823 }
0824 
0825 QString KGrEditor::getTitle()
0826 {
0827     if (editLevel <= 0) {
0828         // Generate a special title for a new level.
0829         return (i18n ("New Level"));
0830     }
0831 
0832     // Set title string to "Game-name - NNN" or "Game-name - NNN - Level-name".
0833     KGrGameData * gameData = gameList.at(gameIndex);
0834     QString levelNumber = QString::number(editLevel).rightJustified(3, QLatin1Char('0'));
0835 
0836     QString levelTitle = (levelName.length() <= 0)
0837                     ?
0838                     i18nc ("Game name - level number.",
0839                            "%1 - %2",
0840                            gameData->name, levelNumber)
0841                     :
0842                     i18nc ("Game name - level number - level name.",
0843                            "%1 - %2 - %3",
0844                            gameData->name, levelNumber, levelName);
0845     return (levelTitle);
0846 }
0847 
0848 QString KGrEditor::getLevelFilePath (KGrGameData * gameData, int lev)
0849 {
0850     QString filePath = userDataDir + QLatin1String("levels/") + gameData->prefix +
0851                        QString::number(lev).rightJustified(3, QLatin1Char('0')) + QStringLiteral(".grl");
0852     return (filePath);
0853 }
0854 
0855 /******************************************************************************/
0856 /*********************   EDIT ACTION SLOTS   **********************************/
0857 /******************************************************************************/
0858 
0859 void KGrEditor::doEdit (int button)
0860 {
0861     if (mouseDisabled) {
0862         return;
0863     }
0864 
0865     // Mouse button down: start making changes.
0866     int i, j;
0867     Q_EMIT getMousePos (i, j);
0868     qCDebug(KGOLDRUNNER_LOG) << "Button" << button << "at" << i << j;
0869 
0870     switch (button) {
0871     case Qt::LeftButton:
0872         paintEditObj = true;
0873         insertEditObj (i, j, editObj);
0874         oldI = i;
0875         oldJ = j;
0876         break;
0877     case Qt::RightButton:
0878         paintAltObj = true;
0879         insertEditObj (i, j, FREE);
0880         oldI = i;
0881         oldJ = j;
0882         break;
0883     default:
0884         break;
0885     }
0886 }
0887 
0888 void KGrEditor::tick()
0889 {
0890     if (mouseDisabled) {
0891         return;
0892     }
0893 
0894     // Check if a mouse-button is down: left = paint, right = erase.
0895     if (paintEditObj || paintAltObj) {
0896 
0897         int i, j;
0898         Q_EMIT getMousePos (i, j);
0899 
0900         // Check if the mouse has moved.
0901         if ((i != oldI) || (j != oldJ)) {
0902             // If so, paint or erase a cell.
0903             insertEditObj (i, j, (paintEditObj) ? editObj : FREE);
0904             oldI = i;
0905             oldJ = j;
0906         }
0907     }
0908 }
0909 
0910 void KGrEditor::endEdit (int button)
0911 {
0912     if (mouseDisabled) {
0913         return;
0914     }
0915 
0916     // Mouse button released: finish making changes.
0917     int i, j;
0918     Q_EMIT getMousePos (i, j);
0919 
0920     switch (button) {
0921     case Qt::LeftButton:
0922         paintEditObj = false;
0923         if ((i != oldI) || (j != oldJ)) {
0924             insertEditObj (i, j, editObj);
0925         }
0926         break;
0927     case Qt::RightButton:
0928         paintAltObj = false;
0929         if ((i != oldI) || (j != oldJ)) {
0930             insertEditObj (i, j, FREE);
0931         }
0932         break;
0933     default:
0934         break;
0935     }
0936 }
0937 
0938 #include "moc_kgreditor.cpp"