From 15e3ec42ce60b9c4636fcaea489716d6fd57dcd2 Mon Sep 17 00:00:00 2001 From: alfongj Date: Thu, 11 Aug 2011 21:33:48 +0100 Subject: [PATCH] Closes #39, closes #40, closes #45, updates #31, #33, #35, #46 & solved bugs --- .../math/games/builder/fig/FigFontManager.as | 6 + .../games/builder/fig/TreeGridFigWriter.as | 1 + .../builder/io/ExtensiveFormXMLReader.as | 323 ------------ .../lse/math/games/builder/io/FileManager.as | 57 +- ...tensiveFormXMLWriter.as => XMLExporter.as} | 252 ++++----- .../lse/math/games/builder/io/XMLImporter.as | 488 ++++++++++++++++++ .../math/games/builder/model/ExtensiveForm.as | 73 ++- .../src/lse/math/games/builder/model/Iset.as | 15 +- .../src/lse/math/games/builder/model/Node.as | 3 +- ...{ActionHandler.as => TreeActionHandler.as} | 331 ++++++------ .../builder/presenter/TreeGridPresenter.as | 60 ++- .../games/builder/settings/FileSettings.as | 123 +++++ .../lse/math/games/builder/settings/SCodes.as | 102 +++- .../math/games/builder/settings/Settings.mxml | 284 ++++++++-- .../games/builder/settings/UserSettings.as | 61 ++- .../src/lse/math/games/builder/view/Main.mxml | 217 +++++--- .../math/games/builder/view/OutputWindow.mxml | 14 +- .../games/builder/viewmodel/MatrixInput.as | 333 ------------ .../games/builder/viewmodel/MatrixPainter.as | 2 +- .../math/games/builder/viewmodel/TreeGrid.as | 71 ++- .../viewmodel/TreeGridActionFactory.as | 24 +- .../builder/viewmodel/TreeGridPainter.as | 18 +- .../builder/viewmodel/TreeGridSetPainter.as | 36 +- .../viewmodel/action/AddChildAction.as | 11 +- .../viewmodel/action/ChangePlayerAction.as | 20 +- .../builder/viewmodel/action/CutAction.as | 8 +- .../builder/viewmodel/action/DeleteAction.as | 8 +- .../viewmodel/action/DissolveAction.as | 10 +- .../viewmodel/action/LabelChangeAction.as | 6 +- .../viewmodel/action/MakeChanceAction.as | 20 +- .../builder/viewmodel/action/MergeAction.as | 4 +- .../viewmodel/action/PayChangeAction.as | 6 +- .../viewmodel/action/PerfectRecallAction.as | 2 + .../builder/viewmodel/action/RotateAction.as | 32 +- gui-builder/src/util/Log.as | 5 +- gui-builder/test/src/TestSuite.as | 2 + web-service/war/builder/index.jsp | 41 +- 37 files changed, 1750 insertions(+), 1319 deletions(-) delete mode 100644 gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLReader.as rename gui-builder/src/lse/math/games/builder/io/{ExtensiveFormXMLWriter.as => XMLExporter.as} (59%) create mode 100644 gui-builder/src/lse/math/games/builder/io/XMLImporter.as rename gui-builder/src/lse/math/games/builder/presenter/{ActionHandler.as => TreeActionHandler.as} (76%) create mode 100644 gui-builder/src/lse/math/games/builder/settings/FileSettings.as delete mode 100644 gui-builder/src/lse/math/games/builder/viewmodel/MatrixInput.as diff --git a/gui-builder/src/lse/math/games/builder/fig/FigFontManager.as b/gui-builder/src/lse/math/games/builder/fig/FigFontManager.as index f2c067c..fb10208 100644 --- a/gui-builder/src/lse/math/games/builder/fig/FigFontManager.as +++ b/gui-builder/src/lse/math/games/builder/fig/FigFontManager.as @@ -168,6 +168,12 @@ package lse.math.games.builder.fig return baseFonts; } + /** Returns true if the font is available */ + public static function isFontAvailable(fontName:String):Boolean + { + return (_fontFamilyEnums.hasOwnProperty(fontName)); + } + /** Returns a font's code in the enum of fonts of the fig*/ public static function figEnumValue(fontFamily:String, weight:String, posture:String):int { diff --git a/gui-builder/src/lse/math/games/builder/fig/TreeGridFigWriter.as b/gui-builder/src/lse/math/games/builder/fig/TreeGridFigWriter.as index 36ce331..3df6afd 100644 --- a/gui-builder/src/lse/math/games/builder/fig/TreeGridFigWriter.as +++ b/gui-builder/src/lse/math/games/builder/fig/TreeGridFigWriter.as @@ -62,6 +62,7 @@ package lse.math.games.builder.fig var graphics:FigGraphics = new FigGraphics(buffer); graphics.addColor(grid.player1Color); graphics.addColor(grid.player2Color); + //TODO: 3PL //Saves onto the file the results of all the painter operations that form the tree painter.paint(graphics, width, height); diff --git a/gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLReader.as b/gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLReader.as deleted file mode 100644 index 5bab196..0000000 --- a/gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLReader.as +++ /dev/null @@ -1,323 +0,0 @@ -package lse.math.games.builder.io -{ - import flash.utils.Dictionary; - - import lse.math.games.builder.model.ExtensiveForm; - import lse.math.games.builder.model.Iset; - import lse.math.games.builder.model.Move; - import lse.math.games.builder.model.Node; - import lse.math.games.builder.model.Outcome; - import lse.math.games.builder.model.Player; - import lse.math.games.builder.model.Rational; - - import mx.controls.Alert; - - /** - * TODO: This class needs to be adapted to the new standard format in - * https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B331281Yuj61ZTM2Y2M0YzMtODQyNi00NGVmLWI1MjctMDY0Mzc2ZGM2MjFj&hl=en_US - * - * Also, a new NormalFormXMLReader and NormalFormXMLWriter will most probably be needed after the normalform importing is worked out - * - * @author Mark Egesdal - */ - public class ExtensiveFormXMLReader - { - private var nodes:Dictionary; - private var isets:Dictionary; - private var isetObjToId:Dictionary; - private var singletons:Vector.; - private var moves:Dictionary; - private var players:Dictionary; - - private var xml:XML = null; - private var tree:ExtensiveForm = null; - - public function ExtensiveFormXMLReader(xml:XML) - { - this.xml = xml; - } - - public function load(tree:ExtensiveForm):ExtensiveForm - { - this.tree = tree; - tree.clearTree(); - - nodes = new Dictionary(); - isets = new Dictionary(); - isetObjToId = new Dictionary(); - singletons = new Vector.(); - moves = new Dictionary(); - players = new Dictionary(); - - for each(var child:XML in xml.children()) { - if (child.name() == "iset") { - processIset(child); - } else if (child.name() == "node") { - processNode(child, null); - } else if (child.name() == "outcome") { - processOutcome(child, null); - } else { - trace("Ignoring unknown element:\r\n" + child); - } - } - - var iset:Iset = null; - for (var isetId:String in isets) { - iset = isets[isetId] as Iset; - if (iset != tree.root.iset) { - tree.addIset(iset); - } - } - for each (iset in singletons) { - if (iset != tree.root.iset) { - tree.addIset(iset); - } - } - - hookupAndVerifyMoves(); - - return tree; - } - - //TODO: there has got to be a more efficient algorithm - private function hookupAndVerifyMoves():void - { - for (var iset:Iset = tree.root.iset; iset != null; iset = iset.nextIset) { - - iset.sort(); - - //for each child of the first node, hook up move - var baseline:Node = iset.firstNode; - for (var baselineChild:Node = baseline.firstChild, childIdx:int = 0; baselineChild != null; baselineChild = baselineChild.sibling, ++childIdx) - { - iset.assignMove(baselineChild.reachedby); - - // make sure all the rest of the nodes have the child with the same move - // TODO: it does not necessarily need to be at the same index - for (var baselineMate:Node = baseline.nextInIset; baselineMate != null; baselineMate = baselineMate.nextInIset) - { - var mateChild:Node = baselineMate.firstChild; - for (var i:int = 0; i < childIdx; ++i) { - mateChild = mateChild.sibling; - } - if (mateChild.reachedby != baselineChild.reachedby) { - if (mateChild.reachedby == null) { - throw new Error("node does not contain an incoming move"); - } else { - throw new Error("Iset " + iset.idx + " is inconsistent for node " + baselineMate.number /*+ " at " + baselineMate.setIdx*/ + " for child " + mateChild.number + " at " + childIdx + " with move " + mateChild.reachedby); - } - } - } - } - } - } - - private function getPlayer(playerId:String):Player - { - if (playerId == Player.CHANCE_NAME) { - return Player.CHANCE; - } - - var player:Player = players[playerId]; - if (player == null) { - player = new Player(playerId, tree); - players[playerId] = player; - } - return player; - } - - private function processIset(elem:XML):void - { - var id:String = elem.@id; - var iset:Iset = isets[id]; - - if (iset == null) { - var player:Player = (elem.@player != undefined) ? getPlayer(elem.@player) : Player.CHANCE; - iset = new Iset(player); - isets[id] = iset; - isetObjToId[iset] = id; - } - - for each (var child:XML in elem.children()) - { - if (child.name() == "node") { - processNode(child, null); - } else { - trace("Ignoring unknown element:\r\n" + child); - } - } - } - - private function processNode(elem:XML, parentNode:Node):void - { - //init - var node:Node = null; - if (elem.@id != undefined) - { - var id:String = elem.@id; - node = nodes[id]; - - if (node == null) { - trace("XMLReader: creating new node " + id); - node = tree.createNode(); - nodes[id] = node; - } else { - trace("XMLReader: processing previously created node " + id); - - } - } else { - node = tree.createNode(); - } - - //assign parent - if (parentNode == null && elem.@parent != undefined) - { - var parentId:String = elem.@parent; - parentNode = nodes[parentId]; - - if (parentNode == null) { - parentNode = tree.createNode(); - nodes[parentId] = parentNode; - } - } - if (parentNode != null) { - parentNode.addChild(node); - } else { - tree.root = node; - } - - // process iset - var isetId:String = null; - if (elem.parent() != null && elem.parent().name() == "iset") { - isetId = elem.parent().@id; - if (elem.@iset != undefined) trace("Warning: @iset attribute is set for a node nested in an iset tag. Ignored."); - } else if (elem.@iset != undefined) { - isetId = elem.@iset; - } - - var iset:Iset = null; - var player:Player = (elem.@player != undefined) ? getPlayer(elem.@player) : Player.CHANCE; - if (isetId == null) { - iset = new Iset(player); - singletons.push(iset); // root is already taken care of - } else { - //look it up in the map, if it doesn't exist create it and add it - iset = isets[isetId]; - if (iset == null) { - iset = new Iset(player); - isets[isetId] = iset; - isetObjToId[iset] = isetId; - } else { - if (player != Player.CHANCE) { - if (iset.player != Player.CHANCE && player != iset.player) { - trace("Warning: @player attribute conflicts with earlier iset player assignment. Ignored."); - } - while (iset.player != player) { - iset.changePlayer(tree.firstPlayer); - } - } - } - } - iset.insertNode(node); - - // set up the moves - processMove(elem, node, parentNode); -/* if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parentNode != null && parentNode.iset != null) ? String(parentNode.iset.idx) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - node.reachedby = move; - } else if (parentNode != null) { - // assume this comes from a chance node with a probability of zero - node.reachedby = new Move(); - } - - if (elem.@prob != undefined && node.reachedby != null) { - node.reachedby.prob = Rational.parse(elem.@prob); - } -*/ - for each (var child:XML in elem.children()) { - if (child.name() == "node") { - processNode(child, node); - } else if (child.name() == "outcome") { - processOutcome(child, node); - } /*else { - trace("Ignoring unknown element:\r\n" + child); - }*/ - } - } - - private function processOutcome(elem:XML, parent:Node):void - { - // Create wrapping node - // get parent from dictionary... if it doesn't exist then the outcome must be the root - var wrapNode:Node = parent != null ? parent.newChild() : tree.createNode(); - if (parent == null) { - tree.root = wrapNode; - } - - // set up the moves - processMove(elem, wrapNode, parent); - /* if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parent != null && parent.iset != null) ? String(parent.iset.idx) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - wrapNode.reachedby = move; - } - - if (elem.@prob != undefined && wrapNode.reachedby != null) { - wrapNode.reachedby.prob = Rational.parse(elem.@prob); - }*/ - - var outcome:Outcome = wrapNode.makeTerminal(); - for each (var child:XML in elem.children()) { - if (child.name() == "payoff") { - var playerId:String = child.@player; - var payoff:Rational = Rational.parse(child.@value); - - var player:Player = players[playerId]; - if (player == null) { - player = new Player(playerId, tree); - players[playerId] = player; - } - outcome.setPay(player, payoff); - } /*else { - trace("Ignoring unknown element:\r\n" + child); - }*/ - } - } - - private function processMove(elem:XML, node:Node, parent:Node):void - { - if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parent != null && parent.iset != null) ? String(isetObjToId[parent.iset]) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - node.reachedby = move; - } else if (parent != null) { - // assume this comes from a chance node with a probability of zero - node.reachedby = new Move(); - } - - if (elem.@prob != undefined && node.reachedby != null) { - node.reachedby.prob = Rational.parse(elem.@prob); - } - } - } -} diff --git a/gui-builder/src/lse/math/games/builder/io/FileManager.as b/gui-builder/src/lse/math/games/builder/io/FileManager.as index d2cc62b..8bfe07f 100644 --- a/gui-builder/src/lse/math/games/builder/io/FileManager.as +++ b/gui-builder/src/lse/math/games/builder/io/FileManager.as @@ -10,6 +10,7 @@ package lse.math.games.builder.io import flash.utils.setTimeout; import lse.math.games.builder.presenter.TreeGridPresenter; + import lse.math.games.builder.settings.FileSettings; import lse.math.games.builder.settings.UserSettings; import util.Log; @@ -40,7 +41,7 @@ package lse.math.games.builder.io private var controller:TreeGridPresenter; private var settings:UserSettings = UserSettings.instance; - private var treeStorage:SharedObject; + private var cookies:SharedObject; private var log:Log = Log.instance; @@ -52,10 +53,10 @@ package lse.math.games.builder.io setTimeout(autosave, AUTOSAVE_INTERVAL); //Look if there is an autosave, and load it if so - treeStorage = SharedObject.getLocal( "autosave", "/" ); - if(treeStorage != null && treeStorage.data["treeXML"] !=null) + cookies = SharedObject.getLocal( "autosave", "/" ); + if(cookies != null && cookies.data[""] !=null) { - setTimeout(askLoadTree, 3000); //Leave 3 seconds for the creation of canvas + setTimeout(askLoadFile, 3000); //Leave 3 seconds for the creation of canvas } //Add callback for the askBeforeQuit function @@ -98,10 +99,10 @@ package lse.math.games.builder.io if(settings.cookiesStorable /*&& autosave_on */) { - if(treeStorage == null) - treeStorage = SharedObject.getLocal( "autosave", "/" ); + if(cookies == null) + cookies = SharedObject.getLocal( "autosave", "/" ); - treeStorage.setProperty("treeXML", value); + cookies.setProperty("", value); unsavedChanges = false; } } @@ -136,37 +137,38 @@ package lse.math.games.builder.io * It returns true if there are unsaved changes, false if there aren't. */ private function askBeforeQuit():Boolean { - - if(treeStorage != null) - treeStorage.clear(); + if(cookies != null) + cookies.clear(); return _unsavedChanges; } //Asks if to load a saved tree - private function askLoadTree():void { - PromptTwoButtons.show(loadAutoSavedTree, "There is " + - "an auto-saved tree from last execution. Would you like to load it?") + private function askLoadFile():void { + PromptTwoButtons.show(loadAutoSavedFile, "There is " + + "an auto-saved file from last execution. Would you like to load it?") } /* Loads a tree saved via autosave */ - private function loadAutoSavedTree():void { + private function loadAutoSavedFile():void { if(PromptTwoButtons.buttonPressed == PromptTwoButtons.OK) { - backupXML = treeStorage.data["treeXML"] as XML; - controller.loadTreeFromXML(_backupXML); + backupXML = cookies.data["autosave"] as XML; + //TODO #33 check the type of thing stored, call correspondant function + controller.loadFromXML(_backupXML); } else { clear(); } } - /** Resets the filename */ + /** Resets the filename, backup & cookies */ public function clear():void { filename = "Untitled"; backupXML = null; - treeStorage = SharedObject.getLocal( "autosave", "/" ); - if(treeStorage!=null) - treeStorage.clear(); + FileSettings.instance.clear(); + cookies = SharedObject.getLocal( "autosave", "/" ); + if(cookies!=null) + cookies.clear(); } /* @@ -227,16 +229,16 @@ package lse.math.games.builder.io /** * Opens a dialog for saving the tree in xml format */ - public function saveXML(treeXML:XML):void + public function saveXML(xml:XML):void { - backupXML = treeXML; + backupXML = xml; fr = new FileReference(); fr.addEventListener(Event.COMPLETE, onSaveComplete); fr.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); - fr.save(treeXML.toXMLString(), filename+".xml"); + fr.save(xml.toXMLString(), filename+".xml"); } /** @@ -295,8 +297,9 @@ package lse.math.games.builder.io //read the bytes of the file as a string var text:String = fr.data.readUTFBytes(fr.data.bytesAvailable); - var xml:XML = new XML(text); - controller.loadTreeFromXML(xml); + var xml:XML = new XML(text); + + controller.loadFromXML(xml); backupXML = xml; filename = getNameFromFile(fr); @@ -317,8 +320,8 @@ package lse.math.games.builder.io unsavedChanges = false; //As the user has its tree saved as a .xml, he wouldn't like to have it also as a SharedObject - if(treeStorage != null) - treeStorage.clear(); + if(cookies != null) + cookies.clear(); fr = null; } diff --git a/gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLWriter.as b/gui-builder/src/lse/math/games/builder/io/XMLExporter.as similarity index 59% rename from gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLWriter.as rename to gui-builder/src/lse/math/games/builder/io/XMLExporter.as index 67f84db..a4ba00f 100644 --- a/gui-builder/src/lse/math/games/builder/io/ExtensiveFormXMLWriter.as +++ b/gui-builder/src/lse/math/games/builder/io/XMLExporter.as @@ -1,119 +1,135 @@ -package lse.math.games.builder.io -{ - import lse.math.games.builder.model.ExtensiveForm; - import lse.math.games.builder.model.Iset; - import lse.math.games.builder.model.Node; - import lse.math.games.builder.model.Player; - - /** - * This class needs to be adapted to follow the new XML standard format in: - * https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B331281Yuj61ZTM2Y2M0YzMtODQyNi00NGVmLWI1MjctMDY0Mzc2ZGM2MjFj&hl=en_US - * - * @author Mark Egesdal - */ - public class ExtensiveFormXMLWriter - { - public function write(tree:ExtensiveForm):XML - { - var xml:XML = ; - - var isets:Vector. = new Vector.(); - xml.appendChild(getNodeElem(tree.root, isets, tree)); - - /* Suspending writing Ahmad's way... perhaps I can be more clever with how I order the isets, but this way - * seems to lose the left-to-right ordering of the tree - for (var h:Iset = tree.root.iset; h != null; h = h.nextIset) { - xml.appendChild(getIsetElem(h, tree)); - }*/ - return xml; - } - - private function getNodeElem(n:Node, isets:Vector., tree:ExtensiveForm):XML - { - var nodeElem:XML; - if (n.outcome == null) { - nodeElem = ; - - if (n.iset != null && n.iset.numNodes > 1) { - nodeElem.@iset = n.iset.idx; - } - if (isets.indexOf(n.iset) < 0 && n.iset.player != Player.CHANCE) { - nodeElem.@player = n.iset.player.name; - isets.push(n.iset); - } - - for (var child:Node = n.firstChild; child != null; child = child.sibling) { - nodeElem.appendChild(getNodeElem(child, isets, tree)); - } - } else { - nodeElem = ; - - for (var player:Player = tree.firstPlayer; player != null; player = player.nextPlayer) - { - var payoffElem:XML = ; - payoffElem.@player = player.name; - payoffElem.@value = n.outcome.pay(player); // TODO: write out as fraction? - nodeElem.appendChild(payoffElem); - } - } - - if (n.reachedby != null && n.parent.iset.player == Player.CHANCE) { - nodeElem.@prob = n.reachedby.label; // TODO: write out as a fraction - } else if (n.reachedby != null) { - nodeElem.@move = n.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? - } - return nodeElem; - } - - private function getIsetElem(h:Iset, tree:ExtensiveForm):XML - { - var isetElem:XML; - if (h.numNodes > 1) { - isetElem = - isetElem.@id = h.idx; - if (h.player != Player.CHANCE) isetElem.@player = h.player.name; - - for (var n:Node = h.firstNode; n != null; n = n.nextInIset) { - isetElem.appendChild(getNodeElemForIset(n, tree)); - } - } else { - isetElem = getNodeElemForIset(h.firstNode, tree); - if (h.player != Player.CHANCE) isetElem.@player = h.player.name; - } - return isetElem; - } - - private function getNodeElemForIset(node:Node, tree:ExtensiveForm):XML - { - var nodeElem:XML = ; - nodeElem.@id = node.number; - if (node.parent != null) { - nodeElem.@parent = node.parent.number; - } - if (node.reachedby != null && node.reachedby.isChance) { - nodeElem.@prob = node.reachedby.prob; // TODO: write out as a fraction - } else if (node.reachedby != null) { - nodeElem.@move = node.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? - } - - for (var child:Node = node.firstChild; child != null; child = child.sibling) { - if (child.outcome != null) { - var outcomeElem:XML = ; - if (child.reachedby != null && child.reachedby.isChance) { - outcomeElem.@prob = child.reachedby.prob; // TODO: write out as a fraction - } else { - outcomeElem.@move = child.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? - } - for (var player:Player = tree.firstPlayer; player != null; player = player.nextPlayer) { - var payoffElem:XML = ; - payoffElem.@player = player.name; - payoffElem.@value = child.outcome.pay(player); // TODO: write out as fraction? - outcomeElem.appendChild(payoffElem); - } - nodeElem.appendChild(outcomeElem); - } - } - return nodeElem; - } - } +package lse.math.games.builder.io +{ + import lse.math.games.builder.model.ExtensiveForm; + import lse.math.games.builder.model.Iset; + import lse.math.games.builder.model.Node; + import lse.math.games.builder.model.Player; + + import util.Log; + + /** + * TODO: Document + * + * @author Mark Egesdal + */ + //TODO: Do something with gameDescription info + //TODO: In the future, write node and iset names & maybe? payoffs in non-outcomes + //TODO #33 writeMatrix + //TODO #33 write players first, when they are in the spec + public class XMLExporter + { + private var log:Log = Log.instance; + + + + public function writeTree(tree:ExtensiveForm):XML + { + var xml:XML = + + + + + ; + + //TODO loadSettings + + xml.extensiveForm.appendChild(getNodeElem(tree.root, tree)); + + return xml; + } + + private function getNodeElem(n:Node, tree:ExtensiveForm):XML + { + var nodeElem:XML; + if (!n.isLeaf) { + nodeElem = ; + + if (n.iset != null) + { + if(n.iset.numNodes > 1) + nodeElem.@iset = n.iset.idx; + + if (n.iset.player != Player.CHANCE) { + nodeElem.@player = n.iset.player.name; + } + } + + for (var child:Node = n.firstChild; child != null; child = child.sibling) { + nodeElem.appendChild(getNodeElem(child, tree)); + } + } else { + nodeElem = ; + + if(n.outcome != null) + for (var player:Player = tree.firstPlayer; player != null; player = player.nextPlayer) + { + var payoffElem:XML = ; + payoffElem.@player = player.name; + payoffElem.@value = n.outcome.pay(player).toString(); + nodeElem.appendChild(payoffElem); + } + } + + if (n.reachedby != null) + { + if(n.parent.iset.player == Player.CHANCE) { + nodeElem.@prob = n.reachedby.label; + } else { + nodeElem.@move = n.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? + } + } + return nodeElem; + } + + private function getIsetElem(h:Iset, tree:ExtensiveForm):XML + { + var isetElem:XML; + if (h.numNodes > 1) { + isetElem = + isetElem.@id = h.idx; + if (h.player != Player.CHANCE) isetElem.@player = h.player.name; + + for (var n:Node = h.firstNode; n != null; n = n.nextInIset) { + isetElem.appendChild(getNodeElemForIset(n, tree)); + } + } else { + isetElem = getNodeElemForIset(h.firstNode, tree); + if (h.player != Player.CHANCE) isetElem.@player = h.player.name; + } + return isetElem; + } + + private function getNodeElemForIset(node:Node, tree:ExtensiveForm):XML + { + var nodeElem:XML = ; + nodeElem.@id = node.number; + if (node.parent != null) { + nodeElem.@parent = node.parent.number; + } + if (node.reachedby != null && node.reachedby.isChance) { + nodeElem.@prob = node.reachedby.prob; // TODO: write out as a fraction + } else if (node.reachedby != null) { + nodeElem.@move = node.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? + } + + for (var child:Node = node.firstChild; child != null; child = child.sibling) { + if (child.outcome != null) { + var outcomeElem:XML = ; + if (child.reachedby != null && child.reachedby.isChance) { + outcomeElem.@prob = child.reachedby.prob; // TODO: write out as a fraction + } else { + outcomeElem.@move = child.reachedby.label; // TODO: add a unique constraint in the program to prevent errors here? + } + for (var player:Player = tree.firstPlayer; player != null; player = player.nextPlayer) { + var payoffElem:XML = ; + payoffElem.@player = player.name; + payoffElem.@value = child.outcome.pay(player); // TODO: write out as fraction? + outcomeElem.appendChild(payoffElem); + } + nodeElem.appendChild(outcomeElem); + } + } + return nodeElem; + } + } } \ No newline at end of file diff --git a/gui-builder/src/lse/math/games/builder/io/XMLImporter.as b/gui-builder/src/lse/math/games/builder/io/XMLImporter.as new file mode 100644 index 0000000..8c97c53 --- /dev/null +++ b/gui-builder/src/lse/math/games/builder/io/XMLImporter.as @@ -0,0 +1,488 @@ +package lse.math.games.builder.io +{ + import flash.utils.Dictionary; + + import lse.math.games.builder.fig.FigFontManager; + import lse.math.games.builder.model.ExtensiveForm; + import lse.math.games.builder.model.Iset; + import lse.math.games.builder.model.Move; + import lse.math.games.builder.model.Node; + import lse.math.games.builder.model.NormalForm; + import lse.math.games.builder.model.Outcome; + import lse.math.games.builder.model.Player; + import lse.math.games.builder.model.Rational; + import lse.math.games.builder.settings.FileSettings; + import lse.math.games.builder.settings.SCodes; + import lse.math.games.builder.viewmodel.TreeGrid; + + import util.Log; + + /** + * XMLImporter loads trees and matrixes from XML data.

