module wijmo.grid.sheet { 'use strict'; /** * Defines the UndoableStack class. * * It deals with the undo\redo for the Flexsheet control. */ export class UndoableStack { private MAX_STACK_SIZE = 500; private _owner: FlexSheet; private _stack = []; private _pointer = -1; private _pendingAction: _UndoableAction; private _resizingTriggered = false; /** * Initializes a new instance of a @see:UndoableStack class. * * @param owener The @see: FlexSheet control that the UndoableStack works for. */ constructor(owner: FlexSheet) { this._owner = owner; // Handles the cell edit action for editing cell this._owner.prepareCellForEdit.addHandler(this._initCellEditAction, this); this._owner.cellEditEnded.addHandler(this._afterProcessCellEditAction, this); // Handles the cell edit action for copy\paste operation this._owner.pasting.addHandler(this._initCellEditAction, this); this._owner.pasted.addHandler(this._afterProcessCellEditAction, this); // Handles the resize column action this._owner.resizingColumn.addHandler((sender: FlexGrid, e: CellRangeEventArgs) => { if (!this._resizingTriggered) { this._pendingAction = new _ColumnResizeAction(this._owner, e.col); this._resizingTriggered = true; } }, this) this._owner.resizedColumn.addHandler((sender: FlexGrid, e: CellRangeEventArgs) => { if (this._pendingAction instanceof _ColumnResizeAction && this._pendingAction.saveNewState()) { this.addAction(this._pendingAction); } this._pendingAction = null; this._resizingTriggered = false; }, this); // Handles the resize row action this._owner.resizingRow.addHandler((sender: FlexGrid, e: CellRangeEventArgs) => { if (!this._resizingTriggered) { this._pendingAction = new _RowResizeAction(this._owner, e.row); this._resizingTriggered = true; } }, this) this._owner.resizedRow.addHandler((sender: FlexGrid, e: CellRangeEventArgs) => { if (this._pendingAction instanceof _RowResizeAction && this._pendingAction.saveNewState()) { this.addAction(this._pendingAction); } this._pendingAction = null; this._resizingTriggered = false; }, this); } /** * Check whether undo can be done. */ get canUndo(): boolean { return this._pointer > -1 && this._pointer < this._stack.length; } /** * Check whether redo can be done. */ get canRedo(): boolean { return this._pointer + 1 > -1 && this._pointer + 1 < this._stack.length; } /** * Occurs when the undoable stack changed. */ undoableStackChanged = new wijmo.Event(); /** * Raises the undoableStackChanged event. */ onUndoableStackChanged() { this.undoableStackChanged.raise(this); } /** * Executes an undo command. */ undo() { var action: _UndoableAction; if (this.canUndo) { action = this._stack[this._pointer]; this._beforeUndoRedo(action); action.undo(); this._pointer--; this.onUndoableStackChanged(); } } /** * Executes an redo command. */ redo() { var action: _UndoableAction; if (this.canRedo) { this._pointer++; action = this._stack[this._pointer]; this._beforeUndoRedo(action); action.redo(); this.onUndoableStackChanged(); } } /** * Add the undoable action into the undo stack. */ addAction(action: _UndoableAction) { // trim stack if (this._stack.length > 0 && this._stack.length > this._pointer + 1) { this._stack.splice(this._pointer + 1, this._stack.length - this._pointer - 1); } if (this._stack.length >= this.MAX_STACK_SIZE) { this._stack.splice(0, this._stack.length - this.MAX_STACK_SIZE + 1); } // update pointer and add action to stack this._pointer = this._stack.length; this._stack.push(action); this.onUndoableStackChanged(); } /** * Clear the undo stack. */ clear() { this._stack.length = 0; } // initialize the cell edit action. private _initCellEditAction() { this._pendingAction = new _EditAction(this._owner); } // after processing the cell edit action. private _afterProcessCellEditAction() { if (this._pendingAction instanceof _EditAction && this._pendingAction.saveNewState()) { this.addAction(this._pendingAction); } this._pendingAction = null; } // Called before an action is undone or redone. private _beforeUndoRedo(action: _UndoableAction) { this._owner.selectedSheetIndex = action.sheetIndex; } } }