gcsx_folder.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /* GCSx
  2. ** FOLDER.CPP
  3. **
  4. ** Folders within world browser (user-definable)
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. FolderEdit::FolderEdit(WorldEdit* myWorld, int myId) : items() { start_func
  25. assert(myWorld);
  26. id = myId;
  27. cached = 0;
  28. lockCount = 0;
  29. cacheFile = NULL;
  30. world = myWorld;
  31. sorted = 1;
  32. defaultFont = 0;
  33. defaultTileSet = 0;
  34. defaultScene = 0;
  35. defaultAnimGroup = 0;
  36. defaultScript = 0;
  37. defaultLibrary = 0;
  38. icon = 0;
  39. // Use an "Untitled" name
  40. if (myId) {
  41. name = "New Folder";
  42. headerModified = contentModified = 1;
  43. }
  44. else {
  45. name = blankString;
  46. headerModified = contentModified = 0;
  47. }
  48. browserNode = NULL;
  49. disassociated = 0;
  50. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_CREATE, this, 0, 0);
  51. }
  52. FolderEdit::~FolderEdit() { start_func
  53. delete cacheFile;
  54. // @TODO: doesn't appear to be needed; no way to delete accidentally
  55. // if (!disassociated) desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_DELETE, this, 0, 0);
  56. // World should be the one deleting us, which will delete our node also
  57. }
  58. void FolderEdit::cacheLoad() throw_File { start_func
  59. if (cached) {
  60. // load into memory
  61. assert(cacheFile);
  62. assert(cacheFile->getVersion() <= 1);
  63. /*
  64. cacheFile->rewind();
  65. try {
  66. // (this is where we would load content, if folders had any)
  67. }
  68. catch (...) {
  69. // Recache ourselves
  70. throw;
  71. }
  72. */
  73. // We retain this pointer and reuse it IF we
  74. // unlock to 0 AND aren't modified
  75. cached = 0;
  76. }
  77. }
  78. void FolderEdit::loadHeader(FileRead* file) throw_File { start_func
  79. assert(items.empty());
  80. if (file->getVersion() > 1) {
  81. throw FileException("Unsupported folder version %d", file->getVersion());
  82. }
  83. file->readStr(name);
  84. id = file->readInt();
  85. sorted = file->readInt();
  86. defaultFont = file->readInt();
  87. defaultTileSet = file->readInt();
  88. defaultScene = file->readInt();
  89. defaultAnimGroup = file->readInt();
  90. defaultScript = file->readInt();
  91. defaultLibrary = file->readInt();
  92. icon = file->readInt();
  93. int itemCount = file->readInt();
  94. // Check validity (1)
  95. if ((!id) || (itemCount < 0) || (icon < 0) || (icon > 36) ||
  96. ((sorted != 0) && (sorted != 1)) ||
  97. ((defaultFont != 0) && (defaultFont != 1)) ||
  98. ((defaultTileSet != 0) && (defaultTileSet != 1)) ||
  99. ((defaultScene != 0) && (defaultScene != 1)) ||
  100. ((defaultAnimGroup != 0) && (defaultAnimGroup != 1)) ||
  101. ((defaultScript != 0) && (defaultScript != 1)) ||
  102. ((defaultLibrary != 0) && (defaultLibrary != 1))) {
  103. throw FileException("Corrupted folder header");
  104. }
  105. // Read items
  106. for (; itemCount > 0; --itemCount) {
  107. FolderItem work;
  108. work.itemBlockType = file->readInt();
  109. work.itemId = file->readInt();
  110. work.item = getWorldEdit()->findItem(work.itemBlockType, work.itemId);
  111. // Check validity (2)
  112. // Catches errors in type or id
  113. if (work.item == NULL) {
  114. throw FileException("Corrupted folder header");
  115. }
  116. items.push_back(work);
  117. }
  118. }
  119. void FolderEdit::loadContent(FileRead* file) { start_func
  120. delete cacheFile;
  121. cacheFile = file;
  122. cached = 1;
  123. }
  124. int FolderEdit::isContentCached() const { start_func
  125. return cached;
  126. }
  127. int FolderEdit::markLock() throw_File { start_func
  128. cacheLoad();
  129. return ++lockCount;
  130. }
  131. int FolderEdit::markUnlock() { start_func
  132. --lockCount;
  133. assert(lockCount >= 0);
  134. if ((lockCount == 0) && (cacheFile)) {
  135. // Recache ourselves
  136. cached = 1;
  137. }
  138. return lockCount;
  139. }
  140. int FolderEdit::isLocked() const { start_func
  141. return lockCount;
  142. }
  143. void FolderEdit::disassociate() throw_File { start_func
  144. // Ensure not cached- our exception point
  145. cacheLoad();
  146. // Drop browser node
  147. dropBrowser();
  148. // Notify objects
  149. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_DELETE, this, 0, 0);
  150. disassociated = 1;
  151. }
  152. void FolderEdit::reassociate(TreeView* node) { start_func
  153. assert(disassociated);
  154. // Notify objects
  155. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_CREATE, this, 0, 0);
  156. // Browser node
  157. addToBrowser(node, 0);
  158. // Ensure written to file
  159. headerModified = contentModified = 1;
  160. disassociated = 0;
  161. }
  162. void FolderEdit::setHeaderModified() { start_func
  163. if (!headerModified) headerModified = 1;
  164. }
  165. void FolderEdit::setContentModified() { start_func
  166. if (!contentModified) {
  167. contentModified = 1;
  168. // No possibility of returning to cache
  169. delete cacheFile;
  170. cacheFile = NULL;
  171. }
  172. }
  173. void FolderEdit::dropBrowser() { start_func
  174. browserNode = NULL;
  175. }
  176. void FolderEdit::addToBrowser(TreeView* node, int makeCurrent) { start_func
  177. browserNode = new TreeView(name, this, 0, treeviewEventWrap);
  178. browserNode->setIcon(icon + 1, icon);
  179. node->insert(browserNode, makeCurrent);
  180. // @TODO: Add subitems
  181. }
  182. int FolderEdit::treeviewEvent(int code, int command, int check) { start_func
  183. if (check) {
  184. if (command == EDIT_DELETE) return Window::COMMAND_ENABLE;
  185. return Window::COMMAND_HIDE;
  186. }
  187. switch (command) {
  188. case LV_RCLICK:
  189. // @TODO: Should actually be a popup with this as an item
  190. propertiesDialog(desktop->findPreviousFocusWindow());
  191. return 1;
  192. case EDIT_DELETE:
  193. case LV_DELETE:
  194. // Handles any error conditions/cancellation for us
  195. getWorldEdit()->deleteFolder(this, desktop->findPreviousFocusWindow());
  196. return 1;
  197. }
  198. return 0;
  199. }
  200. int FolderEdit::treeviewEventWrap(void* ptr, int code, int command, int check) { start_func
  201. return ((FolderEdit*)ptr)->treeviewEvent(code, command, check);
  202. }
  203. void FolderEdit::setName(const string& newName, Window* srcWin, Window* exWin) throw_Undo { start_func
  204. if (name != newName) {
  205. setHeaderModified();
  206. if (world) {
  207. getWorldEdit()->undo.storeUndoName(UndoBuffer::UNDO_FOLDERNAME, id, name, newName, srcWin);
  208. getWorldEdit()->deindexFolder(this);
  209. }
  210. name = newName;
  211. if (world) getWorldEdit()->indexFolder(this);
  212. if (browserNode) browserNode->changeName(newName);
  213. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_NAME, this, 0, 0, exWin);
  214. }
  215. }
  216. void FolderEdit::addItem(SaveLoad* item, int index, Window* srcWin, Window* exWin) throw_Undo { start_func
  217. assert(item);
  218. FolderItem work;
  219. work.itemBlockType = item->getBlockType();
  220. work.itemId = item->getId();
  221. work.item = item;
  222. assert((work.itemBlockType == WorldFileLoad::BLOCKTYPE_TILESET) ||
  223. (work.itemBlockType == WorldFileLoad::BLOCKTYPE_SCENE) ||
  224. (work.itemBlockType == WorldFileLoad::BLOCKTYPE_ANIMGROUP) ||
  225. (work.itemBlockType == WorldFileLoad::BLOCKTYPE_SCRIPT) ||
  226. (work.itemBlockType == WorldFileLoad::BLOCKTYPE_FOLDER));
  227. assert(work.itemId);
  228. #ifndef NDEBUG
  229. // Assert item isn't in list already
  230. vector<FolderItem>::iterator pos;
  231. vector<FolderItem>::iterator end = items.end();
  232. for (pos = items.begin(); pos != end; ++pos) {
  233. assert(((*pos).itemBlockType != work.itemBlockType) || ((*pos).itemId != work.itemId));
  234. }
  235. #endif
  236. // Out of range -or- default = last position (@TODO: sorted)
  237. if ((index < 0) || (index > (int)items.size()))
  238. index = items.size();
  239. setHeaderModified();
  240. if (world) {
  241. getWorldEdit()->undo.storeUndoFolderAdd(id, item, index, srcWin);
  242. }
  243. // Add to list
  244. // @TODO: insert at 'index' (right now we assume at end, which is safe... right now.)
  245. items.push_back(work);
  246. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_CONTENT, this, 0, 0, exWin);
  247. // @TODO: Update browser node
  248. }
  249. void FolderEdit::removeItem(SaveLoad* item, Window* srcWin, Window* exWin) throw_Undo { start_func
  250. assert(item);
  251. vector<FolderItem>::iterator pos;
  252. vector<FolderItem>::iterator end = items.end();
  253. int index = 0;
  254. for (pos = items.begin(); pos != end; ++pos, ++index) {
  255. if ((*pos).item == item) {
  256. setHeaderModified();
  257. if (world) {
  258. getWorldEdit()->undo.storeUndoFolderRemove(id, item, index, srcWin);
  259. }
  260. items.erase(pos);
  261. desktop->broadcastObjChange(OBJ_FOLDER | OBJMOD_CONTENT, this, 0, 0, exWin);
  262. // @TODO: update browser node
  263. return;
  264. }
  265. }
  266. assert(0);
  267. }
  268. int FolderEdit::propertiesDialog(Window* srcWin, Window* exWin) { start_func
  269. string newName = name;
  270. if (1) { // @TODO: if (FolderPropertiesDialog::create()->run(&newName, this)) {
  271. try {
  272. markLock();
  273. }
  274. catch (FileException& e) {
  275. guiErrorBox(string(e.details), errorTitleFile);
  276. return 0;
  277. }
  278. try {
  279. getWorldEdit()->undo.preUndoBlock();
  280. setName(newName, srcWin, exWin);
  281. getWorldEdit()->undo.postUndoBlock();
  282. }
  283. catch (UndoException& e) {
  284. markUnlock();
  285. return 0;
  286. }
  287. markUnlock();
  288. return 1;
  289. }
  290. return 0;
  291. }
  292. int FolderEdit::isHeaderModified() const { start_func
  293. return headerModified;
  294. }
  295. int FolderEdit::isContentModified() const { start_func
  296. return contentModified;
  297. }
  298. Uint32 FolderEdit::saveHeader(FileWrite* file) throw_File { start_func
  299. file->writeStr(name);
  300. file->writeInt(id);
  301. file->writeInt(sorted);
  302. file->writeInt(defaultFont);
  303. file->writeInt(defaultTileSet);
  304. file->writeInt(defaultScene);
  305. file->writeInt(defaultAnimGroup);
  306. file->writeInt(defaultScript);
  307. file->writeInt(defaultLibrary);
  308. file->writeInt(icon);
  309. file->writeInt(items.size());
  310. // Write items
  311. vector<FolderItem>::iterator pos;
  312. vector<FolderItem>::iterator end = items.end();
  313. for (pos = items.begin(); pos != end; ++pos) {
  314. file->writeInt((*pos).itemBlockType);
  315. file->writeInt((*pos).itemId);
  316. }
  317. return 1;
  318. }
  319. void FolderEdit::saveContent(FileWrite* file) throw_File { start_func
  320. }
  321. void FolderEdit::saveSuccess() { start_func
  322. headerModified = 0;
  323. contentModified = 0;
  324. }
  325. void FolderEdit::cachedContent(FileRead* file, int oldData) { start_func
  326. // If already cached..
  327. if (cached) {
  328. delete cacheFile;
  329. cacheFile = file;
  330. }
  331. else if (oldData) {
  332. delete file;
  333. }
  334. else {
  335. // Cache ourselves?
  336. if (!lockCount) {
  337. delete cacheFile;
  338. cacheFile = file;
  339. cached = 1;
  340. }
  341. else {
  342. delete file;
  343. }
  344. }
  345. }