+ * + * Instructions to use it: + *

  1. Make an instance of it calling the constructor with the xml you want to read
  2. + *
  3. Check the type of info stored: an Ext Form Tree, a Strat Form Matri, or UNKNOWN (Broken)
  4. + *
  5. Call the corresponding loader
+ * + * @author Mark Egesdal & alfongj + */ + //TODO: Do something with gameDescription info + //TODO: In the future, load node and iset names & maybe? payoffs in non-outcomes + //TODO #33 loadMatrix + //TODO #33 load players first, when they are in the spec + public class XMLImporter + { + //Types of info contained: + /** The XML file is not of a known type */ + public const UNKNOWN:int = -1; + /** The XML file represents a tree in Extensive Form */ + public const EF:int = 1; + /** The XML file represents a matrix in Strategic Form */ + public const SF:int = 2; + + //File header properties + private var _version:int; + private var _type:int; + private var _displayInfo:Boolean = false; //If the XML contains the tag + private var _gameDescInfo:Boolean = false; //If the XML contains the tag + + //Objects contained + private var nodes:Dictionary; + private var isets:Dictionary; + private var isetObjToId:Dictionary; + private var singletons:Vector.; + private var moves:Dictionary; + private var players:Dictionary; + + private var xml:XML = null; + private var fileSettings:FileSettings; + private var tree:ExtensiveForm = null; + private var matrix:NormalForm = null; + private var log:Log = Log.instance; + private var lastIsetIdx:int = 0; + + + + /** Stores a reference to the xml to be loaded and analyses the data in it */ + public function XMLImporter(xml:XML) + { + this.xml = xml; + + analyseData(); + } + + /** + * Version number of the XML file being read.
+ * The old version will be considered version 0.
+ * An unknown version will be considered version -1 + */ + public function get version():int { return _version; } + + /** + * Type of the file being read. It can be: + *
  • 'EF': Extensive Form
  • + *
  • 'SF': Strategic Form
  • + *
  • 'BROKEN': None of them
  • + */ + public function get type():int { return _type; } + + /** If the xml file contains any display info (i.e., inside tags) */ + public function get displayInfo():Boolean { return _displayInfo; } + + /** If the xml file contains any game description info (i.e., inside tags) */ + public function get gameDescInfo():Boolean { return _gameDescInfo; } + + + + //Populates version, type, gameDescInfo and displayInfo properties + private function analyseData():void { + //ANALYSE VERSION INFO + var firstChild:XML = xml[0]; + if(firstChild.name() == "extensiveForm") + { + _version = 0; + log.add(Log.ERROR, "Warning: This file is of an old format. Although gte is still "+ + "capable of opening it, it may not be supported in future versions, so please overwrite "+ + "it now with an updated file by pressing the 'Save' button (Ctrl+S)"); + } + else if(firstChild.name() == "gte") + { + //TODO #33: Once version is included in the spec, return it + _version = 1 + } else _version = -1; + + //ANALYSE TYPE, DISPINFO & GAMEDESCRIPTIONINFO + if(_version == -1) + _type = UNKNOWN; + if(_version == 0) + _type = EF; + else { + var ef:Boolean = false; + var sf:Boolean = false; + + for each(var header:XML in firstChild.children()) + { + var name:String = header.name(); + if(name==null) + log.add(Log.ERROR_THROW, "Corrupt XML file: empty child of "); + + if(name == "display") + if(header.child("*").length()>0) + _displayInfo = true; + if(name == "gameDescription") + if(header.text().length()>0) + _gameDescInfo = true; + if(name == "extensiveForm") + ef = true; + if(name == "strategicForm") + sf = true; + } + + if(ef){ + _type = EF; + + if(sf) + log.add(Log.ERROR_HIDDEN, "The XML file being loaded contained info for "+ + "both an EF and a SF game. Ignoring the lattest"); + } else { + if(sf) + _type = SF; + else { + log.add(Log.ERROR, "Corrupt XML file: couldn't find any game information."); + _type = UNKNOWN; + } + } + } + } + + /** Loads into a extensive form tree the xml data */ + public function loadTree(tree:ExtensiveForm):ExtensiveForm + { + //Clear previous file settings (from another file, possibly) + fileSettings = FileSettings.instance; + fileSettings.clear(); + + lastIsetIdx = 0; + + if(_type != EF) + { + log.add(Log.ERROR_THROW, "Tried to load a tree from a non-tree game file"); + return null; + } + else { + if(_displayInfo) //Load settings, if any + { + loadSettingsOnTree(tree as TreeGrid); + } + + this.tree = tree; + tree.clearTree(); + + nodes = new Dictionary(); + isets = new Dictionary(); + isetObjToId = new Dictionary(); + singletons = new Vector.(); + moves = new Dictionary(); + players = new Dictionary(); + + //Depending on the version, the tree data will be in a different part + var childrenList:XMLList; + if(_version == 0) + childrenList = xml.children(); + else + childrenList = xml.extensiveForm.children(); + + //Start processing the tree + for each(var child:XML in childrenList) { + if (child.name() == "node") { + processNode(child, null); + } else if (child.name() == "outcome") { + processOutcome(child, null); + } else { + log.add(Log.ERROR_HIDDEN, "Ignoring unknown element: " + child); + } + } + + //Add the found isets to the tree + var iset:Iset = null; + for (var isetId:String in isets) { + iset = isets[isetId] as Iset; + if (iset != tree.root.iset) { + tree.addIset(iset); + } + } + for each (iset in singletons) { + if (iset != tree.root.iset) { + tree.addIset(iset); + } + } + + hookupAndVerifyMoves(); + + //TODO: 3PL + while(tree.numPlayers<2) + { + tree.newPlayer(""+(tree.numPlayers+1)); + } + + return tree; + } + } + + //Parses all settings found under the tags and loads them into the tree + //Mustn't be called if _displayInfo is false + private function loadSettingsOnTree(tree:TreeGrid):void + { + for each(var child:XML in xml.display.children()) + { + if (child.name() == "color") { + var player:XMLList = child.@player; + if(player!=null) + { + if(player.toString()=="1") + { + fileSettings.setValue(SCodes.FILE_PLAYER_1_COLOR, getColorFromHexString(child.text())); + } else if(player.toString()=="2") + fileSettings.setValue(SCodes.FILE_PLAYER_2_COLOR, getColorFromHexString(child.text())); + } + } else if (child.name() == "font") { + var fontFamily:String = child.text(); + if(FigFontManager.isFontAvailable(fontFamily)) + fileSettings.setValue(SCodes.FILE_FONT, fontFamily); + } else if (child.name() == "strokeWidth") { + var width:int = int(child.text()); + if(width>=1) + fileSettings.setValue(SCodes.FILE_STROKE_WIDTH, width); + } else if (child.name() == "nodeDiameter") { + var nDiam:Number = Number(child.text()); + if(nDiam>0) + fileSettings.setValue(SCodes.FILE_NODE_DIAMETER, nDiam); + } else if (child.name() == "isetDiameter") { + var iDiam:Number = Number(child.text()); + if(iDiam>0) + fileSettings.setValue(SCodes.FILE_ISET_DIAMETER, iDiam); + } else if (child.name() == "levelDistance") { + var distance:int = int(child.text()); + if(distance>=1) + fileSettings.setValue(SCodes.FILE_LEVEL_DISTANCE, distance); + } else { + log.add(Log.ERROR_HIDDEN, "Ignoring unknown settings element: " + child); + } + } + } + + /* Parses a node with its move and iset info, adds it to the tree, + * and acts recursively by processing its children nodes and outcomes + */ + private function processNode(elem:XML, parentNode:Node):void + { + //init + var node:Node = null; + if (elem.@id != undefined) + { + var id:String = elem.@id; + node = nodes[id]; + + if (node == null) { //Creating a new node + node = tree.createNode(); + nodes[id] = node; //TODO #46 createNode(id) + } + } else { + node = tree.createNode(); + } + + //assign parent + if (parentNode == null && elem.@parent != undefined) + { + var parentId:String = elem.@parent; + + parentNode = nodes[parentId]; + + if (parentNode == null) { + parentNode = tree.createNode(); //TODO #46 createNode(parentId) + nodes[parentId] = parentNode; + } + } + if (parentNode != null) { + parentNode.addChild(node); + } else { + tree.root = node; + } + + // process iset + var isetId:String = null; + if (elem.@iset != undefined) { + isetId = elem.@iset; + } + + var iset:Iset = null; + var player:Player = (elem.@player != undefined) ? getPlayer(elem.@player) : Player.CHANCE; + if (isetId == null) { + iset = new Iset(player); + iset.idx = lastIsetIdx++; //This idx is different from isetId. It's not useful inside this class + singletons.push(iset); // root is already taken care of + } else { + //look it up in the map, if it doesn't exist create it and add it + iset = isets[isetId]; + if (iset == null) { + iset = new Iset(player); + iset.idx = lastIsetIdx++; //This idx is different from isetId. It's not useful inside this class + isets[isetId] = iset; + isetObjToId[iset] = isetId; + } else { + if (player != Player.CHANCE) { + if (iset.player != Player.CHANCE && player != iset.player) { + log.add(Log.ERROR_HIDDEN, "Warning: @player attribute conflicts with earlier iset player assignment. Ignored."); + } + while (iset.player != player) { + iset.changePlayer(tree.firstPlayer); + } + } + } + } + iset.insertNode(node); + + // set up the moves + processMove(elem, node, parentNode); + + for each (var child:XML in elem.children()) { + if (child.name() == "node") { + processNode(child, node); + } else if (child.name() == "outcome") { + processOutcome(child, node); + } else if (child.name() == "payoff") { + log.add(Log.HINT, "The tree contained interior payoffs, which aren't currently "+ + "supported in gte and therefore have been ignored."); + } else { + log.add(Log.ERROR_HIDDEN, "Ignoring unknown element: " + child); + } + } + } + + /* Parses an outcome, creates it and its wrapping node, and inserts it to its parent */ + private function processOutcome(elem:XML, parent:Node):void + { + // Create wrapping node + // get parent from dictionary... if it doesn't exist then the outcome must be the root + var wrapNode:Node = parent != null ? parent.newChild() : tree.createNode(); + if (parent == null) { + tree.root = wrapNode; + } + + // set up the moves + processMove(elem, wrapNode, parent); + + if(elem.child("*").length() == 0) + { + if(tree.numPlayers == 0) + tree.newPlayer("1"); + wrapNode.makeNonTerminal(); + + //TODO: ERROR parsing non terminal leaves: they dont draw labels + } else + { + var outcome:Outcome = wrapNode.makeTerminal(); + for each (var child:XML in elem.children()) { + if (child.name() == "payoff") { + var playerId:String = child.@player; + var payoff:Rational = Rational.parse(child.@value); + + var player:Player = players[playerId]; + if (player == null) { + player = new Player(playerId, tree); + players[playerId] = player; + } + outcome.setPay(player, payoff); + } else { + log.add(Log.ERROR_HIDDEN, "Ignoring unknown element: " + child); + } + } + } + } + + /* Parses, creates, processes and inserts a move into its node */ + private function processMove(elem:XML, node:Node, parent:Node):void + { + if (elem.@move != undefined) { + var moveId:String = elem.@move; + var moveIsetId:String = (parent != null && parent.iset != null) ? String(isetObjToId[parent.iset]) : ""; + var move:Move = moves[moveIsetId + "::" + moveId]; + if (move == null) { + move = new Move(); + move.label = moveId; + moves[moveIsetId + "::" + moveId] = move; + } + node.reachedby = move; + } else if (parent != null) { + // assume this comes from a chance node with a probability of zero + node.reachedby = new Move(); + } + + if (elem.@prob != undefined && node.reachedby != null) { + node.reachedby.prob = Rational.parse(elem.@prob); + } + } + + //TODO: there has got to be a more efficient algorithm + private function hookupAndVerifyMoves():void + { + for (var iset:Iset = tree.root.iset; iset != null; iset = iset.nextIset) { + + iset.sort(); + + //for each child of the first node, hook up move + var baseline:Node = iset.firstNode; + for (var baselineChild:Node = baseline.firstChild, childIdx:int = 0; baselineChild != null; baselineChild = baselineChild.sibling, ++childIdx) + { + iset.assignMove(baselineChild.reachedby); + + // make sure all the rest of the nodes have the child with the same move + // TODO: it does not necessarily need to be at the same index + for (var baselineMate:Node = baseline.nextInIset; baselineMate != null; baselineMate = baselineMate.nextInIset) + { + var mateChild:Node = baselineMate.firstChild; + for (var i:int = 0; i < childIdx; ++i) { + mateChild = mateChild.sibling; + } + if (mateChild.reachedby != baselineChild.reachedby) { + if (mateChild.reachedby == null) { + log.add(Log.ERROR_THROW, "node does not contain an incoming move"); + } else { + log.add(Log.ERROR_THROW, "Iset " + iset.idx + " is inconsistent for node " + baselineMate.number /*+ " at " + baselineMate.setIdx*/ + " for child " + mateChild.number + " at " + childIdx + " with move " + mateChild.reachedby); + } + } + } + } + } + } + + //Returns in a uint a color from the following hex formats: + //#RRGGBB, 0xRRGGBB, RRGGBB + private function getColorFromHexString(hex:String):uint + { + if(hex.indexOf("#")==0) + hex = hex.substr(1); + + if(hex.indexOf("0x")!=0) + hex = "0x"+hex; + + return uint(hex); + } + + //Returns a player from a String ID + private function getPlayer(playerId:String):Player + { + if (playerId == Player.CHANCE_NAME) { + return Player.CHANCE; + } + + var player:Player = players[playerId]; + if (player == null) { + player = new Player(playerId, tree); + players[playerId] = player; + } + return player; + } + } +} diff --git a/gui-builder/src/lse/math/games/builder/model/ExtensiveForm.as b/gui-builder/src/lse/math/games/builder/model/ExtensiveForm.as index de43a4b..c0348bf 100644 --- a/gui-builder/src/lse/math/games/builder/model/ExtensiveForm.as +++ b/gui-builder/src/lse/math/games/builder/model/ExtensiveForm.as @@ -1,5 +1,7 @@ package lse.math.games.builder.model { + import flash.net.FileReference; + import util.Log; /** @@ -18,6 +20,8 @@ package lse.math.games.builder.model private var _firstPlayer:Player = null; private var lastNodeNumber:int = 0; + private var lastOrderedNodeNumber:int = 0; + private var lastOrderedIsetNumber:int = 0; private var log:Log = Log.instance; @@ -42,6 +46,18 @@ package lse.math.games.builder.model return count; } + /** Number of players in the tree */ + public function get numPlayers():int { + var count:int = 0; + var player:Player = _firstPlayer; + while(player!=null) + { + count++; + player = player.nextPlayer; + } + return count; + } + /** Player who moves first in the tree */ //TODO: Explain if other relations inside player or something must change also, when set public function set firstPlayer(player:Player):void { _firstPlayer = player; } @@ -77,6 +93,39 @@ package lse.math.games.builder.model return new Node(this, number); } + /** + * Assigns nodes and isets ids in pre-order. It should be called after all + * actions that might change the tree pre-order representation. + */ + public function orderIds():void + { + //Reset all iset numbers + var h:Iset = root.iset; + while(h!=null) + { + h.idx = -1; + h = h.nextIset; + } + + lastOrderedNodeNumber = 0; + lastOrderedIsetNumber = 0; + recOrderIds(_root); + } + + //Recursively adds new idxs to nodes and isets + private function recOrderIds(node:Node):void + { + node.number = lastOrderedNodeNumber++; + if(node.iset!=null && node.iset.idx == -1) node.iset.idx = lastOrderedIsetNumber++; + + var child:Node = node.firstChild; + while(child!=null) + { + recOrderIds(child); + child = child.sibling; + } + } + /** Searchs for a node with a determinate 'number' (id). If it finds it, it returns it, else it returns null */ public function getNodeById(number:int):Node { @@ -110,8 +159,8 @@ package lse.math.games.builder.model /** * Adds an Iset to the tree, at the end of the linked list of isets - * (which doesn't directly relate to the physiscal position in the tree). - *
    If the Iset was already in the list, it does nothing + * (which doesn't directly relate to the spatial location in the tree). + *
    If the Iset was already in the list, it does nothing. * @return (int) The added isets' idx after insertion */ public function addIset(toAdd:Iset):int @@ -126,13 +175,13 @@ package lse.math.games.builder.model idx = h.idx; log.add(Log.ERROR_HIDDEN, "Pretended to add an iset equal to the one existing with idx "+idx); break; - } else if (h.nextIset == null) { + } else if (h.nextIset == null || h.nextIset.idx>toAdd.idx) { break; } h = h.nextIset; } if (idx == -1) { - h.insertAfter(toAdd); + h.insertAfter(toAdd, false); } return toAdd.idx; } @@ -251,10 +300,20 @@ package lse.math.games.builder.model } } + /** Save a TXT representation of the tree */ + public function saveTreeTXT():void + { + new FileReference().save(printTree(), "treedump.txt"); + } + + private var treeLog:String = ""; + //used for debugging - public function printTree():void + public function printTree():String { + treeLog = ""; recPrintTree(root); + return treeLog; } private function recPrintTree(x:Node):void // preorder: node, then children @@ -265,13 +324,13 @@ package lse.math.games.builder.model } var y:Node = x.firstChild; - log.add(Log.DEBUG, indent + x.toString() + ((y == null) ? " (leaf)" : "")); + treeLog += (indent + x.toString() + ((y == null) ? " (leaf)" : ("")) +"\n"); while (y != null) { recPrintTree(y); y = y.sibling; - } + } } public function toString():String diff --git a/gui-builder/src/lse/math/games/builder/model/Iset.as b/gui-builder/src/lse/math/games/builder/model/Iset.as index afb32e0..f553391 100644 --- a/gui-builder/src/lse/math/games/builder/model/Iset.as +++ b/gui-builder/src/lse/math/games/builder/model/Iset.as @@ -481,6 +481,7 @@ package lse.math.games.builder.model public function get nextIset():Iset { return _nextIset; } public function get prevIset():Iset { return _prevIset; } public function get idx():int { return _idx; } + public function set idx(value:int):void { _idx = value; } /** Creates a new iset and appends it at the end of the linked list */ public function newIset(player:Player):Iset @@ -504,9 +505,9 @@ package lse.math.games.builder.model /** * Inserts an Iset after this one in the linked list of Isets, - * updating the idx's accordingly. + * updating the idx's accordingly if modifyIdx is true */ - public function insertAfter(toAdd:Iset):void + public function insertAfter(toAdd:Iset, modifyIdx:Boolean):void { toAdd._nextIset = _nextIset; _nextIset = toAdd; @@ -517,12 +518,16 @@ package lse.math.games.builder.model toAdd._prevIset = this; - toAdd._idx = _idx + 1; - for (var h:Iset = toAdd._nextIset; h != null; h = h._nextIset) { - ++h._idx; + if(modifyIdx) + { + toAdd._idx = _idx + 1; + for (var h:Iset = toAdd._nextIset; h != null; h = h._nextIset) { + ++h._idx; + } } } + //Removes this Iset from the LinkedList internal function remove():void { diff --git a/gui-builder/src/lse/math/games/builder/model/Node.as b/gui-builder/src/lse/math/games/builder/model/Node.as index b9556d4..b99c394 100644 --- a/gui-builder/src/lse/math/games/builder/model/Node.as +++ b/gui-builder/src/lse/math/games/builder/model/Node.as @@ -60,6 +60,7 @@ package lse.math.games.builder.model /** Number (or id) of this node inside the tree */ public function get number():int { return _number; } // for specification of tree + public function set number(value:int):void { _number = value; } /** First child of this node. Can be null if there isn't any */ public function get firstChild():Node { return _firstchild; } @@ -264,7 +265,7 @@ package lse.math.games.builder.model if (_father != null) { _iset = new Iset( (_father.iset.player != Player.CHANCE && _father.iset.player.nextPlayer != null) ? _father.iset.player.nextPlayer : _tree.firstPlayer); - _father.iset.insertAfter(_iset); + _father.iset.insertAfter(_iset, true); } else { _iset = new Iset(_tree.firstPlayer); //trace("Node.makeNonTerminal(): new root iset"); diff --git a/gui-builder/src/lse/math/games/builder/presenter/ActionHandler.as b/gui-builder/src/lse/math/games/builder/presenter/TreeActionHandler.as similarity index 76% rename from gui-builder/src/lse/math/games/builder/presenter/ActionHandler.as rename to gui-builder/src/lse/math/games/builder/presenter/TreeActionHandler.as index ab4de0e..d478f30 100644 --- a/gui-builder/src/lse/math/games/builder/presenter/ActionHandler.as +++ b/gui-builder/src/lse/math/games/builder/presenter/TreeActionHandler.as @@ -1,164 +1,169 @@ -package lse.math.games.builder.presenter -{ - import flash.utils.getTimer; - - import lse.math.games.builder.io.ExtensiveFormXMLReader; - import lse.math.games.builder.io.ExtensiveFormXMLWriter; - import lse.math.games.builder.io.FileManager; - import lse.math.games.builder.viewmodel.DepthAdjuster; - import lse.math.games.builder.viewmodel.TreeGrid; - - import util.Log; - - /** - * Class that handles Action executing, as well as undoing and redoing, and reseting the workspace either - * to the default (just root) tree, or to one loaded via an xml container.

    - * - * @author Mark - */ - public class ActionHandler - { - //TODO: SETTING - private const MINIMUM_BUFFER_SIZE:int = 10; //Number of actions that will be undoable, as a minimum - private const MAXIMUM_TIME_UNDO:int = 1000; //Maximum milliseconds that the 'undo' command should take - - private var _undone:Vector. = new Vector.(); - private var _history:Vector. = new Vector.(); - private var _xml:XML = null; //TODO: store as an actual TreeGrid obj? It would imply making it and its properties clonable - //however, the time lost in creating and accesing the _xml is small, lower than 20ms, so I think it is not worth changing - private var _fileManager:FileManager; - - private var log:util.Log = Log.instance; - - - - public function ActionHandler(fileManager:FileManager) { - _fileManager = fileManager; - } - - /** Executes action, and stores it in 'history' vector, if it changes Data */ - public function processAction(action:IAction, grid:TreeGrid):void - { - if (action != null) { - if (action.changesData) { // TODO: else add it to a pending list of actions todo - - while(_undone.length>0) _undone.pop(); //Empty the 'undone' queue - action.doAction(grid); - _fileManager.unsavedChanges = true; - manageBuffer(action, grid); - } else - action.doAction(grid); - } - } - - /* - * Pushes the action into the buffer of done actions. - * If the list of actions exceeds in processing time MAXIMUM_TIME_UNDO - * and in length MINIMUM_BUFFER_SIDE, it deletes the last stored action, - * and updates with it the recovery _xml. - */ - private function manageBuffer(action:IAction, grid:TreeGrid):void - { - _history.push(action); - - var totalTimeInBuffer:int = 0; - var i:int = _history.length; - while(--i) totalTimeInBuffer += _history[i].timeElapsed; - - if(totalTimeInBuffer > MAXIMUM_TIME_UNDO && _history.length > MINIMUM_BUFFER_SIZE) - { - var recGrid:TreeGrid = new TreeGrid(); - initTree(recGrid); //Create a treegrid from the recovery xml - - _history.shift().doAction(recGrid); //Update the recovery grid while - //erasing the last action - - var xmlWriter:ExtensiveFormXMLWriter = new ExtensiveFormXMLWriter(); - _xml = xmlWriter.write(recGrid); //Store the grid into xml again - } - } - - /** - * Undoes last action by resetting the tree and reapplying all actions that had been done. - * @return True if there was operation to undo, False if not - */ - public function undo(grid:TreeGrid):Boolean - { - if (_history.length > 0) { - initTree(grid); - _undone.push(_history.pop()); - - var redoStack:Vector. = new Vector.(); - while (_history.length > 0) { - redoStack.push(_history.pop()); - } - - while (redoStack.length > 0) - { - var todo:IAction = redoStack.pop(); - todo.doAction(grid); - _history.push(todo); - } - - _fileManager.unsavedChanges = true; - - return true; - } else { - return false; - } - } - - /** - * Repeats last undone Action. - * @return True if there was operation to redo, False if not - */ - public function redo(grid:TreeGrid):Boolean - { - if (_undone.length != 0) - { - var todo:IAction = _undone.pop(); - todo.doAction(grid); - _history.push(todo); - - _fileManager.unsavedChanges = true; - - return true; - } else { - return false; - } - } - - //Restores tree from a xml file or creates a default one (just the root) - private function initTree(grid:TreeGrid):void - { - if (_xml != null) { - var reader:ExtensiveFormXMLReader = new ExtensiveFormXMLReader(_xml); - reader.load(grid); - - var depthAdjuster:IAction = new DepthAdjuster(); - depthAdjuster.doAction(grid); - } else { - grid.clearTree(); - grid.defaultTree(); - } - } - - /** Loads a tree from a xml file, and resets history and undone data */ - public function load(xml:XML, grid:TreeGrid):void - { - _xml = xml; - initTree(grid); - _undone = new Vector.(); - _history = new Vector.(); - } - - /** Creates the default tree (just the root) and resets history and undone data */ - public function reset(grid:TreeGrid):void - { - _xml = null; - initTree(grid); - _undone = new Vector.(); - _history = new Vector.(); - } - } +package lse.math.games.builder.presenter +{ + import flash.utils.getTimer; + + import lse.math.games.builder.io.XMLExporter; + import lse.math.games.builder.io.FileManager; + import lse.math.games.builder.io.XMLImporter; + import lse.math.games.builder.settings.SCodes; + import lse.math.games.builder.settings.Settings; + import lse.math.games.builder.settings.UserSettings; + import lse.math.games.builder.viewmodel.DepthAdjuster; + import lse.math.games.builder.viewmodel.TreeGrid; + + import util.Log; + + //TODO #33: Do an equivalent class for matrixes and matrix actions + + /** + * Class that handles Action executing, as well as undoing and redoing, and reseting the workspace either + * to the default (just root) tree, or to one loaded via an xml container.

    + * + * @author Mark + */ + public class TreeActionHandler + { + private const MAXIMUM_TIME_UNDO:int = 1000; //When the undo buffer actions sum up more than these ms, + //it starts trimming, always keeping at least the + //MINIMUM_BUFFER_SIZE in settings. + + private var _undone:Vector. = new Vector.(); + private var _history:Vector. = new Vector.(); + private var _xml:XML = null; + private var _fileManager:FileManager; + private var settings:UserSettings = UserSettings.instance; + private var log:util.Log = Log.instance; + + + + public function TreeActionHandler(fileManager:FileManager) { + _fileManager = fileManager; + } + + /** Executes action, and stores it in 'history' vector, if it changes Data */ + public function processAction(action:IAction, grid:TreeGrid):void + { + if (action != null) { + if (action.changesData) { // TODO: else add it to a pending list of actions todo + + while(_undone.length>0) _undone.pop(); //Empty the 'undone' queue + action.doAction(grid); + _fileManager.unsavedChanges = true; + manageBuffer(action, grid); + } else + action.doAction(grid); + } + } + + /* + * Pushes the action into the buffer of done actions. + * If the list of actions exceeds in processing time MAXIMUM_TIME_UNDO + * and in length MINIMUM_BUFFER_SIDE, it deletes the last stored action, + * and updates with it the recovery _xml. + */ + private function manageBuffer(action:IAction, grid:TreeGrid):void + { + _history.push(action); + + var totalTimeInBuffer:int = 0; + var i:int = _history.length; + while(--i) totalTimeInBuffer += _history[i].timeElapsed; + + if(totalTimeInBuffer > MAXIMUM_TIME_UNDO + && _history.length > (settings.getValue(SCodes.MINIMUM_BUFFER_SIZE) as int)) + { + var recGrid:TreeGrid = new TreeGrid(); + initTree(recGrid); //Create a treegrid from the recovery xml + + _history.shift().doAction(recGrid); //Update the recovery grid while + //erasing the last action + + var xmlWriter:XMLExporter = new XMLExporter(); + _xml = xmlWriter.writeTree(recGrid); //Store the grid into xml again + } + } + + /** + * Undoes last action by resetting the tree and reapplying all actions that had been done. + * @return True if there was operation to undo, False if not + */ + public function undo(grid:TreeGrid):Boolean + { + if (_history.length > 0) { + initTree(grid); + _undone.push(_history.pop()); + + var redoStack:Vector. = new Vector.(); + while (_history.length > 0) { + redoStack.push(_history.pop()); + } + + while (redoStack.length > 0) + { + var todo:IAction = redoStack.pop(); + todo.doAction(grid); + _history.push(todo); + } + + _fileManager.unsavedChanges = true; + + return true; + } else { + return false; + } + } + + /** + * Repeats last undone Action. + * @return True if there was operation to redo, False if not + */ + public function redo(grid:TreeGrid):Boolean + { + if (_undone.length != 0) + { + var todo:IAction = _undone.pop(); + todo.doAction(grid); + _history.push(todo); + + _fileManager.unsavedChanges = true; + + return true; + } else { + return false; + } + } + + //Restores tree from a xml file or creates a default one (just the root) + private function initTree(grid:TreeGrid):void + { + if (_xml != null) { + var reader:XMLImporter = new XMLImporter(_xml); + reader.loadTree(grid); + + var depthAdjuster:IAction = new DepthAdjuster(); + depthAdjuster.doAction(grid); + } else { + grid.clearTree(); + grid.defaultTree(); + } + } + + /** Loads a tree from a xml file, and resets history and undone data */ + public function load(xml:XML, grid:TreeGrid):void + { + _xml = xml; + initTree(grid); + _undone = new Vector.(); + _history = new Vector.(); + } + + /** Creates the default tree (just the root) and resets history and undone data */ + public function reset(grid:TreeGrid):void + { + _xml = null; + initTree(grid); + _undone = new Vector.(); + _history = new Vector.(); + } + } } \ No newline at end of file diff --git a/gui-builder/src/lse/math/games/builder/presenter/TreeGridPresenter.as b/gui-builder/src/lse/math/games/builder/presenter/TreeGridPresenter.as index 0b3ccd7..4465a90 100644 --- a/gui-builder/src/lse/math/games/builder/presenter/TreeGridPresenter.as +++ b/gui-builder/src/lse/math/games/builder/presenter/TreeGridPresenter.as @@ -20,7 +20,7 @@ package lse.math.games.builder.presenter import lse.math.games.builder.fig.FigFontManager; import lse.math.games.builder.fig.TreeGridFigWriter; - import lse.math.games.builder.io.ExtensiveFormXMLWriter; + import lse.math.games.builder.io.XMLExporter; import lse.math.games.builder.io.FileManager; import lse.math.games.builder.model.Iset; import lse.math.games.builder.model.Node; @@ -28,6 +28,7 @@ package lse.math.games.builder.presenter import lse.math.games.builder.model.Player; import lse.math.games.builder.model.Rational; import lse.math.games.builder.model.Strategy; + import lse.math.games.builder.settings.FileSettings; import lse.math.games.builder.view.Canvas; import lse.math.games.builder.view.MouseScroller; import lse.math.games.builder.viewmodel.TreeGrid; @@ -44,11 +45,13 @@ package lse.math.games.builder.presenter /** * @author Mark Egesdal */ + + //TODO: #33 Presenter & MatrixGridPresenter public class TreeGridPresenter { private var _canvas:Canvas; private var _fileManager:FileManager; - private var _actionHandler:ActionHandler; + private var _treeActionHandler:TreeActionHandler; private var _grid:TreeGrid; private var _gridData:ArrayCollection = new ArrayCollection(); @@ -57,8 +60,7 @@ package lse.math.games.builder.presenter private var log:Log = Log.instance; -// private var _log:Log = Log.instance; -// private var _lastLogMessage:String = _log.lastMessage; + //private var _modelDirty:Boolean = true; @@ -66,16 +68,9 @@ package lse.math.games.builder.presenter { _gridData.addEventListener("collectionChange", onOutcomeEdit); _fileManager = new FileManager(this); - _actionHandler = new ActionHandler(_fileManager); + _treeActionHandler = new TreeActionHandler(_fileManager); } -// //TODO: This doesn't update the gui -// public function get lastLogMessage():String -// { -// _lastLogMessage = _log.lastMessage; -// return _lastLogMessage; -// } - public function set grid(value:TreeGrid):void { _grid = value; } @@ -393,7 +388,7 @@ package lse.math.games.builder.presenter { var action:IAction = getAction(_grid); if (action != null) { - _actionHandler.processAction(action, _grid); + _treeActionHandler.processAction(action, _grid); invalidate(action.changesData, action.changesSize, action.changesDisplay); } } @@ -410,7 +405,7 @@ package lse.math.games.builder.presenter selectedNode = -1; var action:IAction = getClickAction(_grid, x, y); if (action != null) { - _actionHandler.processAction(action, _grid); + _treeActionHandler.processAction(action, _grid); invalidate(action.changesData, action.changesSize, action.changesDisplay); } } @@ -418,7 +413,7 @@ package lse.math.games.builder.presenter public function undo():void { - if (_actionHandler.undo(_grid)) { + if (_treeActionHandler.undo(_grid)) { invalidate(true, true, true); } else { log.add(Log.HINT, "No more operations to undo"); @@ -427,7 +422,7 @@ package lse.math.games.builder.presenter public function redo():void { - if (_actionHandler.redo(_grid)) { + if (_treeActionHandler.redo(_grid)) { invalidate(true, true, true); } else { log.add(Log.HINT, "No more operations to redo"); @@ -450,7 +445,7 @@ package lse.math.games.builder.presenter } } if (chain != null) { - _actionHandler.processAction(chain, _grid); + _treeActionHandler.processAction(chain, _grid); invalidate(chain.changesData, chain.changesSize, chain.changesDisplay); } } @@ -497,7 +492,7 @@ package lse.math.games.builder.presenter public function clear():void { resetZoom(); - _actionHandler.reset(_grid); + _treeActionHandler.reset(_grid); fileManager.clear(); invalidate(true, true, true); } @@ -545,22 +540,31 @@ package lse.math.games.builder.presenter * Returns a XML file with the current tree packaged in it */ public function saveCurrentTreeToXML():XML { - var xmlWriter:ExtensiveFormXMLWriter = new ExtensiveFormXMLWriter(); - return xmlWriter.write(_grid); + var xmlWriter:XMLExporter = new XMLExporter(); + return xmlWriter.writeTree(_grid); } /** - * Loads a tree from xml, deleting any previous states + * Loads a tree/matrix from xml, deleting any previous states */ - public function loadTreeFromXML(xml:XML):void + public function loadFromXML(xml:XML):void { - _actionHandler.load(xml, _grid); - isZeroSum = false; + //TODO #33 check what type of data is inside the xml + loadTreeFromXML(xml); + invalidate(true, true, true); } + private function loadTreeFromXML(xml:XML):void + { + _treeActionHandler.load(xml, _grid); + isZeroSum = false; + } + + //TODO #33 loadMatrixFromXML + /** - * Opens a dialog for selecting and opening a tree in xml format + * Opens a dialog for selecting and opening a file in xml format */ public function open():void { @@ -569,6 +573,8 @@ package lse.math.games.builder.presenter // URL Request Handler below here... public function runAlgorithm(algo:Object, seed:String):void + //TODO #33: Although the functionality should be the same, depending on the nf or ef mode, + //the data for running the algo might need to be generated { if (algo == null || algo.service == null || algo.service == undefined || algo.url == null || algo.url == undefined) { @@ -603,8 +609,8 @@ package lse.math.games.builder.presenter private function getTreeParam():String { - var xmlWriter:ExtensiveFormXMLWriter = new ExtensiveFormXMLWriter(); - var treeXML:XML = xmlWriter.write(_grid); + var xmlWriter:XMLExporter = new XMLExporter(); + var treeXML:XML = xmlWriter.writeTree(_grid); XML.prettyPrinting = false; var value:String = treeXML.toXMLString(); XML.prettyPrinting = true; diff --git a/gui-builder/src/lse/math/games/builder/settings/FileSettings.as b/gui-builder/src/lse/math/games/builder/settings/FileSettings.as new file mode 100644 index 0000000..86a473e --- /dev/null +++ b/gui-builder/src/lse/math/games/builder/settings/FileSettings.as @@ -0,0 +1,123 @@ +package lse.math.games.builder.settings +{ + import flash.errors.IllegalOperationError; + + import mx.controls.Alert; + + /** + * This class stores the file in use's settings.

    + * + * It is a singleton, and instead of calling the constructor directly, you + * should use FileSettings.instance.

    + * + * Instructions: + *

      + *
    • Check that a setting is stored using hasValue.
    • + *
    • Store and access run-time values of the settings using getValue and setValue functions.
    • + *
    • Delete all settings with clear() everytime you close a file (or open a new one)
    • + *
    • Set these settings as the default ones with setAsDefault()
    • + *

    + * + * @see SCodes SCodes - For reference on the actual Settings that there exist + *
    @see Settings Settings - for the graphic settings editor, and how to add new settings
    + * + * @author alfongj + */ + public class FileSettings + { + private static var _instance:FileSettings = null; + + private var data:Object; + + /** + * DO NOT CALL THE CONSTRUCTOR DIRECTLY, INSTEAD USE FileSettings.instance + *
    This acts like a private constructor + */ + public function FileSettings( lock:Class ){ + if( lock != FileSettingsSingletonLock ){ + throw new IllegalOperationError( "Settings is a Singleton, please use the static instance method instead." ); + } else { + clear(); + } + } + + /** + * The Singleton instance of the Settings to use when accessing properties and methods of the Settings. + */ + public static function get instance():FileSettings + { + if(_instance == null) + { + _instance = new FileSettings(FileSettingsSingletonLock); + } + + return _instance; + } + + + /** Returns true if it contains value for a specified key */ + public function hasValue( key:Object ):Boolean { + return data.hasOwnProperty(key); + } + + /** + * Retrieves a value in the settings by the specified key.

    + * + * @param key (Object) the key that the desired setting data was saved under + * @return (Object) returns the data saved by key + */ + public function getValue( key:Object ):Object { + return data[key]; + } + + /** + * Saves a value into the settings under the specified key.

    + * + * @param key (String) the key under which to save the value + * @param value (Object) the value to be saved + */ + public function setValue( key:String, value:Object ):void { + data[key] = value; + } + + + + /** Clears the settings. Should be called when a new file is opened */ + public function clear():void + { + data = new Object(); + loadDefaults(); + } + + /* Loads all stored values from user's local 'flash cookies' */ + private function loadDefaults():void + { + var defSettings:UserSettings = UserSettings.instance; + + defSettings.getValue("dummy"); //Run this in case defSettings are not loaded + + for(var key:String in defSettings.data) + { + if(key.indexOf("DEFAULT")==0) + { + var newKey:String = "FILE"+key.substr(7); + data[newKey] = defSettings.data[key]; + } + } + } + + /** Sets the current file settings as the default ones */ + public function setAsDefault():void + { + var defSettings:UserSettings = UserSettings.instance; + for(var key:String in data) + { + var defKey:String = "DEFAULT"+key.substr(4); + defSettings.setValue( defKey, data[key] ); + } + } + } +} + +//Dummy private class for making the constructor artificially private +class FileSettingsSingletonLock {} \ No newline at end of file diff --git a/gui-builder/src/lse/math/games/builder/settings/SCodes.as b/gui-builder/src/lse/math/games/builder/settings/SCodes.as index bf6c9e7..e3b76a0 100644 --- a/gui-builder/src/lse/math/games/builder/settings/SCodes.as +++ b/gui-builder/src/lse/math/games/builder/settings/SCodes.as @@ -3,10 +3,10 @@ package lse.math.games.builder.settings /** * Enum containing each Setting code, with a brief description of what the associated content should be and mean. * - *

    When adding a new setting, its key inside UserSettings.as and Settings.mxml + *

    When adding a new setting, its key inside UserSettings.as / FileSetting.as and Settings.mxml * must be the same and taken from here, for reference purposes * - *

    You can call defaultSettings() to load a default value for each setting + *

    You can call defaultSettings() to load a default value for each setting (except from File Settings) */ public final class SCodes { @@ -16,37 +16,106 @@ package lse.math.games.builder.settings *

  • false: Settings are discarded once the user closes the browser window
  • *

    Default: false */ - public static var STORE_SETTINGS_LOCALLY:String = "STORE_SETTINGS_LOCALLY"; + public static const STORE_SETTINGS_LOCALLY:String = "STORE_SETTINGS_LOCALLY"; /** *

  • true: The output after running an algo, instead of in an external pop-up, will be shown in an internal flash one
  • *
  • false: The output will be shown in a browser pop-up
  • *

    Default: false */ - public static var DISPLAY_OUTPUT_INTERNALLY:String = "DISPLAY_OUTPUT_INTERNALLY"; + public static const DISPLAY_OUTPUT_INTERNALLY:String = "DISPLAY_OUTPUT_INTERNALLY"; - /* <--- GRAPHIC SETTINGS ---> */ + /** + * int containing the number of undoable actions, at least, that the user would like to keep + *

    Default: 10 + */ + public static const MINIMUM_BUFFER_SIZE:String = "MINIMUM_BUFFER_SIZE"; + + + + /* <--- DEFAULT GRAPHIC SETTINGS (applied to trees with no graphic information) ---> */ /** * uint with player 1's color *

    Default: Red */ - public static var PLAYER_1_COLOR:String = "PLAYER_1_COLOR"; + public static const DEFAULT_PLAYER_1_COLOR:String = "DEFAULT_PLAYER_1_COLOR"; /** * uint with player 2's color *

    Default: Blue */ - public static var PLAYER_2_COLOR:String = "PLAYER_2_COLOR"; + public static const DEFAULT_PLAYER_2_COLOR:String = "DEFAULT_PLAYER_2_COLOR"; /** * String with the default font family to be used *

    Default: Times */ - public static var DEFAULT_FONT:String = "DEFAULT_FONT"; - //It is called DEFAULT because in the future there might be also more specific ones, such as payoff_font or player_font - //and then this one will be the used in whatever's not got a specific one + public static const DEFAULT_FONT:String = "DEFAULT_FONT"; + + /** + * Number representing the width of the strokes of lines for drawing isets and moves + *

    Default: 1.0 + */ + public static const DEFAULT_STROKE_WIDTH:String = "DEFAULT_STROKE_WIDTH"; + + /** + * int representing the diameter of nodes + *

    Default: 7 + */ + public static const DEFAULT_NODE_DIAMETER:String = "DEFAULT_NODE_DIAMETER"; + + /** + * Number representing the diameter of isets + *

    Default: 25 + */ + public static const DEFAULT_ISET_DIAMETER:String = "DEFAULT_ISET_DIAMETER"; + + /** + * int representing the distance between two consecutive node levels + *

    Default: 75 + */ + public static const DEFAULT_LEVEL_DISTANCE:String = "DEFAULT_LEVEL_DISTANCE"; + + + + /* <--- FILE GRAPHIC SETTINGS (settings not stored in the users PC but on XML files. Loaded with FileSettings) ---> */ + //NOTE: All of these must have their "DEFAULT_" counterpart, else FileSettings.setAsDefault() will need to be modified + /** uint with player 1's color */ + public static const FILE_PLAYER_1_COLOR:String = "FILE_PLAYER_1_COLOR"; + + /** uint with player 2's color */ + public static const FILE_PLAYER_2_COLOR:String = "FILE_PLAYER_2_COLOR"; + + /** String with the FILE font family to be used */ + public static const FILE_FONT:String = "FILE_FONT"; + + /** int representing the width of the strokes of lines for drawing isets and moves */ + public static const FILE_STROKE_WIDTH:String = "FILE_STROKE_WIDTH"; + + /** Number representing the diameter of nodes */ + public static const FILE_NODE_DIAMETER:String = "FILE_NODE_DIAMETER"; + + /** Number representing the diameter of isets */ + public static const FILE_ISET_DIAMETER:String = "FILE_ISET_DIAMETER"; + + /** int representing the distance between two consecutive node levels */ + public static const FILE_LEVEL_DISTANCE:String = "FILE_LEVEL_DISTANCE"; + + + + + /* <--- OTHER SETTINGS (Not shown under the Settings panel) ---> */ + + /** + * Boolean with the current expand status of the webcontainer of the GUI + *

    Default: False + */ + public static const EXPANDED:String = "EXPANDED"; + + /* <--- DEFAULT SETTINGS ---> */ + /** Creates a default entry for each setting that a UserSetting object should have*/ public static function defaultSettings():void { @@ -55,12 +124,17 @@ package lse.math.games.builder.settings //GENERAL SETTINGS settings.setValue(STORE_SETTINGS_LOCALLY, false); settings.setValue(DISPLAY_OUTPUT_INTERNALLY, false); + settings.setValue(MINIMUM_BUFFER_SIZE, 10); //GRAPHIC SETTINGS - settings.setValue(PLAYER_1_COLOR, 0xFF0000); - settings.setValue(PLAYER_2_COLOR, 0x0000FF); + settings.setValue(DEFAULT_PLAYER_1_COLOR, 0xFF0000); + settings.setValue(DEFAULT_PLAYER_2_COLOR, 0x0000FF); settings.setValue(DEFAULT_FONT, "Times"); - - + settings.setValue(DEFAULT_STROKE_WIDTH, 1); + settings.setValue(DEFAULT_NODE_DIAMETER, new Number(7)); + settings.setValue(DEFAULT_ISET_DIAMETER, new Number(25)); + settings.setValue(DEFAULT_LEVEL_DISTANCE, 75); + //OTHER SETTINGS + settings.setValue(EXPANDED, false); } } } \ No newline at end of file diff --git a/gui-builder/src/lse/math/games/builder/settings/Settings.mxml b/gui-builder/src/lse/math/games/builder/settings/Settings.mxml index 852d72a..b7b97bd 100644 --- a/gui-builder/src/lse/math/games/builder/settings/Settings.mxml +++ b/gui-builder/src/lse/math/games/builder/settings/Settings.mxml @@ -25,7 +25,7 @@ *

    • Set the id of the color picker/toggle/textinput... to the same as the identifier that you defined earlier
    • *
    • Set the 'creationComplete' hanler to get the property of the item (e.g., 'selectedColor', 'selected', etc) * with settings.getValue(SCodes.THE_ID_YOU_CREATED) as WhateverTypeItShouldBe
    • - *
    • Set the 'change' behaviour of your setting, which will normally just be setSetting(SCodes.THE_ID_YOU_CREATED, + *
    • Set the 'change' behaviour of your setting, which will normally just be setGlobalSetting/setFileSetting(SCodes.THE_ID_YOU_CREATED, * THE_ID_YOU_CREATED.valueYouReInterestedIn)
    * *
  • Finally, don't forget to use your settings value wherever you need it with @@ -40,7 +40,8 @@ private var unsavedChanges:Boolean = false; - private var settings:UserSettings = UserSettings.instance; + private var glbSettings:UserSettings = UserSettings.instance; + private var fileSettings:FileSettings = FileSettings.instance; private var _controller:TreeGridPresenter; private var log:Log = Log.instance; @@ -49,35 +50,56 @@ public function set controller(value:TreeGridPresenter):void { _controller = value; } - //Sets a setting - private function setSetting(key:String, value:Object):void + //Sets a global setting + private function setGlobalSetting(key:String, value:Object):void { if(!unsavedChanges) { - if(settings.getValue(key) == null) + if(glbSettings.getValue(key) == null) unsavedChanges = true; - else if(settings.getValue(key) != value) + else if(glbSettings.getValue(key) != value) unsavedChanges = true; } - settings.setValue(key, value); + glbSettings.setValue(key, value); + + //invalidate display + if(_controller != null) + _controller.invalidate(true, true, true); + else + log.add(Log.ERROR_HIDDEN, "No controller defined ", "Settings"); + } + + //Sets a file setting + private function setFileSetting(key:String, value:Object):void + { + fileSettings.setValue(key, value); + + //invalidate display + if(_controller != null) + _controller.invalidate(true, true, true); + else + log.add(Log.ERROR_HIDDEN, "No controller defined ", "Settings"); } - + + //Sets the file settings as default and closes + private function setFileSettingsAsDefaultAndExit():void + { + fileSettings.setAsDefault(); + if(_controller != null) + _controller.invalidate(true, true, true); + else + log.add(Log.ERROR_HIDDEN, "No controller defined ", "Settings"); + + unsavedChanges = true; + exit(); + } + //Saves settings if necessary, and closes the settings panel private function exit():void { - if(unsavedChanges) - { - //invalidate display - if(_controller != null) - _controller.invalidate(true, true, true); - else - log.add(Log.ERROR_HIDDEN, "No controller defined ", "Settings"); - - //Store settings locally if the preference is activated - if(settings.getValue(SCodes.STORE_SETTINGS_LOCALLY) as Boolean && settings.cookiesStorable && !settings.saveCookies()) - throw new Error("Couldn't store the cookies, please retry"); - } + if(unsavedChanges) + glbSettings.saveCookiesIfPossible(); PopUpManager.removePopUp(this); } @@ -95,68 +117,83 @@ - + + + + + + + - - + + - - + - + + change="setFileSetting(SCodes.FILE_PLAYER_2_COLOR,event.target.selectedColor);"/> - + change="setFileSetting(SCodes.FILE_FONT, FILE_FONT.selectedItem);"> - + @@ -169,18 +206,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/gui-builder/src/lse/math/games/builder/settings/UserSettings.as b/gui-builder/src/lse/math/games/builder/settings/UserSettings.as index 31e4a71..8788e13 100644 --- a/gui-builder/src/lse/math/games/builder/settings/UserSettings.as +++ b/gui-builder/src/lse/math/games/builder/settings/UserSettings.as @@ -5,7 +5,7 @@ package lse.math.games.builder.settings import flash.system.Security; import util.Log; - + /** * This class stores user settings, and can dump them to a local 'cookie-like' * shared object, if the user allows local flash storage.

    @@ -32,7 +32,7 @@ package lse.math.games.builder.settings private const FILENAME:String = "settings"; - private var data:Object; + private var _data:Object; private var _settings_RSO:SharedObject = null; private var _loaded:Boolean = false; @@ -49,7 +49,7 @@ package lse.math.games.builder.settings throw new IllegalOperationError( "Settings is a Singleton, please use the static instance method instead." ); } else { _settings_RSO = SharedObject.getLocal( FILENAME, "/" ); - data = new Object(); + _data = new Object(); } } @@ -66,11 +66,8 @@ package lse.math.games.builder.settings return _instance; } - /** If the settings have been loaded from local storage */ - public function get loaded():Boolean - { - return _loaded; - } + /* Returns the data array, necessary for loading defaults in FileSettings */ + internal function get data():Object {return _data;} /** * Retrieves a value in the settings by the specified key.

    @@ -81,7 +78,7 @@ package lse.math.games.builder.settings public function getValue( key:Object ):Object { if(!_loaded) firstLoad(); - return data[key]; + return _data[key]; } /** @@ -91,7 +88,9 @@ package lse.math.games.builder.settings * @param value (Object) the value to be saved */ public function setValue( key:String, value:Object ):void { - data[key] = value; + if(!_loaded) + firstLoad(); + _data[key] = value; } /** @@ -122,10 +121,26 @@ package lse.math.games.builder.settings { if(_loaded) log.add(Log.ERROR_HIDDEN, "Warning: Non-saved settings are potentially being overwriting "); + + var prevLoadedStatus:Boolean = _loaded; + + _loaded = true; //this should be here, else we enter an infinite loop SCodes.defaultSettings(); + _loaded = prevLoadedStatus; //Now we restore to the previous status loadCookies(); } + /* Loads all stored values from user's local 'flash cookies' */ + private function loadCookies():void + { + for(var key:String in _settings_RSO.data) + { + _data[key] = _settings_RSO.data[key]; + } + + _loaded = true; + } + /** * Checks if user allows 'cookies' storage, and if there is space available.
    * If there isn't, it prompts the user to accept them @@ -138,28 +153,34 @@ package lse.math.games.builder.settings Security.showSettings(); } - /** Loads all stored values from user's local 'flash cookies' */ - public function loadCookies():void + /** + * Saves cookies if the preference is activated, and if cookies are storable. + * @return True if they are saved correctly. + */ + public function saveCookiesIfPossible():Boolean { - for(var key:String in _settings_RSO.data) + var saved:Boolean = false; + if(getValue(SCodes.STORE_SETTINGS_LOCALLY) as Boolean && cookiesStorable) { - data[key] = _settings_RSO.data[key]; + saved = saveCookies(); + if(!saved) + log.add(Log.ERROR, "Couldn't store the cookies for an unknown error, please retry"); } - _loaded = true; + return saved; } - /** + /* * Saves all stored values onto user's local 'flash cookies'. * * @return (Boolean) true if the values were successfully saved */ - public function saveCookies():Boolean + private function saveCookies():Boolean { - for(var key:String in data) + for(var key:String in _data) { - _settings_RSO.setProperty( key, data[key] ); - if(_settings_RSO.data[key] != data[key]) + _settings_RSO.setProperty( key, _data[key] ); + if(_settings_RSO.data[key] != _data[key]) return false; } diff --git a/gui-builder/src/lse/math/games/builder/view/Main.mxml b/gui-builder/src/lse/math/games/builder/view/Main.mxml index eab520f..859ffdf 100644 --- a/gui-builder/src/lse/math/games/builder/view/Main.mxml +++ b/gui-builder/src/lse/math/games/builder/view/Main.mxml @@ -37,7 +37,6 @@ public var xfalgos:Array = new Array(); //TODO: If possible, move the folowing two to the - [Bindable] private var log:Log = Log.instance; [Bindable] @@ -45,10 +44,36 @@ private var fileManager:FileManager; private var eventManager:EventManager = EventManager.instance; - private var settings:UserSettings = UserSettings.instance; + private var settings:UserSettings = UserSettings.instance; - + + /* <--- --- TOOLBARS-RELATED FUNCTIONS --- ---> */ + + //Executes the clear() function from the TreeGridController, epending on the button pressed as a result of the prompt + private function clearDependingOnPromptResult():void + { + if(PromptTwoButtons.buttonPressed == PromptTwoButtons.OK) + controller.clear(); + } + + //Pops up the -internal or external- output window + private function showOutput(text:String):void { + if (ExternalInterface.available && !settings.getValue(SCodes.DISPLAY_OUTPUT_INTERNALLY) as Boolean) { + ExternalInterface.call('writeSolution', text); + log.add(Log.HINT, "You don't see the output? Then you must allow popups " + + "in your browser. Don't forget to save your changes before. Or you could " + + "activate the 'Display output internally' setting in the Settings panel."); + } + else { + var o:OutputWindow = new OutputWindow(); + PopUpManager.addPopUp(o, this, false); + o.setText(text); + PopUpManager.centerPopUp(o); + } + } + + //Populates from the external html container's params the list of algos private function initVars():void { // FOR TESTING ONLY... comment this and uncomment one below before building for prod @@ -88,43 +113,34 @@ } } - // Handles click on the canvas - private function handleClickOnCanvas():void - { - if(opModeBar2.selectedIndex <= 7) - controller.doActionAt(canvas.mouseX, canvas.mouseY); - else - treePainter.selectAndEdit(controller, canvas.mouseX, canvas.mouseY); - } - - - - /* <--- --- TOOLBARS-RELATED FUNCTIONS --- ---> */ - //Pops up the settings edition window private function showSettings():void { var s:Settings = new Settings(); s.controller = controller; - PopUpManager.addPopUp(s, this, true); + PopUpManager.addPopUp(s, this, false); PopUpManager.centerPopUp(s); } - //Pops up the -internal or external- output window - private function showOutput(text:String):void { - if (ExternalInterface.available && !settings.getValue(SCodes.DISPLAY_OUTPUT_INTERNALLY) as Boolean) { - ExternalInterface.call('writeSolution', text); - log.add(Log.HINT, "You don't see the output? Then you must allow popups " + - "in your browser. Don't forget to save your changes before. Or you could " + - "activate the 'Display output internally' setting in the Settings panel."); - } - else { - var o:OutputWindow = new OutputWindow(); - PopUpManager.addPopUp(o, this, false); - PopUpManager.centerPopUp(o); - o.height = canvas.height-50; - o.setText(text); - } + [Bindable] + private var expanded:Boolean = false; + + //Checks the expand setting, and updates the 'expanded' var & the webcontainer accordingly + private function updateExpandStatus():void + { + expanded = settings.getValue(SCodes.EXPANDED) as Boolean; + ExternalInterface.call("expand", expanded); + updateLogLineDimensions(); + } + + //Should be called from the expand button, and changes the expand status, updating everything + private function switchExpandStatus():void + { + expanded = !expanded; + settings.setValue(SCodes.EXPANDED, expanded); + settings.saveCookiesIfPossible(); + ExternalInterface.call("expand", expanded); + updateLogLineDimensions(); } //Return the action corresponding to the button pressed in opModeBar2 @@ -169,11 +185,17 @@ } } - //Executes the clear() function from the TreeGridController, epending on the button pressed as a result of the prompt - private function clearDependingOnPromptResult():void + + + /* <--- --- CANVAS-RELATED FUNCTIONS --- ---> */ + + // Handles click on the canvas + private function handleClickOnCanvas():void { - if(PromptTwoButtons.buttonPressed == PromptTwoButtons.OK) - controller.clear(); + if(opModeBar2.selectedIndex <= 7) + controller.doActionAt(canvas.mouseX, canvas.mouseY); + else + treePainter.selectAndEdit(controller, canvas.mouseX, canvas.mouseY); } @@ -188,22 +210,35 @@ private var lastLogLineTimeoutId:int = -1; - //Updates the LogLine with the text from + //Updates the LogLine with the text from a HINT event private function updateLogLine(evt:TextEvent):void { logLine.text = evt.text; if(lastLogLineTimeoutId != -1) clearTimeout(lastLogLineTimeoutId); - var optimalMsForReading:int = Math.max(3000, 1000+evt.text.length*67); + var optimalMsForReading:int = Math.max(3000, evt.text.length*67); //These ms are calculated from average lowest reading speed in english (200 wpm) - //and average of letters per word in english (4.5), plus two extra sources of extra - //time: counting the non-letter characters as letters, and giving one sec at the start for noticing - //there is something written. + //and average of letters per word in english (4.5), plus a source of extra + //time: counting the non-letter characters as letters lastLogLineTimeoutId = setTimeout(clearLogLine, optimalMsForReading); } + //Changes the dimensions according to the expanded status + private function updateLogLineDimensions():void + { + if(expanded) + { + logLineContainer.left = 1; + logLineContainer.right = 1; + } else + { + logLineContainer.left = 0; + logLineContainer.right = 0; + } + } + //Erases the logLine text private function clearLogLine():void { lastLogLineTimeoutId = -1; @@ -329,6 +364,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -403,6 +488,7 @@ + @@ -411,48 +497,13 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gui-builder/src/lse/math/games/builder/view/OutputWindow.mxml b/gui-builder/src/lse/math/games/builder/view/OutputWindow.mxml index 99ad4f8..5fdf93c 100644 --- a/gui-builder/src/lse/math/games/builder/view/OutputWindow.mxml +++ b/gui-builder/src/lse/math/games/builder/view/OutputWindow.mxml @@ -1,12 +1,13 @@ maxLength) maxLength = line.length; + } + + this.width = Math.max(200, maxLength*7+100); + this.height = Math.min(parent.height - 150, 14*textInLines.length + 70); } protected function closeHandler(event:CloseEvent):void diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/MatrixInput.as b/gui-builder/src/lse/math/games/builder/viewmodel/MatrixInput.as deleted file mode 100644 index dfc02b5..0000000 --- a/gui-builder/src/lse/math/games/builder/viewmodel/MatrixInput.as +++ /dev/null @@ -1,333 +0,0 @@ -package lse.math.games.builder.viewmodel -{ - //TODO: Remove unnecesary ones - import flash.utils.Dictionary; - - import lse.math.games.builder.model.ExtensiveForm; - import lse.math.games.builder.model.Iset; - import lse.math.games.builder.model.Move; - import lse.math.games.builder.model.Node; - import lse.math.games.builder.model.Outcome; - import lse.math.games.builder.model.Player; - import lse.math.games.builder.model.Rational; - - import mx.controls.Alert; - - /** - * This class handles the input of a Tree in Strategic Form via text editing the payoff matrixes of the players - * TODO: Change the current functions (taken from ExtFormXMLReader) and use them as necessary - * TODO: Complete all functionality - * TODO: Try to make it for more than 2 players. If not, wirte a to-do alerting it - * TODO: It must throw an action, or be handled by an action or something, that can be undone - * - * @author alfongj - */ - public class MatrixInput - { - /*private var nodes:Dictionary; - private var isets:Dictionary; - private var isetObjToId:Dictionary; - private var singletons:Vector.; - private var moves:Dictionary; - private var players:Dictionary; - - private var xml:XML = null; - private var tree:ExtensiveForm = null; - - public function MatrixInput(xml:XML) - { - //TODO - this.xml = xml; - } - - public function load(tree:ExtensiveForm):ExtensiveForm - { - //TODO - this.tree = tree; - tree.clearTree(); - - nodes = new Dictionary(); - isets = new Dictionary(); - isetObjToId = new Dictionary(); - singletons = new Vector.(); - moves = new Dictionary(); - players = new Dictionary(); - - for each(var child:XML in xml.children()) { - if (child.name() == "iset") { - processIset(child); - } else if (child.name() == "node") { - processNode(child, null); - } else if (child.name() == "outcome") { - processOutcome(child, null); - } else { - trace("Ignoring unknown element:\r\n" + child); - } - } - - var iset:Iset = null; - for (var isetId:String in isets) { - iset = isets[isetId] as Iset; - if (iset != tree.root.iset) { - tree.addIset(iset); - } - } - for each (iset in singletons) { - if (iset != tree.root.iset) { - tree.addIset(iset); - } - } - - hookupAndVerifyMoves(); - - return tree; - } - - //there has got to be a more efficient algorithm - private function hookupAndVerifyMoves():void - { - //TODO - for (var iset:Iset = tree.root.iset; iset != null; iset = iset.nextIset) { - - iset.sort(); - - //for each child of the first node, hook up move - var baseline:Node = iset.firstNode; - for (var baselineChild:Node = baseline.firstChild, childIdx:int = 0; baselineChild != null; baselineChild = baselineChild.sibling, ++childIdx) - { - iset.assignMove(baselineChild.reachedby); - - // make sure all the rest of the nodes have the child with the same move - // TODO: it does not necessarily need to be at the same index - for (var baselineMate:Node = baseline.nextInIset; baselineMate != null; baselineMate = baselineMate.nextInIset) - { - var mateChild:Node = baselineMate.firstChild; - for (var i:int = 0; i < childIdx; ++i) { - mateChild = mateChild.sibling; - } - if (mateChild.reachedby != baselineChild.reachedby) { - if (mateChild.reachedby == null) { - throw new Error("node does not contain an incoming move"); - } else { - throw new Error("Iset " + iset.idx + " is inconsistent for node " + baselineMate.number /*+ " at " + baselineMate.setIdx*/ + " for child " + mateChild.number + " at " + childIdx + " with move " + mateChild.reachedby); - } - } - } - } - } - } - - private function getPlayer(playerId:String):Player - { - //TODO - if (playerId == Player.CHANCE_NAME) { - return Player.CHANCE; - } - - var player:Player = players[playerId]; - if (player == null) { - player = new Player(playerId, tree); - players[playerId] = player; - } - return player; - } - - private function processIset(elem:XML):void - { - //TODO - var id:String = elem.@id; - var iset:Iset = isets[id]; - - if (iset == null) { - var player:Player = (elem.@player != undefined) ? getPlayer(elem.@player) : Player.CHANCE; - iset = new Iset(player, tree); - isets[id] = iset; - isetObjToId[iset] = id; - } - - for each (var child:XML in elem.children()) - { - if (child.name() == "node") { - processNode(child, null); - } else { - trace("Ignoring unknown element:\r\n" + child); - } - } - } - - private function processNode(elem:XML, parentNode:Node):void - { - //TODO - //init - var node:Node = null; - if (elem.@id != undefined) - { - var id:String = elem.@id; - node = nodes[id]; - - if (node == null) { - trace("XMLReader: creating new node " + id); - node = tree.createNode(); - nodes[id] = node; - } else { - trace("XMLReader: processing previously created node " + id); - - } - } else { - node = tree.createNode(); - } - - //assign parent - if (parentNode == null && elem.@parent != undefined) - { - var parentId:String = elem.@parent; - parentNode = nodes[parentId]; - - if (parentNode == null) { - parentNode = tree.createNode(); - nodes[parentId] = parentNode; - } - } - if (parentNode != null) { - parentNode.addChild(node); - } else { - tree.root = node; - } - - // process iset - var isetId:String = null; - if (elem.parent() != null && elem.parent().name() == "iset") { - isetId = elem.parent().@id; - if (elem.@iset != undefined) trace("Warning: @iset attribute is set for a node nested in an iset tag. Ignored."); - } else if (elem.@iset != undefined) { - isetId = elem.@iset; - } - - var iset:Iset = null; - var player:Player = (elem.@player != undefined) ? getPlayer(elem.@player) : Player.CHANCE; - if (isetId == null) { - iset = new Iset(player, tree); - singletons.push(iset); // root is already taken care of - } else { - //look it up in the map, if it doesn't exist create it and add it - iset = isets[isetId]; - if (iset == null) { - iset = new Iset(player, tree); - isets[isetId] = iset; - isetObjToId[iset] = isetId; - } else { - if (player != Player.CHANCE) { - if (iset.player != Player.CHANCE && player != iset.player) { - trace("Warning: @player attribute conflicts with earlier iset player assignment. Ignored."); - } - while (iset.player != player) { - iset.changePlayer(tree.firstPlayer); - } - } - } - } - iset.insertNode(node); - - // set up the moves - processMove(elem, node, parentNode); -/* if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parentNode != null && parentNode.iset != null) ? String(parentNode.iset.idx) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - node.reachedby = move; - } else if (parentNode != null) { - // assume this comes from a chance node with a probability of zero - node.reachedby = new Move(); - } - - if (elem.@prob != undefined && node.reachedby != null) { - node.reachedby.prob = Rational.parse(elem.@prob); - } -*/ - for each (var child:XML in elem.children()) { - if (child.name() == "node") { - processNode(child, node); - } else if (child.name() == "outcome") { - processOutcome(child, node); - } /*else { - trace("Ignoring unknown element:\r\n" + child); - }*/ - } - } - - private function processOutcome(elem:XML, parent:Node):void - { - //TODO - // Create wrapping node - // get parent from dictionary... if it doesn't exist then the outcome must be the root - var wrapNode:Node = parent != null ? parent.newChild() : tree.createNode(); - if (parent == null) { - tree.root = wrapNode; - } - - // set up the moves - processMove(elem, wrapNode, parent); - /* if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parent != null && parent.iset != null) ? String(parent.iset.idx) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - wrapNode.reachedby = move; - } - - if (elem.@prob != undefined && wrapNode.reachedby != null) { - wrapNode.reachedby.prob = Rational.parse(elem.@prob); - }*/ - - var outcome:Outcome = wrapNode.makeTerminal(); - for each (var child:XML in elem.children()) { - if (child.name() == "payoff") { - var playerId:String = child.@player; - var payoff:Rational = Rational.parse(child.@value); - - var player:Player = players[playerId]; - if (player == null) { - player = new Player(playerId, tree); - players[playerId] = player; - } - outcome.setPay(player, payoff); - } /*else { - trace("Ignoring unknown element:\r\n" + child); - }*/ - } - } - - private function processMove(elem:XML, node:Node, parent:Node):void - { - //TODO - if (elem.@move != undefined) { - var moveId:String = elem.@move; - var moveIsetId:String = (parent != null && parent.iset != null) ? String(isetObjToId[parent.iset]) : ""; - var move:Move = moves[moveIsetId + "::" + moveId]; - if (move == null) { - move = new Move(); - move.label = moveId; - moves[moveIsetId + "::" + moveId] = move; - } - node.reachedby = move; - } else if (parent != null) { - // assume this comes from a chance node with a probability of zero - node.reachedby = new Move(); - } - - if (elem.@prob != undefined && node.reachedby != null) { - node.reachedby.prob = Rational.parse(elem.@prob); - } - } */ - } -} diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/MatrixPainter.as b/gui-builder/src/lse/math/games/builder/viewmodel/MatrixPainter.as index bcff9ac..d04d460 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/MatrixPainter.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/MatrixPainter.as @@ -106,7 +106,7 @@ package lse.math.games.builder.viewmodel g.fillRect(0, 0, width, height); g.color = 0x000000; - g.stroke = this.scale * _grid.linewidth; + g.stroke = this.scale * _grid.strokeWidth; paintMatrix(g, width, height, nf); } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGrid.as b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGrid.as index f1af183..50f653d 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGrid.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGrid.as @@ -3,6 +3,7 @@ package lse.math.games.builder.viewmodel import lse.math.games.builder.model.ExtensiveForm; import lse.math.games.builder.model.Iset; import lse.math.games.builder.model.Node; + import lse.math.games.builder.settings.FileSettings; import lse.math.games.builder.settings.SCodes; import lse.math.games.builder.settings.UserSettings; @@ -22,10 +23,7 @@ package lse.math.games.builder.viewmodel * @author Mark Egesdal */ public class TreeGrid extends ExtensiveForm - { - public static const NODE_DIAM:Number = 7; //TODO: SETTING - public static const ISET_DIAM:Number = 25; //TODO: SETTING - + { //TODO: Let rotation adjust these margins... or at least account //for the label sizes in the min measurements public static const MIN_MARGIN_TOP:Number = 24; @@ -35,18 +33,15 @@ package lse.math.games.builder.viewmodel public var scale:Number = 1.0; //Current scale of the canvas private var _rotate:int = 0; - private var _leveldistance:int; - private var _linewidth:Number; - private var _ovallinewidth:Number; private var _mergeBase:Iset = null; private var _selectedNodeId:int = -1; private var _isZeroSum:Boolean = true; private var _isNormalReduced:Boolean = true; - private var _maxPayoff:Number = 25; //TODO: PREFERENCE + private var _maxPayoff:Number = 25; //TODO: SETTING - private var userSettings:UserSettings = UserSettings.instance; + private var fileSettings:FileSettings = FileSettings.instance; private var log:Log = Log.instance; @@ -54,12 +49,8 @@ package lse.math.games.builder.viewmodel public function TreeGrid() { defaultTree(); - defaultSettings(); } - //TODO: For all settings that can be chosen either from userSettings or from currentTreeSettings, - //the getters should be the ones that control from which of the sources they return - /** Direction of the tree, being 0 root-up, 1 root-left, 2 root-down, 3 root-right */ public function get rotate():int { return _rotate; } public function set rotate(value:int):void @@ -72,14 +63,32 @@ package lse.math.games.builder.viewmodel _rotate = value; } + /* <--- --- GRAPHIC SETTINGS GETTERS --- ---> */ + + /** Color of nodes, labels and payoffs of the first player */ + public function get player1Color():uint { return fileSettings.getValue(SCodes.FILE_PLAYER_1_COLOR) as uint; } + + /** Color of nodes, labels and payoffs of the second player */ + public function get player2Color():uint { return fileSettings.getValue(SCodes.FILE_PLAYER_2_COLOR) as uint; } + + //TODO: 3PL Check wherever player1Color was used, and those might all have to be modified + + /** Font family used as a default for labels in nodes, isets, labels and payoffs */ + public function get fontFamily():String { return fileSettings.getValue(SCodes.FILE_FONT) as String; } + + /** Diameter in pixels of node points */ + public function get nodeDiameter():Number { return fileSettings.getValue(SCodes.FILE_NODE_DIAMETER) as Number;} + + /** Diameter in pixels of iset rounded ends */ + public function get isetDiameter():Number { return fileSettings.getValue(SCodes.FILE_ISET_DIAMETER) as Number;} + /** Vertical distance in points/pixels between nodes in two consecutive levels */ - public function get leveldistance():int { return _leveldistance; } + public function get leveldistance():int { return fileSettings.getValue(SCodes.FILE_LEVEL_DISTANCE) as int; } /** Width in points/pixels of lines connecting nodes and lines forming isets */ - public function get linewidth():int { return _linewidth; } + public function get strokeWidth():Number { return fileSettings.getValue(SCodes.FILE_STROKE_WIDTH) as Number; } - /** Width in points/pixels of curves. Should be the same as linewidth, for aesthetical reasons */ - public function get ovallinewidth():int { return _ovallinewidth; } + /*<--- --- SELECTED THINGS --- ---> */ /** Id corresponding to the selected node. -1 if there is no node currently selected */ public function get selectedNodeId():int { return _selectedNodeId; } @@ -87,16 +96,9 @@ package lse.math.games.builder.viewmodel /** Iset 'selected' as a base for merging with another. Null if there isn't one selected */ public function get mergeBase():Iset { return _mergeBase; } - public function set mergeBase(value:Iset):void { _mergeBase = value; } - - /** Color of nodes, labels and payoffs of the first player */ - public function get player1Color():uint { return userSettings.getValue(SCodes.PLAYER_1_COLOR) as uint; } - - /** Color of nodes, labels and payoffs of the second player */ - public function get player2Color():uint { return userSettings.getValue(SCodes.PLAYER_2_COLOR) as uint; } + public function set mergeBase(value:Iset):void { _mergeBase = value; } - /** Font family used as a default for labels in nodes, isets, labels and payoffs */ - public function get fontFamily():String { return userSettings.getValue(SCodes.DEFAULT_FONT) as String; } + /* <--- --- OTHERS --- ---> */ /** If each pair of payoffs sum 0 (two player only) */ //TODO: 3PLAYERCHECK @@ -119,23 +121,12 @@ package lse.math.games.builder.viewmodel public function defaultTree():void { this.newPlayer("1"); - this.newPlayer("2"); + this.newPlayer("2"); //TODO 3PL this.root = createNode(); this.root.makeNonTerminal(); } - /** Gives default values for some settings */ - //TODO: Remove when SETTINGS system is finished - public function defaultSettings():void - { - _rotate = 0; - _leveldistance = 75; - _linewidth = 1.0; // line width for drawing Moves - _ovallinewidth = 1.0; // line width for drawing Isets - //TODO: SETTINGS - } - // Creates a TreeGridNode with a determinate 'number' (id) override protected function newNode(number:int):Node { return new TreeGridNode(this, number); @@ -334,7 +325,7 @@ package lse.math.games.builder.viewmodel */ private function coordsInNode(node:Node, x:int, y:int):Boolean { - var halfSide:Number = NODE_DIAM*scale; + var halfSide:Number = nodeDiameter*scale; var n:TreeGridNode = node as TreeGridNode; return ((n.xpos - halfSide < x)&& (x < halfSide + n.xpos) && @@ -361,7 +352,7 @@ package lse.math.games.builder.viewmodel private function coordsInIset(h:Iset, x:Number, y:Number):Boolean { var found:Boolean = true; - var radius:Number = ISET_DIAM * scale / 2; + var radius:Number = isetDiameter * scale / 2; var node:Node = getNodeInIsetBeforeCoords(h, x, y, radius); if (node == null) { diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridActionFactory.as b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridActionFactory.as index 9b590e6..eb57aa1 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridActionFactory.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridActionFactory.as @@ -80,17 +80,17 @@ package lse.math.games.builder.viewmodel return chain; } - [Deprecated(replacement="orientationXXX()")] - public function rotateRight(grid:TreeGrid):IAction - { - return new RotateAction(RotateAction.CLOCKWISE); - } - - [Deprecated(replacement="orientationXXX()")] - public function rotateLeft(grid:TreeGrid):IAction - { - return new RotateAction(RotateAction.COUNTERCLOCKWISE); - } +// [Deprecated(replacement="orientationXXX()")] +// public function rotateRight(grid:TreeGrid):IAction +// { +// return new RotateAction(RotateAction.CLOCKWISE); +// } +// +// [Deprecated(replacement="orientationXXX()")] +// public function rotateLeft(grid:TreeGrid):IAction +// { +// return new RotateAction(RotateAction.COUNTERCLOCKWISE); +// } /** Displays the tree with root on top */ public function orientationUp(grid:TreeGrid):IAction @@ -143,7 +143,7 @@ package lse.math.games.builder.viewmodel { var beforeCut:Node = null; for (var h:Iset = grid.root.iset; h != null; h = h.nextIset) { - beforeCut = grid.getNodeInIsetBeforeCoords(h, x, y, TreeGrid.ISET_DIAM * grid.scale/2); + beforeCut = grid.getNodeInIsetBeforeCoords(h, x, y, grid.isetDiameter * grid.scale/2); if (beforeCut != null) { break; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridPainter.as b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridPainter.as index d868a04..86dc767 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridPainter.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridPainter.as @@ -68,12 +68,12 @@ package lse.math.games.builder.viewmodel _minBreadth = 0; } else { //TODO: calculate based on leaf label height/widths - _minBreadth = this.scale * ((_leafCount + 1) * TreeGrid.ISET_DIAM + _leafCount * TreeGrid.NODE_DIAM + 2 * _grid.ovallinewidth); + _minBreadth = this.scale * ((_leafCount + 1) * _grid.isetDiameter + _leafCount * _grid.nodeDiameter + 2 * _grid.strokeWidth); } var maxdepth:int = _grid.maxDepth(); //TODO: calculate leveldistance dynamically based on label height/width - _minDepth = this.scale * (maxdepth * _grid.leveldistance + TreeGrid.ISET_DIAM + 2 * _grid.ovallinewidth); + _minDepth = this.scale * (maxdepth * _grid.leveldistance + _grid.isetDiameter + 2 * _grid.strokeWidth); } override public function paint(g:IGraphics, width:Number, height:Number):void @@ -115,7 +115,7 @@ package lse.math.games.builder.viewmodel marginRight += marginAdjustLR; } - var leafDistance:Number = (( -_leafCount * this.scale * TreeGrid.ISET_DIAM) + + var leafDistance:Number = (( -_leafCount * this.scale * _grid.isetDiameter) + (grid.rotate == 0 || grid.rotate == 2 ? width - marginLeft - marginRight : height - marginTop - marginBottom)) / (_leafCount + 1); @@ -127,7 +127,7 @@ package lse.math.games.builder.viewmodel // Graphics painting private function paintTree(g:IGraphics, grid:TreeGrid):void { - g.stroke = this.scale * grid.linewidth; + g.stroke = this.scale * grid.strokeWidth; recPaintTree(g, grid, grid.root as TreeGridNode); } @@ -157,9 +157,9 @@ package lse.math.games.builder.viewmodel if (n.outcome == null) { g.color = getNodeColor(n, grid, selectionFound); if (n.iset.player == Player.CHANCE) { - g.fillRect(n.xpos - this.scale * TreeGrid.NODE_DIAM/2, n.ypos - scale * TreeGrid.NODE_DIAM/2, scale * TreeGrid.NODE_DIAM, scale * TreeGrid.NODE_DIAM); + g.fillRect(n.xpos - this.scale * grid.nodeDiameter/2, n.ypos - scale * grid.nodeDiameter/2, scale * grid.nodeDiameter, scale * grid.nodeDiameter); } else { - g.fillCircle(n.xpos, n.ypos, this.scale * TreeGrid.NODE_DIAM / 2); + g.fillCircle(n.xpos, n.ypos, this.scale * grid.nodeDiameter / 2); } } @@ -170,7 +170,7 @@ package lse.math.games.builder.viewmodel { var color:uint = isSelected ? 0xFFD700 : 0x000000; if (!isSelected && n.iset != null && n.iset.player != Player.CHANCE) { - color = (grid.firstPlayer == n.iset.player ? grid.player1Color : grid.player2Color); + color = (grid.firstPlayer == n.iset.player ? grid.player1Color : grid.player2Color); if (n.iset == grid.mergeBase) { color ^= 0xFFFFFF; // complement color &= 0x7FFF7F; // not too bright, and greenish @@ -392,7 +392,7 @@ package lse.math.games.builder.viewmodel if (n.isLeaf) { var breadthPos:Number = (drawnumber + 1) * leafdistance + - this.scale * TreeGrid.ISET_DIAM * (drawnumber + 0.5); + this.scale * _grid.isetDiameter * (drawnumber + 0.5); if (grid.rotate == 0 || grid.rotate == 2) { n.xpos = breadthPos + marginLeft; @@ -425,7 +425,7 @@ package lse.math.games.builder.viewmodel private function positionNodeDepth(n:TreeGridNode, grid:TreeGrid, width:Number, height:Number):void { - var depthPos:Number = this.scale * (n.depth * grid.leveldistance + TreeGrid.ISET_DIAM / 2); + var depthPos:Number = this.scale * (n.depth * grid.leveldistance + _grid.isetDiameter / 2); if (grid.rotate == 0) { n.ypos = depthPos + marginTop; } else if (grid.rotate == 1) { diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridSetPainter.as b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridSetPainter.as index 9b59b4a..e717080 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridSetPainter.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/TreeGridSetPainter.as @@ -58,7 +58,7 @@ package lse.math.games.builder.viewmodel override public function paint(g:IGraphics, width:Number, height:Number):void { - g.stroke = this.scale * _grid.ovallinewidth; + g.stroke = this.scale * _grid.strokeWidth; paintGrid(g, width, height, _grid); } @@ -103,7 +103,7 @@ package lse.math.games.builder.viewmodel var group:Vector. = depthGroups[key]; var first:TreeGridNode = group[0]; var last:TreeGridNode = group[group.length - 1]; - paintSameDepthGroup(g, first, last); + paintSameDepthGroup(g, first, last, grid); keys.push(key); } // extract and sort the keys by depth @@ -131,10 +131,10 @@ package lse.math.games.builder.viewmodel // else draw at the top of the first group if (!isLabelPositioned) { - positionSingletonSetLabel(h, grid, label, this.scale * TreeGrid.ISET_DIAM/2); + positionSingletonSetLabel(h, grid, label, this.scale * grid.isetDiameter/2); } } else { - positionSingletonSetLabel(h, grid, label, this.scale * TreeGrid.NODE_DIAM/2); + positionSingletonSetLabel(h, grid, label, this.scale * grid.nodeDiameter/2); } } h = h.nextIset; @@ -157,11 +157,11 @@ package lse.math.games.builder.viewmodel { var x:Number, y:Number; if (grid.rotate == 0 || grid.rotate == 2) { - x = first.xpos != last.xpos ? (first.xpos + last.xpos) / 2 - label.width / 2 : first.xpos + this.scale * TreeGrid.ISET_DIAM; + x = first.xpos != last.xpos ? (first.xpos + last.xpos) / 2 - label.width / 2 : first.xpos + this.scale * grid.isetDiameter; y = first.ypos + label.ascent / 2; } else { x = first.xpos - label.width / 2; - y = first.ypos != last.ypos ? (first.ypos + last.ypos) / 2 + label.ascent / 2 : first.ypos + this.scale * TreeGrid.ISET_DIAM; + y = first.ypos != last.ypos ? (first.ypos + last.ypos) / 2 + label.ascent / 2 : first.ypos + this.scale * grid.isetDiameter; } this.moveLabel(label, x, y); } @@ -214,14 +214,14 @@ package lse.math.games.builder.viewmodel var mid:Number = (intersectionRight + intersectionLeft) / 2; if (grid.rotate == 0 || grid.rotate == 2) { startX = mid; - startY = top[0].ypos + (grid.rotate == 0 ? this.scale * TreeGrid.ISET_DIAM / 2 : - this.scale * TreeGrid.ISET_DIAM / 2); + startY = top[0].ypos + (grid.rotate == 0 ? this.scale * grid.isetDiameter / 2 : - this.scale * grid.isetDiameter / 2); endX = mid; - endY = bottom[0].ypos + (grid.rotate == 2 ? this.scale * TreeGrid.ISET_DIAM / 2 : - this.scale * TreeGrid.ISET_DIAM / 2); + endY = bottom[0].ypos + (grid.rotate == 2 ? this.scale * grid.isetDiameter / 2 : - this.scale * grid.isetDiameter / 2); } else { startY = mid; - startX = top[0].xpos + (grid.rotate == 1 ? this.scale * TreeGrid.ISET_DIAM / 2 : - this.scale * TreeGrid.ISET_DIAM / 2); + startX = top[0].xpos + (grid.rotate == 1 ? this.scale * grid.isetDiameter / 2 : - this.scale * grid.isetDiameter / 2); endY = mid; - endX = bottom[0].xpos + (grid.rotate == 3 ? this.scale * TreeGrid.ISET_DIAM / 2 : - this.scale * TreeGrid.ISET_DIAM / 2); + endX = bottom[0].xpos + (grid.rotate == 3 ? this.scale * grid.isetDiameter / 2 : - this.scale * grid.isetDiameter / 2); } } else { // we go node to node @@ -248,8 +248,8 @@ package lse.math.games.builder.viewmodel var slope:Number = (endY - startY) / (endX - startX); var angle:Number = Math.atan(slope); - var deltaX:Number = Math.cos(angle) * this.scale * TreeGrid.ISET_DIAM / 2; - var deltaY:Number = Math.sin(angle) * this.scale * TreeGrid.ISET_DIAM / 2; + var deltaX:Number = Math.cos(angle) * this.scale * grid.isetDiameter / 2; + var deltaY:Number = Math.sin(angle) * this.scale * grid.isetDiameter / 2; //trace("angle " + int(angle/Math.PI * 180) + " deltaX " + int(deltaX) + " deltaY " + int(deltaY) + " startX " + int(startX) + " startY " + int(startY) + " endX " + int(endX) + " endY " + int(endY)); if (endX < startX) { // quadrant 2 & 3 @@ -265,14 +265,14 @@ package lse.math.games.builder.viewmodel g.drawDashedLine(startX, startY, endX, endY); } - private function paintSameDepthGroup(g:IGraphics, first:TreeGridNode, last:TreeGridNode):void + private function paintSameDepthGroup(g:IGraphics, first:TreeGridNode, last:TreeGridNode, grid:TreeGrid):void { - var x:Number = (first.xpos < last.xpos ? first.xpos : last.xpos) - this.scale * TreeGrid.ISET_DIAM / 2; - var y:Number = (first.ypos < last.ypos ? first.ypos : last.ypos) - this.scale * TreeGrid.ISET_DIAM / 2; - var width:Number = Math.abs(last.xpos - first.xpos) + this.scale * TreeGrid.ISET_DIAM; - var height:Number = Math.abs(last.ypos - first.ypos) + this.scale * TreeGrid.ISET_DIAM; + var x:Number = (first.xpos < last.xpos ? first.xpos : last.xpos) - this.scale * grid.isetDiameter / 2; + var y:Number = (first.ypos < last.ypos ? first.ypos : last.ypos) - this.scale * grid.isetDiameter / 2; + var width:Number = Math.abs(last.xpos - first.xpos) + this.scale * grid.isetDiameter; + var height:Number = Math.abs(last.ypos - first.ypos) + this.scale * grid.isetDiameter; - g.drawRoundRect(x, y, width, height, this.scale * TreeGrid.ISET_DIAM); + g.drawRoundRect(x, y, width, height, this.scale * grid.isetDiameter); } private function getSetColor(h:Iset, grid:TreeGrid):uint diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/AddChildAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/AddChildAction.as index 06ea341..3949f97 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/AddChildAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/AddChildAction.as @@ -11,6 +11,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.viewmodel.DepthAdjuster; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Adds children to all the nodes in a selected iset/ in a selected node's iset. * If the nodes are leaves, then two children per node are added, else just one @@ -24,6 +26,7 @@ package lse.math.games.builder.viewmodel.action private var _isetId:int = -1; private var _nodeId:int = -1; private static var _depthAdjuster:IAction = new DepthAdjuster(); //TODO: remove and use ActionChain or onAdd decorator + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -55,12 +58,16 @@ package lse.math.games.builder.viewmodel.action node.makeNonTerminal(); addChildrenTo(node.iset, grid); _depthAdjuster.doAction(grid); - } - } + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "AddChildAction"); + } else + log.add(Log.ERROR, "Couldn't find any iset with idx "+_isetId, "AddChildAction"); var labeler:AutoLabeller = new AutoLabeller; labeler.doAction(grid); + grid.orderIds(); + _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/ChangePlayerAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/ChangePlayerAction.as index 98e7759..dc83212 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/ChangePlayerAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/ChangePlayerAction.as @@ -7,6 +7,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Changes the player of all nodes inside selected Iset/selected node's Iset *

  • Changes Data
  • @@ -17,7 +19,8 @@ package lse.math.games.builder.viewmodel.action public class ChangePlayerAction implements IAction { private var _isetId:int = -1; - private var _nodeId:int = -1; + private var _nodeId:int = -1; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -32,15 +35,20 @@ package lse.math.games.builder.viewmodel.action } public function doAction(grid:TreeGrid):void - { + { var prevTime:int = getTimer(); var iset:Iset = grid.getIsetById(_isetId); if (iset == null) { - var node:Node = grid.getNodeById(_nodeId); - if (node != null) { - iset = node.makeNonTerminal(); - } + if(_nodeId >= 0) + { + var node:Node = grid.getNodeById(_nodeId); + if (node != null) { + iset = node.makeNonTerminal(); + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "ChangePlayerAction"); + } else + log.add(Log.ERROR, "Couldn't find any iset with idx "+_isetId, "ChangePlayerAction"); } else { iset.changePlayer(grid.firstPlayer); } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/CutAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/CutAction.as index 1835528..9b8d388 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/CutAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/CutAction.as @@ -6,6 +6,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.AutoLabeller; import lse.math.games.builder.viewmodel.TreeGrid; + + import util.Log; /** * Divides the iset of selected node into two parts, one on the left ending in the Iset, @@ -18,6 +20,7 @@ package lse.math.games.builder.viewmodel.action public class CutAction implements IAction { private var _nodeId:int = -1; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -40,7 +43,10 @@ package lse.math.games.builder.viewmodel.action var labeler:AutoLabeller = new AutoLabeller; labeler.doAction(grid); - } + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "CutAction"); + + grid.orderIds(); _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/DeleteAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/DeleteAction.as index d46ed4d..1def871 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/DeleteAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/DeleteAction.as @@ -7,6 +7,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.viewmodel.AutoLabeller; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Removes a node and its children *
  • Is undoable
  • @@ -18,6 +20,7 @@ package lse.math.games.builder.viewmodel.action public class DeleteAction implements IAction { private var _nodeId:int = -1; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -40,7 +43,10 @@ package lse.math.games.builder.viewmodel.action var labeler:AutoLabeller = new AutoLabeller; labeler.doAction(grid); - } + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "DeleteAction"); + + grid.orderIds(); _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/DissolveAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/DissolveAction.as index f804edb..90ce358 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/DissolveAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/DissolveAction.as @@ -7,6 +7,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.viewmodel.AutoLabeller; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Dissolves selected Iset *
  • Changes Data
  • @@ -16,7 +18,8 @@ package lse.math.games.builder.viewmodel.action */ public class DissolveAction implements IAction { - private var _isetId:int = -1; + private var _isetId:int = -1; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -39,7 +42,10 @@ package lse.math.games.builder.viewmodel.action var labeler:AutoLabeller = new AutoLabeller; labeler.doAction(grid); - } + } else + log.add(Log.ERROR, "Couldn't find any iset with idx "+_isetId, "DissolveAction"); + + grid.orderIds(); _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/LabelChangeAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/LabelChangeAction.as index 9c629fd..8ca614f 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/LabelChangeAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/LabelChangeAction.as @@ -6,6 +6,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.model.Node; import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.TreeGrid; + + import util.Log; /** * Changes a selected node's label to a new one @@ -18,6 +20,7 @@ package lse.math.games.builder.viewmodel.action { private var _nodeId:int; private var _label:String; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -41,7 +44,8 @@ package lse.math.games.builder.viewmodel.action if (move != null) { move.label = _label; } - } + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "LabelChangeAction"); _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/MakeChanceAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/MakeChanceAction.as index c075f5a..710e119 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/MakeChanceAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/MakeChanceAction.as @@ -7,6 +7,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Makes a node or all nodes in iset to be chances (eliminating player data and replacing move labels with probabilities) *
  • Changes Data
  • @@ -18,6 +20,7 @@ package lse.math.games.builder.viewmodel.action { private var _isetId:int = -1; private var _nodeId:int = -1; + private var log:Log = Log.instance; private var _onDissolve:IAction; @@ -44,11 +47,16 @@ package lse.math.games.builder.viewmodel.action var iset:Iset = grid.getIsetById(_isetId); if (iset == null) { - var node:Node = grid.getNodeById(_nodeId); - if (node != null) { - iset = node.makeNonTerminal(); - iset.makeChance(); - } + if(_nodeId >= 0) + { + var node:Node = grid.getNodeById(_nodeId); + if (node != null) { + iset = node.makeNonTerminal(); + iset.makeChance(); + } else + log.add(Log.ERROR, "Couldn't find any node with idx "+_nodeId, "MakeChanceAction"); + } else + log.add(Log.ERROR, "Couldn't find any iset with idx "+_isetId, "MakeChanceAction"); } else { var dissolve:Boolean = iset.numNodes > 1; iset.makeChance(); @@ -57,6 +65,8 @@ package lse.math.games.builder.viewmodel.action } } + grid.orderIds(); + _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/MergeAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/MergeAction.as index e04346b..da93f14 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/MergeAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/MergeAction.as @@ -16,7 +16,7 @@ package lse.math.games.builder.viewmodel.action public class MergeAction implements IAction { private var _mergeId:int = -1; - private var _baseId:int = -1; + private var _baseId:int = -1; private var _onMerge:IAction; @@ -60,6 +60,8 @@ package lse.math.games.builder.viewmodel.action grid.mergeBase = null; } + grid.orderIds(); + _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/PayChangeAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/PayChangeAction.as index 095f627..c96556a 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/PayChangeAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/PayChangeAction.as @@ -8,6 +8,8 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** * Changes the payoffs of a terminal node @@ -21,6 +23,7 @@ package lse.math.games.builder.viewmodel.action private var _nodeId:int; private var _pay1:Rational; private var _pay2:Rational; + private var log:Log = Log.instance; private var _timeElapsed:int = 0; @@ -47,7 +50,8 @@ package lse.math.games.builder.viewmodel.action outcome.setPay(grid.firstPlayer, _pay1); if(_pay2!=null) outcome.setPay(grid.firstPlayer.nextPlayer, _pay2); - } + } else + log.add(Log.ERROR, "Couldn't find any suitable node with idx "+_nodeId, "PayChangeAction"); _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/PerfectRecallAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/PerfectRecallAction.as index f56d93c..75deedf 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/PerfectRecallAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/PerfectRecallAction.as @@ -32,6 +32,8 @@ package lse.math.games.builder.viewmodel.action var labeler:AutoLabeller = new AutoLabeller; labeler.doAction(grid); + grid.orderIds(); + _timeElapsed = getTimer() - prevTime; } diff --git a/gui-builder/src/lse/math/games/builder/viewmodel/action/RotateAction.as b/gui-builder/src/lse/math/games/builder/viewmodel/action/RotateAction.as index 17aa2fa..a571d5a 100644 --- a/gui-builder/src/lse/math/games/builder/viewmodel/action/RotateAction.as +++ b/gui-builder/src/lse/math/games/builder/viewmodel/action/RotateAction.as @@ -5,19 +5,19 @@ package lse.math.games.builder.viewmodel.action import lse.math.games.builder.presenter.IAction; import lse.math.games.builder.viewmodel.TreeGrid; + import util.Log; + /** - * Rotates the tree clock or counterclockwise + * Rotates the tree to a certain orientation.

    *

  • NOT Changes Data
  • *
  • Changes Size
  • *
  • Changes Display
  • - * @author Mark Egesdal + * @author Mark Egesdal & alfongj */ public class RotateAction implements IAction { - [Deprecated] - public static const CLOCKWISE:String = "clockwise"; - [Deprecated] - public static const COUNTERCLOCKWISE:String = "counterclockwise"; + //public static const CLOCKWISE:String = "clockwise"; + //public static const COUNTERCLOCKWISE:String = "counterclockwise"; //Root positions public static const UP:String = "up"; @@ -37,9 +37,9 @@ package lse.math.games.builder.viewmodel.action { switch(direction) { - case CLOCKWISE: - case COUNTERCLOCKWISE: - trace("Rotation direction " + direction + " is deprecated. Please check the Documentation"); +// case CLOCKWISE: +// case COUNTERCLOCKWISE: +// trace("Rotation direction " + direction + " is deprecated. Please check the Documentation"); case UP: case DOWN: case RIGHT: @@ -47,7 +47,7 @@ package lse.math.games.builder.viewmodel.action _direction = direction; break; default: - throw new Error("Rotate direction must be one of 'clockwise' or 'counterclockwise'"); + Log.instance.add(Log.ERROR_HIDDEN, "Rotate direction must be UP, DOWN, RIGHT or LEFT"); } } @@ -56,12 +56,12 @@ package lse.math.games.builder.viewmodel.action switch(_direction) { - case CLOCKWISE: - grid.rotateRight(); - break; - case COUNTERCLOCKWISE: - grid.rotateLeft(); - break; +// case CLOCKWISE: +// grid.rotateRight(); +// break; +// case COUNTERCLOCKWISE: +// grid.rotateLeft(); +// break; case UP: grid.rotate = 0; break; diff --git a/gui-builder/src/util/Log.as b/gui-builder/src/util/Log.as index d143803..70b6178 100644 --- a/gui-builder/src/util/Log.as +++ b/gui-builder/src/util/Log.as @@ -149,8 +149,9 @@ package util public function saveLogDump() : void { //TODO: I don't know the reason why, but the save method is ignoring the new line characters. In NotePad++ it is displayed correctly, not so in Notepad - var fr:FileReference = new FileReference; - fr.save(logDump(), "log.txt"); +// var fr:FileReference = new FileReference; +// fr.save(logDump(), "log.txt"); + //TODO: Better post to the dev in charge, should be a setting that enables it } /** Returns a String with the content of the arraylist of lines (last LOG_MAX_LENGTH lines as a default, or 'numLines' if specified)*/ diff --git a/gui-builder/test/src/TestSuite.as b/gui-builder/test/src/TestSuite.as index 1b7ffef..d03b33f 100644 --- a/gui-builder/test/src/TestSuite.as +++ b/gui-builder/test/src/TestSuite.as @@ -16,5 +16,7 @@ package public var playerTest:lse.math.games.builder.model.PlayerTest; public var rationalTest:lse.math.games.builder.model.RationalTest; public var strategyTest:lse.math.games.builder.model.StrategyTest; + + public var nodePriorityQueueTest:lse.math.games.builder.viewmodel.NodePriorityQueueTest; } } \ No newline at end of file diff --git a/web-service/war/builder/index.jsp b/web-service/war/builder/index.jsp index 3d24a53..9c0fcbd 100644 --- a/web-service/war/builder/index.jsp +++ b/web-service/war/builder/index.jsp @@ -27,15 +27,12 @@ allowFullscreen: "true", allowScriptAccess: "always", bgcolor: "#FFFFFF", - wmode: "transparent" }; var attributes = { id:"GuiBuilder" }; attributes.align = "middle"; - var fullwindow = true; - swfobject.embedSWF( "GuiBuilder.swf", "flashContainer", @@ -79,23 +76,10 @@ outputWindow.resizeTo(500,desiredHeight); } - //Expands / Contracts the gui - function expand() + //Expands / Contracts the gui if setting is true / false + function expand(setting) { - if(fullwindow) //Contract - { - document.getElementById("titleContainer").style.display = ""; - document.getElementById("creditsContainer").style.display = ""; - - document.getElementById("GTEContainer").style.padding = "2px 5px 5px 5px"; - document.getElementById("GTEContainer").style.border ="1px solid #808080"; - - document.getElementById("GTEContainer").style.height = "580px"; - document.getElementById("GTEContainer").style.width = "85%"; - - document.getElementById("expandButton").innerHTML = "Expand"; - } - else //Expand + if(setting) //Expand { //Hide title and credits document.getElementById("titleContainer").style.display = "none"; @@ -108,12 +92,18 @@ //Maximize GTEContainer & solutionContainer document.getElementById("GTEContainer").style.height = "100%"; document.getElementById("GTEContainer").style.width = "100%"; + } + else //Contract + { + document.getElementById("titleContainer").style.display = ""; + document.getElementById("creditsContainer").style.display = ""; - //Change button text - document.getElementById("expandButton").innerHTML = "Contract"; + document.getElementById("GTEContainer").style.padding = "2px 5px 5px 5px"; + document.getElementById("GTEContainer").style.border ="1px solid #808080"; + + document.getElementById("GTEContainer").style.height = "580px"; + document.getElementById("GTEContainer").style.width = "85%"; } - - fullwindow = !fullwindow; } //Returns the flashmovie @@ -142,7 +132,7 @@ object:focus { outline:none; } - +
    @@ -167,14 +157,12 @@ - - @@ -193,7 +181,6 @@
    -