devexpress-diagram
Version:
DevExpress Diagram Control
183 lines (163 loc) • 6.33 kB
text/typescript
import { CompositionHistoryItem, HistoryItem } from "./HistoryItem";
import { ModelManipulator } from "../Model/ModelManipulator";
import { EventDispatcher } from "../Utils";
export class History {
private static MAX_HISTORY_ITEM_COUNT: number = 100;
modelManipulator: ModelManipulator;
historyItems: HistoryItem[] = [];
startDataSyncItem: HistoryItem;
currentIndex: number = -1;
transaction: CompositionHistoryItem;
private incrementalId: number = -1;
private transactionLevel: number = -1;
private unmodifiedIndex: number = -1;
private currTransactionId: number = 0;
onChanged: EventDispatcher<IHistoryChangesListener> = new EventDispatcher();
constructor(modelManipulator: ModelManipulator) {
this.modelManipulator = modelManipulator;
}
public isModified(): boolean {
if(this.unmodifiedIndex === this.currentIndex)
return false;
const startIndex = Math.min(this.unmodifiedIndex, this.currentIndex);
const endIndex = Math.max(this.unmodifiedIndex, this.currentIndex);
for(let i = startIndex + 1; i <= endIndex; i++)
if(this.historyItems[i].changeModified())
return true;
return false;
}
public undo() {
if(!this.canUndo())
return;
this.historyItems[this.currentIndex].undo(this.modelManipulator);
this.currentIndex--;
this.raiseChanged();
}
public redo() {
if(!this.canRedo())
return;
if(this.startDataSyncItem) {
this.startDataSyncItem.undo(this.modelManipulator);
this.startDataSyncItem = undefined;
}
this.currentIndex++;
this.historyItems[this.currentIndex].redo(this.modelManipulator);
this.raiseChanged();
}
public canUndo(): boolean {
return this.currentIndex >= 0;
}
public canRedo(): boolean {
return this.currentIndex < this.historyItems.length - 1;
}
public beginTransaction(): number {
this.transactionLevel++;
if(this.transactionLevel === 0)
this.transaction = new CompositionHistoryItem();
const id: number = this.currTransactionId++;
return id;
}
public endTransaction(isDataSyncTransaction?: boolean) {
if(--this.transactionLevel >= 0)
return;
const transactionLength = this.transaction.historyItems.length;
if(transactionLength > 0) {
const historyItem = transactionLength > 1 ? this.transaction : this.transaction.historyItems.pop();
if(isDataSyncTransaction)
this.addDataSyncItem(historyItem);
else
this.addInternal(historyItem);
}
if(transactionLength > 0 && !isDataSyncTransaction)
this.raiseChanged();
delete this.transaction;
}
public addAndRedo(historyItem: HistoryItem) {
this.add(historyItem);
historyItem.redo(this.modelManipulator);
this.raiseChanged();
}
public add(historyItem: HistoryItem) {
if(this.transactionLevel >= 0)
this.transaction.add(historyItem);
else
this.addInternal(historyItem);
}
private addInternal(historyItem: HistoryItem) {
if(this.currentIndex < this.historyItems.length - 1) {
this.historyItems.splice(this.currentIndex + 1);
this.unmodifiedIndex = Math.min(this.unmodifiedIndex, this.currentIndex);
}
this.historyItems.push(historyItem);
this.currentIndex++;
this.deleteOldItems();
}
private addDataSyncItem(historyItem: HistoryItem) {
const toHistoryItem = this.historyItems[this.currentIndex];
if(toHistoryItem) {
let compositionHistoryItem: CompositionHistoryItem;
if(toHistoryItem instanceof CompositionHistoryItem)
compositionHistoryItem = toHistoryItem;
else {
this.historyItems.splice(this.currentIndex, 1);
compositionHistoryItem = new CompositionHistoryItem();
this.historyItems.push(compositionHistoryItem);
compositionHistoryItem.historyItems.push(toHistoryItem);
}
compositionHistoryItem.dataSyncItems.push(historyItem);
}
else if(this.historyItems.length)
this.startDataSyncItem = historyItem;
}
private deleteOldItems() {
const exceedItemsCount: number = this.historyItems.length - History.MAX_HISTORY_ITEM_COUNT;
if(exceedItemsCount > 0 && this.currentIndex > exceedItemsCount) {
this.historyItems.splice(0, exceedItemsCount);
this.currentIndex -= exceedItemsCount;
}
}
public getNextId() {
this.incrementalId++;
return this.incrementalId;
}
public clear() {
this.currentIndex = -1;
this.unmodifiedIndex = -1;
this.incrementalId = -1;
this.historyItems = [];
delete this.transaction;
this.transactionLevel = -1;
}
public resetModified() {
this.unmodifiedIndex = this.currentIndex;
}
public getCurrentItemId(): number {
if(this.currentIndex === -1)
return -1;
const currentItem = this.historyItems[this.currentIndex];
if(currentItem.uniqueId === -1)
currentItem.uniqueId = this.getNextId();
return currentItem.uniqueId;
}
public undoTransaction() {
const items = this.transaction.historyItems;
while(items.length)
items.pop().undo(this.modelManipulator);
}
public undoTransactionTo(item: HistoryItem) {
const items = this.transaction.historyItems;
while(items.length) {
const ti = items.pop();
ti.undo(this.modelManipulator);
if(ti === item)
return;
}
}
raiseChanged() {
if(this.transactionLevel === -1)
this.onChanged.raise("notifyHistoryChanged");
}
}
export interface IHistoryChangesListener {
notifyHistoryChanged();
}