gcsx_worldedit.cpp 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235
  1. /* GCSx
  2. ** WORLDEDIT.CPP
  3. **
  4. ** World storage
  5. ** EDITOR members
  6. */
  7. /*****************************************************************************
  8. ** Copyright (C) 2003-2006 Janson
  9. **
  10. ** This program is free software; you can redistribute it and/or modify
  11. ** it under the terms of the GNU General Public License as published by
  12. ** the Free Software Foundation; either version 2 of the License, or
  13. ** (at your option) any later version.
  14. **
  15. ** This program is distributed in the hope that it will be useful,
  16. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. ** GNU General Public License for more details.
  19. **
  20. ** You should have received a copy of the GNU General Public License
  21. ** along with this program; if not, write to the Free Software
  22. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  23. *****************************************************************************/
  24. #include "all.h"
  25. set<WorldEdit*>* WorldEdit::allWorlds = NULL;
  26. WorldEdit::WorldEdit() throw_File : World(), layersById(), spawnsById(), foldersById(), undo(this) { start_func
  27. headerModified = 1; // We have a title!
  28. contentModified = 1;
  29. browserNode = NULL;
  30. saveFile = NULL;
  31. file = saveFile = new WorldFile(); // Only exception point
  32. saveFile->newBlock(WorldFile::BLOCKTYPE_WORLDINFO, this);
  33. if (!allWorlds) allWorlds = new set<WorldEdit*>;
  34. allWorlds->insert(this);
  35. desktop->broadcastObjChange(OBJ_WORLD | OBJMOD_CREATE, this, 0, 0);
  36. }
  37. WorldEdit::WorldEdit(const char* wFilename) throw_File : World(), layersById(), spawnsById(), undo(this) { start_func
  38. headerModified = contentModified = 0;
  39. browserNode = NULL;
  40. saveFile = NULL;
  41. filename = NULL;
  42. TileSetEdit* tileset = NULL;
  43. SceneEdit* scene = NULL;
  44. AnimGroupEdit* animgroup = NULL;
  45. ScriptEdit* script = NULL;
  46. FolderEdit* folder = NULL;
  47. try {
  48. filename = new string(wFilename);
  49. file = saveFile = new WorldFile(filename); // One of many exception points (+claimBlocks)
  50. Uint32 type;
  51. Uint32 id;
  52. // Loop pass 1- World Info and TileSets
  53. saveFile->scanBlocks();
  54. while (saveFile->nextBlock(&type, &id)) {
  55. switch (type) {
  56. case WorldFileLoad::BLOCKTYPE_WORLDINFO:
  57. saveFile->claimBlock(id, this);
  58. break;
  59. case WorldFileLoad::BLOCKTYPE_TILESET:
  60. tileset = new TileSetEdit(this, 0);
  61. saveFile->claimBlock(id, tileset);
  62. indexTileSet(tileset);
  63. tileset = NULL;
  64. break;
  65. }
  66. }
  67. // Loop pass 2- Scenes and Animation Groups (need tilesets)
  68. saveFile->scanBlocks();
  69. while (saveFile->nextBlock(&type, &id)) {
  70. switch (type) {
  71. case WorldFileLoad::BLOCKTYPE_ANIMGROUP:
  72. animgroup = new AnimGroupEdit(this);
  73. saveFile->claimBlock(id, animgroup);
  74. indexAnimGroup(animgroup);
  75. animgroup = NULL;
  76. break;
  77. }
  78. }
  79. // Loop pass 3- Scripts (need tilesets/animgroups)
  80. saveFile->scanBlocks();
  81. while (saveFile->nextBlock(&type, &id)) {
  82. switch (type) {
  83. case WorldFileLoad::BLOCKTYPE_SCRIPT:
  84. script = new ScriptEdit(this);
  85. saveFile->claimBlock(id, script);
  86. indexScript(script);
  87. script = NULL;
  88. break;
  89. }
  90. }
  91. // Loop pass 4- Scenes (need tilesets/animgroups/scripts for spawns)
  92. saveFile->scanBlocks();
  93. while (saveFile->nextBlock(&type, &id)) {
  94. switch (type) {
  95. case WorldFileLoad::BLOCKTYPE_SCENE:
  96. scene = new SceneEdit(this);
  97. saveFile->claimBlock(id, scene);
  98. indexScene(scene);
  99. scene = NULL;
  100. break;
  101. }
  102. }
  103. // Loop pass 5- Folders (need all items that might go in a folder)
  104. saveFile->scanBlocks();
  105. while (saveFile->nextBlock(&type, &id)) {
  106. switch (type) {
  107. case WorldFileLoad::BLOCKTYPE_FOLDER:
  108. folder = new FolderEdit(this);
  109. saveFile->claimBlock(id, folder);
  110. indexFolder(folder);
  111. folder = NULL;
  112. break;
  113. }
  114. }
  115. if (!allWorlds) allWorlds = new set<WorldEdit*>;
  116. allWorlds->insert(this);
  117. }
  118. catch (...) {
  119. // Temporaries
  120. delete tileset;
  121. delete scene;
  122. delete animgroup;
  123. delete script;
  124. delete folder;
  125. // Collections
  126. deleteCollections();
  127. // File
  128. delete saveFile;
  129. delete filename;
  130. file = saveFile = NULL;
  131. filename = NULL;
  132. throw;
  133. }
  134. desktop->broadcastObjChange(OBJ_WORLD | OBJMOD_CREATE, this, 0, 0);
  135. }
  136. WorldEdit::~WorldEdit() { start_func
  137. // May not be needed, but a good safety
  138. desktop->broadcastObjChange(OBJ_WORLD | OBJMOD_DELETE, this, 0, 0);
  139. // Our node must be deleted outside this destructor
  140. if (allWorlds) {
  141. allWorlds->erase(this);
  142. if (allWorlds->empty()) {
  143. delete allWorlds;
  144. allWorlds = NULL;
  145. }
  146. }
  147. }
  148. void WorldEdit::addToBrowser(TreeView* worldBrowser) { start_func
  149. TreeView* myNode = new TreeView(title, this, TREEVIEW_WORLD, treeviewEventWrap);
  150. myNode->setIcon(3);
  151. TreeView* subNode = NULL;
  152. TreeView* subAddNode = NULL;
  153. TileSetIndex::iterator tilesetPos;
  154. TileSetIndex::iterator tilesetEnd = tilesetsById.end();
  155. SceneIndex::iterator scenePos;
  156. SceneIndex::iterator sceneEnd = scenesById.end();
  157. AnimGroupIndex::iterator animgroupPos;
  158. AnimGroupIndex::iterator animgroupEnd = animgroupsById.end();
  159. ScriptIndex::iterator scriptPos;
  160. ScriptIndex::iterator scriptEnd = scriptsById.end();
  161. FolderIndex::iterator folderPos;
  162. FolderIndex::iterator folderEnd = foldersById.end();
  163. myNode->expand(1);
  164. subNode = new TreeView("Images", this, TREEVIEW_TILESET, treeviewEventWrap);
  165. subNode->setIcon(1, 0);
  166. // Add existing libraries
  167. for (tilesetPos = tilesetsById.begin(); tilesetPos != tilesetEnd; ++tilesetPos) {
  168. if (!(*tilesetPos).second->getIsFont()) {
  169. dynamic_cast<TileSetEdit*>((*tilesetPos).second)->addToBrowser(subNode, 0);
  170. }
  171. }
  172. myNode->insert(subNode, 0);
  173. subNode = new TreeView("Fonts", this, TREEVIEW_FONT, treeviewEventWrap);
  174. subNode->setIcon(1, 0);
  175. // Add existing libraries
  176. for (tilesetPos = tilesetsById.begin(); tilesetPos != tilesetEnd; ++tilesetPos) {
  177. if ((*tilesetPos).second->getIsFont()) {
  178. dynamic_cast<TileSetEdit*>((*tilesetPos).second)->addToBrowser(subNode, 0);
  179. }
  180. }
  181. myNode->insert(subNode, 0);
  182. subNode = new TreeView("Scenes", this, TREEVIEW_SCENE, treeviewEventWrap);
  183. subNode->setIcon(1, 0);
  184. // Add existing scenes
  185. for (scenePos = scenesById.begin(); scenePos != sceneEnd; ++scenePos) {
  186. (dynamic_cast<SceneEdit*>((*scenePos).second))->addToBrowser(subNode, 0);
  187. }
  188. myNode->insert(subNode, 0);
  189. subNode = new TreeView("Scripts", this, TREEVIEW_SCRIPT, treeviewEventWrap);
  190. subNode->setIcon(1, 0);
  191. // Add existing scripts
  192. for (scriptPos = scriptsById.begin(); scriptPos != scriptEnd; ++scriptPos) {
  193. if ((*scriptPos).second->getType() != Script::SCRIPT_NOTE) {
  194. dynamic_cast<ScriptEdit*>((*scriptPos).second)->addToBrowser(subNode, 0);
  195. }
  196. }
  197. myNode->insert(subNode, 0);
  198. subNode = new TreeView("Animations", this, TREEVIEW_ANIMGROUP, treeviewEventWrap);
  199. subNode->setIcon(1, 0);
  200. // Add existing spite libraries
  201. for (animgroupPos = animgroupsById.begin(); animgroupPos != animgroupEnd; ++animgroupPos) {
  202. dynamic_cast<AnimGroupEdit*>((*animgroupPos).second)->addToBrowser(subNode, 0);
  203. }
  204. myNode->insert(subNode, 0);
  205. subNode = new TreeView("Notes", this, TREEVIEW_NOTES, treeviewEventWrap);
  206. subNode->setIcon(1, 0);
  207. // Add existing notes
  208. for (scriptPos = scriptsById.begin(); scriptPos != scriptEnd; ++scriptPos) {
  209. if ((*scriptPos).second->getType() == Script::SCRIPT_NOTE) {
  210. dynamic_cast<ScriptEdit*>((*scriptPos).second)->addToBrowser(subNode, 0);
  211. }
  212. }
  213. myNode->insert(subNode, 0);
  214. // @TODO: Clearly this block is a temporary setup
  215. subNode = new TreeView("Folders", this, TREEVIEW_FOLDER, treeviewEventWrap);
  216. subNode->setIcon(1, 0);
  217. // Add existing folders
  218. for (folderPos = foldersById.begin(); folderPos != folderEnd; ++folderPos) {
  219. dynamic_cast<FolderEdit*>((*folderPos).second)->addToBrowser(subNode, 0);
  220. }
  221. myNode->insert(subNode, 0);
  222. worldBrowser->insert(myNode);
  223. browserNode = myNode;
  224. }
  225. int WorldEdit::deleteTileset(TileSetEdit* toDelete, Window* srcWin) { start_func
  226. // Scan all scenes/layers, ensure tileset is not in use
  227. SceneIndex::iterator posS;
  228. SceneIndex::iterator endS = scenesById.end();
  229. for (posS = scenesById.begin(); posS != endS; ++posS) {
  230. for (int count = (*posS).second->getLayerCount() - 1; count >= 0; --count) {
  231. Layer* layer = (*posS).second->getLayer(count);
  232. if (layer->getTileSet() == toDelete) {
  233. guiErrorBox(formatString("Cannot delete %s- used by scene %s", toDelete->getName().c_str(), (*posS).second->getName().c_str()), errorTitleInUse);
  234. return 0;
  235. }
  236. }
  237. }
  238. // Scan all animation groups, ensure tileset is not in use
  239. AnimGroupIndex::iterator posL;
  240. AnimGroupIndex::iterator endL = animgroupsById.end();
  241. for (posL = animgroupsById.begin(); posL != endL; ++posL) {
  242. if ((*posL).second->usesTileSet(toDelete)) {
  243. guiErrorBox(formatString("Cannot delete %s- used by animation group %s", toDelete->getName().c_str(), (*posL).second->getName().c_str()), errorTitleInUse);
  244. return 0;
  245. }
  246. }
  247. // Verify tileset is in our list
  248. TileSet* verify = findTileSet(toDelete->getId());
  249. if (verify) {
  250. int noUndo = 0;
  251. // Remove browser node
  252. TreeView* node = browserNode->findRecursive(toDelete);
  253. if (node) node->findParent()->remove(node);
  254. // Disassociate, delete if fails
  255. try {
  256. toDelete->disassociate();
  257. }
  258. catch (FileException& e) {
  259. // File error, object must be deleted anyways, but can't be undone
  260. guiErrorBox(formatString("Deletion of object will not be undoable- %s", e.details), errorTitleFile);
  261. undo.clearUndo();
  262. noUndo = 1;
  263. }
  264. int noDelete = 0;
  265. if (!noUndo) {
  266. try {
  267. if (undo.storeUndoDelete(UndoBuffer::UNDO_TILEDELETE, toDelete->getId(), toDelete, srcWin))
  268. noDelete = 1;
  269. }
  270. catch (UndoException& e) {
  271. toDelete->reassociate(browserNode->find(toDelete->getIsFont() ? "Fonts" : "Images"));
  272. return 0;
  273. }
  274. }
  275. // Delete it from list, delete it from file
  276. deindexTileSet(toDelete);
  277. saveFile->discardBlock(toDelete);
  278. // Delete entire object if not claimed by undo
  279. if (!noDelete) delete toDelete;
  280. }
  281. return 1;
  282. }
  283. int WorldEdit::deleteScene(SceneEdit* toDelete, Window* srcWin) { start_func
  284. // Verify scene is in our list
  285. Scene* verify = findScene(toDelete->getId());
  286. if (verify) {
  287. int noUndo = 0;
  288. // Remove browser node
  289. TreeView* node = browserNode->findRecursive(toDelete);
  290. if (node) node->findParent()->remove(node);
  291. // Disassociate, delete if fails
  292. try {
  293. toDelete->disassociate();
  294. }
  295. catch (FileException& e) {
  296. // File error, object must be deleted anyways, but can't be undone
  297. guiErrorBox(formatString("Deletion of object will not be undoable- %s", e.details), errorTitleFile);
  298. undo.clearUndo();
  299. noUndo = 1;
  300. }
  301. int noDelete = 0;
  302. if (!noUndo) {
  303. try {
  304. if (undo.storeUndoDelete(UndoBuffer::UNDO_SCENEDELETE, toDelete->getId(), toDelete, srcWin))
  305. noDelete = 1;
  306. }
  307. catch (UndoException& e) {
  308. toDelete->reassociate(browserNode->find("Scenes"));
  309. return 0;
  310. }
  311. }
  312. // Delete it from list, delete it from file
  313. deindexScene(toDelete);
  314. saveFile->discardBlock(toDelete);
  315. // Delete entire object if not claimed by undo
  316. if (!noDelete) delete toDelete;
  317. }
  318. return 1;
  319. }
  320. int WorldEdit::deleteScript(ScriptEdit* toDelete, Window* srcWin) { start_func
  321. // Verify script is in our list
  322. Script* verify = findScript(toDelete->getId());
  323. if (verify) {
  324. int noUndo = 0;
  325. // Remove browser node
  326. TreeView* node = browserNode->findRecursive(toDelete);
  327. if (node) node->findParent()->remove(node);
  328. // Disassociate, delete if fails
  329. try {
  330. toDelete->disassociate();
  331. }
  332. catch (FileException& e) {
  333. // File error, object must be deleted anyways, but can't be undone
  334. guiErrorBox(formatString("Deletion of object will not be undoable- %s", e.details), errorTitleFile);
  335. undo.clearUndo();
  336. noUndo = 1;
  337. }
  338. int noDelete = 0;
  339. if (!noUndo) {
  340. try {
  341. if (undo.storeUndoDelete(UndoBuffer::UNDO_SCRIPTDELETE, toDelete->getId(), toDelete, srcWin))
  342. noDelete = 1;
  343. }
  344. catch (UndoException& e) {
  345. toDelete->reassociate(browserNode->find("Scripts"));
  346. return 0;
  347. }
  348. }
  349. // Delete it from list, delete it from file
  350. deindexScript(toDelete);
  351. saveFile->discardBlock(toDelete);
  352. // Delete entire object if not claimed by undo
  353. if (!noDelete) delete toDelete;
  354. }
  355. return 1;
  356. }
  357. int WorldEdit::deleteAnimGroup(AnimGroupEdit* toDelete, Window* srcWin) { start_func
  358. // Verify animation group is in our list
  359. AnimGroup* verify = findAnimGroup(toDelete->getId());
  360. if (verify) {
  361. int noUndo = 0;
  362. // Remove browser node
  363. TreeView* node = browserNode->findRecursive(toDelete);
  364. if (node) node->findParent()->remove(node);
  365. // Disassociate, delete if fails
  366. try {
  367. toDelete->disassociate();
  368. }
  369. catch (FileException& e) {
  370. // File error, object must be deleted anyways, but can't be undone
  371. guiErrorBox(formatString("Deletion of object will not be undoable- %s", e.details), errorTitleFile);
  372. undo.clearUndo();
  373. noUndo = 1;
  374. }
  375. int noDelete = 0;
  376. if (!noUndo) {
  377. try {
  378. if (undo.storeUndoDelete(UndoBuffer::UNDO_ANIMGROUPDELETE, toDelete->getId(), toDelete, srcWin))
  379. noDelete = 1;
  380. }
  381. catch (UndoException& e) {
  382. toDelete->reassociate(browserNode->find("Animations"));
  383. return 0;
  384. }
  385. }
  386. // Delete it from list, delete it from file
  387. deindexAnimGroup(toDelete);
  388. saveFile->discardBlock(toDelete);
  389. // Delete entire object if not claimed by undo
  390. if (!noDelete) delete toDelete;
  391. }
  392. return 1;
  393. }
  394. int WorldEdit::deleteFolder(FolderEdit* toDelete, Window* srcWin) { start_func
  395. // @TODO: Scan folder, ensure empty (if not, prompt user and move items elsewhere)
  396. // Verify folder is in our list
  397. FolderEdit* verify = findFolder(toDelete->getId());
  398. if (verify) {
  399. int noUndo = 0;
  400. // Remove browser node
  401. TreeView* node = browserNode->findRecursive(toDelete);
  402. if (node) node->findParent()->remove(node);
  403. // Disassociate, delete if fails
  404. try {
  405. toDelete->disassociate();
  406. }
  407. catch (FileException& e) {
  408. // File error, object must be deleted anyways, but can't be undone
  409. guiErrorBox(formatString("Deletion of object will not be undoable- %s", e.details), errorTitleFile);
  410. undo.clearUndo();
  411. noUndo = 1;
  412. }
  413. int noDelete = 0;
  414. if (!noUndo) {
  415. try {
  416. if (undo.storeUndoDelete(UndoBuffer::UNDO_FOLDERDELETE, toDelete->getId(), toDelete, srcWin))
  417. noDelete = 1;
  418. }
  419. catch (UndoException& e) {
  420. toDelete->reassociate(browserNode->find("Folders"));
  421. return 0;
  422. }
  423. }
  424. // Delete it from list, delete it from file
  425. deindexFolder(toDelete);
  426. saveFile->discardBlock(toDelete);
  427. // Delete entire object if not claimed by undo
  428. if (!noDelete) delete toDelete;
  429. }
  430. return 1;
  431. }
  432. void WorldEdit::importTileSet(TileSetEdit* toAdd, int openWindow, Window* srcWin) { start_func
  433. try {
  434. indexTileSet(toAdd);
  435. // (our only exception point)
  436. undo.storeUndoCreate(UndoBuffer::UNDO_TILECREATE, toAdd->getId(), srcWin);
  437. toAdd->addToBrowser(browserNode->find(toAdd->getIsFont() ? "Fonts" : "Images"));
  438. if (openWindow) toAdd->openBrowseWindow();
  439. saveFile->newBlock(WorldFile::BLOCKTYPE_TILESET, toAdd);
  440. }
  441. catch (UndoException& e) {
  442. delete toAdd;
  443. }
  444. }
  445. void WorldEdit::importScene(SceneEdit* toAdd, int openWindow, Window* srcWin) { start_func
  446. try {
  447. indexScene(toAdd);
  448. // (our only exception point)
  449. undo.storeUndoCreate(UndoBuffer::UNDO_SCENECREATE, toAdd->getId(), srcWin);
  450. toAdd->addToBrowser(browserNode->find("Scenes"));
  451. if (openWindow) toAdd->openEditWindow();
  452. saveFile->newBlock(WorldFile::BLOCKTYPE_SCENE, toAdd);
  453. }
  454. catch (UndoException& e) {
  455. delete toAdd;
  456. }
  457. }
  458. void WorldEdit::readdTileset(TileSetEdit* toAdd) { start_func
  459. indexTileSet(toAdd);
  460. saveFile->newBlock(WorldFile::BLOCKTYPE_TILESET, toAdd);
  461. toAdd->reassociate(browserNode->find(toAdd->getIsFont() ? "Fonts" : "Images"));
  462. }
  463. void WorldEdit::readdScene(SceneEdit* toAdd) { start_func
  464. indexScene(toAdd);
  465. saveFile->newBlock(WorldFile::BLOCKTYPE_SCENE, toAdd);
  466. toAdd->reassociate(browserNode->find("Scenes"));
  467. }
  468. void WorldEdit::readdScript(ScriptEdit* toAdd) { start_func
  469. indexScript(toAdd);
  470. saveFile->newBlock(WorldFile::BLOCKTYPE_SCRIPT, toAdd);
  471. toAdd->reassociate(browserNode->find("Scripts"));
  472. }
  473. void WorldEdit::readdAnimGroup(AnimGroupEdit* toAdd) { start_func
  474. indexAnimGroup(toAdd);
  475. saveFile->newBlock(WorldFile::BLOCKTYPE_ANIMGROUP, toAdd);
  476. toAdd->reassociate(browserNode->find("Animations"));
  477. }
  478. void WorldEdit::readdFolder(FolderEdit* toAdd) { start_func
  479. indexFolder(toAdd);
  480. saveFile->newBlock(WorldFile::BLOCKTYPE_FOLDER, toAdd);
  481. toAdd->reassociate(browserNode->find("Folders"));
  482. }
  483. int WorldEdit::commandEvent(int code) { start_func
  484. switch (code) {
  485. case FILE_SAVE:
  486. save();
  487. return 1;
  488. case FILE_SAVEAS:
  489. saveAs();
  490. return 1;
  491. case FILE_CLOSE:
  492. close();
  493. return 1;
  494. case EDIT_UNDO:
  495. undo.undoPerform();
  496. return 1;
  497. case EDIT_REDO:
  498. undo.redoPerform();
  499. return 1;
  500. case NEW_SCENE: {
  501. SceneEdit* myScene = new SceneEdit(this, unusedSceneId());
  502. SceneEdit* toDelete = myScene;
  503. indexScene(myScene);
  504. try {
  505. undo.preUndoBlock();
  506. if (undo.storeUndoCreate(UndoBuffer::UNDO_SCENECREATE, myScene->getId(), desktop->findPreviousFocusWindow())) toDelete = NULL;
  507. // If not OK, cancel creation of scene
  508. if (!myScene->propertiesDialog()) {
  509. // (this may delete and send msg)
  510. undo.cancelUndoBlock();
  511. // (if not, this does)
  512. if (toDelete) {
  513. undo.disableUndo();
  514. deleteScene(toDelete);
  515. undo.enableUndo();
  516. }
  517. return 1;
  518. }
  519. undo.postUndoBlock();
  520. myScene->addToBrowser(browserNode->find("Scenes"));
  521. myScene->openEditWindow();
  522. saveFile->newBlock(WorldFile::BLOCKTYPE_SCENE, myScene);
  523. }
  524. catch (UndoException& e) {
  525. delete toDelete;
  526. return 1;
  527. }
  528. return 1;
  529. }
  530. case NEW_ANIM: {
  531. AnimGroupEdit* myAnimGroup = new AnimGroupEdit(this, unusedAnimGroupId());
  532. AnimGroupEdit* toDelete = myAnimGroup;
  533. indexAnimGroup(myAnimGroup);
  534. try {
  535. undo.preUndoBlock();
  536. if (undo.storeUndoCreate(UndoBuffer::UNDO_ANIMGROUPCREATE, myAnimGroup->getId(), desktop->findPreviousFocusWindow())) toDelete = NULL;
  537. // If not OK, cancel creation of anim group
  538. if (!myAnimGroup->propertiesDialog()) {
  539. // (this may delete and send msg)
  540. undo.cancelUndoBlock();
  541. // (if not, this does)
  542. if (toDelete) {
  543. undo.disableUndo();
  544. deleteAnimGroup(toDelete);
  545. undo.enableUndo();
  546. }
  547. return 1;
  548. }
  549. undo.postUndoBlock();
  550. myAnimGroup->addToBrowser(browserNode->find("Animations"));
  551. myAnimGroup->openBrowseWindow();
  552. saveFile->newBlock(WorldFile::BLOCKTYPE_ANIMGROUP, myAnimGroup);
  553. }
  554. catch (UndoException& e) {
  555. delete toDelete;
  556. return 1;
  557. }
  558. return 1;
  559. }
  560. case NEW_FOLDER: {
  561. FolderEdit* myFolder = new FolderEdit(this, unusedFolderId());
  562. FolderEdit* toDelete = myFolder;
  563. indexFolder(myFolder);
  564. try {
  565. undo.preUndoBlock();
  566. if (undo.storeUndoCreate(UndoBuffer::UNDO_FOLDERCREATE, myFolder->getId(), desktop->findPreviousFocusWindow())) toDelete = NULL;
  567. // If not OK, cancel creation of folder
  568. if (!myFolder->propertiesDialog()) {
  569. // (this may delete and send msg)
  570. undo.cancelUndoBlock();
  571. // (if not, this does)
  572. if (toDelete) {
  573. undo.disableUndo();
  574. deleteFolder(toDelete);
  575. undo.enableUndo();
  576. }
  577. return 1;
  578. }
  579. undo.postUndoBlock();
  580. myFolder->addToBrowser(browserNode->find("Folders"));
  581. saveFile->newBlock(WorldFile::BLOCKTYPE_FOLDER, myFolder);
  582. }
  583. catch (UndoException& e) {
  584. delete toDelete;
  585. return 1;
  586. }
  587. return 1;
  588. }
  589. case NEW_NOTES:
  590. case NEW_SCRIPT:
  591. case NEW_LIBRARY: {
  592. Script::ScriptType type = code == NEW_NOTES ? Script::SCRIPT_NOTE : (code == NEW_SCRIPT ? Script::SCRIPT_CODE : Script::SCRIPT_LIBRARY);
  593. ScriptEdit* myScript = new ScriptEdit(this, type, unusedScriptId());
  594. ScriptEdit* toDelete = myScript;
  595. indexScript(myScript);
  596. try {
  597. undo.preUndoBlock();
  598. if (undo.storeUndoCreate(UndoBuffer::UNDO_SCRIPTCREATE, myScript->getId(), desktop->findPreviousFocusWindow())) toDelete = NULL;
  599. // If not OK, cancel creation of script
  600. if (!myScript->propertiesDialog()) {
  601. // (this may delete and send msg)
  602. undo.cancelUndoBlock();
  603. // (if not, this does)
  604. if (toDelete) {
  605. undo.disableUndo();
  606. deleteScript(toDelete);
  607. undo.enableUndo();
  608. }
  609. return 1;
  610. }
  611. undo.postUndoBlock();
  612. myScript->addToBrowser(browserNode->find(code == NEW_NOTES ? "Notes" : "Scripts"));
  613. myScript->openEditWindow();
  614. saveFile->newBlock(WorldFile::BLOCKTYPE_SCRIPT, myScript);
  615. }
  616. catch (UndoException& e) {
  617. delete toDelete;
  618. return 1;
  619. }
  620. return 1;
  621. }
  622. case NEW_IMPORT_IMAGE: {
  623. ImportImgDialog::createTileSet()->run(this, desktop->findPreviousFocusWindow());
  624. return 1;
  625. }
  626. case NEW_IMPORT_FONT: {
  627. ImportImgDialog::createFontSet()->run(this, desktop->findPreviousFocusWindow());
  628. return 1;
  629. }
  630. case NEW_IMAGE:
  631. case NEW_FONT: {
  632. TileSetEdit* myTileSet = new TileSetEdit(this, code == NEW_FONT ? 1 : 0, unusedTileSetId());
  633. TileSetEdit* toDelete = myTileSet;
  634. indexTileSet(myTileSet);
  635. try {
  636. undo.preUndoBlock();
  637. if (undo.storeUndoCreate(UndoBuffer::UNDO_TILECREATE, myTileSet->getId(), desktop->findPreviousFocusWindow())) toDelete = NULL;
  638. // If not OK, cancel creation of tile set
  639. if (!myTileSet->propertiesDialog(1)) {
  640. // (this may delete and send msg)
  641. undo.cancelUndoBlock();
  642. // (if not, this does)
  643. if (toDelete) {
  644. undo.disableUndo();
  645. deleteTileset(toDelete);
  646. undo.enableUndo();
  647. }
  648. return 1;
  649. }
  650. undo.postUndoBlock();
  651. myTileSet->addToBrowser(browserNode->find(code == NEW_FONT ? "Fonts" : "Images"));
  652. myTileSet->openBrowseWindow();
  653. saveFile->newBlock(WorldFile::BLOCKTYPE_TILESET, myTileSet);
  654. }
  655. catch (UndoException& e) {
  656. delete toDelete;
  657. return 1;
  658. }
  659. return 1;
  660. }
  661. }
  662. return 0;
  663. }
  664. Window::CommandSupport WorldEdit::supportsCommand(int code) const { start_func
  665. switch (code) {
  666. case FILE_SAVE:
  667. case FILE_SAVEAS:
  668. case FILE_CLOSE:
  669. return Window::COMMAND_ENABLE;
  670. case EDIT_UNDO:
  671. if (undo.undoAvailable()) return Window::COMMAND_ENABLE;
  672. return Window::COMMAND_DISABLE;
  673. case EDIT_REDO:
  674. if (undo.redoAvailable()) return Window::COMMAND_ENABLE;
  675. return Window::COMMAND_DISABLE;
  676. }
  677. return Window::COMMAND_HIDE;
  678. }
  679. int WorldEdit::treeviewEvent(int code, int command, int check) { start_func
  680. if (check) {
  681. return supportsCommand(command);
  682. }
  683. switch (code) {
  684. case TREEVIEW_WORLD:
  685. switch (command) {
  686. case LV_RCLICK:
  687. // @TODO: Should actually be a popup with this as an item
  688. propertiesDialog(desktop->findPreviousFocusWindow());
  689. return 1;
  690. case LV_DELETE:
  691. close();
  692. return 1;
  693. }
  694. return commandEvent(command);
  695. }
  696. return 0;
  697. }
  698. int WorldEdit::treeviewEventWrap(void* ptr, int code, int command, int check) { start_func
  699. return ((WorldEdit*)ptr)->treeviewEvent(code, command, check);
  700. }
  701. int WorldEdit::close(int justSave, int dontAsk) { start_func
  702. // Modified?
  703. if ((saveFile->isModified()) && (!dontAsk)) {
  704. int result = guiMessageBox(formatString("World '%s' has been modified- save?", title.c_str()), "World Modified", &messageBoxYes, &messageBoxNo, &messageBoxCancel);
  705. if ((result == 3) || (result == 0)) return 0;
  706. if (result == 1) {
  707. if (!save()) return 0;
  708. }
  709. }
  710. if (justSave) return 1;
  711. // Delete browser node from world browser
  712. // Also deletes, ex: tileset browser nodes
  713. if (browserNode) {
  714. browserNode->findParent()->remove(browserNode);
  715. }
  716. // Delete
  717. delete this;
  718. return 1;
  719. }
  720. int WorldEdit::modifiedAll() { start_func
  721. if (!allWorlds) return 1;
  722. set<WorldEdit*>::iterator pos;
  723. set<WorldEdit*>::iterator end = allWorlds->end();
  724. for (pos = allWorlds->begin(); pos != end; ++pos) {
  725. assert(*pos);
  726. // Close it; cancel rest of process anytime cancel is hit
  727. if (!(*pos)->close(1)) return 0;
  728. }
  729. return 1;
  730. }
  731. void WorldEdit::closeAll(int dontAsk) { start_func
  732. if (!allWorlds) return;
  733. if ((!dontAsk) && (!modifiedAll())) return;
  734. set<WorldEdit*>::iterator pos;
  735. set<WorldEdit*>::iterator next;
  736. set<WorldEdit*>::iterator end = allWorlds->end();
  737. for (pos = allWorlds->begin(); pos != end; pos = next) {
  738. assert(*pos);
  739. // Note the next element in sequence now, before we close it
  740. next = pos;
  741. ++next;
  742. // Close it; no questions asked
  743. (*pos)->close(0, 1);
  744. }
  745. }
  746. void WorldEdit::saveAll() { start_func
  747. if (!allWorlds) return;
  748. set<WorldEdit*>::iterator pos;
  749. set<WorldEdit*>::iterator end = allWorlds->end();
  750. for (pos = allWorlds->begin(); pos != end; ++pos) {
  751. assert(*pos);
  752. // Save it; cancel rest of process anytime cancel is hit
  753. if (!(*pos)->save()) return;
  754. }
  755. }
  756. void WorldEdit::setTitle(const string& newTitle, Window* srcWin, Window* exWin) throw_Undo { start_func
  757. if (title != newTitle) {
  758. setHeaderModified();
  759. undo.storeUndoName(UndoBuffer::UNDO_WORLDNAME, 0, title, newTitle, srcWin);
  760. title = newTitle;
  761. if (browserNode) browserNode->changeName(newTitle);
  762. desktop->broadcastObjChange(OBJ_WORLD | OBJMOD_NAME, this, 0, 0, exWin);
  763. }
  764. }
  765. void WorldEdit::setStartScene(int sId, Window* srcWin, Window* exWin) throw_Undo { start_func
  766. if (sId != startingScene) {
  767. setHeaderModified();
  768. undo.storeUndoWorldStart(startingScene, srcWin);
  769. startingScene = sId;
  770. desktop->broadcastObjChange(OBJ_WORLD | OBJMOD_START, this, 0, 0, exWin);
  771. }
  772. }
  773. int WorldEdit::propertiesDialog(Window* srcWin, Window* exWin) { start_func
  774. string newTitle = title;
  775. int newStart = startingScene;
  776. if (WorldPropertiesDialog::create()->run(&newTitle, &newStart, this)) {
  777. try {
  778. undo.preUndoBlock();
  779. setTitle(newTitle, srcWin, exWin);
  780. setStartScene(newStart, srcWin, exWin);
  781. undo.postUndoBlock();
  782. }
  783. catch (UndoException& e) {
  784. return 0;
  785. }
  786. return 1;
  787. }
  788. return 0;
  789. }
  790. void WorldEdit::setHeaderModified() { start_func
  791. if (!headerModified) headerModified = 1;
  792. }
  793. void WorldEdit::setGlobalLinksModified() { start_func
  794. // Called often, so we won't actually clear the global links here
  795. globalLinksDone = 0;
  796. setHeaderModified();
  797. }
  798. int WorldEdit::isHeaderModified() const { start_func
  799. return headerModified;
  800. }
  801. int WorldEdit::isContentModified() const { start_func
  802. return contentModified;
  803. }
  804. Uint32 WorldEdit::saveHeader(FileWrite* file) throw_File { start_func
  805. file->writeStr(title);
  806. file->writeInt(startingScene);
  807. if (globalLinksDone) {
  808. assert(globalLinksCount == globalLinks.size());
  809. file->writeInt(globalLinksCount);
  810. GlobalMap::iterator pos = globalLinks.begin();
  811. while (pos != globalLinks.end()) {
  812. file->writeStr((*pos).first);
  813. file->writeInt((*pos).second.first);
  814. file->writeStr((*pos).second.second);
  815. }
  816. }
  817. else {
  818. // -1 for 'global var list not present'
  819. file->writeInt((Uint32)-1);
  820. }
  821. return 1;
  822. }
  823. void WorldEdit::loadContent(FileRead* file) { start_func
  824. // (currently no content)
  825. delete file;
  826. }
  827. void WorldEdit::saveContent(FileWrite* file) { start_func
  828. // (currently no content)
  829. }
  830. void WorldEdit::saveSuccess() { start_func
  831. headerModified = contentModified = 0;
  832. }
  833. void WorldEdit::cachedContent(FileRead* file, int oldData) { start_func
  834. // (we don't cache)
  835. delete file;
  836. }
  837. int WorldEdit::save() { start_func
  838. if (saveFile->isNew()) {
  839. return saveAs();
  840. }
  841. try {
  842. // This will full save whenever appropriate
  843. saveFile->save();
  844. }
  845. catch (FileException& e) {
  846. guiErrorBox(e.details, errorTitleFile);
  847. }
  848. return 1;
  849. }
  850. int WorldEdit::saveAs() { start_func
  851. string* newFilename = NULL;
  852. if (filename) newFilename = new string(*filename);
  853. else newFilename = new string(blankString);
  854. if (fileSaveAs(FILETYPE_WORLD, *newFilename)) {
  855. try {
  856. saveFile->saveAs(newFilename);
  857. }
  858. catch (FileException& e) {
  859. guiErrorBox(e.details, errorTitleFile);
  860. }
  861. delete filename;
  862. filename = NULL;
  863. filename = newFilename;
  864. }
  865. else {
  866. delete newFilename;
  867. return 0;
  868. }
  869. return 1;
  870. }
  871. int WorldEdit::verifyWorld(WorldEdit* verify) { start_func
  872. if (!allWorlds) return 0;
  873. if (allWorlds->find(verify) != allWorlds->end()) return 1;
  874. return 0;
  875. }
  876. int WorldEdit::countWorlds() { start_func
  877. if (!allWorlds) return 0;
  878. return allWorlds->size();
  879. }
  880. WorldEdit* WorldEdit::listWorlds(int pos) { start_func
  881. if (!allWorlds) return NULL;
  882. if (pos < 0) return NULL;
  883. set<WorldEdit*>::iterator iter = allWorlds->begin();
  884. set<WorldEdit*>::iterator end = allWorlds->end();
  885. for (int numpos = 0; iter != end; ++iter, ++numpos) {
  886. assert(*iter);
  887. if (numpos == pos) return *iter;
  888. }
  889. return NULL;
  890. }
  891. // @TODO: start counting not at 1 but at last id assigned
  892. int WorldEdit::unusedSceneId() const { start_func
  893. int expectId = 1;
  894. SceneIndex::const_iterator end = scenesById.end();
  895. for (SceneIndex::const_iterator pos = scenesById.begin(); pos != end; ++pos) {
  896. if ((*pos).first > expectId) break;
  897. ++expectId;
  898. }
  899. return expectId;
  900. }
  901. int WorldEdit::unusedTileSetId() const { start_func
  902. int expectId = 1;
  903. TileSetIndex::const_iterator end = tilesetsById.end();
  904. for (TileSetIndex::const_iterator pos = tilesetsById.begin(); pos != end; ++pos) {
  905. if ((*pos).first > expectId) break;
  906. ++expectId;
  907. }
  908. return expectId;
  909. }
  910. int WorldEdit::unusedAnimGroupId() const { start_func
  911. int expectId = 1;
  912. AnimGroupIndex::const_iterator end = animgroupsById.end();
  913. for (AnimGroupIndex::const_iterator pos = animgroupsById.begin(); pos != end; ++pos) {
  914. if ((*pos).first > expectId) break;
  915. ++expectId;
  916. }
  917. return expectId;
  918. }
  919. int WorldEdit::unusedScriptId() const { start_func
  920. int expectId = SUB_ENTITY_FIRST;
  921. ScriptIndex::const_iterator end = scriptsById.end();
  922. for (ScriptIndex::const_iterator pos = scriptsById.begin(); pos != end; ++pos) {
  923. if ((*pos).first > expectId) break;
  924. ++expectId;
  925. }
  926. return expectId;
  927. }
  928. int WorldEdit::unusedLayerId() const { start_func
  929. int expectId = 1;
  930. LayerIndex::const_iterator end = layersById.end();
  931. for (LayerIndex::const_iterator pos = layersById.begin(); pos != end; ++pos) {
  932. if ((*pos).first > expectId) break;
  933. ++expectId;
  934. }
  935. return expectId;
  936. }
  937. int WorldEdit::unusedSpawnId() const { start_func
  938. int expectId = 1;
  939. SpawnIndex::const_iterator end = spawnsById.end();
  940. for (SpawnIndex::const_iterator pos = spawnsById.begin(); pos != end; ++pos) {
  941. if ((*pos).first > expectId) break;
  942. ++expectId;
  943. }
  944. return expectId;
  945. }
  946. int WorldEdit::unusedFolderId() const { start_func
  947. int expectId = 1;
  948. FolderIndex::const_iterator end = foldersById.end();
  949. for (FolderIndex::const_iterator pos = foldersById.begin(); pos != end; ++pos) {
  950. if ((*pos).first > expectId) break;
  951. ++expectId;
  952. }
  953. return expectId;
  954. }
  955. void WorldEdit::indexFolder(FolderEdit* addFolder) { start_func
  956. assert(foldersById.find(addFolder->getId()) == foldersById.end());
  957. foldersById.insert(pair<int, FolderEdit*>(addFolder->getId(), addFolder));
  958. }
  959. void WorldEdit::deindexFolder(FolderEdit* remFolder) { start_func
  960. foldersById.erase(remFolder->getId());
  961. }
  962. FolderEdit* WorldEdit::findFolder(int fId) const { start_func
  963. FolderIndex::const_iterator loc = foldersById.find(fId);
  964. if (loc == foldersById.end()) return NULL;
  965. return (*loc).second;
  966. }
  967. void WorldEdit::indexLayer(LayerEdit* addLayer) { start_func
  968. assert(layersById.find(addLayer->getId()) == layersById.end());
  969. layersById.insert(pair<int, LayerEdit*>(addLayer->getId(), addLayer));
  970. }
  971. void WorldEdit::deindexLayer(LayerEdit* remLayer) { start_func
  972. layersById.erase(remLayer->getId());
  973. }
  974. LayerEdit* WorldEdit::findLayer(int fId) const { start_func
  975. LayerIndex::const_iterator loc = layersById.find(fId);
  976. if (loc == layersById.end()) return NULL;
  977. return (*loc).second;
  978. }
  979. void WorldEdit::indexSpawn(SpawnEdit* addSpawn) { start_func
  980. assert(spawnsById.find(addSpawn->getId()) == spawnsById.end());
  981. spawnsById.insert(pair<int, SpawnEdit*>(addSpawn->getId(), addSpawn));
  982. }
  983. void WorldEdit::deindexSpawn(SpawnEdit* remSpawn) { start_func
  984. spawnsById.erase(remSpawn->getId());
  985. }
  986. SpawnEdit* WorldEdit::findSpawn(int fId) const { start_func
  987. SpawnIndex::const_iterator loc = spawnsById.find(fId);
  988. if (loc == spawnsById.end()) return NULL;
  989. return (*loc).second;
  990. }
  991. void WorldEdit::indexScene(Scene* addScene) { start_func
  992. World::indexScene(addScene);
  993. SceneEdit* addEdit = dynamic_cast<SceneEdit*>(addScene);
  994. int count = addEdit->getLayerCount();
  995. for (int pos = 0; pos < count; ++pos) {
  996. LayerEdit* layer = addEdit->getLayerEdit(pos);
  997. indexLayer(layer);
  998. int countS = layer->getSpawnCount();
  999. for (int posS = 0; posS < countS; ++posS) {
  1000. assert(layer->getSpawn(posS));
  1001. indexSpawn(layer->getSpawn(posS));
  1002. }
  1003. }
  1004. }
  1005. void WorldEdit::deindexScene(Scene* remScene) { start_func
  1006. SceneEdit* remEdit = dynamic_cast<SceneEdit*>(remScene);
  1007. int count = remEdit->getLayerCount();
  1008. for (int pos = 0; pos < count; ++pos) {
  1009. LayerEdit* layer = remEdit->getLayerEdit(pos);
  1010. int countS = layer->getSpawnCount();
  1011. for (int posS = 0; posS < countS; ++posS) {
  1012. deindexSpawn(layer->getSpawn(posS));
  1013. }
  1014. deindexLayer(layer);
  1015. }
  1016. World::deindexScene(remScene);
  1017. }
  1018. SaveLoad* WorldEdit::findItem(int blockType, int id) { start_func
  1019. switch (blockType) {
  1020. case WorldFileLoad::BLOCKTYPE_TILESET:
  1021. return dynamic_cast<TileSetEdit*>(findTileSet(id));
  1022. case WorldFileLoad::BLOCKTYPE_SCENE:
  1023. return dynamic_cast<SceneEdit*>(findScene(id));
  1024. case WorldFileLoad::BLOCKTYPE_ANIMGROUP:
  1025. return dynamic_cast<AnimGroupEdit*>(findAnimGroup(id));
  1026. case WorldFileLoad::BLOCKTYPE_SCRIPT:
  1027. return dynamic_cast<ScriptEdit*>(findScript(id));
  1028. case WorldFileLoad::BLOCKTYPE_FOLDER:
  1029. return findFolder(id);
  1030. default:
  1031. return NULL;
  1032. }
  1033. }