blob: 845d90455e751df7ffb6f9f03b0df95239f681a1 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package flashx.undo
{
/**
* The UndoManager class manages the history of editing operations on a text flow so
* that these operations can be undone and redone.
*
* <p>The undo manager maintains two stacks of IOperation objects. When a reversible
* operation is executed, it is placed on the undo stack. If that operation is undone,
* it is removed from the undo stack, reversed, and placed on the redo stack. Likewise,
* if that operation is then redone, it is removed from the redo stack, re-executed, and
* then placed onto the undo stack again. If another operation is executed first, the redo
* stack is cleared.</p>
*
* <p>If the TextFlow is modified directly (not via
* calls to the edit manager, but directly via calls to the managed FlowElement objects), then the edit manager
* clears the undo stack to prevent the stack from getting out of sync with the current state.</p>
*
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*
* @see flashx.textLayout.edit.EditManager
*/
public class UndoManager implements IUndoManager
{
private var undoStack:Array;
private var redoStack:Array;
private var _undoAndRedoItemLimit:int = 25;
/**
* Creates an UndoManager object.
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function UndoManager()
{
undoStack = new Array();
redoStack = new Array();
}
/**
* @copy IUndoManager#clearAll()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function clearAll():void
{
undoStack.length = 0;
redoStack.length = 0;
}
/**
* @copy IUndoManager#canUndo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function canUndo():Boolean
{
return undoStack.length > 0;
}
/**
* @copy IUndoManager#peekUndo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function peekUndo():IOperation
{
return undoStack.length > 0 ? undoStack[undoStack.length-1] : null;
}
/**
* @copy IUndoManager#popUndo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function popUndo():IOperation
{
return IOperation(undoStack.pop());
}
/**
* @copy IUndoManager#pushUndo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function pushUndo(operation:IOperation):void
{
undoStack.push(operation);
trimUndoRedoStacks();
}
/**
* @copy IUndoManager#canRedo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function canRedo():Boolean
{
return redoStack.length > 0;
}
/**
* @copy IUndoManager#clearRedo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function clearRedo():void
{
redoStack.length = 0;
}
/**
* @copy IUndoManager#peekRedo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function peekRedo():IOperation
{
return redoStack.length > 0 ? redoStack[redoStack.length-1] : null;
}
/**
* @copy IUndoManager#popRedo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function popRedo():IOperation
{
return IOperation(redoStack.pop());
}
/**
* @copy IUndoManager#pushRedo()
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function pushRedo(operation:IOperation):void
{
redoStack.push(operation);
trimUndoRedoStacks();
}
/**
* @copy IUndoManager#undoAndRedoItemLimit
* @tiptext The maximum number of undoable or redoable operations to track.
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get undoAndRedoItemLimit():int
{ return _undoAndRedoItemLimit; }
public function set undoAndRedoItemLimit(value:int):void
{
_undoAndRedoItemLimit = value;
trimUndoRedoStacks();
}
/**
* @copy IUndoManager#undo()
* @see flashx.textLayout.edit.IEditManager#undo()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function undo():void
{
if (canUndo())
{
var undoOp:IOperation = popUndo();
undoOp.performUndo();
}
}
/**
* @copy IUndoManager#redo()
*
* @see flashx.textLayout.edit.IEditManager#redo()
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function redo():void
{
if (canRedo())
{
var redoOp:IOperation = popRedo();
redoOp.performRedo();
}
}
/** trim the sizes of the undo/redo stacks to the maximum limits */
private function trimUndoRedoStacks():void
{
// trim the undoStack and the redoStack so its in bounds
var numItems:int = undoStack.length + redoStack.length;
if (numItems > _undoAndRedoItemLimit)
{
// trim redoStack first
var numToSplice:int = Math.min(numItems-_undoAndRedoItemLimit,redoStack.length);
if (numToSplice)
{
redoStack.splice(0,numToSplice);
numItems = undoStack.length+redoStack.length;
}
// trim some undoable items
if (numItems > _undoAndRedoItemLimit)
{
numToSplice = Math.min(numItems-_undoAndRedoItemLimit,undoStack.length);
undoStack.splice(0,numToSplice);
}
}
}
}
}