From 948a5c600c5b49c951e4710c228c3e73ce334a6c Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 23 Oct 2012 14:33:22 -0400 Subject: [PATCH] Eliminate controllerstate abstraction --- css/app.css | 2 +- index.html | 15 +- js/iD/Controller.js | 10 +- js/iD/actions/CreateEntityAction.js | 5 +- js/iD/actions/CreatePOIAction.js | 15 +- js/iD/actions/MoveNodeAction.js | 18 +- js/iD/actions/UndoStack.js | 190 ++++++++++------------ js/iD/actions/UndoableAction.js | 43 +---- js/iD/controller/ControllerState.js | 62 ------- js/iD/controller/edit/EditBaseState.js | 10 +- js/iD/controller/edit/NoSelection.js | 2 +- js/iD/controller/edit/SelectedPOINode.js | 10 +- js/iD/controller/shape/DrawWay.js | 4 +- js/iD/controller/shape/NoSelection.js | 19 ++- js/iD/controller/shape/SelectedPOINode.js | 5 +- js/iD/controller/shape/SelectedWay.js | 5 +- js/iD/renderer/Map.js | 2 +- 17 files changed, 155 insertions(+), 262 deletions(-) delete mode 100755 js/iD/controller/ControllerState.js diff --git a/css/app.css b/css/app.css index a2faca6ea..80010a180 100644 --- a/css/app.css +++ b/css/app.css @@ -77,7 +77,7 @@ table th { } #modebuttons { - width:300px; + width:500px; position:absolute; left:0px; top:0px; diff --git a/index.html b/index.html index 00071bdb8..ed50e5dc4 100755 --- a/index.html +++ b/index.html @@ -12,6 +12,7 @@
+ @@ -86,6 +87,14 @@ require(["dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom","dojo/Evented controller.setState(new iD.controller.shape.NoSelection()); }); + $('#undo').click(function() { + controller.undoStack.undo(); + }); + + $('#redo').click(function() { + controller.undoStack.redo(); + }); + // ---------------------------------------------------- // Map control handlers @@ -118,7 +127,11 @@ require(["dojo/dom-geometry","dojo/dom-class","dojo/on","dojo/dom","dojo/Evented + + Area + +
diff --git a/js/iD/Controller.js b/js/iD/Controller.js index f21f998d8..724ee2534 100755 --- a/js/iD/Controller.js +++ b/js/iD/Controller.js @@ -14,7 +14,7 @@ declare("iD.Controller", [Evented], { constructor:function(map) { // summary: The Controller marshalls ControllerStates and passes events to them. this.map = map; - this.undoStack = new iD.actions.UndoStack(); + this.undoStack = new iD.UndoStack(); this.editorCache = {}; }, @@ -29,17 +29,15 @@ declare("iD.Controller", [Evented], { if (this.state) { this.emit("exitState", { bubbles: true, - cancelable: true, - state: this.state.stateNameAsArray() + cancelable: true }); } - newState.setController(this); + newState.controller = this; this.state = newState; newState.enterState(); this.emit("enterState", { bubbles: true, - cancelable: true, - state: this.state.stateNameAsArray() + cancelable: true }); }, diff --git a/js/iD/actions/CreateEntityAction.js b/js/iD/actions/CreateEntityAction.js index 74ef2400a..48fa94fdd 100644 --- a/js/iD/actions/CreateEntityAction.js +++ b/js/iD/actions/CreateEntityAction.js @@ -16,7 +16,7 @@ declare("iD.actions.CreateEntityAction", [iD.actions.UndoableEntityAction], { this.setName("Create " + entity.entityType); }, - doAction: function() { + run: function() { // summary: Call out to the specified method (in the Controller) to create the entity. // See undoAction for explanation of special redo handling. if (this.deleteAction!==null) { @@ -28,7 +28,7 @@ declare("iD.actions.CreateEntityAction", [iD.actions.UndoableEntityAction], { return this.SUCCESS; }, - undoAction: function() { + undo: function() { // summary: Special handling for undoing a create. When undo is called, instead // of simply removing the entity, we work through to make a Delete[Entity]Action, // call that, and store it for later. Then, when this action is called again @@ -43,7 +43,6 @@ declare("iD.actions.CreateEntityAction", [iD.actions.UndoableEntityAction], { // summary: Set the associated delete action (see undoAction for explanation). deleteAction = action; } - }); // ---------------------------------------------------------------------- diff --git a/js/iD/actions/CreatePOIAction.js b/js/iD/actions/CreatePOIAction.js index 7e0659baf..e12afc23b 100644 --- a/js/iD/actions/CreatePOIAction.js +++ b/js/iD/actions/CreatePOIAction.js @@ -17,34 +17,31 @@ declare("iD.actions.CreatePOIAction", [iD.actions.CompositeUndoableAction], { constructor: function(connection, tags, lat, lon) { // summary: Create a new node and set it as a POI. Used by drag-and-drop. Note that the // node is remembered, so that on redo we can just reinstate it. - this.setName('Create POI'); + this.setName('Create POI: ' + iD.Util.friendlyName(tags)); this.connection = connection; this.tags = tags; this.lat = lat; this.lon = lon; }, - doAction:function() { + run: function() { if (this.newNode === null) { this.newNode = this.connection.doCreateNode(this.tags, this.lat, this.lon, _.bind(this.push, this)); } - this.inherited(arguments); this.connection.registerPOI(this.newNode); - return this.SUCCESS; + return true; }, - undoAction:function() { - this.inherited(arguments); + undo: function() { this.connection.unregisterPOI(this.newNode); - return this.SUCCESS; + return true; }, - getNode:function() { + getNode: function() { return this.newNode; } - }); // ---------------------------------------------------------------------- diff --git a/js/iD/actions/MoveNodeAction.js b/js/iD/actions/MoveNodeAction.js index 700db8f8c..5af2f4b5f 100644 --- a/js/iD/actions/MoveNodeAction.js +++ b/js/iD/actions/MoveNodeAction.js @@ -15,7 +15,7 @@ declare("iD.actions.MoveNodeAction", [iD.actions.UndoableEntityAction], { newLon: NaN, setLatLon: null, - constructor:function(node, newLat, newLon, setLatLon) { + constructor: function(node, newLat, newLon, setLatLon) { // summary: Move a node to a new position. this.entity = node; this.newLat = newLat; @@ -24,25 +24,28 @@ declare("iD.actions.MoveNodeAction", [iD.actions.UndoableEntityAction], { this.createTime = new Date().getTime(); }, - doAction:function() { + run: function() { var node = this.entity; this.oldLat = node.lat; this.oldLon = node.lon; - if (this.oldLat==this.newLat && this.oldLon==this.newLon) { return NO_CHANGE; } + if (this.oldLat === this.newLat && + this.oldLon === this.newLon) { + return NO_CHANGE; + } this.setLatLon(this.newLat, this.newLon); this.markDirty(); node.refresh(); - return this.SUCCESS; + return true; }, - undoAction:function() { + undo: function() { this.setLatLon(this.oldLat, this.oldLon); this.markClean(); this.refresh(); - return this.SUCCESS; + return true; }, - mergePrevious:function(prev) { + mergePrevious: function(prev) { if (prev.declaredClass!=this.declaredClass) { return false; } if (prev.entity == this.entity && prev.createTime+1000>this.createTime) { @@ -52,7 +55,6 @@ declare("iD.actions.MoveNodeAction", [iD.actions.UndoableEntityAction], { } return false; } - }); // ---------------------------------------------------------------------- diff --git a/js/iD/actions/UndoStack.js b/js/iD/actions/UndoStack.js index ee0f2ef17..9129dab59 100644 --- a/js/iD/actions/UndoStack.js +++ b/js/iD/actions/UndoStack.js @@ -1,124 +1,104 @@ // iD/actions/UndoStack.js // ** FIXME: a couple of AS3-isms in undoIfAction/removeLastIfAction -define(['dojo/_base/declare'], function(declare){ - // ---------------------------------------------------------------------- // UndoStack base class -declare("iD.actions.UndoStack", null, { - - undoActions: null, - redoActions: null, +if (typeof iD === 'undefined') iD = {}; - FAIL: 0, - SUCCESS: 1, - NO_CHANGE: 2, +iD.UndoStack = function() { - constructor: function() { - // summary: An undo stack. There can be any number of these, but almost all operations will - // take place on the global undo stack - implemented as a singleton-like property of - // the Controller. - this.undoActions=[]; - this.redoActions=[]; - }, - - addAction: function(_action) { - // summary: Do an action, and add it to the undo stack if it succeeded. - var result = _action.doAction(); - switch (result) { - case this.FAIL: - // do something bad - break; + var stack = {}, + undoActions = [], + redoActions = []; - case this.NO_CHANGE: - break; + var FAIL = 0, + SUCCESS = 1, + NO_CHANGE = 2; - case this.SUCCESS: - default: - if (this.undoActions.length>0) { - var previous = this.undoActions[this.undoActions.length-1]; - if (_action.mergePrevious(previous)) { - _action.wasDirty = previous.wasDirty; - _action.connectionWasDirty = previous.connectionWasDirty; - this.undoActions.pop(); - } - } - this.undoActions.push(_action); - this.redoActions=[]; - break; - } - }, + stack.add = function(action) { + // summary: Add an action to the undo stack + if (undoActions.length > 0) { + var previous = undoActions[undoActions.length - 1]; + if (action.mergePrevious(previous)) { + action.wasDirty = previous.wasDirty; + action.connectionWasDirty = previous.connectionWasDirty; + undoActions.pop(); + } + } + undoActions.push(action); + redoActions = []; + }; - breakUndo: function() { - // summary: Wipe the undo stack - typically used after saving. - this.undoActions = []; - this.redoActions = []; - }, + stack.breakUndo = function() { + // summary: Wipe the undo stack - typically used after saving. + undoActions = []; + redoActions = []; + }; - canUndo: function() { - // summary: Are there any items on the undo stack? - return this.undoActions.length > 0; - }, + stack.canUndo = function() { + // summary: Are there any items on the undo stack? + return undoActions.length > 0; + }; - canRedo: function() { - // summary: Are there any redoable actions? - return this.redoActions.length > 0; - }, + stack.canRedo = function() { + // summary: Are there any redoable actions? + return redoActions.length > 0; + }; - undo: function() { - // summary: Undo the most recent action, and add it to the top of the redo stack. - if (!this.undoActions.length) { return; } - var action = undoActions.pop(); - action.undoAction(); - redoActions.push(action); - }, - undoIfAction: function(_action) { - // summary: Undo the most recent action _only_ if it was of a certain type. - // Fixme: isInstanceOf needs to be made into JavaScript. - if (!this.undoActions.length) { return; } - if (this.undoActions[this.undoActions.length-1].isInstanceOf(_action)) { - this.undo(); - return true; - } - return false; - }, - removeLastIfAction: function(_action) { - // summary: Remove the most recent action from the stack _only_ if it was of a certain type. - // Fixme: isInstanceOf needs to be made into JavaScript. - if (this.undoActions.length && this.undoActions[this.undoActions.length-1].isInstanceOf(_action)) { - this.undoActions.pop(); - } - }, + stack.undo = function() { + // summary: Undo the most recent action, and add it to the top of the redo stack. + if (!stack.canUndo()) { return; } + var action = undoActions.pop(); + action.undo(); + redoActions.push(action); + }; - getUndoDescription: function() { - // summary: Get the name of the topmost item on the undo stack. - if (!this.undoActions.length) return null; - if (this.undoActions[this.undoActions.length-1].name) { - return this.undoActions[this.undoActions.length-1].name; - } - return null; - }, + stack.undoIfAction = function(_action) { + // summary: Undo the most recent action _only_ if it was of a certain type. + // Fixme: isInstanceOf needs to be made into JavaScript. + if (!undoActions.length) { return; } + if (undoActions[undoActions.length-1].isInstanceOf(_action)) { + undo(); + return true; + } + return false; + }; - getRedoDescription: function() { - // summary: Get the name of the topmost item on the redo stack. - if (!this.redoActions.length) return null; - if (this.redoActions[this.redoActions.length-1].name) { - return this.redoActions[this.redoActions.length-1].name; - } - return null; - }, + stack.removeLastIfAction = function(_action) { + // summary: Remove the most recent action from the stack _only_ if it was of a certain type. + // Fixme: isInstanceOf needs to be made into JavaScript. + if (undoActions.length && + undoActions[undoActions.length - 1].isInstanceOf(_action)) { + undoActions.pop(); + } + }; - redo: function() { - // summary: Takes the action most recently undone, does it, and adds it to the undo stack. - if (!this.redoActions.length) { return; } - var action = this.redoActions.pop(); - action.doAction(); - this.undoActions.push(action); - } + stack.getUndoDescription = function() { + // summary: Get the name of the topmost item on the undo stack. + if (!undoActions.length) return null; + if (undoActions[undoActions.length - 1].name) { + return undoActions[undoActions.length - 1].name; + } + return null; + }; -}); + stack.getRedoDescription = function() { + // summary: Get the name of the topmost item on the redo stack. + if (!redoActions.length) return null; + if (redoActions[redoActions.length - 1].name) { + return redoActions[redoActions.length - 1].name; + } + return null; + }; -// ---------------------------------------------------------------------- -// End of module -}); + stack.redo = function() { + // summary: Takes the action most recently undone, does it, and adds it to the undo stack. + if (!stack.canRedo()) { return; } + var action = redoActions.pop(); + action.run(); + undoActions.push(action); + }; + + return stack; +}; diff --git a/js/iD/actions/UndoableAction.js b/js/iD/actions/UndoableAction.js index 003c7557a..0971af7e8 100644 --- a/js/iD/actions/UndoableAction.js +++ b/js/iD/actions/UndoableAction.js @@ -7,42 +7,14 @@ define(['dojo/_base/declare'], function(declare){ // UndoableAction base class declare("iD.actions.UndoableAction", null, { - - FAIL: 0, - SUCCESS: 1, - NO_CHANGE: 2, - - name: "", - - constructor:function() { - // summary: An UndoableAction is a user-induced change to the map data (held within the Connection) - // which can be undone. The UndoableAction doesn't actually change the data: it provides - // the logic around the change, but the change itself is generally made by the model. - // Therefore the UndoableAction constructor needs to be passed a reference - // to a method that actually makes the change - for example, MoveNodeAction will be - // passed a reference to Node._setLatLonImmediate. - }, - - doAction:function() { - // summary: Do the action. - return FAIL; - }, - - undoAction:function() { - // summary: Undo the action. - return FAIL; - }, - mergePrevious:function() { // summary: If two successive actions can be merged (in particular, two successive node moves), do that. return false; }, - setName:function(_name) { // summary: Set the name of an action. For UI and debugging purposes. this.name=_name; } - }); // ---------------------------------------------------------------------- @@ -63,14 +35,14 @@ declare("iD.actions.UndoableEntityAction", [iD.actions.UndoableAction], { }, markDirty:function() { - // summary: Mark a change to the entity ('dirtying' it). + // summary: Mark a change to the entity ('dirtying' it). if (!this.initialised) this.init(); if (!this.wasDirty) this.entity._markDirty(); if (!this.connectionWasDirty) this.entity.connection.modified = true; }, markClean:function() { - // summary: If the entity was clean before, revert the dirty flag to that state. + // summary: If the entity was clean before, revert the dirty flag to that state. if (!this.initialised) this.init(); if (!this.wasDirty) this.entity._markClean(); if (!this.connectionWasDirty) this.entity.connection.modified = false; @@ -102,7 +74,7 @@ declare("iD.actions.CompositeUndoableAction", [iD.actions.UndoableAction], { // summary: A CompositeUndoableAction is a group of user-induced changes to the map data // (held within the Connection) which are executed, and therefore undone, // in a batch. For example, creating a new node and a new way containing it. - this.actions=[]; + this.actions = []; }, push:function(action) { @@ -112,14 +84,14 @@ declare("iD.actions.CompositeUndoableAction", [iD.actions.UndoableAction], { clearActions:function() { // summary: Clear the list of actions. - this.actions=[]; + this.actions = []; }, doAction:function() { // summary: Execute all the actions one-by-one as a transaction (i.e. roll them all back if one fails). if (this.actionsDone) { return this.FAIL; } - var somethingDone=false; - for (var i=0; i