gcsx_sceneedit.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /* GCSx
  2. ** SCENEEDIT.CPP
  3. **
  4. ** Scene support
  5. ** Expands basic Scene to include editor functionality
  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. SceneEdit::SceneEdit(WorldEdit* myWorld, int myId) : Scene(myWorld, myId) { start_func
  26. if (myId) assert(myWorld);
  27. if (myId) {
  28. // Find an unused "Untitled" name
  29. string findName("scene");
  30. int pos = 1;
  31. while (myWorld->findScene(findName)) {
  32. findName = "scene ";
  33. findName += intToStr(++pos);
  34. }
  35. findName[0] = toupper(findName[0]);
  36. name = findName;
  37. headerModified = contentModified = 1;
  38. }
  39. else headerModified = contentModified = 0;
  40. nameL = name;
  41. toLower(nameL);
  42. for (int pos = 0; pos < MAX_LAYERS; ++pos) {
  43. layerEdits[pos] = NULL;
  44. }
  45. browserNode = NULL;
  46. disassociated = 0;
  47. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_CREATE, this, 0, 0);
  48. }
  49. SceneEdit::~SceneEdit() { start_func
  50. // @TODO: doesn't appear to be needed; no way to delete accidentally
  51. // if (!disassociated) desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_DELETE, this, 0, 0);
  52. // World should be the one deleting us, which will delete our node also
  53. // Deleting layers[] will clear layerEdits[] also
  54. }
  55. void SceneEdit::setInfo(WorldEdit* myWorld, int newId) { start_func
  56. world = myWorld;
  57. id = newId;
  58. }
  59. void SceneEdit::disassociate() throw_File { start_func
  60. // Ensure not cached- our exception point
  61. cacheLoad();
  62. // Drop browser node
  63. dropBrowser();
  64. // Notify objects
  65. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_DELETE, this, 0, 0);
  66. disassociated = 1;
  67. }
  68. void SceneEdit::reassociate(TreeView* node) { start_func
  69. assert(disassociated);
  70. // Notify objects
  71. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_CREATE, this, 0, 0);
  72. // Browser node
  73. addToBrowser(node, 0);
  74. // Ensure written to file
  75. headerModified = contentModified = 1;
  76. disassociated = 0;
  77. }
  78. void SceneEdit::setHeaderModified() { start_func
  79. if (!headerModified) headerModified = 1;
  80. }
  81. void SceneEdit::setContentModified() { start_func
  82. if (!contentModified) {
  83. contentModified = 1;
  84. // No possibility of returning to cache
  85. delete cacheFile;
  86. cacheFile = NULL;
  87. }
  88. }
  89. LayerEdit* SceneEdit::getLayerEdit(int pos) { start_func
  90. assert(pos >= 0);
  91. assert(pos < layerCount);
  92. return layerEdits[pos];
  93. }
  94. void SceneEdit::dropBrowser() { start_func
  95. browserNode = NULL;
  96. }
  97. void SceneEdit::addToBrowser(TreeView* node, int makeCurrent) { start_func
  98. browserNode = new TreeView(name, this, 0, treeviewEventWrap);
  99. browserNode->setIcon(6);
  100. node->insert(browserNode, makeCurrent);
  101. }
  102. int SceneEdit::treeviewEvent(int code, int command, int check) { start_func
  103. assert(world);
  104. if (check) {
  105. if (command == EDIT_DELETE) return Window::COMMAND_ENABLE;
  106. return Window::COMMAND_HIDE;
  107. }
  108. switch (command) {
  109. case LV_SELECT:
  110. openEditWindow();
  111. return 1;
  112. case LV_RCLICK:
  113. // @TODO: Should actually be a popup with this as an item
  114. propertiesDialog(desktop->findPreviousFocusWindow());
  115. return 1;
  116. case EDIT_DELETE:
  117. case LV_DELETE:
  118. // Handles any error conditions/cancellation for us
  119. getWorldEdit()->deleteScene(this, desktop->findPreviousFocusWindow());
  120. return 1;
  121. }
  122. return 0;
  123. }
  124. int SceneEdit::treeviewEventWrap(void* ptr, int code, int command, int check) { start_func
  125. return ((SceneEdit*)ptr)->treeviewEvent(code, command, check);
  126. }
  127. int SceneEdit::insertLayer(class LayerEdit* newLayer, int pos, Window* srcWin, Window* exWin, int readd) throw_File { start_func
  128. assert(layerCount < MAX_LAYERS);
  129. assert(pos >= 0);
  130. assert(pos <= layerCount);
  131. assert(newLayer);
  132. int retValue = -1;
  133. // Lock it (do this first in case exception)
  134. // Only the first marklock might except, and it doesn't
  135. // need any of the below to be done prior as it doesn't
  136. // refer to scene or layer data to lock
  137. for (int lock = 0; lock < lockCount; ++lock) {
  138. newLayer->markLock(); // Exception point
  139. }
  140. setHeaderModified();
  141. setContentModified();
  142. // Undo
  143. if (world) {
  144. getWorldEdit()->indexLayer(newLayer);
  145. try {
  146. if (getWorldEdit()->undo.storeUndoLayerCreate(newLayer->getId(), id, pos, srcWin)) retValue = 1;
  147. }
  148. catch (UndoException& e) {
  149. getWorldEdit()->deindexLayer(newLayer);
  150. for (int lock = 0; lock < lockCount; ++lock) {
  151. newLayer->markUnlock();
  152. }
  153. return 0;
  154. }
  155. }
  156. // Move layers to make room
  157. for (int movePos = layerCount; movePos > pos; --movePos) {
  158. layers[movePos] = layers[movePos - 1];
  159. layerEdits[movePos] = layerEdits[movePos - 1];
  160. }
  161. // Insert new layer
  162. layers[pos] = layerEdits[pos] = newLayer;
  163. ++layerCount;
  164. if (readd) newLayer->reassociate();
  165. // Notify
  166. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_LAYERADD, this, pos, 0, exWin);
  167. return retValue;
  168. }
  169. int SceneEdit::deleteLayer(int pos, Window* srcWin, Window* exWin) throw_File { start_func
  170. assert(pos >= 0);
  171. assert(pos < layerCount);
  172. setHeaderModified();
  173. setContentModified();
  174. // Unlock it (file exception point)
  175. layerEdits[pos]->disassociate();
  176. for (int lock = 0; lock < lockCount; ++lock) {
  177. layers[pos]->markUnlock();
  178. }
  179. // Undo
  180. LayerEdit* delObj = layerEdits[pos];
  181. if (world) {
  182. try {
  183. if (getWorldEdit()->undo.storeUndoLayerDelete(layers[pos]->getId(), id, pos, layerEdits[pos], srcWin))
  184. delObj = NULL;
  185. }
  186. catch (UndoException& e) {
  187. // Relock it
  188. try {
  189. layerEdits[pos]->reassociate();
  190. for (int lock = 0; lock < lockCount; ++lock) {
  191. layers[pos]->markLock();
  192. }
  193. }
  194. catch (FileException& e) {
  195. // Fatal because obviously we just unlocked it, we can't recover properly
  196. fatalCrash(0, "Error relocking layer after cancelling delete: %s", e.details);
  197. }
  198. return 0;
  199. }
  200. }
  201. // Delete
  202. if (world) getWorldEdit()->deindexLayer(layerEdits[pos]);
  203. layers[pos] = NULL;
  204. layerEdits[pos] = NULL;
  205. // Move layers to delete
  206. --layerCount;
  207. for (int movePos = pos; movePos < layerCount; ++movePos) {
  208. layers[movePos] = layers[movePos + 1];
  209. layerEdits[movePos] = layerEdits[movePos + 1];
  210. }
  211. layers[layerCount] = NULL;
  212. layerEdits[layerCount] = NULL;
  213. // @TODO: Some adjustment of sprites may be necessary
  214. // Notify of adjustment (delete had it's own notification)
  215. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_LAYERDEL, this, pos, 0, exWin);
  216. delete delObj;
  217. return 1;
  218. }
  219. void SceneEdit::swapLayer(int pos1, int pos2, Window* srcWin, Window* exWin) throw_Undo { start_func
  220. assert(pos1 >= 0);
  221. assert(pos1 < layerCount);
  222. assert(pos2 >= 0);
  223. assert(pos2 < layerCount);
  224. if (pos1 != pos2) {
  225. setHeaderModified();
  226. setContentModified(); // Because layers must be saved in different order now
  227. if (world) getWorldEdit()->undo.storeUndoLayerSwap(id, pos1, pos2, srcWin);
  228. swap(layers[pos1], layers[pos2]);
  229. swap(layerEdits[pos1], layerEdits[pos2]);
  230. // @TODO: Adjust sprites?
  231. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_LAYERMOVE, this, pos1, pos2, exWin);
  232. }
  233. }
  234. void SceneEdit::setName(const string& newName, Window* srcWin, Window* exWin) throw_Undo { start_func
  235. if (name != newName) {
  236. setHeaderModified();
  237. if (world) {
  238. getWorldEdit()->undo.storeUndoName(UndoBuffer::UNDO_SCENENAME, id, name, newName, srcWin);
  239. world->deindexScene(this);
  240. }
  241. name = newName;
  242. nameL = name;
  243. toLower(nameL);
  244. if (world) world->indexScene(this);
  245. if (browserNode) browserNode->changeName(newName);
  246. desktop->broadcastObjChange(OBJ_SCENE | OBJMOD_NAME, this, 0, 0, exWin);
  247. }
  248. }
  249. int SceneEdit::newLayer(Window* srcWin, Window* exWin) throw_File { start_func
  250. assert(world);
  251. LayerEdit* addLayer = NULL;
  252. LayerEdit* toDelete = NULL;
  253. int layerResult = 0;
  254. if (layerCount == MAX_LAYERS) {
  255. guiErrorBox("The maximum number of layers has been reached for this scene.", "Too Many Layers");
  256. return 1;
  257. }
  258. toDelete = addLayer = new LayerEdit(getWorldEdit(), this, getWorldEdit()->unusedLayerId());
  259. getWorldEdit()->undo.preUndoBlock();
  260. try {
  261. layerResult = insertLayer(addLayer, layerCount, srcWin, exWin); // Exception point
  262. }
  263. catch (...) {
  264. getWorldEdit()->undo.cancelUndoBlock();
  265. delete toDelete;
  266. throw;
  267. }
  268. // Undo successful- don't need to delete ourselves now
  269. if (layerResult > 0) toDelete = NULL;
  270. if (!layerResult) {
  271. getWorldEdit()->undo.cancelUndoBlock();
  272. delete toDelete;
  273. return 0;
  274. }
  275. if (!addLayer->propertiesDialog(1, Layer::LAYER_TILE, srcWin, exWin)) {
  276. try {
  277. // (deletes object too)
  278. if (toDelete) deleteLayer(layerCount - 1, srcWin, exWin);
  279. }
  280. catch (FileException& e) {
  281. fatalCrash(0, "File error undoing layer addition: %s", e.details);
  282. }
  283. getWorldEdit()->undo.cancelUndoBlock();
  284. return 0;
  285. }
  286. getWorldEdit()->undo.postUndoBlock();
  287. return 1;
  288. }
  289. const string SceneEdit::tileButton("\tTile");
  290. const string SceneEdit::imageButton("\tImage");
  291. const string SceneEdit::fontButton("\tFont");
  292. const string SceneEdit::addLayerButton("\tAdd New Layer");
  293. void SceneEdit::openEditWindow() { start_func
  294. try {
  295. // Add layer if no layers
  296. if (layerCount == 0) {
  297. if (!newLayer()) return; // Exception point
  298. }
  299. // Check layer types available
  300. int types = 0;
  301. Layer::LayerType edit; // Type to edit
  302. while (types == 0) {
  303. int tileType = 0;
  304. int imageType = 0;
  305. int fontType = 0;
  306. for (int pos = 0; pos < layerCount; ++pos) {
  307. Layer::LayerType type = layers[pos]->getType();
  308. if (type != Layer::LAYER_EMPTY) edit = type;
  309. switch (type) {
  310. case Layer::LAYER_TILE:
  311. if (!tileType) ++types;
  312. ++tileType;
  313. break;
  314. case Layer::LAYER_IMAGE:
  315. if (!imageType) ++types;
  316. ++imageType;
  317. break;
  318. case Layer::LAYER_FONT:
  319. if (!fontType) ++types;
  320. ++fontType;
  321. break;
  322. case Layer::LAYER_EMPTY:
  323. break;
  324. }
  325. }
  326. // No non-empty layers?
  327. if (types == 0) {
  328. if (guiConfirmBox("All layers are empty layers- add a new layer?", "No Editable Layers")) {
  329. if (!newLayer()) return; // Exception point
  330. }
  331. else return;
  332. }
  333. // Multiple types of layers? Choose type of layer to edit
  334. if (types > 1) {
  335. const string* button[3] = { NULL, NULL, NULL };
  336. if (tileType) button[0] = &tileButton;
  337. if (imageType) button[1] = &imageButton;
  338. if (fontType) button[2] = &fontButton;
  339. int result = guiMessageBox("Which type of layer would you like to edit?", "Multiple Layer Types",
  340. button[0], button[1], button[2], &addLayerButton, &messageBoxCancel);
  341. switch (result) {
  342. case 0:
  343. case 5:
  344. return;
  345. case 4:
  346. if (!newLayer()) return; // Exception point
  347. // (loop)
  348. types = 0;
  349. break;
  350. case 1:
  351. edit = Layer::LAYER_TILE;
  352. break;
  353. case 2:
  354. edit = Layer::LAYER_IMAGE;
  355. break;
  356. case 3:
  357. edit = Layer::LAYER_FONT;
  358. break;
  359. }
  360. }
  361. }
  362. switch (edit) {
  363. case Layer::LAYER_TILE:
  364. new SceneEditLayer(this);
  365. break;
  366. case Layer::LAYER_IMAGE:
  367. guiErrorBox("Image layer editing has not been implemented", "@TODO:");
  368. break;
  369. case Layer::LAYER_FONT:
  370. guiErrorBox("Font layer editing has not been implemented", "@TODO:");
  371. break;
  372. case Layer::LAYER_EMPTY:
  373. // (should nver reach here)
  374. assert(0);
  375. break;
  376. }
  377. }
  378. catch (FileException& e) {
  379. guiErrorBox(string(e.details), errorTitleFile);
  380. }
  381. }
  382. int SceneEdit::propertiesDialog(Window* srcWin, Window* exWin) { start_func
  383. string newName = name;
  384. if (ScenePropertiesDialog::create()->run(&newName, this)) {
  385. try {
  386. setName(newName, srcWin, exWin);
  387. }
  388. catch (UndoException& e) {
  389. return 0;
  390. }
  391. return 1;
  392. }
  393. return 0;
  394. }
  395. int SceneEdit::isHeaderModified() const { start_func
  396. return headerModified;
  397. }
  398. int SceneEdit::isContentModified() const { start_func
  399. return contentModified;
  400. }
  401. void SceneEdit::loadHeader(FileRead* file) throw_File { start_func
  402. assert(world);
  403. if (file->getVersion() > 1) {
  404. throw FileException("Unsupported scene version %d", file->getVersion());
  405. }
  406. file->readStr(name);
  407. nameL = name;
  408. toLower(nameL);
  409. layerCount = file->readInt();
  410. id = file->readInt();
  411. for (int pos = 0; pos < layerCount; ++pos) {
  412. layers[pos] = layerEdits[pos] = new LayerEdit(getWorldEdit(), this);
  413. layers[pos]->loadHeader(file);
  414. }
  415. // Check validity
  416. if ((layerCount > MAX_LAYERS) || (layerCount < 0) || (!id)) {
  417. throw FileException("Corrupted scene header");
  418. }
  419. }
  420. Uint32 SceneEdit::saveHeader(FileWrite* file) throw_File { start_func
  421. file->writeStr(name);
  422. file->writeInt(layerCount);
  423. file->writeInt(id);
  424. for (int pos = 0; pos < layerCount; ++pos) {
  425. layerEdits[pos]->saveHeader(file);
  426. }
  427. return 1;
  428. }
  429. void SceneEdit::saveContent(FileWrite* file) throw_File { start_func
  430. for (int pos = 0; pos < layerCount; ++pos) {
  431. assert(layerEdits[pos]);
  432. layerEdits[pos]->saveContent(file);
  433. }
  434. }
  435. void SceneEdit::saveSuccess() { start_func
  436. headerModified = 0;
  437. contentModified = 0;
  438. for (int pos = 0; pos < layerCount; ++pos) {
  439. assert(layerEdits[pos]);
  440. layerEdits[pos]->saveSuccess();
  441. }
  442. }
  443. void SceneEdit::cachedContent(FileRead* file, int oldData) { start_func
  444. // If already cached..
  445. if (cached) {
  446. delete cacheFile;
  447. cacheFile = file;
  448. }
  449. else if (oldData) {
  450. delete file;
  451. }
  452. else {
  453. // Cache ourselves?
  454. if (!lockCount) {
  455. delete cacheFile;
  456. cacheFile = file;
  457. for (int pos = 0; pos < layerCount; ++pos) {
  458. assert(layers[pos]);
  459. layers[pos]->recacheContent();
  460. }
  461. cached = 1;
  462. }
  463. else {
  464. delete file;
  465. }
  466. }
  467. }