1 rio.Undo = {
  2 	setQueue: function(queue) {
  3 		if (!this._bindingPoints) {
  4 			this._bindingPoints = new (rio.Attr.create({
  5 				attrAccessors: [
  6 					"queue", 
  7 					["hasUndos", false], 
  8 					["hasRedos", false]
  9 				]
 10 			}))();
 11 			
 12 			this._bindingPoints.bind("queue.undos", {
 13 				empty: function(empty) {
 14 					this._bindingPoints.setHasUndos(!empty);
 15 				}.bind(this)
 16 			});
 17 			this._bindingPoints.bind("queue.redos", {
 18 				empty: function(empty) {
 19 					this._bindingPoints.setHasRedos(!empty);
 20 				}.bind(this)
 21 			});
 22 			
 23 			this.hasUndos = this._bindingPoints.hasUndos;
 24 			this.hasRedos = this._bindingPoints.hasRedos;
 25 		}
 26 		
 27 		this._bindingPoints.setQueue(queue);
 28 		this._queue = queue;
 29 	},
 30 	
 31 	undoQueueEmpty: function() {
 32 		return this._queue.getUndos().empty();
 33 	},
 34 	
 35 	redoQueueEmpty: function() {
 36 		return this._queue.getRedos().empty();
 37 	},
 38 	
 39 	isProcessingUndo: function() {
 40 		return this._queue.getProcessingUndo();
 41 	},
 42 	
 43 	isProcessingRedo: function() {
 44 		return this._queue.getProcessingRedo();
 45 	},
 46 	
 47 	setProcessingUndo: function(val) {
 48 		this._queue.setProcessingUndo(val);
 49 	},
 50 
 51 	setProcessingRedo: function(val) {
 52 		this._queue.setProcessingRedo(val);
 53 	},
 54 	
 55 	registerUndo: function(undo, dontClearRedos) {
 56 		if (!dontClearRedos) {
 57 			this._queue.getRedos().clear();
 58 		}
 59 		this._queue.getUndos().push(function() {
 60 			rio.Undo.setProcessingUndo(true);
 61 			try {
 62 				undo();
 63 			} finally {
 64 				rio.Undo.setProcessingUndo(false);
 65 			}
 66 		});
 67 	},
 68 
 69 	registerRedo: function(redo) {
 70 		this._queue.getRedos().push(function() {
 71 			rio.Undo.setProcessingRedo(true);
 72 			try {
 73 				redo();
 74 			} finally {
 75 				rio.Undo.setProcessingRedo(false);
 76 			}
 77 		});
 78 	},
 79 	
 80 	undo: function() {
 81 		this._doAction("undo");
 82 	},
 83 	
 84 	redo: function() {
 85 		this._doAction("redo");
 86 	},
 87 
 88 	_actionProgress: false,
 89 	_actionQueue: [],
 90 	_doAction: function(action) {
 91 		if (this._actionProgress) {
 92 			this._actionQueue.push(this[action].bind(this));
 93 			return;
 94 		}
 95 		this._actionProgress = true;
 96 		try {
 97 			var toDo = this._queue[("get-" + action + "s").camelize()]().pop();
 98 			if (toDo) { 
 99 				rio.Attr.transaction(function() {
100 					toDo();
101 				});
102 			}
103 		} finally {
104 			this._afterAction();
105 		}
106 	},
107 	
108 	_doAfterAction: function() {
109 		(function() {
110 			this._afterAction();
111 		}.bind(this)).defer();
112 	},
113 	
114 	_afterAction: function() {
115 		var next = this._actionQueue.shift();
116 		this._actionProgress = false;
117 		if (next) {
118 			next();
119 		}
120 	},
121 	
122 	toString: function() { return "Undo"; }
123 };
124 
125 rio.UndoQueue = rio.Attr.create({
126 	attrAccessors: [
127 		["undos", []],
128 		["redos", []],
129 		["processingUndo", false],
130 		["processingRedo", false]
131 	]
132 });
133 
134 rio.Undo.setQueue(new rio.UndoQueue());
135