アンドゥー可能なTextArea

UndoableTextArea(アンドゥー可能なTextArea)を先日作りました。
特徴としてはカーソル位置も対象としているところでしょうか。

2008.6.11追記
入力完了時のカーソル位置を覚えているので、カーソル移動→入力→Undoで位置がずれます。バグです・・orz

2008.6.12さらに追記
修正版(http://d.hatena.ne.jp/masatoshisw20/20080612/1213254313)

editableの切り替えを編集開始・確定のトリガーにしてるところは一般的じゃないかも。
focusInとfocusOutをトリガーにするように変更すれば、フォーカスの切り替えが編集開始・確定になります。

<?xml version="1.0" encoding="utf-8"?>
<mx:TextArea xmlns:mx="http://www.adobe.com/2006/mxml"
	keyDown="keyDown(event)"
	change="change(event)"
	creationComplete="init()">
	
	<mx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			
			private var edits:ArrayCollection;
			private var index:int = -1;

			private function init():void {
				enableUndo(editable);
				this.addEventListener("editableChanged", editableChanged);
			}	
			
			private function editableChanged(ev:Event):void {
				enableUndo(editable);
			}
			
			private function enableUndo(value:Boolean):void {
				if (value) {
					edits = new ArrayCollection();
					index = -1;
					addEdit();
				} else {
					if (edits != null) {
						edits.removeAll();
						edits = null;
					}
				}
			}
			
			private function addEdit():void {
				cleanUselessEdits(edits.length);
				edits.addItem({str:text, cursorIndex:selectionEndIndex});
				index = edits.length - 1;
			}
	
			private function cleanUselessEdits(length:int):void {
				for (var i:int = length - 1; i > index; i--) {
					edits.removeItemAt(edits.length - 1);
				}
			}
			
			public function undo():void {
				if (!canUndo()) {
					return;
				}
				restoreState(edits.getItemAt(--index));
			}
	
			public function redo():void {
				if (!canRedo()) {
					return;
				}
				restoreState(edits.getItemAt(++index));
			}

			private function restoreState(edit:Object):void {
				text = edit.str;
				setSelection(edit.cursorIndex, edit.cursorIndex);
			}
			
			public function canUndo():Boolean {
				if (index < 1) {
					return false;
				} else {
					return true;
				}
			}
	
			public function canRedo():Boolean {
				if (index == edits.length - 1) {
					return false;
				} else {
					return true;
				}
			}
			
			public function clearUndoHistory():void {
				edits.removeAll();
				index = -1;
			}

			private function keyDown(ev:KeyboardEvent):void {
				if (ev.controlKey) {
					if (ev.keyCode == Keyboard.Z) {
						undo();
						ev.preventDefault();
					} else if (ev.keyCode == Keyboard.Y) {
						redo();
						ev.preventDefault();
					}
				}
			}
			
			private function change(ev:Event):void {
				addEdit();
			}
			
		]]>
	</mx:Script>
	
</mx:TextArea>