devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
292 lines (291 loc) • 20.3 kB
JavaScript
import { LayoutPositionCreatorConflictFlags, LayoutPositionMainSubDocumentCreator, LayoutPositionOtherSubDocumentCreator } from '../../layout-engine/layout-position-creator';
import { DocumentLayoutDetailsLevel } from '../../layout/document-layout-details-level';
import { LayoutNumberingListBox } from '../../layout/main-structures/layout-boxes/layout-numbering-list-box';
import { LayoutParagraphMarkBox } from '../../layout/main-structures/layout-boxes/layout-paragraph-mark-box';
import { Field } from '../../model/fields/field';
import { ListLevelFontNameHistoryItem, ListLevelFontSizeHistoryItem } from '../../model/history/items/list-level-character-properties-history-items';
import { ListLevelDisplayFormatStringHistoryItem, ListLevelFormatHistoryItem, ListLevelOriginalLeftIndentHistoryItem } from '../../model/history/items/list-level-properties-history-items';
import { AddAbstractNumberingListHistoryItem, AddNumberingListHistoryItem, AddParagraphToListHistoryItem } from '../../model/history/items/numbering-list-history-items';
import { ParagraphFirstLineIndentHistoryItem, ParagraphFirstLineIndentTypeHistoryItem, ParagraphLeftIndentHistoryItem } from '../../model/history/items/paragraph-properties-history-items';
import { RemoveIntervalHistoryItem } from '../../model/history/items/remove-interval-history-item';
import { NumberingHelper } from '../../model/numbering-lists/numbering-helper';
import { AbstractNumberingList, NumberingList, NumberingType } from '../../model/numbering-lists/numbering-list';
import { NumberingListIndexCalculator } from '../../model/numbering-lists/numbering-list-index-calculator';
import { ParagraphFirstLineIndent } from '../../model/paragraph/paragraph-properties';
import { RichUtils } from '../../model/rich-utils';
import { SubDocumentInterval } from '../../model/sub-document';
import { UnitConverter } from '@devexpress/utils/lib/class/unit-converter';
import { Errors } from '@devexpress/utils/lib/errors';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { isNumber } from '@devexpress/utils/lib/utils/common';
import { ListUtils } from '@devexpress/utils/lib/utils/list';
import { SelectionHistoryItem } from '../../model/history/selection/selection-history-item';
import { CommandBase } from '../command-base';
import { IntervalCommandStateEx } from '../command-states';
export class NumberingListCommandBaseBase extends CommandBase {
getState(options = this.convertToCommandOptions(undefined)) {
const intervals = ListUtils.deepCopy(options.intervalsInfo.intervals);
return new IntervalCommandStateEx(this.isEnabled(), intervals, this.getValue(intervals, options.subDocument));
}
getValue(intervals, subDocument) {
return this.areAllParagraphsHasValidNumberingListType(intervals, subDocument);
}
getIntervalsForModifying() {
return RichUtils.getIntervalsOfSelectedParagraphs(this.selection.intervals, this.selection.activeSubDocument);
}
DEPRECATEDConvertOptionsParameter(parameter) {
return isNumber(parameter) ? parameter : -1;
}
deleteNumberingList(paragraphIndices, subDocument) {
NumberingHelper.deleteNumberingList(this.control.modelManager, subDocument, paragraphIndices);
}
insertNumberingList(paragraphIndices, startIndex, subDocument) {
var calculator = new NumberingListIndexCalculator(subDocument, this.getNumberingListType(), startIndex === undefined ? -1 : startIndex);
var targetListInfo = calculator.getTargetListInfo(paragraphIndices);
var targetListIndex = targetListInfo ? targetListInfo.listIndex : this.createNewList(this.getAbstractNumberingList());
var targetListLevelIndex = targetListInfo ? targetListInfo.listlevelIndex : -1;
var paragraphsLayoutPositions = this.getParagraphsLayoutPositions(paragraphIndices, subDocument);
var paragraphsLevelIndices = this.getParagraphsLevelIndices(paragraphIndices, paragraphsLayoutPositions, !!targetListInfo, targetListIndex, targetListLevelIndex, subDocument);
this.insertNumberingListCore(paragraphIndices, targetListIndex, paragraphsLevelIndices, paragraphsLayoutPositions, subDocument);
}
changeNumberingList(paragraphIndices, subDocument) {
let paragraph = subDocument.paragraphs[paragraphIndices[0]];
let numberingListIndex = paragraph.getNumberingListIndex();
let targetListIndex = this.createNewList(this.getAbstractNumberingList());
this.assignLevelsIndents(paragraphIndices[0], targetListIndex, subDocument);
for (let i = 0, paragraph; paragraph = subDocument.paragraphs[i]; i++) {
if (paragraph.getNumberingListIndex() === numberingListIndex)
this.history.addAndRedo(new AddParagraphToListHistoryItem(this.modelManipulator, subDocument, i, targetListIndex, paragraph.getListLevelIndex()));
}
}
modifyLevels(paragraphIndices, subDocument) {
let paragraph = subDocument.paragraphs[paragraphIndices[0]];
let numberingListIndex = paragraph.getNumberingListIndex();
const targetListIndex = this.createNewList(this.getAbstractNumberingList());
const targetList = this.control.modelManager.model.numberingLists[targetListIndex];
const abstractNumberingListIndex = this.control.modelManager.model.numberingLists[numberingListIndex].abstractNumberingListIndex;
ListUtils.forEach(paragraphIndices, (index) => {
const paragraph = subDocument.paragraphs[index];
if (paragraph.getNumberingListIndex() === numberingListIndex) {
const levelIndex = paragraph.getListLevelIndex();
const targetListLevel = targetList.levels[levelIndex];
this.history.addAndRedo(new ListLevelDisplayFormatStringHistoryItem(this.modelManipulator, true, abstractNumberingListIndex, levelIndex, targetListLevel.getListLevelProperties().displayFormatString));
this.history.addAndRedo(new ListLevelFormatHistoryItem(this.modelManipulator, true, abstractNumberingListIndex, levelIndex, targetListLevel.getListLevelProperties().format));
this.history.addAndRedo(new ListLevelFontNameHistoryItem(this.modelManipulator, true, abstractNumberingListIndex, levelIndex, targetListLevel.getCharacterMergedProperties().fontInfo, true));
this.history.addAndRedo(new ListLevelFontSizeHistoryItem(this.modelManipulator, true, abstractNumberingListIndex, levelIndex, targetListLevel.getCharacterMergedProperties().fontSize, true));
}
});
}
getAbstractNumberingList() {
return this.control.modelManager.model.abstractNumberingListTemplates[this.getNumberingListTemplateIndex(this.getNumberingListType())];
}
insertNumberingListCore(paragraphIndices, targetListIndex, paragraphsLevelIndices, paragraphsLayoutPositions, subDocument) {
var paragraphIndicesLength = paragraphIndices.length;
for (let i = 0; i < paragraphIndicesLength; i++) {
let paragraphIndex = paragraphIndices[i];
var paragraph = subDocument.paragraphs[paragraphIndex];
this.processOldNumberingList(paragraph, subDocument);
var targetListLevel = paragraphsLevelIndices[i];
if (!paragraph.isInList())
this.deleteLeadingWhiteSpaces(paragraph, paragraphsLayoutPositions[i].row.boxes, targetListLevel < 0, subDocument);
if (targetListLevel >= 0) {
this.history.addAndRedo(new AddParagraphToListHistoryItem(this.modelManipulator, subDocument, paragraphIndex, targetListIndex, targetListLevel));
this.history.addAndRedo(new ParagraphLeftIndentHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, new FixedInterval(paragraph.startLogPosition.value, 1)), paragraph.maskedParagraphProperties.leftIndent, false));
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, new FixedInterval(paragraph.startLogPosition.value, 1)), paragraph.maskedParagraphProperties.firstLineIndent, false));
}
}
}
processOldNumberingList(paragraph, subDocument) {
if (paragraph.isInList()) {
if (paragraph.numberingListIndex == NumberingList.NumberingListNotSettedIndex) {
var leftIndent = paragraph.getParagraphMergedProperties().leftIndent;
this.history.addAndRedo(new ParagraphLeftIndentHistoryItem(this.modelManipulator, new SubDocumentInterval(subDocument, new FixedInterval(paragraph.startLogPosition.value, 1)), leftIndent, true));
}
}
}
deleteLeadingWhiteSpaces(paragraph, boxes, replaceOnIndent, subDocument) {
var length = 0;
var leftIndent = 0;
var manipulator = this.modelManipulator;
for (var i = 0, box; box = boxes[i]; i++) {
if (box.isWhitespace() && this.notInsideField(paragraph.startLogPosition.value + length + box.getLength(), subDocument)) {
length += box.getLength();
leftIndent += box.width;
}
else
break;
}
if (length > 0) {
this.correctSelectionIntervals(new FixedInterval(paragraph.startLogPosition.value, length));
this.history.addAndRedo(new RemoveIntervalHistoryItem(manipulator, new SubDocumentInterval(subDocument, new FixedInterval(paragraph.startLogPosition.value, length)), false));
}
if (replaceOnIndent && leftIndent > 0) {
leftIndent = UnitConverter.pixelsToTwips(leftIndent);
var properties = paragraph.getParagraphMergedProperties();
var interval = paragraph.interval;
if (properties.firstLineIndentType === ParagraphFirstLineIndent.Hanging) {
if (leftIndent < properties.firstLineIndent)
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), properties.firstLineIndent - leftIndent, true));
else if (properties.firstLineIndent === leftIndent) {
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), 0, true));
this.history.addAndRedo(new ParagraphFirstLineIndentTypeHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), ParagraphFirstLineIndent.None, true));
}
else {
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), leftIndent - properties.firstLineIndent, true));
this.history.addAndRedo(new ParagraphFirstLineIndentTypeHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), ParagraphFirstLineIndent.Indented, true));
}
}
else {
this.history.addAndRedo(new ParagraphFirstLineIndentHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), properties.firstLineIndent + leftIndent, true));
if (properties.firstLineIndentType === ParagraphFirstLineIndent.None)
this.history.addAndRedo(new ParagraphFirstLineIndentTypeHistoryItem(manipulator, new SubDocumentInterval(subDocument, interval), ParagraphFirstLineIndent.Indented, true));
}
}
}
notInsideField(position, subDocument) {
return Field.binaryIndexOf(subDocument.fields, position) < 0;
}
correctSelectionIntervals(removingInterval) {
var intervals = ListUtils.deepCopy(this.selection.intervalsInfo.intervals);
for (let i = 0, interval; interval = intervals[i]; i++) {
if (interval.start > removingInterval.start) {
var newSelectionEnd = Math.max(removingInterval.start, interval.end - removingInterval.length);
var newSelectionStart = Math.max(removingInterval.start, interval.start - removingInterval.length);
intervals[i] = FixedInterval.fromPositions(newSelectionStart, newSelectionEnd);
}
}
this.history.addAndRedo(new SelectionHistoryItem(this.modelManipulator, this.selection, this.selection.getState(), this.selection.getState().setIntervals(intervals)));
}
getParagraphsLayoutPositions(paragraphIndices, subDocument) {
var result = [];
var paragraphIndicesLength = paragraphIndices.length;
for (let i = 0; i < paragraphIndicesLength; i++) {
var paragraphIndex = paragraphIndices[i];
var paragraph = subDocument.paragraphs[paragraphIndex];
var logPosition = paragraph.startLogPosition.value;
var endRowConflictTags = new LayoutPositionCreatorConflictFlags().setDefault(false);
var middleRowConflictTags = new LayoutPositionCreatorConflictFlags().setDefault(false);
result.push(subDocument.isMain()
? LayoutPositionMainSubDocumentCreator.ensureLayoutPosition(this.control.layoutFormatterManager, subDocument, logPosition, DocumentLayoutDetailsLevel.Box, endRowConflictTags, middleRowConflictTags)
: new LayoutPositionOtherSubDocumentCreator(this.control.layout, subDocument, logPosition, this.selection.pageIndex, DocumentLayoutDetailsLevel.Box).create(endRowConflictTags, middleRowConflictTags));
}
return result;
}
getParagraphsLevelIndices(paragraphIndices, layoutPositions, _continueNumberingList, listIndex, listLevelIndex, subDocument) {
const result = [];
const numberingList = this.control.modelManager.model.numberingLists[listIndex];
const paragraphIndicesLength = paragraphIndices.length;
for (let i = 0; i < paragraphIndicesLength; i++) {
var paragraphIndex = paragraphIndices[i];
if (listLevelIndex < 0) {
const paragraph = subDocument.paragraphs[paragraphIndex];
const layoutPosition = layoutPositions[i];
let offsetLeft = layoutPosition.row.x;
const cellInfo = layoutPosition.row.tableCellInfo;
if (cellInfo)
offsetLeft -= cellInfo.x;
const box = layoutPosition.row.numberingListBox ? layoutPosition.row.numberingListBox :
this.getStartBox(layoutPosition.row.boxes);
offsetLeft += box instanceof LayoutNumberingListBox ? box.textBox.x : box.x;
if (box instanceof LayoutParagraphMarkBox && (paragraphIndicesLength > 1 && (paragraph.length <= 1 || i !== 0)))
result.push(-1);
else
result.push(this.calculateParagraphListLevel(offsetLeft, paragraph, numberingList));
}
else
result.push(listLevelIndex);
}
this.assignLevelsIndents(paragraphIndices[0], listIndex, subDocument);
return result;
}
calculateParagraphListLevel(layoutLeftIndent, _paragraph, numberingList) {
var modelLeftIndent = UnitConverter.pixelsToTwips(layoutLeftIndent);
for (var i = 0, level; level = numberingList.levels[i]; i++) {
var levelParagraphProperties = level.getParagraphMergedProperties();
var actualNumberingPosition = levelParagraphProperties.firstLineIndentType == ParagraphFirstLineIndent.Hanging ?
(levelParagraphProperties.leftIndent - levelParagraphProperties.firstLineIndent) : levelParagraphProperties.leftIndent;
if (modelLeftIndent <= actualNumberingPosition)
return i;
}
return numberingList.levels.length - 1;
}
getStartBox(boxes) {
for (var i = 0, box; box = boxes[i]; i++) {
if (!box.isWhitespace())
return box;
}
return boxes[0];
}
createNewList(template) {
var abstractNumberingList = new AbstractNumberingList(this.control.modelManager.model);
abstractNumberingList.copyFrom(template);
abstractNumberingList.resetId();
this.history.addAndRedo(new AddAbstractNumberingListHistoryItem(this.modelManipulator, abstractNumberingList));
var abstractNumberingListIndex = this.control.modelManager.model.abstractNumberingLists.length - 1;
var numberingList = new NumberingList(this.control.modelManager.model, abstractNumberingListIndex);
this.history.addAndRedo(new AddNumberingListHistoryItem(this.modelManipulator, numberingList));
return this.control.modelManager.model.numberingLists.length - 1;
}
processParagraphByIndex(_paragraphIndex) {
return true;
}
getNumberingListTemplateIndex(type) {
return NumberingHelper.getNumberingListTemplateIndex(this.control.modelManager.model, type);
}
areAllParagraphsHasValidNumberingListType(intervals, subDocument) {
var levelType = this.getNumberingListType();
var paragraphIndices = subDocument.getParagraphIndicesByIntervals(intervals);
for (let i = paragraphIndices.length - 1; i >= 0; i--) {
let paragraphIndex = paragraphIndices[i];
var paragraph = subDocument.paragraphs[paragraphIndex];
if (!paragraph.isInList() || paragraph.getNumberingList().getLevelType(paragraph.getListLevelIndex()) !== levelType)
return false;
}
return true;
}
getNumberingListType() {
throw new Error(Errors.NotImplemented);
}
assignLevelsIndents(paragraphIndex, listIndex, subDocument) {
var numberingList = this.control.modelManager.model.numberingLists[listIndex];
this.assignLevelsIndentsCore(paragraphIndex, listIndex, numberingList.levels, subDocument);
}
assignLevelsIndentsCore(paragraphIndex, listIndex, listLevels, subDocument) {
var paragraph = subDocument.paragraphs[paragraphIndex];
if (!paragraph.isInList())
return;
let originNumberingList = paragraph.getNumberingList();
for (var i = 0, listLevel; listLevel = listLevels[i]; i++) {
let originListLevelProperties = originNumberingList.levels[0].getListLevelProperties();
let listLevelProperties = listLevel.getListLevelProperties();
if (originListLevelProperties.originalLeftIndent !== listLevelProperties.originalLeftIndent)
this.history.addAndRedo(new ListLevelOriginalLeftIndentHistoryItem(this.modelManipulator, false, listIndex, i, originListLevelProperties.originalLeftIndent));
}
}
}
export class NumberingListCommandBase extends NumberingListCommandBaseBase {
executeCore(state, options) {
this.history.beginTransaction();
const subDocument = options.subDocument;
var paragraphIndices = subDocument.getParagraphIndicesByIntervals(state.intervals);
let targetStartIndex = options.param;
if (state.value)
this.deleteNumberingList(paragraphIndices, subDocument);
else if (this.selection.isCollapsed() && options.subDocument.paragraphs[paragraphIndices[0]].isInList() && targetStartIndex < 0) {
const isMultilevelListType = this.getNumberingListType() == NumberingType.MultiLevel;
const firstLevelSelected = ListUtils.unsafeAnyOf(paragraphIndices, (index) => {
const paragraph = this.selection.activeSubDocument.paragraphs[index];
return paragraph.getListLevelIndex() == 0;
});
if (isMultilevelListType || firstLevelSelected)
this.changeNumberingList(paragraphIndices, subDocument);
else
this.modifyLevels(paragraphIndices, subDocument);
}
else
this.insertNumberingList(paragraphIndices, targetStartIndex, subDocument);
this.history.endTransaction();
this.selection.changeState(newState => newState.setEndOfLine(false));
return true;
}
}