devexpress-richedit
Version:
DevExpress Rich Text Editor is an advanced word-processing tool designed for working with rich text documents.
254 lines (253 loc) • 13.4 kB
JavaScript
import { EvtUtils } from '@devexpress/utils/lib/utils/evt';
import { TextBufferChangedSubDocumentChange } from './model/changes/sub-document/text/text-buffer-changed';
import { InsertTextHistoryItem } from './model/history/items/insert-text-history-item';
import { HistoryItemIntervalState } from './model/history/states/history-item-state';
import { HistoryItemTextBufferStateObject } from './model/history/states/history-item-state-object';
import { InsertTextManipulatorParams } from './model/manipulators/text-manipulator/insert-text-manipulator-params';
import { RunType } from './model/runs/run-type';
import { SubDocumentPosition } from './model/sub-document';
import { Log } from './rich-utils/debug/logger/base-logger/log';
import { LogSource } from './rich-utils/debug/logger/base-logger/log-source';
import { Browser } from '@devexpress/utils/lib/browser';
import { Errors } from '@devexpress/utils/lib/errors';
import { Point } from '@devexpress/utils/lib/geometry/point';
import { FixedInterval } from '@devexpress/utils/lib/intervals/fixed';
import { EnumUtils } from '@devexpress/utils/lib/utils/enum';
import { KeyCode, KeyUtils } from '@devexpress/utils/lib/utils/key';
import { SearchUtils } from '@devexpress/utils/lib/utils/search';
import { RichEditClientCommand } from './commands/client-command';
import { CommandSimpleOptions } from './commands/command-base';
import { ReadOnlyMode } from './interfaces/i-rich-edit-core';
import { SelectionHistoryItem } from './model/history/selection/selection-history-item';
import { MouseHandler } from './mouse-handler/mouse-handler/mouse-handler';
import { TouchHandler } from './mouse-handler/touch-handler/touch-handler';
import { LogObjToStrCanvas } from './rich-utils/debug/logger/canvas-logger/log-obj-to-str-canvas';
import { RichLayoutUtils } from './rich-utils/layout/rich-layout-utils';
import { StringUtils } from '@devexpress/utils/lib/utils/string';
export var MouseButton;
(function (MouseButton) {
MouseButton[MouseButton["None"] = 0] = "None";
MouseButton[MouseButton["Left"] = 1] = "Left";
MouseButton[MouseButton["Right"] = 2] = "Right";
MouseButton[MouseButton["Middle"] = 4] = "Middle";
})(MouseButton || (MouseButton = {}));
export class EventManager {
set mouseWheelEvent(val) {
this.mouseHandler.mouseWheelEvent = val;
this.touchHandler.mouseWheelEvent = val;
}
constructor(control, boxVisualizerManager) {
this.moveLocked = false;
this.shouldPreventContextMenuEvent = true;
this.accumulatedText = "";
this.accumulatedTextInsertId = null;
this.accumulatedTextMaxLength = Browser.Chrome ? 3 : 9;
this.control = control;
this.mouseHandler = new MouseHandler(control, boxVisualizerManager);
this.touchHandler = new TouchHandler(control, boxVisualizerManager);
this.inputTypeHandlers = this.createInputTypeHandlers();
}
dispose() {
clearTimeout(this.accumulatedTextInsertId);
clearTimeout(this.lockMouseMoveTimerId);
clearTimeout(this.onMouseUpTimerId);
}
onShortcut(shortcutCode) {
if (this.accumulatedText.length)
this.insertFunc();
this.control.shortcutManager.processShortcut(shortcutCode);
this.mouseHandler.onShortcut(shortcutCode);
if (EnumUtils.isAnyOf(shortcutCode, KeyCode.Enter, KeyCode.Tab, KeyCode.Space) && this.control.selection.lastSelectedInterval.start > 0)
this.control.autoCorrectService.performAutoCorrect();
}
onMouseDown(evt) {
Log.print(LogSource.EventManager, "onMouseDown", LogObjToStrCanvas.richMouseEvent(evt));
if (!this.control.clientSideEvents.raisePointerDown(evt.mouseEvent)) {
this.lockMouseMove();
this.mouseHandler.onMouseDown(evt);
}
}
onMouseMove(evt) {
Log.print(LogSource.EventManager, "onMouseMove", LogObjToStrCanvas.richMouseEvent(evt));
if (this.moveLocked)
return;
this.mouseHandler.onMouseMove(evt);
}
onMouseUp(evt) {
Log.print(LogSource.EventManager, "onMouseUp", LogObjToStrCanvas.richMouseEvent(evt));
let handled = false;
if (evt.layoutPoint)
handled = this.control.clientSideEvents.raisePointerUp(evt.mouseEvent);
if (!handled) {
this.lockMouseMove();
this.mouseHandler.onMouseUp(evt);
if (evt.layoutPoint) {
if (Browser.TouchUI)
this.control.inputController.setPosition(evt.absolutePoint.x + 2, evt.absolutePoint.y + 2);
this.onMouseUpTimerId = setTimeout(() => this.control.focusManager.captureFocus(), 0);
}
}
}
onTouchStart(evt) {
Log.print(LogSource.EventManager, "onTouchStart", LogObjToStrCanvas.richMouseEvent(evt));
if (!this.control.clientSideEvents.raisePointerDown(evt.mouseEvent))
this.touchHandler.onTouchStart(evt);
}
onTouchEnd(evt) {
Log.print(LogSource.EventManager, "onTouchEnd", LogObjToStrCanvas.richMouseEvent(evt));
let handled = false;
if (evt.layoutPoint)
handled = this.control.clientSideEvents.raisePointerUp(evt.mouseEvent);
if (!handled)
this.touchHandler.onTouchEnd(evt);
}
onTouchMove(evt) {
Log.print(LogSource.EventManager, "onTouchMove", LogObjToStrCanvas.richMouseEvent(evt));
return this.touchHandler.onTouchMove(evt);
}
onDoubleTap(evt) {
Log.print(LogSource.EventManager, "onDoubleTap", LogObjToStrCanvas.richMouseEvent(evt));
this.touchHandler.onDoubleTap(evt);
}
onGestureStart(evt) {
this.touchHandler.onGestureStart(evt);
}
onMouseDblClick(evt) {
Log.print(LogSource.EventManager, "onMouseDoubleClick", LogObjToStrCanvas.richMouseEvent(evt));
this.mouseHandler.onMouseDoubleClick(evt);
this.control.inputController.setPosition(evt.absolutePoint.x, evt.absolutePoint.y);
}
onMouseWheel(evt) {
Log.print(LogSource.EventManager, "onMouseWheel", LogObjToStrCanvas.richMouseEvent(evt));
this.mouseHandler.onMouseWheel(evt);
}
onText(text, isUpdated) {
if (isUpdated)
this.modifyLastInsertedSymbol(text);
else {
if (this.control.commandManager.clipboardTimerId === null) {
this.accumulatedText += text;
if (this.accumulatedTextInsertId) {
clearTimeout(this.accumulatedTextInsertId);
this.accumulatedTextInsertId = null;
}
if (this.accumulatedText.length > this.accumulatedTextMaxLength)
this.insertFunc();
else
this.accumulatedTextInsertId = setTimeout(() => this.insertFunc.apply(this), 1);
}
}
}
onTextReplace(text, length) {
if (length != undefined)
this.modifyLastText(text, length);
else
RichLayoutUtils.modifyTextUnderCursor(this.control, text);
}
modifyLastText(text, length) {
if (length === 0) {
if (!StringUtils.isNullOrEmpty(text)) {
this.control.beginUpdate();
this.control.commandManager.getCommand(RichEditClientCommand.InsertText).execute(this.control.commandManager.isPublicApiCall, new CommandSimpleOptions(this.control, text));
this.control.endUpdate();
}
return true;
}
let endInterval = this.control.selection.lastSelectedInterval.start;
let startInterval = endInterval - length;
let intervalForModify = new FixedInterval(startInterval, endInterval - startInterval);
const insertTextHistoryItem = this.control.modelManager.modelManipulator.text.getLastModifiableHistoryItem((hi) => hi instanceof InsertTextHistoryItem);
if (!insertTextHistoryItem)
return false;
if (!StringUtils.isNullOrEmpty(text)) {
const setSelectionHistoryItem = this.control.modelManager.modelManipulator.text.getLastModifiableHistoryItem((hi) => hi instanceof SelectionHistoryItem);
this.control.modelManager.modelManipulator.range.removeIntervalWithoutHistory(insertTextHistoryItem.params.subDocPos.subDocument, intervalForModify, false);
this.control.modelManager.modelManipulator.text.insertTextInner(new InsertTextManipulatorParams(new SubDocumentPosition(insertTextHistoryItem.params.subDocPos.subDocument, intervalForModify.start), insertTextHistoryItem.params.charPropsBundle, RunType.TextRun, text));
setSelectionHistoryItem.newState.intervalsInfo.intervals[0].start = startInterval + text.length;
insertTextHistoryItem.params.text = text;
var newPositionSelection = setSelectionHistoryItem.newState.intervalsInfo.intervals[0].start;
this.control.selection.changeState((newState) => newState.setPosition(newPositionSelection));
this.control.commandManager.lastTextInsertDate = new Date(0);
}
else
this.control.modelManager.modelManipulator.range.removeIntervalInner(insertTextHistoryItem.params.subDocPos.subDocument, intervalForModify, false);
return true;
}
modifyLastInsertedSymbol(symbol) {
if (symbol.length !== 1)
throw new Error(Errors.InternalException);
const insertTextHistoryInfo = this.control.modelManager.modelManipulator.text.getLastModifiableHistoryItem((hi) => hi instanceof InsertTextHistoryItem);
if (!insertTextHistoryInfo)
throw new Error(Errors.InternalException);
const textLength = insertTextHistoryInfo.params.text.length;
insertTextHistoryInfo.params.text = insertTextHistoryInfo.params.text.substr(0, textLength - 1) + symbol;
this.updateSymbol(insertTextHistoryInfo.params.subDocPos.subDocument, insertTextHistoryInfo.params.subDocPos.position + textLength - 1, symbol);
}
updateSymbol(subDocument, position, symbol) {
const state = new HistoryItemIntervalState();
const chunkIndex = SearchUtils.normedInterpolationIndexOf(subDocument.chunks, c => c.startLogPosition.value, position);
const chunk = subDocument.chunks[chunkIndex];
const chunkRelativePosition = position - chunk.startLogPosition.value;
const oldSymbol = chunk.textBuffer.substr(chunkRelativePosition, 1);
state.register(new HistoryItemTextBufferStateObject(position, symbol));
chunk.textBuffer = chunk.textBuffer.substr(0, chunkRelativePosition) + symbol + chunk.textBuffer.substr(chunkRelativePosition + 1);
this.control.modelManager.modelManipulator.notifyModelChanged(new TextBufferChangedSubDocumentChange(subDocument.id, state));
return oldSymbol;
}
insertFunc() {
const insertCommand = this.control.commandManager.getCommand(RichEditClientCommand.InsertText);
const textWasInserted = insertCommand.execute(this.control.commandManager.isPublicApiCall, this.accumulatedText);
this.accumulatedText = "";
this.accumulatedTextInsertId = -1;
if (textWasInserted)
this.control.autoCorrectService.performAutoCorrect();
}
onFocusIn() {
if (this.control.readOnly === ReadOnlyMode.None) {
if (!this.isFocused())
this.control.clientSideEvents.raiseGotFocus();
this.control.focusManager.isInFocus = true;
}
}
onFocusOut() {
if (this.isFocused())
this.control.clientSideEvents.raiseLostFocus();
this.control.focusManager.isInFocus = false;
}
isFocused() {
return this.control.focusManager.isInFocus;
}
lockMouseMove() {
this.moveLocked = true;
this.lockMouseMoveTimerId = setTimeout(() => this.moveLocked = false, 0);
}
onInput(inputType) {
const commandId = this.inputTypeHandlers[inputType];
if (!commandId)
return false;
const command = this.control.commandManager.getCommand(commandId);
command.execute(false);
return true;
}
createInputTypeHandlers() {
const result = {};
result["insertParagraph"] = RichEditClientCommand.InsertParagraph;
result["deleteContentBackward"] = RichEditClientCommand.ToggleBackspaceKey;
return result;
}
}
export class RichMouseEvent {
constructor(evt, layoutPoint, source, scrollTop, scrollLeft) {
this.layoutPoint = layoutPoint;
this.absolutePoint = new Point(EvtUtils.getEventX(evt), EvtUtils.getEventY(evt));
this.scroll = new Point(scrollLeft, scrollTop);
this.modifiers = KeyUtils.getKeyModifiers(evt);
this.button = this.isLeftButtonPressed(evt) ? MouseButton.Left : MouseButton.Right;
this.middleButtonPressed = !!(evt.buttons & MouseButton.Middle);
this.source = source;
this.mouseEvent = evt;
}
isLeftButtonPressed(evt) {
return !Browser.MSTouchUI ? EvtUtils.isLeftButtonPressed(evt) : evt.button != 2;
}
}