PageRenderTime 6218ms CodeModel.GetById 26ms RepoModel.GetById 2ms app.codeStats 0ms

/src/Ogmo.as

http://github.com/MattThorson/Ogmo-Editor
ActionScript | 661 lines | 441 code | 111 blank | 109 comment | 50 complexity | a1b5750e89af5481fecb19bd806d69f1 MD5 | raw file
  1. package
  2. {
  3. import editor.*;
  4. import editor.ui.*;
  5. import flash.display.NativeWindowDisplayState;
  6. import flash.display.Stage;
  7. import flash.display.StageScaleMode;
  8. import flash.events.InvokeEvent;
  9. import flash.events.KeyboardEvent;
  10. import flash.events.MouseEvent;
  11. import flash.filesystem.File;
  12. import flash.filesystem.FileMode;
  13. import flash.filesystem.FileStream;
  14. import flash.geom.Point;
  15. import flash.geom.Rectangle;
  16. import flash.net.SharedObject;
  17. import flash.net.URLRequest;
  18. import flash.net.navigateToURL;
  19. import flash.desktop.NativeApplication;
  20. import flash.display.NativeWindow;
  21. import flash.display.NativeWindowInitOptions;
  22. import flash.display.Sprite;
  23. import flash.events.Event;
  24. import flash.net.FileFilter;
  25. import flash.net.FileReference;
  26. import flash.system.Capabilities;
  27. import flash.system.System;
  28. public class Ogmo extends Sprite
  29. {
  30. //Assets
  31. [Embed(source = '../assets/folder.png')]
  32. static public const ImgFolder:Class;
  33. [Embed(source = '../assets/arrow.png')]
  34. static public const ImgArrow:Class;
  35. //Consts
  36. static private const PROJECT_EXT:String = "oep";
  37. static private const LEVEL_EXT:String = "oel";
  38. static private const NEW_LEVEL_NAME:String = "NewLevel." + LEVEL_EXT;
  39. static private const PROJECT_FILTER:Array = [ new FileFilter("Ogmo Editor Project Files", "*." + PROJECT_EXT) ]
  40. static private const LEVEL_FILTER:Array = [ new FileFilter("Ogmo Editor Level Files", "*." + LEVEL_EXT) ]
  41. static private const PROJECT_HISTORY_LIMIT:uint = 3;
  42. static public const STAGE_DEFAULT_WIDTH:int = 800;
  43. static public const STAGE_DEFAULT_HEIGHT:int = 600;
  44. /**
  45. * The singleton instance of the master sprite. Contains the level and project and all the UI
  46. */
  47. static public var ogmo:Ogmo;
  48. /**
  49. * The currently loaded level
  50. */
  51. static public var level:Level;
  52. /**
  53. * The currently loaded project
  54. */
  55. static public var project:Project;
  56. /**
  57. * The container of all the UI windows
  58. */
  59. static public var windows:Windows;
  60. /**
  61. * The container of the top window menu (File, Edit, etc)
  62. */
  63. static public var windowMenu:WindowMenu;
  64. /**
  65. * Whether editor UI objects should ignore keystrokes (ex: if a textfield is capturing input)
  66. */
  67. static public var missKeys:Boolean;
  68. /**
  69. * Whether the editing grid is currently visible
  70. */
  71. static public var gridOn:Boolean;
  72. /**
  73. * The keycode of the CTRL key (the apple key on macs)
  74. */
  75. static public var keycode_ctrl:int;
  76. /**
  77. * Whether the user is on a mac
  78. */
  79. static public var mac:Boolean;
  80. /**
  81. * A temporary file object for use when loading project or level files
  82. */
  83. static public var tempFile:File;
  84. /**
  85. * Currently displayed message (in a message box in the middle of the window)
  86. */
  87. static public var message:Message;
  88. /**
  89. * Ogmo Editor version number
  90. */
  91. static public var version:String;
  92. /**
  93. * Previously-opened projects (so the user can quickly re-open them)
  94. */
  95. static public var projectHistory:Array;
  96. /**
  97. * A temporary rectangle for use anywhere, to avoid instantiating new rectangles
  98. */
  99. static public var rect:Rectangle = new Rectangle;
  100. /**
  101. * Another temp rectangle for generic use
  102. */
  103. static public var rect2:Rectangle = new Rectangle;
  104. /**
  105. * A temporary point for use anywhere, to avoid instantiating new points
  106. */
  107. static public var point:Point = new Point;
  108. public function Ogmo()
  109. {
  110. ogmo = this;
  111. addEventListener(Event.ADDED_TO_STAGE, init);
  112. //Get the version number
  113. var descriptor:XML = NativeApplication.nativeApplication.applicationDescriptor;
  114. var ns:Namespace = descriptor.namespaceDeclarations()[0];
  115. version = descriptor.ns::version;
  116. //Init keystates
  117. missKeys = false;
  118. //If running on a Mac, use Apple key instead of CTRL as modifier key
  119. if (Capabilities.os.indexOf("Mac") != -1)
  120. {
  121. mac = true;
  122. keycode_ctrl = 15;
  123. }
  124. else
  125. {
  126. mac = false;
  127. keycode_ctrl = 17;
  128. }
  129. }
  130. private function init(e:Event):void
  131. {
  132. removeEventListener(Event.ADDED_TO_STAGE, init);
  133. //Init some window properties
  134. stage.frameRate = 60;
  135. stage.scaleMode = StageScaleMode.NO_SCALE;
  136. stage.nativeWindow.width = 816;
  137. stage.nativeWindow.height = 658;
  138. stage.nativeWindow.minSize = new Point(816, 658);
  139. //Init the menu
  140. windowMenu = new WindowMenu(stage);
  141. //Load settings
  142. loadSettings();
  143. //Init grid stuff
  144. gridOn = true;
  145. //Add the splash
  146. var bg:BG = new BG;
  147. addChild(bg);
  148. //init listeners
  149. stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
  150. stage.nativeWindow.addEventListener(Event.CLOSING, onExit);
  151. //Init window title
  152. setWindowTitle();
  153. //add the main menu
  154. initMainMenu();
  155. //If a file was opened
  156. NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvokeEvent);
  157. }
  158. private function onInvokeEvent(e:InvokeEvent):void
  159. {
  160. //Open the selected file (if applicable)
  161. if (e.currentDirectory != null && e.arguments.length > 0)
  162. {
  163. var file:File = e.currentDirectory.resolvePath(e.arguments[ 0 ]);
  164. if (file.extension == PROJECT_EXT)
  165. {
  166. //First close the current project if one is open
  167. if (level)
  168. closeProject();
  169. //Open the specified project
  170. loadProject(file);
  171. stage.nativeWindow.notifyUser("Project Opened");
  172. }
  173. else if (file.extension == LEVEL_EXT)
  174. {
  175. if (level)
  176. {
  177. //First close the current level
  178. closeLevel();
  179. //Open the specified level
  180. loadLevel(file);
  181. stage.nativeWindow.notifyUser("Level Opened");
  182. }
  183. else
  184. showMessage("Cannot open a level when\nno project is open!", 5000);
  185. }
  186. else
  187. {
  188. //Not sure what it is?
  189. showMessage("Unrecognized filetype:\n\"" + file.extension + "\"", 5000);
  190. }
  191. }
  192. }
  193. static public function quit():void
  194. {
  195. ogmo.onExit();
  196. NativeApplication.nativeApplication.exit();
  197. }
  198. public function initMainMenu():void
  199. {
  200. addChild(new MainMenu);
  201. addChild(new InfoWindow);
  202. addChild(new VersionWindow);
  203. }
  204. public function destroyMainMenu():void
  205. {
  206. for (var i:int = 0; i < numChildren; i++)
  207. {
  208. if (getChildAt(i) is MainMenu || getChildAt(i) is InfoWindow || getChildAt(i) is VersionWindow)
  209. {
  210. removeChildAt(i);
  211. i--;
  212. }
  213. }
  214. }
  215. private function initWindows():void
  216. {
  217. addChild(windows = new Windows);
  218. }
  219. static public function setWindowTitle():void
  220. {
  221. if (project)
  222. ogmo.stage.nativeWindow.title = project.name + " - " + level.levelName;
  223. else
  224. ogmo.stage.nativeWindow.title = "Ogmo Editor " + version;
  225. }
  226. static public function toggleDebugWindow():void
  227. {
  228. var d:DebugWindow = getDebugWindow();
  229. if (d == null)
  230. ogmo.addChild(new DebugWindow);
  231. else
  232. ogmo.removeChild(d);
  233. windowMenu.refreshState();
  234. }
  235. static public function getDebugWindow():DebugWindow
  236. {
  237. for (var i:int = 0; i < ogmo.numChildren; i++)
  238. {
  239. if (ogmo.getChildAt(i) is DebugWindow)
  240. return ogmo.getChildAt(i) as DebugWindow;
  241. }
  242. return null;
  243. }
  244. static public function openProjectDirectory():void
  245. {
  246. try
  247. {
  248. project.workingDirectory.openWithDefaultApplication();
  249. }
  250. catch (e:Error)
  251. {
  252. trace(project.workingDirectory.url);
  253. navigateToURL(new URLRequest(project.workingDirectory.url));
  254. }
  255. }
  256. static public function openWebsite():void
  257. {
  258. navigateToURL(new URLRequest("http://ogmoeditor.com/"));
  259. }
  260. /* =================== MESSAGE SYSTEM =================== */
  261. static public function showMessage(text:String, time:int = 2000, large:Boolean = false):void
  262. {
  263. clearMessage();
  264. message = new Message(text, time, large);
  265. ogmo.addChild(message);
  266. }
  267. static public function clearMessage():void
  268. {
  269. if (message)
  270. {
  271. ogmo.removeChild(message);
  272. message = null;
  273. }
  274. }
  275. /* =================== PROJECT STUFF =================== */
  276. public function closeProject():void
  277. {
  278. project = null;
  279. closeLevel();
  280. ogmo.removeChild(windows);
  281. windows = null;
  282. System.gc();
  283. //reset the window title
  284. setWindowTitle();
  285. //refresh the window menu
  286. windowMenu.refreshState();
  287. //re-add the main menu
  288. ogmo.initMainMenu();
  289. }
  290. public function reloadProject():void
  291. {
  292. tempFile = project.file;
  293. closeProject();
  294. onLoadProjectSelect();
  295. }
  296. public function lookForProject():void
  297. {
  298. tempFile = new File;
  299. tempFile.addEventListener(Event.SELECT, onLoadProjectSelect, false, 0, true);
  300. tempFile.browseForOpen("Open Project File", PROJECT_FILTER);
  301. }
  302. public function loadProject(file:File):void
  303. {
  304. tempFile = file;
  305. onLoadProjectSelect();
  306. }
  307. private function onLoadProjectSelect(e:Event = null):void
  308. {
  309. //Clear error message if there is one
  310. clearMessage();
  311. //Create the project
  312. project = new Project();
  313. //Populate it
  314. if (Capabilities.isDebugger)
  315. {
  316. //Don't catch errors if debug build (so you get a stack trace)
  317. project.constructProject(tempFile);
  318. }
  319. else
  320. {
  321. //Catch errors if release build
  322. try
  323. {
  324. project.constructProject(tempFile);
  325. }
  326. catch (e:Error)
  327. {
  328. trace(e.getStackTrace);
  329. showMessage("Error loading project file:\n" + e.message, -1, true);
  330. return;
  331. }
  332. }
  333. //remove the main menu
  334. destroyMainMenu();
  335. //Init Windows
  336. initWindows();
  337. //Add a new level
  338. newLevel(false);
  339. //Add to history
  340. addToProjectHistory(project.file, project.name);
  341. //Show the message
  342. showMessage(project.name + "\nloaded!");
  343. //refresh the window menu
  344. windowMenu.refreshState();
  345. //Clean up
  346. System.gc();
  347. }
  348. /* =================== SAVING / LOADING LEVELS =================== */
  349. public function closeLevel():void
  350. {
  351. if (level)
  352. ogmo.removeChild(level);
  353. level = null;
  354. }
  355. public function newLevel(message:Boolean = true):void
  356. {
  357. closeLevel();
  358. ogmo.addChildAt(level = new Level(NEW_LEVEL_NAME), 1);
  359. if (message)
  360. showMessage("New Level");
  361. System.gc();
  362. //set the window title
  363. setWindowTitle();
  364. //Init the level info window
  365. windows.windowLevelInfo.populate();
  366. }
  367. public function saveLevel():void
  368. {
  369. tempFile = new File(Ogmo.project.savingDirectory);
  370. tempFile.addEventListener(Event.SELECT, onSaveLevelSelect, false, 0, true);
  371. tempFile.save(level.xml, level.levelName);
  372. }
  373. private function onSaveLevelSelect(e:Event):void
  374. {
  375. level.levelName = tempFile.name;
  376. level.saved = true;
  377. showMessage("Saved Level As:\n" + tempFile.name);
  378. tempFile = null;
  379. //set the window title
  380. setWindowTitle();
  381. }
  382. public function lookForLevel():void
  383. {
  384. tempFile = new File;
  385. tempFile.addEventListener(Event.SELECT, onLoadLevelSelect, false, 0, true);
  386. tempFile.addEventListener(Event.CANCEL, onLoadLevelCancel, false, 0, true );
  387. tempFile.browseForOpen("Open Level File", LEVEL_FILTER);
  388. }
  389. public function loadLevel(file:File):void
  390. {
  391. tempFile = file;
  392. onLoadLevelSelect();
  393. }
  394. private function onLoadLevelCancel(e:Event = null):void
  395. {
  396. tempFile = null;
  397. }
  398. private function onLoadLevelSelect(e:Event = null):void
  399. {
  400. //Open the stream
  401. var stream:FileStream = new FileStream;
  402. stream.open(tempFile, FileMode.READ);
  403. //Read the level
  404. var lvl:XML = new XML(stream.readUTFBytes(stream.bytesAvailable));
  405. stream.close();
  406. //Error and abort if not a valid level file
  407. if (lvl.name().localName != "level")
  408. {
  409. showMessage("Could not load level file:\nRoot element is not a <level> tag.", -1, true);
  410. onLoadLevelCancel();
  411. return;
  412. }
  413. //store the old level; close it
  414. var tempLevel:Level = level;
  415. closeLevel();
  416. //Make the new level and add it
  417. level = new Level(tempFile.name);
  418. ogmo.addChildAt(level, 1);
  419. //Populate it
  420. if (Capabilities.isDebugger)
  421. {
  422. //Don't catch errors if debug build
  423. level.xml = lvl;
  424. }
  425. else
  426. {
  427. //Catch errors if release build
  428. try
  429. {
  430. level.xml = lvl;
  431. }
  432. catch (e:Error)
  433. {
  434. showMessage("Could not load level file:\n" + e.message, -1, true);
  435. onLoadLevelCancel();
  436. //Go back to the old level
  437. closeLevel();
  438. level = tempLevel;
  439. ogmo.addChildAt(level, 1);
  440. level.initListeners();
  441. level.setLayer(level.currentLayerNum);
  442. return;
  443. }
  444. }
  445. //Show the message
  446. showMessage("Opened Level:\n" + tempFile.name);
  447. //set the window title
  448. setWindowTitle();
  449. //Init level info window
  450. windows.windowLevelInfo.populate();
  451. //Clean up
  452. tempFile = null;
  453. System.gc();
  454. }
  455. /* =================== SETTINGS =================== */
  456. private function saveSettings():void
  457. {
  458. //Window bounds
  459. if (stage.nativeWindow.displayState == NativeWindowDisplayState.MAXIMIZED)
  460. Settings.settings.window[0].maximized[0] = true;
  461. else if (stage.nativeWindow.displayState == NativeWindowDisplayState.NORMAL)
  462. {
  463. Settings.settings.window[0].x[0] = stage.nativeWindow.x;
  464. Settings.settings.window[0].y[0] = stage.nativeWindow.y;
  465. Settings.settings.window[0].width[0] = stage.nativeWindow.width;
  466. Settings.settings.window[0].height[0] = stage.nativeWindow.height;
  467. Settings.settings.window[0].maximized[0] = false;
  468. }
  469. //Project history
  470. Settings.settings.projectHistory[0] = <projectHistory />;
  471. for each (var ob:Object in projectHistory)
  472. {
  473. var o:XML = <project />;
  474. o.@file = ob.file;
  475. o.@name = ob.name;
  476. Settings.settings.projectHistory[0].appendChild(o);
  477. }
  478. Settings.saveSettings();
  479. }
  480. private function loadSettings():void
  481. {
  482. Settings.loadSettings();
  483. //Window bounds
  484. stage.nativeWindow.x = Settings.settings.window[0].x[0];
  485. stage.nativeWindow.y = Settings.settings.window[0].y[0];
  486. if (Settings.settings.window[0].width[0] == "-1")
  487. stage.nativeWindow.width = STAGE_DEFAULT_WIDTH;
  488. else
  489. stage.nativeWindow.width = Settings.settings.window[0].width[0];
  490. if (Settings.settings.window[0].height[0] == "-1")
  491. stage.nativeWindow.width = STAGE_DEFAULT_HEIGHT;
  492. else
  493. stage.nativeWindow.height = Settings.settings.window[0].height[0];
  494. if (Settings.settings.window[0].maximized[0] == "true")
  495. stage.nativeWindow.maximize();
  496. //Project history
  497. projectHistory = new Array;
  498. if (Settings.settings.projectHistory[0].project.length())
  499. {
  500. for each (var o:XML in Settings.settings.projectHistory[0].project)
  501. {
  502. var f:File = new File(o.@file);
  503. if (!f.exists)
  504. continue;
  505. var ob:Object = new Object;
  506. ob.file = o.@file;
  507. ob.name = o.@name;
  508. projectHistory.push(ob);
  509. }
  510. }
  511. }
  512. static public function addToProjectHistory(file:File, name:String):void
  513. {
  514. var i:int;
  515. for (i = 0; i < projectHistory.length; i++)
  516. {
  517. if (projectHistory[ i ].file == file.url)
  518. {
  519. projectHistory.splice(i, 1);
  520. break;
  521. }
  522. }
  523. var a:Array = new Array;
  524. a[ 0 ] = new Object;
  525. a[ 0 ].file = file.url;
  526. a[ 0 ].name = name;
  527. for (i = 0; i < projectHistory.length && i < PROJECT_HISTORY_LIMIT - 1; i++)
  528. a[ i + 1 ] = projectHistory[ i ];
  529. projectHistory = a;
  530. }
  531. /* =================== EVENTS =================== */
  532. private function onKeyDown(e:KeyboardEvent):void
  533. {
  534. switch (e.keyCode)
  535. {
  536. //ESC
  537. case (27):
  538. if (level)
  539. closeProject();
  540. else
  541. quit();
  542. break;
  543. }
  544. }
  545. public function onExit(e:Event = null):void
  546. {
  547. saveSettings();
  548. }
  549. }
  550. }