UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

183 lines (163 loc) 6.33 kB
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(); }