devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
163 lines (162 loc) • 10.1 kB
JavaScript
import { LayoutPositionCreatorConflictFlags, LayoutPositionMainSubDocumentCreator, LayoutPositionOtherSubDocumentCreator } from '../../layout-engine/layout-position-creator';
import { DocumentLayoutDetailsLevel } from '../../layout/document-layout-details-level';
import { Field } from '../../model/fields/field';
import { RichEditClientCommand } from '../client-command';
import { SelectionCommandBase, TableNavDirection } from './selection-command-base';
export class GoToNextCharacterCommandBase extends SelectionCommandBase {
executeCore(_state, _options) {
var position = this.getPosition();
if (position == -1)
return false;
this.setSelection(position);
return true;
}
getPosition() {
var selection = this.selection;
var subDocument = this.selection.activeSubDocument;
var initialModelPosition = selection.forwardDirection ? selection.lastSelectedInterval.end : selection.lastSelectedInterval.start;
var initialLayoutPosition = subDocument.isMain()
? LayoutPositionMainSubDocumentCreator.ensureLayoutPosition(this.control.layoutFormatterManager, subDocument, initialModelPosition, DocumentLayoutDetailsLevel.Character, new LayoutPositionCreatorConflictFlags().setDefault(selection.endOfLine), new LayoutPositionCreatorConflictFlags().setCustom(true, true, false, false))
: new LayoutPositionOtherSubDocumentCreator(this.control.layout, subDocument, initialModelPosition, selection.pageIndex, DocumentLayoutDetailsLevel.Character)
.create(new LayoutPositionCreatorConflictFlags().setDefault(selection.endOfLine), new LayoutPositionCreatorConflictFlags().setCustom(true, true, false, false));
var nextCharLayoutPosition;
if (this.extendSelection()) {
nextCharLayoutPosition = initialLayoutPosition;
if (nextCharLayoutPosition.getLogPosition() == initialModelPosition) {
nextCharLayoutPosition = this.getNextCharacterPosition(nextCharLayoutPosition);
if (nextCharLayoutPosition.isPositionBeforeFirstBoxInRow())
nextCharLayoutPosition = this.getNextCharacterPosition(nextCharLayoutPosition);
}
else if (nextCharLayoutPosition.isPositionBeforeFirstBoxInRow())
nextCharLayoutPosition = this.getNextCharacterPosition(nextCharLayoutPosition);
else if (nextCharLayoutPosition.getLogPosition() < initialModelPosition)
nextCharLayoutPosition = this.getNextCharacterPosition(nextCharLayoutPosition);
}
else {
if (!selection.isCollapsed()) {
var pos;
if (selection.forwardDirection) {
pos = initialLayoutPosition.getLogPosition();
}
else {
var selectionEndPosition = selection.lastSelectedInterval.end;
pos = (subDocument.isMain()
? LayoutPositionMainSubDocumentCreator.ensureLayoutPosition(this.control.layoutFormatterManager, subDocument, selectionEndPosition, DocumentLayoutDetailsLevel.Character, new LayoutPositionCreatorConflictFlags().setDefault(selection.endOfLine), new LayoutPositionCreatorConflictFlags().setDefault(true))
: new LayoutPositionOtherSubDocumentCreator(this.control.layout, subDocument, selectionEndPosition, selection.pageIndex, DocumentLayoutDetailsLevel.Character)
.create(new LayoutPositionCreatorConflictFlags().setDefault(selection.endOfLine), new LayoutPositionCreatorConflictFlags().setDefault(true))).getLogPosition();
}
return pos < subDocument.getDocumentEndPosition() ? pos : pos - 1;
}
nextCharLayoutPosition = initialLayoutPosition;
var needFindPosition = nextCharLayoutPosition.getLogPosition() == initialModelPosition;
if (!needFindPosition)
needFindPosition = Field.binaryIndexOf(subDocument.fields, initialModelPosition + 1) >= 0;
if (needFindPosition) {
if (nextCharLayoutPosition.box.getLength() == nextCharLayoutPosition.charOffset)
nextCharLayoutPosition = this.getNextCharacterPosition(nextCharLayoutPosition);
do {
var prevLP = nextCharLayoutPosition;
nextCharLayoutPosition = this.getNextCharacterPosition(prevLP);
if (prevLP === nextCharLayoutPosition) {
nextCharLayoutPosition = initialLayoutPosition;
break;
}
} while (!(nextCharLayoutPosition.box.isVisible() || nextCharLayoutPosition.isPositionBeforeFirstBoxInRow()));
}
}
const nextCharModelPosition = Math.max(initialModelPosition, nextCharLayoutPosition.getLogPosition());
return nextCharModelPosition == initialModelPosition ? -1 : nextCharModelPosition;
}
getNextCharacterPosition(layoutPosition) {
const nextLayoutPosition = layoutPosition.clone();
if (nextLayoutPosition.charOffset + 1 <= nextLayoutPosition.box.getLength()) {
nextLayoutPosition.charOffset++;
return nextLayoutPosition;
}
nextLayoutPosition.charOffset = 0;
if (nextLayoutPosition.boxIndex + 1 < nextLayoutPosition.row.boxes.length) {
nextLayoutPosition.boxIndex++;
nextLayoutPosition.box = nextLayoutPosition.row.boxes[nextLayoutPosition.boxIndex];
return nextLayoutPosition;
}
nextLayoutPosition.boxIndex = 0;
if (nextLayoutPosition.advanceToNextRow(this.control.layout)) {
nextLayoutPosition.box = nextLayoutPosition.row.boxes[0];
return nextLayoutPosition;
}
else
return layoutPosition;
}
}
export class GoToNextCharacterCommand extends GoToNextCharacterCommandBase {
setSelection(position) {
this.selection.deprecatedSetSelection(position, position, false, -1, true);
}
extendSelection() {
return false;
}
executeCore(state, options) {
if (this.collapseTableCellSelectionSpatialFocus(TableNavDirection.Right))
return true;
return super.executeCore(state, options);
}
}
export class ExtendGoToNextCharacterCommand extends GoToNextCharacterCommandBase {
shouldCollapseTableSelectionToCaret() {
return this.shouldCollapseSingleCellTableSelection(TableNavDirection.Left, TableNavDirection.Right);
}
setSelection(position) {
if (!this.selection.changeState((newState) => newState.extendLastInterval(position).resetKeepX().setEndOfLine(false)))
ExtendGoToNextCharacterCommand.jumpThroughFieldToRight(this.selection);
}
extendSelection() {
return true;
}
executeCore(state, options) {
const selection = this.selection;
if (this.shouldCollapseTableSelectionToCaret()) {
this.collapseSingleCellTableSelectionToCaret();
return true;
}
const tableInfo = selection.tableInfo;
const rawTblInfo = tableInfo.rawData;
const lastRowInfo = rawTblInfo.rows.length > 0 ? rawTblInfo.rows[rawTblInfo.rows.length - 1] : null;
const lastCell = lastRowInfo?.cells.length > 0 ? lastRowInfo.cells[lastRowInfo.cells.length - 1].cell : null;
const activePos = selection.forwardDirection ? selection.lastSelectedInterval.end : selection.lastSelectedInterval.start;
const isAtCellEnd = lastCell !== null && selection.tableSelectionAnchorCellPosition < 0 &&
activePos === lastCell.endParagraphPosition.value - 1;
const canSelectCell = tableInfo.isSelected && rawTblInfo.areCellsSelectedInSeries &&
(isAtCellEnd || rawTblInfo.atLeastOneCellFullySelected(selection.intervals));
if (!canSelectCell)
return super.executeCore(state, options);
if (isAtCellEnd)
return this.control.commandManager.getCommand(RichEditClientCommand.SelectTableCellsRange).execute(this.control.commandManager.isPublicApiCall, {
firstCell: lastCell,
lastCell: lastCell,
singleCellEntryDirection: TableNavDirection.Right,
});
const anchorCell = selection.getTableSelectionAnchorCell();
const activeCell = selection.getTableSelectionActiveCell();
const cell = activeCell ? activeCell.parentRow.cells[activeCell.parentRow.cells.indexOf(activeCell) + 1] : null;
if (cell) {
const isShrinkToSingle = !!anchorCell && anchorCell.startParagraphPosition.value === cell.startParagraphPosition.value &&
selection.tableSelectionAnchorCellPosition !== selection.tableSelectionActiveCellPosition;
return this.control.commandManager.getCommand(RichEditClientCommand.SelectTableCellsRange).execute(this.control.commandManager.isPublicApiCall, {
firstCell: anchorCell,
lastCell: cell,
singleCellShrinkDirection: isShrinkToSingle ? TableNavDirection.Right : TableNavDirection.None,
});
}
return this.control.commandManager.getCommand(RichEditClientCommand.ExtendLineDown).execute(this.control.commandManager.isPublicApiCall);
}
static jumpThroughFieldToRight(selection) {
const fields = selection.activeSubDocument.fields;
if (fields.length == 0 || selection.forwardDirection)
return;
var interval = selection.lastSelectedInterval;
var position = interval.start;
var field = fields[Math.max(0, Field.normedBinaryIndexOf(fields, position + 1))];
if (field.getFieldStartPosition() == position)
selection.changeState((newState) => newState.extendLastInterval(field.getFieldEndPosition()).setEndOfLine(false).resetKeepX());
}
}