devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
225 lines (224 loc) • 11.7 kB
JavaScript
import { EventDispatcher } from '../utils/event-dispatcher';
import { Field } from '../model/fields/field';
import { SubDocumentInterval, SubDocumentIntervals } from '../model/sub-document';
import { TableSelectionExtender } from './selected-cells-engine';
import { BatchUpdatableObject } from '@devexpress/utils/lib/class/batch-updatable';
import { IntervalAlgorithms } from '@devexpress/utils/lib/intervals/algorithms';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { SearchUtils } from '@devexpress/utils/lib/utils/search';
import { ModelScrollManager } from '../scroll/model-scroll-manager';
import { ScrollState } from '../scroll/model-states';
import { SelectionFloatingState, SelectionState } from './selection-state';
export class SetSelectionStateOptions {
constructor() {
this.correctIntervalDueToFields = true;
this.correctIntervalDueToTables = true;
this.useFieldUiChecks = true;
this.isForceUpdate = false;
}
noFieldCorrect() {
this.correctIntervalDueToFields = false;
return this;
}
noTablesCorrect() {
this.correctIntervalDueToTables = false;
return this;
}
noFieldUiChecks() {
this.useFieldUiChecks = false;
return this;
}
forceUpdate() {
this.isForceUpdate = true;
return this;
}
}
export class Selection extends BatchUpdatableObject {
get currState() { return this._state; }
get keepX() { return this._state.keepX; }
set keepX(val) { this._state.keepX = val; }
get forwardDirection() { return this._state.forwardDirection; }
set forwardDirection(val) { this._state.forwardDirection = val; }
get endOfLine() { return this._state.endOfLine; }
set endOfLine(val) { this._state.endOfLine = val; }
get pageIndex() { return this._state.pageIndex; }
set pageIndex(val) { this._state.pageIndex = val; }
get intervals() { return this._state.intervalsInfo.intervals; }
get activeSubDocument() { return this._state.intervalsInfo.subDocument; }
get lastSelectedInterval() { return this._state.interval; }
get intervalsInfo() { return this._state.intervalsInfo; }
get prevState() { return this._prevState; }
get multiselection() { return this._state.intervalsInfo.multiselection; }
get anchorPosition() { return this._state.anchorPostion; }
get reversedAnchorPostion() { return this._state.reversedAnchorPostion; }
isCollapsed() { return this._state.intervalsInfo.isCollapsed; }
get subDocumentIntervals() {
return new SubDocumentIntervals(this.activeSubDocument, ListUtils.deepCopy(this._state.intervalsInfo.intervals));
}
get subDocumentInterval() { return this._state.intervalsInfo.subDocInterval; }
get specialRunInfo() { return this._state.intervalsInfo.specialRunInfo; }
;
get tableInfo() { return this._state.intervalsInfo.tableInfo; }
constructor(model, layout, activeSubDocument) {
super();
this.searchIntervals = [];
this.misspelledIntervals = [];
this.onChanged = new EventDispatcher();
this.onSearchChanged = new EventDispatcher();
this.onMisspelledSelectionChanged = new EventDispatcher();
this.model = model;
this.layout = layout;
this.scrollManager = new ModelScrollManager();
this._prevState = SelectionState.getDefault(activeSubDocument);
this._state = SelectionState.getDefault(activeSubDocument);
}
dispose() {
this.onChanged.dispose();
}
setSelection(params) {
const options = new SetSelectionStateOptions();
options.correctIntervalDueToFields = params.correctIntervalDueToFields;
options.correctIntervalDueToTables = params.correctIntervalDueToTables;
options.useFieldUiChecks = params.useFieldUiChecks;
this.changeState((newState) => {
newState.intervalsInfo.intervals = [params.interval];
newState.intervalsInfo.lastIntervalIndex = 0;
newState.endOfLine = params.endOfLine;
newState.keepX = params.keepX;
}, options);
this.resetInputPositionIfNeeded();
}
getState() {
return this._state.clone();
}
changeState(changeState, options = new SetSelectionStateOptions()) {
const newState = this.getState();
changeState(newState);
return this.setState(newState, options);
}
setState(newState, options = new SetSelectionStateOptions()) {
const posOfLastInterval = newState.interval.start;
if (options.correctIntervalDueToTables) {
for (let interval of newState.intervalsInfo.intervals)
TableSelectionExtender.correctIntervalDueToTables(newState.intervalsInfo.subDocument, interval);
}
if (options.correctIntervalDueToFields) {
for (let interval of newState.intervalsInfo.intervals)
if (options.useFieldUiChecks)
Field.correctIntervalDueToFields(newState.intervalsInfo.subDocument, interval);
else
Field.correctIntervalDueToFieldsWithoutUiChecks(newState.intervalsInfo.subDocument, interval);
}
for (let interval of newState.intervalsInfo.intervals)
new SubDocumentInterval(newState.intervalsInfo.subDocument, interval).validateInterval();
newState.intervalsInfo.intervals = IntervalAlgorithms.getMergedIntervals(newState.intervalsInfo.intervals, true);
newState.intervalsInfo.lastIntervalIndex = Math.max(0, SearchUtils.normedInterpolationIndexOf(newState.intervalsInfo.intervals, (curr) => curr.start, posOfLastInterval));
const isSelectionChanged = options.isForceUpdate ||
!ListUtils.equals(this._state.intervalsInfo.intervals, newState.intervalsInfo.intervals) ||
!this._state.partiallyEquals(newState) ||
this._state.intervalsInfo.subDocument != newState.intervalsInfo.subDocument;
if (isSelectionChanged) {
this._prevState = this._state;
this._state = newState;
newState.intervalsInfo.resetTableInfo();
newState.intervalsInfo.specialRunInfo.init(newState.intervalsInfo, this.model);
this.raiseSelectionChanged();
}
return isSelectionChanged;
}
correctAfterTextBufferChanged() {
const intersection = IntervalAlgorithms.getIntersectionsTwoArraysOfInterval([FixedInterval.fromPositions(0, this.activeSubDocument.getDocumentEndPosition())], this.intervals);
this.changeState((newState) => {
const posOfLastInterval = newState.intervalsInfo.interval.start;
newState.intervalsInfo.intervals = intersection;
newState.intervalsInfo.lastIntervalIndex = Math.max(0, SearchUtils.normedInterpolationIndexOf(newState.intervalsInfo.intervals, (curr) => curr.start, posOfLastInterval));
});
}
getFloatingState() {
return new SelectionFloatingState(this.getState());
}
restoreFloatingState(state) {
this.setState(state.finalize());
}
setSearchSelectionIntervals(intervals) {
this.searchIntervals = IntervalAlgorithms.getMergedIntervals(intervals, false);
this.raiseSearchSelectionChanged();
}
resetSearchSelection() {
if (this.searchIntervals.length) {
this.searchIntervals = [];
this.raiseSearchSelectionChanged();
}
}
onUpdateUnlocked(occurredEvents) {
if (occurredEvents & SelectionBatchUpdateEvents.SelectionChanged)
this.raiseSelectionChanged();
if (occurredEvents & SelectionBatchUpdateEvents.SearchSelectionChanged)
this.raiseSearchSelectionChanged();
if (occurredEvents & SelectionBatchUpdateEvents.MisspelledSelectionChanged)
this.raiseMisspelledSelectionChanged();
}
setMisspelledSelectionIntervals(intervals) {
this.misspelledIntervals = IntervalAlgorithms.getMergedIntervals(intervals, false);
this.raiseMisspelledSelectionChanged();
}
raiseSelectionChanged() {
if (this.isUpdateLocked())
this.registerOccurredEvent(SelectionBatchUpdateEvents.SelectionChanged);
else
this.onChanged.listeners.forEach(listener => listener.NotifySelectionChanged(this));
}
raiseSearchSelectionChanged() {
if (this.isUpdateLocked())
this.registerOccurredEvent(SelectionBatchUpdateEvents.SearchSelectionChanged);
else
this.onSearchChanged.listeners.forEach(listener => listener.NotifySearchSelectionChanged());
}
raiseMisspelledSelectionChanged() {
if (this.isUpdateLocked())
this.registerOccurredEvent(SelectionBatchUpdateEvents.MisspelledSelectionChanged);
else
this.onMisspelledSelectionChanged.listeners.forEach(listener => listener.NotifyMisspelledSelectionChanged());
}
deprecatedSetSelection(firstPosition, secondPosition, endOfLine, keepX, _upd, correctIntervalDueToFields = true, correctIntervalDueToTables = true, visibleModelPosition = ModelScrollManager.StandartScrollPosition, useFieldUiChecks = true) {
const options = new SetSelectionStateOptions();
options.useFieldUiChecks = useFieldUiChecks;
options.correctIntervalDueToTables = correctIntervalDueToTables;
options.correctIntervalDueToFields = correctIntervalDueToFields;
this.changeState((newState) => {
newState.setInterval(new FixedInterval(Math.min(firstPosition, secondPosition), Math.abs(firstPosition - secondPosition)))
.setKeepX(keepX)
.setEndOfLine(endOfLine)
.setForwardDirection(secondPosition >= firstPosition);
}, options);
if (visibleModelPosition != ModelScrollManager.DontChangeScrollPosition)
this.scrollManager.setScroll(ModelScrollManager.StandartScrollPosition ?
new ScrollState().byModelPosition(this)
.useCurrentPosition(this)
.useStdRelativePosition()
.useStdOffset() :
new ScrollState().byModelPosition(this)
.setModelPosition(visibleModelPosition)
.useStdRelativePosition()
.useStdOffset());
this.resetInputPositionIfNeeded();
}
shouldResetInputPosition() {
var _a;
const currentState = this.getState();
return currentState.intervalsInfo.subDocument.id != ((_a = this.prevState.intervalsInfo.subDocument) === null || _a === void 0 ? void 0 : _a.id) ||
!ListUtils.equals(currentState.intervalsInfo.intervals, this.prevState.intervalsInfo.intervals);
}
resetInputPositionIfNeeded() {
if (this.shouldResetInputPosition())
this.inputPosition.reset();
}
}
var SelectionBatchUpdateEvents;
(function (SelectionBatchUpdateEvents) {
SelectionBatchUpdateEvents[SelectionBatchUpdateEvents["None"] = 0] = "None";
SelectionBatchUpdateEvents[SelectionBatchUpdateEvents["SelectionChanged"] = 1] = "SelectionChanged";
SelectionBatchUpdateEvents[SelectionBatchUpdateEvents["SearchSelectionChanged"] = 4] = "SearchSelectionChanged";
SelectionBatchUpdateEvents[SelectionBatchUpdateEvents["MisspelledSelectionChanged"] = 8] = "MisspelledSelectionChanged";
})(SelectionBatchUpdateEvents || (SelectionBatchUpdateEvents = {}));