File indexing completed on 2024-10-13 03:43:39
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"