You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ha...@apache.org on 2017/03/16 13:37:46 UTC
[26/42] flex-asjs git commit: And here’s TLF…
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditManager.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditManager.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditManager.as
new file mode 100644
index 0000000..2831b32
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditManager.as
@@ -0,0 +1,2050 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.edit
+{
+ import org.apache.flex.core.IUIBase;
+ import org.apache.flex.events.Event;
+ import org.apache.flex.events.KeyboardEvent;
+ import org.apache.flex.events.MouseEvent;
+ import org.apache.flex.events.utils.EditingKeys;
+ import org.apache.flex.events.utils.KeyConverter;
+ import org.apache.flex.events.utils.WhitespaceKeys;
+ import org.apache.flex.reflection.getQualifiedClassName;
+ import org.apache.flex.text.events.IMEEvent;
+ import org.apache.flex.text.events.TextEvent;
+ import org.apache.flex.textLayout.compose.IFlowComposer;
+ import org.apache.flex.textLayout.container.IContainerController;
+ import org.apache.flex.textLayout.debug.assert;
+ import org.apache.flex.textLayout.elements.GlobalSettings;
+ import org.apache.flex.textLayout.elements.IDivElement;
+ import org.apache.flex.textLayout.elements.IFlowElement;
+ import org.apache.flex.textLayout.elements.IFlowGroupElement;
+ import org.apache.flex.textLayout.elements.IFlowLeafElement;
+ import org.apache.flex.textLayout.elements.IInlineGraphicElement;
+ import org.apache.flex.textLayout.elements.ILinkElement;
+ import org.apache.flex.textLayout.elements.IListElement;
+ import org.apache.flex.textLayout.elements.IListItemElement;
+ import org.apache.flex.textLayout.elements.IParagraphElement;
+ import org.apache.flex.textLayout.elements.ISubParagraphGroupElement;
+ import org.apache.flex.textLayout.elements.ITCYElement;
+ import org.apache.flex.textLayout.elements.ITableCellElement;
+ import org.apache.flex.textLayout.elements.ITableElement;
+ import org.apache.flex.textLayout.elements.ITextFlow;
+ import org.apache.flex.textLayout.events.EditEvent;
+ import org.apache.flex.textLayout.events.FlowOperationEvent;
+ import org.apache.flex.textLayout.formats.ITextLayoutFormat;
+ import org.apache.flex.textLayout.formats.TextLayoutFormat;
+ import org.apache.flex.textLayout.operations.ApplyElementIDOperation;
+ import org.apache.flex.textLayout.operations.ApplyElementTypeNameOperation;
+ import org.apache.flex.textLayout.operations.ApplyFormatOperation;
+ import org.apache.flex.textLayout.operations.ApplyFormatToElementOperation;
+ import org.apache.flex.textLayout.operations.ApplyLinkOperation;
+ import org.apache.flex.textLayout.operations.ApplyTCYOperation;
+ import org.apache.flex.textLayout.operations.ClearFormatOnElementOperation;
+ import org.apache.flex.textLayout.operations.ClearFormatOperation;
+ import org.apache.flex.textLayout.operations.CompositeOperation;
+ import org.apache.flex.textLayout.operations.CreateDivOperation;
+ import org.apache.flex.textLayout.operations.CreateListOperation;
+ import org.apache.flex.textLayout.operations.CreateSubParagraphGroupOperation;
+ import org.apache.flex.textLayout.operations.CutOperation;
+ import org.apache.flex.textLayout.operations.DeleteTextOperation;
+ import org.apache.flex.textLayout.operations.FlowOperation;
+ import org.apache.flex.textLayout.operations.InsertInlineGraphicOperation;
+ import org.apache.flex.textLayout.operations.InsertTableElementOperation;
+ import org.apache.flex.textLayout.operations.InsertTextOperation;
+ import org.apache.flex.textLayout.operations.ModifyInlineGraphicOperation;
+ import org.apache.flex.textLayout.operations.MoveChildrenOperation;
+ import org.apache.flex.textLayout.operations.PasteOperation;
+ import org.apache.flex.textLayout.operations.RedoOperation;
+ import org.apache.flex.textLayout.operations.SplitElementOperation;
+ import org.apache.flex.textLayout.operations.SplitParagraphOperation;
+ import org.apache.flex.textLayout.operations.UndoOperation;
+ import org.apache.flex.textLayout.utils.CharacterUtil;
+ import org.apache.flex.textLayout.utils.NavigationUtil;
+ import org.apache.flex.utils.AnimationUtil;
+ import org.apache.flex.utils.undo.IOperation;
+ import org.apache.flex.utils.undo.IUndoManager;
+// import flash.errors.IllegalOperationError;
+
+
+
+ /**
+ * The EditManager class manages editing changes to a TextFlow.
+ *
+ * <p>To enable text flow editing, assign an EditManager object to the <code>interactionManager</code>
+ * property of the TextFlow object. The edit manager handles changes to the text (such as insertions,
+ * deletions, and format changes). Changes are reversible if the edit manager has an undo manager. The edit
+ * manager triggers the recomposition and display of the text flow, as necessary.</p>
+ *
+ * <p>The EditManager class supports the following keyboard shortcuts:</p>
+ *
+ * <table class="innertable" width="100%">
+ * <tr><th>Keys</th><th>Result</th></tr>
+ * <tr><td>ctrl-z</td><td>undo</td></tr>
+ * <tr><td>ctrl-y</td><td>redo</td></tr>
+ * <tr><td>ctrl-backspace</td><td>deletePreviousWord</td></tr>
+ * <tr><td>ctrl-delete</td><td>deleteNextWord</td></tr>
+ * <tr><td>alt+delete</td><td>deleteNextWord</td></tr>
+ * <tr><td>ctrl+alt-delete</td><td>deleteNextWord</td></tr>
+ * <tr><td>ctrl-shift-hyphen</td><td>insert discretionary hyphen</td></tr>
+ * <tr><td>ctrl+backspace</td><td>deletePreviousWord</td></tr>
+ * <tr><td>alt+backspace</td><td>deletePreviousWord</td></tr>
+ * <tr><td>ctrl+alt-backspace</td><td>deletePreviousWord</td></tr>
+ * <tr><td>INSERT</td><td>toggles overWriteMode</td></tr>
+ * <tr><td>backspace</td><td>deletePreviousCharacter</td></tr>
+ * <tr><td>ENTER</td><td>if textFlow.configuration.manageEnterKey in a list it creates a new list item, otherwise creates a new paragraph</td></tr>
+ * <tr><td>shift-ENTER</td><td>if textFlow.configuration.manageEnterKey creates a new paragraph</td></tr>
+ * <tr><td>TAB</td><td>if textFlow.configuration.manageTabKey in a list it creates nested list, otherwise inserts a TAB or overwrites next character with a TAB</td></tr>
+ * <tr><td>shift-TAB</td><td>if textFlow.configuration.manageTabKey in the first item of a list it moves the item out of the list (promotes it)</td></tr>
+ * </table>
+ *
+ * <p><strong>Note:</strong> The following keys do not work on Windows: alt-backspace, alt-delete, ctrl+alt-backspace,
+ * and ctrl+alt-delete. These keys do not generate an event for the runtime.</p>
+ *
+ * @see org.apache.flex.textLayout.elements.TextFlow
+ * @see flashx.undo.UndoManager
+ *
+ * examples\EditManager_example.as -noswf
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public class EditManager extends SelectionManager implements IEditManager
+ {
+ static public var handleShiftAsSoftReturn:Boolean = true;
+ /**
+ * To minimize expensive recompositions during fast typing, inserts
+ * don't necessarily take place immediately. An insert operation that
+ * hasn't yet executed is held here.
+ */
+ private var pendingInsert:InsertTextOperation;
+
+ /*
+ * The object that has the ENTER_FRAME event listener attached to perform pending inserts.
+ */
+// private var enterFrameListener:DisplayObjectContainer;
+
+ /* True if updates get handled on enter_frame, and not immediately. */
+ private var _delayUpdates:Boolean = false;
+
+ /* True if some operations (e.g., handling of text events) can be delayed to next enterFrame by default. False for immediate handling */
+ private var _allowDelayedOperations:Boolean = true;
+
+ /*
+ * The object that has the ENTER_FRAME event listener attached to perform pending updates.
+ */
+ private var redrawListener:IUIBase;
+
+ /*
+ * Some operations can be undone & redone. The undoManager keeps track
+ * of the operations that have been done or undone so that they can be undone or
+ * redone. I'm not sure if only text operations can be undone. If so, the undoManager
+ * should probably be moved to EditManager.
+ */
+ private var _undoManager:IUndoManager;
+
+ private var _imeSession:IMEClient;
+ private var _imeOperationInProgress:Boolean;
+
+ /**
+ * Indicates whether overwrite mode is on or off.
+ *
+ * <p>If <code>true</code>, then a keystroke overwrites the character following the cursor.
+ * If <code>false</code>, then a keystroke is inserted at the cursor location.</p>
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public static var overwriteMode:Boolean = false;
+
+ /**
+ * Creates an EditManager object.
+ *
+ * <p>Assign an EditManager object to the <code>interactionManager</code> property
+ * of a text flow to enable editing of that text flow. </p>
+ *
+ * <p>To enable support for undoing and redoing changes, pass an
+ * IUndoManager instance to the EditManager constructor. You can use
+ * the <code>flashx.undo.UndoManager</code> class
+ * or create a custom IUndoManager instance. Use a custom IUndoManager instance
+ * to integrate Text Layout Framework changes with an existing
+ * undo manager that is not an instance of the UndoManager class.
+ * To create a custom IUndoManager instance, ensure that the class
+ * you use to define the undo manager
+ * implements the IUndoManager interface.</p>
+ *
+ *
+ * @param undo The UndoManager for the application
+ *
+ * @see org.apache.flex.textLayout.elements.TextFlow#interactionManager
+ * @see flashx.undo.IUndoManager
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function EditManager(undoManager:IUndoManager = null)
+ {
+ super();
+ _undoManager = undoManager;
+ }
+
+ /**
+ * The IUndoManager assigned to this edit manager.
+ *
+ * <p>To allow edits to be undone (and redone), pass an IUndoManager instance to the EditManager
+ * constructor. The undo manager maintains a stack of operations that have been executed, and it can
+ * undo or redo individual operations. </p>
+ *
+ * <p><b>Note:</b> If the TextFlow is modified directly (not via
+ * calls to the EditManager, but directly via calls to the managed FlowElement objects), then the EditManager
+ * clears the undo stack to prevent the stack from getting out of sync with the current state.</p>
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function get undoManager():IUndoManager
+ {
+ return _undoManager;
+ }
+
+ // Backdoor provided so that IMEClient can temporarily use an undo manager to maintain the IME session state.
+ /** @private */
+ public function setUndoManager(undoManager:IUndoManager):void
+ {
+ _undoManager = undoManager;
+ }
+
+ /** @private */
+ override public function editHandler(event:Event):void
+ {
+ if (event.isDefaultPrevented())
+ return;
+
+ super.editHandler(event);
+ switch (event.type)
+ {
+ case EditEvent.CUT:
+ case EditEvent.CLEAR:
+ if (activePosition != anchorPosition)
+ {
+ if (event.type == EditEvent.CUT)
+ TextClipboard.setContents(cutTextScrap());
+ else
+ deleteText(null);
+ event.preventDefault();
+ }
+ break;
+ case EditEvent.PASTE:
+ pasteTextScrap(TextClipboard.getContents());
+ event.preventDefault();
+ break;
+ }
+ }
+
+ // ///////////////////////////////////
+ // keyboard methods
+ // ///////////////////////////////////
+
+ /**
+ * @private
+ * @flexjsignorecoercion org.apache.flex.textLayout.element.IListItemElement
+ */
+ public override function keyDownHandler(event:KeyboardEvent):void
+ {
+ var listItem:IListItemElement;
+ var operationState:SelectionState;
+
+ if (!hasSelection() || event.isDefaultPrevented())
+ return;
+
+ if (redrawListener) // pending redraw; handle this before anything else so TextLines on screen will be up to date
+ updateAllControllers();
+
+ super.keyDownHandler(event);
+
+ if (event.ctrlKey)
+ {
+ // The player subsequently sends a text input event (which should be ignored) as listed below:
+ // CTRL/CMD+z: Only on Mac when using a pre-Argo player version
+ // CTRL/CMD+y: On all platforms (the exact char code for the text input event is platform dependent)
+ if (!event.altKey)
+ {
+ if (_imeSession != null && ((event.code == "KeyZ") || (event.code == "KeyY")))
+ _imeSession.compositionAbandoned(); // must be done before undo or redo start b/c IME session uses undo also for its own rollback
+ // do Operation will also cancel the session.
+
+ switch(event.code)
+ {
+ case "KeyZ": // small z
+ /* pre-Argo and on the mac then ignoreNextTextEvent */
+ // if (!Configuration.versionIsAtLeast(10,1) && (Capabilities.os.search("Mac OS") > -1))
+ ignoreNextTextEvent = true;
+ if(event.shiftKey){
+ redo();
+ event.preventDefault();
+
+ } else {
+ undo();
+ event.preventDefault();
+ }
+ break;
+ case "KeyZ": // small y
+ ignoreNextTextEvent = true;
+ redo();
+ event.preventDefault();
+ break;
+ case EditingKeys.BACKSPACE:
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+ deletePreviousWord();
+ event.preventDefault();
+ break;
+ }
+ if (event.code == EditingKeys.DELETE)
+ {
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+ deleteNextWord();
+ event.preventDefault();
+ }
+
+ if (event.shiftKey)
+ {
+ // detect ctrl-shift-"-" (cnd-shift-"-" on mac) and insert a DH
+ if (event.code == "Minus")
+ {
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+
+ //a discretionary hyphen is being inserted.
+ var discretionaryHyphenString:String = String.fromCharCode(0x000000AD);
+ overwriteMode ? overwriteText(discretionaryHyphenString) : insertText(discretionaryHyphenString);
+ event.preventDefault();
+ }
+ }
+ }
+ }
+ else if (event.altKey)
+ {
+ if (event.code == EditingKeys.BACKSPACE)
+ {
+ deletePreviousWord();
+ event.preventDefault();
+ }
+ else if (event.code == EditingKeys.DELETE)
+ {
+ deleteNextWord();
+ event.preventDefault();
+ }
+ }
+ // not ctrl key or alt key
+ else if (event.code == EditingKeys.DELETE) //del
+ {
+ deleteNextCharacter();
+ event.preventDefault();
+ }
+ else if (event.code == EditingKeys.INSERT) //insert
+ {
+ overwriteMode = !overwriteMode;
+ event.preventDefault();
+ }
+ else switch(event.code) {
+ case EditingKeys.BACKSPACE:
+ deletePreviousCharacter();
+ event.preventDefault();
+ break;
+ case WhitespaceKeys.ENTER:
+ if (textFlow.configuration.manageEnterKey)
+ {
+ var firstLeaf:IFlowLeafElement = textFlow.findLeaf(absoluteStart);
+ listItem = firstLeaf.getParentByType("ListItemElement") as IListItemElement;
+ // if the listitem has a IListElement child then treat this as a regular Paragraph
+ if (listItem && firstLeaf.getParentByType("ListElement") != listItem.getParentByType("ListElement"))
+ listItem = null;
+
+ // inside a list shift-enter splits a paragraph and enter splits the listitem
+ if (listItem && !event.shiftKey)
+ {
+ // if on last item of list and it's empty, remove it and put cursor on a new para immediatly following the list (new para should be wrapped in a new list item if parent of list is another list).
+ if(listItem.textLength == 1 && listItem.parent.getChildIndex(listItem) == listItem.parent.numChildren - 1)
+ {
+ operationState = defaultOperationState();
+ if (!operationState)
+ return;
+
+ doOperation(new MoveChildrenOperation(operationState, listItem.parent, listItem.parent.getChildIndex(listItem), 1, listItem.parent.parent, listItem.parent.parent.getChildIndex(listItem.parent) + 1));
+ }
+ else
+ {
+ splitElement(listItem);
+ // push cursor past the marker
+ selectRange(absoluteStart+1,absoluteStart+1);
+ refreshSelection();
+ }
+ }
+ else if(event.shiftKey &&
+ ((!listItem && textFlow.configuration.shiftEnterLevel > 0) ||
+ textFlow.configuration.shiftEnterLevel > 1)
+ )
+ {
+ overwriteMode ? overwriteText("\u2028") : insertText("\u2028");
+ }
+ else
+ splitParagraph();
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
+ break;
+ case WhitespaceKeys.TAB:
+ if (textFlow.configuration.manageTabKey)
+ {
+ listItem = textFlow.findLeaf(absoluteStart).getParentByType("ListItemElement") as IListItemElement;
+ if (listItem && listItem.getAbsoluteStart() == absoluteStart)
+ {
+ operationState = defaultOperationState();
+ if (!operationState)
+ return;
+
+ if(event.shiftKey)
+ {
+ // unindent by moving list element(s) out of parent into grandparent
+
+ // first make sure there is a grandparent
+ if(listItem.parent.parent is IListElement && listItem.parent.getChildIndex(listItem) == 0)
+ {
+ var source:IFlowGroupElement;
+ var target:IFlowGroupElement;
+ var numElements:int;
+ var sourceIndex:int;
+ var targetIndex:int;
+
+ {
+ source = listItem.parent;
+ target = listItem.parent.parent;
+ numElements = listItem.parent.numChildren;
+ sourceIndex = 0;
+ targetIndex = listItem.parent.parent.getChildIndex(listItem.parent);
+ }
+ doOperation(new MoveChildrenOperation(operationState, source, sourceIndex, numElements, target, targetIndex));
+ }
+ }
+ else
+ {
+ // create new list from list element(s)
+ var element:IFlowGroupElement = listItem;
+ if(listItem.parent.getChildIndex(listItem) == 0)
+ element = listItem.parent;
+
+ doOperation(new CreateListOperation(new SelectionState(textFlow, element.getAbsoluteStart(), element.getAbsoluteStart() + element.textLength), listItem.parent));
+ }
+ }
+ else if (textFlow.nestedInTable()) {
+ var cell:ITableCellElement;
+
+ if (event.shiftKey) {
+ cell = (textFlow.parentElement as ITableCellElement).getPreviousCell();
+ }
+ else {
+ cell = (textFlow.parentElement as ITableCellElement).getNextCell();
+ }
+
+ // select next cell in table
+ if (cell && cell.textFlow && cell.textFlow.interactionManager is EditManager) {
+ //cell.textFlow.interactionManager.selectLastPosition();
+ deselect();
+ cell.textFlow.interactionManager.selectAll();
+ cell.textFlow.interactionManager.setFocus();
+ }
+ }
+ else
+ {
+ var str:String = KeyConverter.convertKey(event.key);
+ overwriteMode ? overwriteText(str) : insertText(str);
+ }
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+
+ /** @private */
+ public override function keyUpHandler(event:KeyboardEvent):void
+ {
+ if (!hasSelection() || event.isDefaultPrevented())
+ return;
+
+ super.keyUpHandler(event);
+
+ if ((textFlow.configuration.manageEnterKey && event.code == WhitespaceKeys.ENTER) || (textFlow.configuration.manageTabKey && event.code == WhitespaceKeys.TAB)) {
+ event.stopImmediatePropagation();
+ }
+ }
+
+ /** @private */
+ public override function keyFocusChangeHandler(event:Event):void
+ {
+ if (textFlow.configuration.manageTabKey)
+ event.preventDefault();
+ }
+
+ /** @private */
+ public override function mouseDownHandler(event:MouseEvent):void
+ {
+ if (redrawListener)
+ updateAllControllers();
+ super.mouseDownHandler(event);
+ }
+
+ /** @private */
+ public override function textInputHandler(event:TextEvent):void
+ {
+ if (!ignoreNextTextEvent)
+ {
+ var charCode:int = event.text.charCodeAt(0);
+ // only if its a space or larger - ignore control characters here
+ if (charCode >= 32)
+ overwriteMode ? overwriteText(event.text) : insertText(event.text);
+ }
+ ignoreNextTextEvent = false;
+ // if the manager is nested inside another one, do not handle it again in the outer one
+ if(superManager)
+ event.preventDefault();
+
+ }
+
+ /** @private */
+ override public function focusOutHandler(event:Event):void
+ {
+ super.focusOutHandler(event);
+ if (_imeSession && selectionFormatState != SelectionFormatState.FOCUSED)
+ _imeSession.compositionAbandoned();
+ }
+
+ /** @private */
+ override public function deactivateHandler(event:Event):void
+ {
+ super.deactivateHandler(event);
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+ }
+
+ /** @private */
+ override public function imeStartCompositionHandler(event:IMEEvent):void
+ {
+ CONFIG::debug{ assert(!_imeSession, "IME session already in progress: IME not reentrant!"); }
+ // CONFIG::debug { Debugging.traceOut("imeStartComposition event"); }
+
+ // any pending operations must be executed first, to
+ // preserve operation order.
+ flushPendingOperations();
+
+ // Coded to avoid dependency on Argo (10.1).
+ if (!(event["imeClient"]))
+ {
+ _imeSession = new IMEClient(this);
+ _imeOperationInProgress = false;
+ event["imeClient"] = _imeSession;
+ }
+ }
+
+ /** @private */
+ override public function setFocus():void
+ {
+ var flowComposer:IFlowComposer = textFlow ? textFlow.flowComposer : null;
+ if (_imeSession && flowComposer && flowComposer.numControllers > 1)
+ {
+ // container with the ime start position gets the key focus
+ _imeSession.setFocus();
+
+ setSelectionFormatState(SelectionFormatState.FOCUSED);
+ }
+ else
+ super.setFocus();
+
+ /* CONFIG::debug
+ {
+ if (textFlow.flowComposer.getControllerAt(0).container.stage)
+ {
+ var focusDI:DisplayObject = textFlow.flowComposer.getControllerAt(0).container.stage.focus;
+ trace("set focus to ", focusDI.hasOwnProperty("name") ? focusDI["name"] : focusDI.toString());
+ }
+ } */
+ }
+ /** @private */
+ public function endIMESession():void
+ {
+ _imeSession = null;
+ var flowComposer:IFlowComposer = textFlow ? textFlow.flowComposer : null;
+ if (flowComposer && flowComposer.numControllers > 1)
+ setFocus();
+ }
+ /** @private */
+ public function beginIMEOperation():void
+ {
+ _imeOperationInProgress = true;
+ beginCompositeOperation();
+ }
+ /** @private */
+ public function endIMEOperation():void
+ {
+ endCompositeOperation();
+ _imeOperationInProgress = false;
+ }
+
+ /** @private We track the nesting level of the doOperation, because in finalize we need to know if
+ we are at the outermost level and need to push the operation on the undo stack and redraw
+ the screen, or if we're in a nested level and need to append the operation to the next
+ level up. */
+ public var captureLevel:int = 0;
+
+ /**
+ * @copy IEditManager#doOperation()
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public override function doOperation(operation:FlowOperation):void
+ {
+ CONFIG::debug { assert(operation.textFlow == this.textFlow,"Operation from a different TextFlow"); }
+
+ // If we get any operation during an IME session that is not owned by the session, we cancel the IME
+ if (_imeSession && !_imeOperationInProgress)
+ _imeSession.compositionAbandoned();
+
+ // any pending operations must be executed first, to
+ // preserve operation order.
+ flushPendingOperations();
+
+ try
+ {
+ captureLevel++;
+ operation = doInternal(operation);
+ }
+ catch(e:Error)
+ {
+ captureLevel--;
+ throw(e);
+ }
+ captureLevel--;
+
+ if (operation) // don't finalize if operation was cancelled
+ finalizeDo(operation);
+ }
+
+ private function finalizeDo(op:FlowOperation):void
+ {
+ // Handle operation if we're in a beginCompositeOperation/endCompositeOperation context
+ // In this case any nested commands we do will get added to the composite operation when
+ // they're done instead of added to the undo stack.
+ var parentOperation:CompositeOperation;
+ if (parentStack && parentStack.length > 0)
+ {
+ var parent:Object = parentStack[parentStack.length - 1];
+ if (parent.captureLevel == captureLevel)
+ parentOperation = parent.operation as CompositeOperation;
+ }
+
+ // CONFIG::debug { assert(captureLevel == 0 || parentOperation != null, "missing parent for nested operation"); }
+
+ if (parentOperation)
+ parentOperation.addOperation(op);
+
+ else if (captureLevel == 0)
+ {
+ captureOperations.length = 0;
+ if (_undoManager)
+ {
+ if (_undoManager.canUndo() && allowOperationMerge)
+ {
+ var lastOp:FlowOperation = _undoManager.peekUndo() as FlowOperation;
+ if (lastOp)
+ {
+ // Try to merge the last operation on the stack with the current
+ // operation. This may modify lastOp, or return a new operation
+ var combinedOp:FlowOperation = lastOp.merge(op);
+ if (combinedOp)
+ {
+ CONFIG::debug { assert(combinedOp.endGeneration == textFlow.generation,"Who did what?"); }
+ CONFIG::debug { assert(combinedOp.canUndo() && combinedOp.endGeneration == op.endGeneration,"Bad operation merge in EditManager"); }
+ _undoManager.popUndo();
+ op = combinedOp;
+ }
+ }
+ }
+ if (op.canUndo())
+ _undoManager.pushUndo(op);
+ allowOperationMerge = true;
+
+ // following operations are no longer redoable
+ _undoManager.clearRedo();
+ }
+
+ handleUpdate();
+
+ if (!_imeSession)
+ {
+ var opEvent:FlowOperationEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_COMPLETE,false,false,op,0,null);
+ textFlow.dispatchEvent(opEvent);
+ }
+ }
+ }
+
+ private var captureOperations:Array = [];
+
+ /** Internal guts of a dooperation - Execute a FlowOperation. This function proceeds in steps.
+ * <p>Step 2. Send a canceallable OperationEvent. If cancelled this method returns immediately.</p>
+ * If it is not cancelled, the listener may "do" other operations by calling back into the EditManager. This will result
+ * in a nested call to do which will post additional commands to the captureOperations array.
+ * <p>Step 3. Execute the operation. The operation returns true or false. false indicates no changes were made.</p>
+ * <p>Step 7. Send a OperationEvent. </p>
+ * The listener may "do" other operations by calling back into the EditManager. This will result
+ * in a nested call to do which will post additional commands to the captureOperations array.
+ * <p>Exception handling. If the operation throws the exception is caught and the error is attached to the event dispatched
+ * at step 7. If the event is not cancelled the error is rethrown.</p>
+ */
+ private function doInternal(op:FlowOperation):FlowOperation
+ {
+ CONFIG::debug { assert(op.textFlow == this.textFlow,"Operation from a different TextFlow"); }
+
+ var captureStart:int = captureOperations.length;
+ var success:Boolean = false;
+ var opEvent:FlowOperationEvent;
+
+ // tell any listeners about the operation
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_BEGIN,false,true,op,captureLevel-1,null);
+ textFlow.dispatchEvent(opEvent);
+ if (opEvent.isDefaultPrevented())
+ return null;
+ // user may replace the operation - TODO: WHAT IF SWITCH TO UNDO/REDO????
+ op = opEvent.operation;
+ if ((op is UndoOperation) || (op is RedoOperation))
+ throw new Error(GlobalSettings.resourceStringFunction("illegalOperation",[ getQualifiedClassName(op) ]));
+ }
+
+ var opError:Error = null;
+ try
+ {
+ // begin this op after pending ops are flushed
+ CONFIG::debug
+ {
+ if (captureLevel <= 1)
+ debugCheckTextFlow();
+ }
+
+ // null return implies no operation was done - just discard it
+ var beforeGeneration:uint = textFlow.generation;
+ op.setGenerations(beforeGeneration,0);
+
+ captureOperations.push(op);
+ success = op.doOperation();
+ if (success) // operation succeeded
+ {
+ textFlow.normalize(); //force normalization at this point. Don't compose unless the captureLevel is 0
+
+ // This has to be done after the normalize, because normalize increments the generation number
+ op.setGenerations(beforeGeneration,textFlow.generation);
+ }
+ else
+ {
+ var index:int = captureOperations.indexOf(op);
+ if (index >= 0)
+ captureOperations.splice(index, 1);
+ }
+ }
+ catch(e:Error)
+ {
+ opError = e;
+ }
+
+ // operation completed - send event whether it succeeded or not.
+ // client can check generation number for changes
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_END,false,true,op,captureLevel-1,opError);
+ textFlow.dispatchEvent(opEvent);
+ opError = opEvent.isDefaultPrevented() ? null : opEvent.error;
+ }
+
+ if (opError)
+ throw (opError);
+
+ // If we fired off any subsidiary operations, create a composite operation to hold them all
+ if (captureOperations.length - captureStart > 1)
+ {
+ op = new CompositeOperation(captureOperations.slice(captureStart));
+ op.setGenerations(FlowOperation(CompositeOperation(op).operations[0]).beginGeneration,textFlow.generation);
+ allowOperationMerge = false;
+ captureOperations.length = captureStart;
+ }
+
+ return success ? op : null;
+ }
+
+ /** @private **/
+ override public function set textFlow(value:ITextFlow):void
+ {
+ flushPendingOperations();
+ if (redrawListener) // detach handler if there is one
+ updateAllControllers();
+ super.textFlow = value;
+ }
+
+ /**
+ * @copy IEditManager.delayUpdates
+ */
+ public function get delayUpdates():Boolean
+ {
+ return _delayUpdates;
+ }
+ public function set delayUpdates(value:Boolean):void
+ {
+ _delayUpdates = value;
+ }
+
+ private function redrawHandler(stamp:int):void
+ {
+ // This is here because it has to take an argument
+ updateAllControllers();
+ }
+
+ /** @copy IEditManager.updateAllControllers
+ */
+ public function updateAllControllers():void
+ {
+ flushPendingOperations();
+
+ if (redrawListener) // detach handler if there is one
+ {
+// redrawListener.removeEventListener(Event.ENTER_FRAME, redrawHandler);
+ redrawListener = null;
+ }
+
+ if (textFlow.flowComposer)
+ {
+ textFlow.flowComposer.updateAllControllers();
+
+ // Scroll to selection
+ if (hasSelection())
+ {
+ var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(activePosition);
+ if (controllerIndex >= 0)
+ textFlow.flowComposer.getControllerAt(controllerIndex).scrollToRange(activePosition,anchorPosition);
+ }
+ }
+
+ selectionChanged(true, false);
+
+ CONFIG::debug { debugCheckTextFlow(); }
+ }
+
+ // By default, the EditManager will update in response to a model change immediately.
+ // Client may also request a delayed update; in this case, we schedule an update on the
+ // next enter frame event.
+ private function handleUpdate():void
+ {
+ if (_delayUpdates)
+ {
+ if (!redrawListener) // only need to attach if we're not already
+ {
+ var controller:IContainerController = textFlow.flowComposer.getControllerAt(0);
+ if (controller)
+ {
+ redrawListener = controller.container;
+ if (redrawListener)
+ AnimationUtil.requestFrame(redrawHandler, redrawListener);
+// redrawListener.addEventListener(Event.ENTER_FRAME, redrawHandler, false, 1.0, true);
+ }
+ }
+ }
+ else // redraw now
+ {
+ updateAllControllers();
+ }
+ }
+
+ /** @copy IEditManager#allowDelayedOperations() */
+ public function get allowDelayedOperations():Boolean
+ {
+ return _allowDelayedOperations;
+ }
+ public function set allowDelayedOperations(value:Boolean):void
+ {
+ if (!value)
+ flushPendingOperations();
+ _allowDelayedOperations = value;
+ }
+
+ /** @private */
+ public override function flushPendingOperations():void
+ {
+ super.flushPendingOperations();
+ if (pendingInsert)
+ {
+ var pi0:InsertTextOperation = pendingInsert;
+ pendingInsert = null;
+// Should not be needed
+// if (enterFrameListener)
+// {
+// enterFrameListener.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
+// enterFrameListener = null;
+// }
+ doOperation(pi0);
+ }
+ }
+
+ /**
+ * @copy IEditManager#undo()
+ *
+ * @see flashx.undo.IUndoManager#undo()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function undo():void
+ {
+ // Cancel out of an IME session if there is one.
+ // Some IMEs are on all the time, and so the undo has to win over the IME,
+ // otherwise you would never be able to undo in Korean.
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+
+ if (undoManager)
+ undoManager.undo();
+ }
+
+ /** @private */
+ public function performUndo(theop:IOperation):void
+ {
+ var operation:FlowOperation = theop as FlowOperation;
+ if ((!operation) || (operation.textFlow != textFlow))
+ return;
+ // tell any listeners about the operation
+ if (!_imeSession)
+ {
+ var undoPsuedoOp:UndoOperation = new UndoOperation(operation);
+ var opEvent:FlowOperationEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_BEGIN,false,true,undoPsuedoOp,0,null);
+ textFlow.dispatchEvent(opEvent);
+ if (opEvent.isDefaultPrevented())
+ {
+ //operation cancelled by user. Push the operation back onto the undo stack
+ undoManager.pushUndo(operation);
+ return;
+ }
+ undoPsuedoOp = opEvent.operation as UndoOperation;
+ if (!undoPsuedoOp)
+ throw new Error(GlobalSettings.resourceStringFunction("illegalOperation",[ getQualifiedClassName(opEvent.operation) ]));
+ operation = undoPsuedoOp.operation;
+ }
+
+ if (operation.endGeneration != textFlow.generation)
+ {
+ //CONFIG::debug { trace("EditManager.undo: skipping undo due to mismatched generation numbers. textFlow",textFlow.generation,org.apache.flex.reflection.getQualifiedClassName(operation),operation.endGeneration); }
+ return;
+ }
+
+ var opError:Error = null;
+ try
+ {
+ CONFIG::debug { debugCheckTextFlow(); }
+
+ var rslt:SelectionState;
+ rslt = operation.undo();
+
+ CONFIG::debug { assert(rslt != null,"undoable operations must return a SelectionState"); }
+ setSelectionState(rslt);
+ if (_undoManager)
+ _undoManager.pushRedo(operation);
+
+ }
+ catch(e:Error)
+ {
+ opError = e;
+ }
+
+ // tell user its complete and give them a chance to cancel the rethrow
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_END,false,true,undoPsuedoOp,0,opError);
+ textFlow.dispatchEvent(opEvent);
+ opError = opEvent.isDefaultPrevented() ? null : opEvent.error;
+ }
+
+ if (opError)
+ throw (opError);
+
+ handleUpdate();
+
+ // push the generation of the textFlow backwards - must be done after update which does a normalize
+ textFlow.setGeneration(operation.beginGeneration);
+
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_COMPLETE,false,false,undoPsuedoOp,0,null);
+ textFlow.dispatchEvent(opEvent);
+ }
+ }
+
+ /**
+ * @copy IEditManager#redo()
+ *
+ * @see flashx.undo.IUndoManager#redo()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function redo():void
+ {
+ // Cancel out of an IME session if there is one.
+ // Some IMEs are on all the time, and so the undo has to win over the IME,
+ // otherwise you would never be able to undo in Korean.
+ if (_imeSession)
+ _imeSession.compositionAbandoned();
+
+ if (undoManager)
+ undoManager.redo();
+ }
+
+ /** @private */
+ public function performRedo(theop:IOperation):void
+ {
+ var opEvent:FlowOperationEvent;
+ var op:FlowOperation = theop as FlowOperation;
+ if ((!op) || (op.textFlow != textFlow))
+ return;
+ // tell any listeners about the operation
+ if (!_imeSession)
+ {
+ var redoPsuedoOp:RedoOperation = new RedoOperation(op);
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_BEGIN,false,true,redoPsuedoOp,0,null);
+ textFlow.dispatchEvent(opEvent);
+ if (opEvent.isDefaultPrevented() && _undoManager)
+ {
+ //user cancelled the event. Push the operation back onto the redo stack
+ _undoManager.pushRedo(op);
+ return;
+ }
+ redoPsuedoOp = opEvent.operation as RedoOperation;
+ if (!redoPsuedoOp)
+ throw new Error(GlobalSettings.resourceStringFunction("illegalOperation",[ getQualifiedClassName(opEvent.operation) ]));
+ op = redoPsuedoOp.operation;
+ }
+
+ if (op.beginGeneration != textFlow.generation)
+ {
+ //CONFIG::debug { trace("EditManager.redo: skipping redo due to mismatched generation numbers."); }
+ return;
+ }
+
+ var opError:Error = null;
+ try
+ {
+ CONFIG::debug { debugCheckTextFlow(); }
+ var rslt:SelectionState;
+ rslt = op.redo();
+
+ CONFIG::debug { assert(rslt != null,"redoable operations must return a SelectionState"); }
+ setSelectionState(rslt);
+ if (_undoManager)
+ _undoManager.pushUndo(op);
+
+
+ }
+ catch(e:Error)
+ {
+ opError = e;
+ }
+
+ // tell user its complete and give them a chance to cancel the rethrow
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_END,false,true,redoPsuedoOp,0,opError);
+ textFlow.dispatchEvent(opEvent);
+ opError = opEvent.isDefaultPrevented() ? null : opEvent.error;
+ }
+ if (opError)
+ throw (opError);
+
+ handleUpdate();
+
+ // push the generation of the textFlow backwards - must be done after update which does a normalize
+ // set the generation of the textFlow to end of redoOp.
+ textFlow.setGeneration(op.endGeneration);
+
+ if (hasSelection())
+ {
+ var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(activePosition);
+ if (controllerIndex >= 0)
+ textFlow.flowComposer.getControllerAt(controllerIndex).scrollToRange(activePosition,anchorPosition);
+ }
+ if (!_imeSession)
+ {
+ opEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_COMPLETE,false,false,redoPsuedoOp,0,null);
+ textFlow.dispatchEvent(opEvent);
+ }
+ }
+
+ /**
+ * @private
+ * Returns the editing mode (READ_ONLY, READ_SELECT, or READ_WRITE) of the EditManager.
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ * @see org.apache.flex.textLayout.edit.EditingMode.
+ */
+ public override function get editingMode():String
+ {
+ return EditingMode.READ_WRITE;
+ }
+
+ // Resolve the operationState.
+ // If the operation state is null...
+ // Return the active selection
+ // If there's no active selection, return null. The caller will have to check
+ // Otherwise (operation not null)
+ // just return it
+ /** @private */
+ public function defaultOperationState(operationState:SelectionState = null):SelectionState
+ {
+ if (operationState)
+ {
+ // flush any pending operations and use marks to preserve the operationState positions
+ var markActive:Mark = createMark();
+ var markAnchor:Mark = createMark();
+ try
+ {
+ markActive.position = operationState.activePosition;
+ markAnchor.position = operationState.anchorPosition;
+ flushPendingOperations();
+ }
+ finally
+ {
+ removeMark(markActive);
+ removeMark(markAnchor);
+ operationState.activePosition = markActive.position;
+ operationState.anchorPosition = markAnchor.position;
+ }
+ }
+ else
+ {
+ flushPendingOperations();
+ if (hasSelection())
+ {
+ // tell the operation that the state is from the SelectionManager so it will update pending point formats
+ operationState = getSelectionState();
+ operationState.selectionManagerOperationState = true;
+ }
+ }
+ return operationState;
+ }
+
+ /**
+ * @copy IEditManager#splitParagraph()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function splitParagraph(operationState:SelectionState = null):IParagraphElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var op:SplitElementOperation = new SplitParagraphOperation(operationState);
+ doOperation(op);
+ return op.newElement as IParagraphElement;
+ }
+
+
+ /** @copy IEditManager#splitElement() */
+ public function splitElement(target:IFlowGroupElement, operationState:SelectionState = null):IFlowGroupElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+ var op:SplitElementOperation = new SplitElementOperation(operationState, target);
+ doOperation(op);
+ return op.newElement;
+ }
+
+ /** @copy IEditManager#createDiv() */
+ public function createDiv(parent:IFlowGroupElement = null, format:ITextLayoutFormat = null, operationState:SelectionState = null):IDivElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var operation:CreateDivOperation = new CreateDivOperation(operationState, parent, format);
+ doOperation(operation);
+ return operation.newDivElement;
+ }
+
+ /** @copy IEditManager#createList() */
+ public function createList(parent:IFlowGroupElement = null, format:ITextLayoutFormat = null, operationState:SelectionState = null):IListElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var operation:CreateListOperation = new CreateListOperation(operationState, parent, format);
+ doOperation(operation);
+ return operation.newListElement;
+ }
+
+ /** @copy IEditManager#moveChildren() */
+ public function moveChildren(source:IFlowGroupElement, sourceIndex:int, numChildren:int, destination:IFlowGroupElement, destinationIndex:int, selectionState:SelectionState = null):void
+ {
+ selectionState = defaultOperationState(selectionState);
+ if (!selectionState)
+ return;
+
+ var operation:MoveChildrenOperation = new MoveChildrenOperation(selectionState, source, sourceIndex, numChildren, destination, destinationIndex);
+ doOperation(operation);
+ }
+
+ /** @copy IEditManager#createSubParagraphGroup() */
+ public function createSubParagraphGroup(parent:IFlowGroupElement = null, format:ITextLayoutFormat = null, operationState:SelectionState = null):ISubParagraphGroupElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var operation:CreateSubParagraphGroupOperation = new CreateSubParagraphGroupOperation(operationState, parent, format);
+ doOperation(operation);
+ return operation.newSubParagraphGroupElement;
+ }
+
+ /**
+ * @copy IEditManager#deleteText()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function deleteText(operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).deleteText(operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new DeleteTextOperation(operationState, operationState, false /* don't allow merge when deleting by range */));
+ }
+
+ /**
+ * @copy IEditManager#deleteNextCharacter()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function deleteNextCharacter(operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).deleteNextCharacter(operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ /*
+ This should not be necessary...
+ // mjzhang : fix for table
+ var leaf:FlowLeafElement = textFlow.findLeaf(operationState.absoluteStart);
+ var para:IParagraphElement = leaf.getParagraph();
+ if ( para.isInTable() )
+ return;
+ */
+ // Delete the next character if it's a caret selection, and allow adejacent delete next's to merge
+ // If it's a range selection, delete the range and disallow merge
+ var deleteOp:DeleteTextOperation;
+ if (operationState.absoluteStart == operationState.absoluteEnd)
+ {
+ var nextPosition:int = NavigationUtil.nextAtomPosition(textFlow, absoluteStart);
+ deleteOp = new DeleteTextOperation(operationState, new SelectionState(textFlow, absoluteStart, nextPosition, pointFormat), true /* allowMerge for deleteForward */);
+ }
+ else
+ deleteOp = new DeleteTextOperation(operationState, operationState, false /* don't allow merge when deleting by range */);
+ doOperation(deleteOp);
+
+ }
+
+ /**
+ * @copy IEditManager#deleteNextWord()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function deleteNextWord(operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).deleteNextWord(operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if ((!operationState) || ((operationState.anchorPosition == operationState.activePosition) && (operationState.anchorPosition >= textFlow.textLength - 1)))
+ return;
+
+ var nextWordSelState:SelectionState = getNextWordForDelete(operationState.absoluteStart);
+ if (nextWordSelState.anchorPosition == nextWordSelState.activePosition)
+ //nothing to delete. No operation required.
+ return;
+
+ setSelectionState(new SelectionState(textFlow, operationState.absoluteStart, operationState.absoluteStart, new TextLayoutFormat(textFlow.findLeaf(operationState.absoluteStart).format)));
+ doOperation(new DeleteTextOperation(operationState, nextWordSelState, false));
+ }
+
+ // Sadly, this is NOT the same as the cursor key movement - specialized for delete forward one word
+ private function getNextWordForDelete(absoluteStart:int):SelectionState
+ {
+ var leafEl:IFlowLeafElement = textFlow.findLeaf(absoluteStart);
+ var paraEl:IParagraphElement = leafEl.getParagraph();
+ var paraElAbsStart:int = paraEl.getAbsoluteStart();
+
+ var nextPosition:int = -1;
+
+ if ((absoluteStart - paraElAbsStart) >= (paraEl.textLength - 1))
+ {
+ // We're at the end of the paragraph, delete the following newline
+ nextPosition = NavigationUtil.nextAtomPosition(textFlow, absoluteStart);
+ }
+ else
+ {
+ var curPos:int = absoluteStart - paraElAbsStart;
+ var curPosCharCode:int = paraEl.getCharCodeAtPosition(curPos);
+ var prevPosCharCode:int = -1;
+ if (curPos > 0) prevPosCharCode = paraEl.getCharCodeAtPosition(curPos - 1);
+// var nextPosCharCode:int = paraEl.getCharCodeAtPosition(curPos + 1);
+ if (!CharacterUtil.isWhitespace(curPosCharCode) && ((curPos == 0) || ((curPos > 0) && CharacterUtil.isWhitespace(prevPosCharCode)))) {
+ nextPosition = NavigationUtil.nextWordPosition(textFlow, absoluteStart);
+ } else {
+ if (CharacterUtil.isWhitespace(curPosCharCode) && ((curPos > 0) && !CharacterUtil.isWhitespace(prevPosCharCode))) {
+ //if at beginning of space word then get through all the spaces
+ curPos = paraEl.findNextWordBoundary(curPos);
+ }
+ nextPosition = paraElAbsStart + paraEl.findNextWordBoundary(curPos);
+ }
+ }
+ return new SelectionState(textFlow, absoluteStart, nextPosition, pointFormat);
+ }
+
+ /**
+ * @copy IEditManager#deletePreviousCharacter()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function deletePreviousCharacter(operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).deletePreviousCharacter(operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ var deleteOp:DeleteTextOperation;
+ if (operationState.absoluteStart == operationState.absoluteEnd)
+ {
+ // with a caret selection, generally delete the previous character, but also check whether to move the paragraph out of its parent chain (like backspacing at the beginning of a list)
+ var leaf:IFlowLeafElement = textFlow.findLeaf(operationState.absoluteStart);
+ var para:IParagraphElement = leaf.getParagraph();
+ var parent:IFlowGroupElement = para.parent;
+
+ var movePara:Boolean = false;
+ if(!(parent is ITextFlow))
+ {
+ if(operationState.absoluteStart == para.getAbsoluteStart() && parent.getChildIndex(para) == 0 && // cursor is at start of this paragraph AND para is at beginning of parent AND
+ (!(parent is IListItemElement) || parent.parent.getChildIndex(parent) == 0)) // if parent is a listItem, it's the first item in the list
+ {
+ movePara = true;
+ }
+ }
+ if(movePara)
+ {
+ /*
+ should not be necessary...
+ // mjzhang: fix for table feature
+ if ( para.isInTable() )
+ return;
+ */
+ var source:IFlowGroupElement;
+ var target:IFlowGroupElement;
+ var numElementsToMove:int;
+ var targetIndex:int;
+
+ if(parent is IListItemElement)
+ {
+ if(parent.parent.parent is IListElement)
+ {
+ // move the whole list item to grandparent list
+ source = parent.parent;
+ numElementsToMove = 1;
+ target = parent.parent.parent;
+ targetIndex = parent.parent.parent.getChildIndex(parent.parent);
+ }
+ else
+ {
+ // move everything inside the list item out into grandparent
+ source = para.parent;
+ numElementsToMove = para.parent.numChildren;
+ target = parent.parent.parent;
+ targetIndex = parent.parent.parent.getChildIndex(parent.parent);
+ }
+ }
+ else
+ {
+ // move just the first paragraph out into grandparent
+ source = para.parent;
+ numElementsToMove = 1;
+ target = parent.parent;
+ targetIndex = parent.parent.getChildIndex(parent);
+ }
+ doOperation(new MoveChildrenOperation(operationState, source, 0, numElementsToMove, target, targetIndex));
+ }
+ else
+ {
+ var beginPrevious:int = NavigationUtil.previousAtomPosition(textFlow, operationState.absoluteStart);
+ deleteOp = new DeleteTextOperation(operationState, new SelectionState(textFlow, beginPrevious, operationState.absoluteStart), true /* allowMerge */);
+ doOperation(deleteOp);
+ }
+ }
+ else // just delete the range
+ {
+ deleteOp = new DeleteTextOperation(operationState);
+ doOperation(deleteOp);
+ }
+ }
+
+ /**
+ * @copy IEditManager#deletePreviousWord()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function deletePreviousWord(operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).deletePreviousWord(operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ var prevWordSelState:SelectionState = getPreviousWordForDelete(operationState.absoluteStart);
+ if (prevWordSelState.anchorPosition == prevWordSelState.activePosition)
+ //there is nothing to delete. No operation required
+ return;
+
+ setSelectionState(new SelectionState(textFlow, operationState.absoluteStart, operationState.absoluteStart, new TextLayoutFormat(textFlow.findLeaf(operationState.absoluteStart).format)));
+ doOperation(new DeleteTextOperation(operationState, prevWordSelState, false /* don't allow merge */));
+ }
+
+ // Sadly, this is NOT the same as the cursor key movement - specialized for delete backward one word
+ private function getPreviousWordForDelete(absoluteStart:int):SelectionState
+ {
+ var leafEl:IFlowLeafElement = textFlow.findLeaf(absoluteStart);
+ var paraEl:IParagraphElement = leafEl.getParagraph();
+ var paraElAbsStart:int = paraEl.getAbsoluteStart();
+
+ if (absoluteStart == paraElAbsStart) // at the start of the paragraph, delete the previous newline. Should insert a space after punctuation.
+ {
+ var beginPrevious:int = NavigationUtil.previousAtomPosition(textFlow, absoluteStart);
+ return new SelectionState(textFlow, beginPrevious, absoluteStart);
+ }
+
+ var curPos:int = absoluteStart - paraElAbsStart;
+ var curPosCharCode:int = paraEl.getCharCodeAtPosition(curPos);
+ var prevPosCharCode:int = paraEl.getCharCodeAtPosition(curPos - 1);
+ var curAbsStart:int = absoluteStart;
+
+ if (CharacterUtil.isWhitespace(curPosCharCode) && (curPos != (paraEl.textLength - 1)))
+ {
+ if (CharacterUtil.isWhitespace(prevPosCharCode)) //this will get you past the spaces
+ {
+ curPos = paraEl.findPreviousWordBoundary(curPos);
+ }
+ if (curPos > 0) {
+ curPos = paraEl.findPreviousWordBoundary(curPos); //this will get you to the beginning of the word before the space.
+ if (curPos > 0) {
+ prevPosCharCode = paraEl.getCharCodeAtPosition(curPos - 1);
+ if (CharacterUtil.isWhitespace(prevPosCharCode)) {
+ curPos = paraEl.findPreviousWordBoundary(curPos);
+ }
+ }
+ }
+ } else { //you are here if you are not on a space
+ if (CharacterUtil.isWhitespace(prevPosCharCode))
+ {
+ curPos = paraEl.findPreviousWordBoundary(curPos); //this will get you past the spaces
+ if (curPos > 0) {
+ curPos = paraEl.findPreviousWordBoundary(curPos);
+ // mjzhang : Fix for bug#2821844 Text controls make bad assumptions with Ctrl Backspace
+ //if (curPos > 0) {
+ // prevPosCharCode = paraEl.getCharCodeAtPosition(curPos - 1);
+ // if (!CharacterUtil.isWhitespace(prevPosCharCode)) {
+ // curAbsStart--; //Microsoft Word insists on keeping the original space
+ // //if the ending position does not have a space.
+ // }
+ //}
+ }
+ } else { //just delete to the previous word boundary
+ curPos = paraEl.findPreviousWordBoundary(curPos);
+ }
+ }
+ return new SelectionState(textFlow, paraElAbsStart + curPos, curAbsStart);
+ }
+
+ public function insertTableElement(table:ITableElement, operationState:SelectionState = null):void
+ {
+ // handle insertions when a table cells is active. (nested tables probably do not work now though...)
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).insertTableElement(table, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ var operation:InsertTableElementOperation = new InsertTableElementOperation(operationState, table);
+ doOperation(operation);
+ }
+
+ /**
+ * @copy IEditManager#insertText()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function insertText(text:String, origOperationState:SelectionState = null):void
+ {
+ // handle insertions when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).insertText(text, origOperationState);
+ return;
+ }
+ // if there's another insert operation waiting to be executed,
+ // just add to it, if possible
+ if (origOperationState == null && pendingInsert)
+ pendingInsert.text += text;
+ else
+ {
+ var operationState:SelectionState = defaultOperationState(origOperationState);
+ if (!operationState)
+ return;
+
+ // rather than execute the insert immediately, create
+ // it and wait for the next frame, in order to batch
+ // keystrokes.
+ pendingInsert = new InsertTextOperation(operationState, text);
+
+ var controller:IContainerController = textFlow.flowComposer.getControllerAt(0);
+ if (captureLevel == 0 && origOperationState == null && controller && controller.container && allowDelayedOperations)
+ {
+ AnimationUtil.requestFrame(enterFrameHandler, controller.container);
+// enterFrameListener = controller.container;
+// enterFrameListener.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 1.0, true);
+ }
+ else
+ flushPendingOperations();
+ }
+ }
+
+
+
+ /**
+ * @copy IEditManager#overwriteText()
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function overwriteText(text:String, operationState:SelectionState = null):void
+ {
+ // handle overwrites when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).overwriteText(text, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+ var selState:SelectionState = getSelectionState();
+ NavigationUtil.nextCharacter(selState,true);
+ doOperation(new InsertTextOperation(operationState, text, selState));
+ }
+
+ /**
+ * @copy IEditManager#insertInlineGraphic()
+ * Returns the new InlineGraphicElement that was created.
+ *
+ * @playerversion Flash 10 + 10.2
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ * @see org.apache.flex.text.engine.TextRotation
+ */
+ public function insertInlineGraphic(source:Object, width:Object, height:Object, options:Object = null, operationState:SelectionState = null):IInlineGraphicElement
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ return (subManager as IEditManager).insertInlineGraphic(source, width, height, options, operationState);
+
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var operation:InsertInlineGraphicOperation = new InsertInlineGraphicOperation(operationState, source, width, height, options);
+ doOperation(operation);
+ return operation.newInlineGraphicElement;
+ }
+
+ /**
+ * @copy IEditManager#modifyInlineGraphic()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function modifyInlineGraphic(source:Object, width:Object, height:Object, options:Object = null, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).modifyInlineGraphic(source, width, height, options, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new ModifyInlineGraphicOperation(operationState, source, width, height, options));
+ }
+
+ /**
+ * @copy IEditManager#applyFormat()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyFormat(leafFormat:ITextLayoutFormat, paragraphFormat:ITextLayoutFormat, containerFormat:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).applyFormat(leafFormat, paragraphFormat, containerFormat, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ // apply to the current selection else remember new format for next char typed
+ doOperation(new ApplyFormatOperation(operationState, leafFormat, paragraphFormat, containerFormat));
+ }
+ /**
+ * @copy IEditManager#clearFormat()
+ *
+ * Known issue is that undefines of leafFormat values with a point selection are not applied at the next insertion.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function clearFormat(leafFormat:ITextLayoutFormat, paragraphFormat:ITextLayoutFormat, containerFormat:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).clearFormat(leafFormat, paragraphFormat, containerFormat, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ // apply to the current selection else remember new format for next char typed
+ doOperation(new ClearFormatOperation(operationState, leafFormat, paragraphFormat, containerFormat));
+ }
+ /**
+ * @copy IEditManager#applyLeafFormat()
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyLeafFormat(characterFormat:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ (subManager as IEditManager).applyLeafFormat(characterFormat, operationState);
+ else
+ applyFormat(characterFormat, null, null, operationState);
+ }
+
+ /**
+ * @copy IEditManager#applyParagraphFormat()
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyParagraphFormat(paragraphFormat:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ (subManager as IEditManager).applyParagraphFormat(paragraphFormat, operationState);
+ else
+ applyFormat(null, paragraphFormat, null, operationState);
+ }
+
+ /**
+ * @copy IEditManager#applyContainerFormat()
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyContainerFormat(containerFormat:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ (subManager as IEditManager).applyContainerFormat(containerFormat, operationState);
+ else
+ applyFormat(null, null, containerFormat, operationState);
+ }
+
+ /**
+ * @copy IEditManager#applyFormatToElement()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyFormatToElement(targetElement:IFlowElement, format:ITextLayoutFormat, relativeStart:int = 0, relativeEnd:int = -1, operationState:SelectionState = null):void
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new ApplyFormatToElementOperation(operationState, targetElement, format, relativeStart, relativeEnd));
+ }
+
+ /**
+ * @copy IEditManager#clearFormatOnElement()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function clearFormatOnElement(targetElement:IFlowElement, format:ITextLayoutFormat, operationState:SelectionState = null):void
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new ClearFormatOnElementOperation(operationState, targetElement, format));
+ }
+
+ /**
+ * @copy IEditManager#cutTextScrap()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.edit.TextScrap
+ */
+ public function cutTextScrap(operationState:SelectionState = null):TextScrap
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ return (subManager as IEditManager).cutTextScrap(operationState);
+
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ if (operationState.anchorPosition == operationState.activePosition)
+ return null;
+
+ var tScrap:TextScrap = TextScrap.createTextScrap(operationState);
+ var beforeOpLen:int = textFlow.textLength;
+ doOperation(new CutOperation(operationState, tScrap));
+ if (operationState.textFlow.textLength != beforeOpLen)
+ {
+ return tScrap;
+ }
+ return null;
+ }
+
+ /**
+ * @copy IEditManager#pasteTextScrap()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.edit.TextScrap
+ */
+ public function pasteTextScrap(scrapToPaste:TextScrap, operationState:SelectionState = null):void
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ {
+ (subManager as IEditManager).pasteTextScrap(scrapToPaste, operationState);
+ return;
+ }
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new PasteOperation(operationState, scrapToPaste));
+ }
+
+ /**
+ * @copy IEditManager#applyTCY()
+ * Returns the new TCYElement that was created.
+ *
+ * @playerversion Flash 10 + 10.2
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyTCY(tcyOn:Boolean, operationState:SelectionState = null):ITCYElement
+ {
+ // handle when a table cells is active.
+ if(subManager && subManager is IEditManager)
+ return (subManager as IEditManager).applyTCY(tcyOn, operationState);
+
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ var operation:ApplyTCYOperation = new ApplyTCYOperation(operationState, tcyOn);
+ doOperation(operation);
+ return operation.newTCYElement;
+ }
+
+ /**
+ * @copy IEditManager#applyLink()
+ * Returns the new ILinkElement that was created.
+ *
+ * @playerversion Flash 10 + 10.2
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function applyLink(href:String, targetString:String = null, extendToLinkBoundary:Boolean=false, operationState:SelectionState = null):ILinkElement
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return null;
+
+ if (operationState.absoluteStart == operationState.absoluteEnd)
+ return null;
+
+ var operation:ApplyLinkOperation = new ApplyLinkOperation(operationState, href, targetString, extendToLinkBoundary);
+ doOperation(operation);
+ return operation.newLinkElement;
+ }
+
+ /**
+ * @copy IEditManager#changeElementID()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function changeElementID(newID:String, targetElement:IFlowElement, relativeStart:int = 0, relativeEnd:int = -1, operationState:SelectionState = null):void
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ if (operationState.absoluteStart == operationState.absoluteEnd)
+ return;
+
+ doOperation(new ApplyElementIDOperation(operationState, targetElement, newID, relativeStart, relativeEnd));
+ }
+
+ [Deprecated(replacement="applyFormatToElement", deprecatedSince="2.0")]
+ /**
+ * @copy IEditManager#changeStyleName()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function changeStyleName(newName:String, targetElement:IFlowElement, relativeStart:int = 0, relativeEnd:int = -1, operationState:SelectionState = null):void
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ var format:TextLayoutFormat = new TextLayoutFormat();
+ format.styleName = newName;
+ doOperation(new ApplyFormatToElementOperation(operationState, targetElement, format, relativeStart, relativeEnd));
+ }
+
+ /**
+ * @copy IEditManager#changeTypeName()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public function changeTypeName(newName:String, targetElement:IFlowElement, relativeStart:int = 0, relativeEnd:int = -1, operationState:SelectionState = null):void
+ {
+ operationState = defaultOperationState(operationState);
+ if (!operationState)
+ return;
+
+ doOperation(new ApplyElementTypeNameOperation(operationState, targetElement, newName, relativeStart, relativeEnd));
+ }
+
+ /* CompositeOperations
+ Normally when you call doOperation, it gets executed immediately. By calling beginCompositeOperation, you can instead accumulate the
+ operations into a CompositeOperation. The CompositeOperation is completed and returned when you call endCompositeOperation, and
+ processing returns to normal state. The client code can then either call doOperation on the CompositeOperation that was returned,
+ or just drop it if the operation should be aborted.
+
+ The parentStack is a stack of pending CompositeOperations.
+ */
+ private var parentStack:Array;
+
+ /**
+ * @copy IEditManager#beginCompositeOperation()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function beginCompositeOperation():void
+ {
+ // not sure if there's issues with composite operations and tables
+ flushPendingOperations();
+
+ if (!parentStack)
+ parentStack = [];
+ var operation:CompositeOperation = new CompositeOperation();
+
+ if (!_imeSession)
+ {
+ var opEvent:FlowOperationEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_BEGIN,false,false,operation,captureLevel,null);
+ textFlow.dispatchEvent(opEvent);
+ }
+
+ CONFIG::debug { assert(!operation.operations || operation.operations.length == 0, "opening a composite operation that already has operations"); }
+ operation.setGenerations(textFlow.generation, 0);
+ ++captureLevel;
+ var parent:Object = {};
+ parent.operation = operation;
+ parent.captureLevel = captureLevel;
+ parentStack.push(parent);
+ }
+
+ /**
+ * @copy IEditManager#endCompositeOperation()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function endCompositeOperation():void
+ {
+ // not sure if there's issues with composite operations and tables
+ CONFIG::debug { assert( parentStack.length > 0 || captureLevel <= 0, "EditManager.endOperation - no composite operation in progress"); }
+
+ --captureLevel;
+
+ var parent:Object = parentStack.pop();
+ var operation:FlowOperation = parent.operation;
+ if (!_imeSession)
+ {
+ var opEvent:FlowOperationEvent = new FlowOperationEvent(FlowOperationEvent.FLOW_OPERATION_END,false,false,operation,captureLevel,null);
+ textFlow.dispatchEvent(opEvent);
+ }
+ operation.setGenerations(operation.beginGeneration, textFlow.generation);
+ finalizeDo(operation);
+ }
+
+ /** @private
+ * Handler function called when the selection has been changed.
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ * @param doDispatchEvent true if a selection changed event will be sent
+ * @param resetPointFormat true if the attributes associated with the caret should be discarded
+ */
+ public override function selectionChanged(doDispatchEvent:Boolean = true, resetPointFormat:Boolean=true):void
+ {
+ if (_imeSession)
+ _imeSession.selectionChanged();
+
+ super.selectionChanged(doDispatchEvent, resetPointFormat);
+ }
+ override public function copy(sharedUndo:Boolean):ISelectionManager
+ {
+ if(sharedUndo)
+ return new EditManager(undoManager);
+ else
+ return new EditManager();
+ }
+
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditingMode.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditingMode.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditingMode.as
new file mode 100644
index 0000000..72e21d3
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/EditingMode.as
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.edit
+{
+ /**
+ * The EditingMode class defines constants used with EditManager class to represent the
+ * read, select, and edit permissions of a document.
+ *
+ * @see org.apache.flex.textLayout.edit.EditManager
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public final class EditingMode
+ {
+ /**
+ * The document is read-only.
+ *
+ * <p>Neither selection nor editing is allowed.</p>
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public static const READ_ONLY:String = "readOnly";
+ /**
+ * The document can be edited.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public static const READ_WRITE:String = "readWrite";
+ /**
+ * The text in the document can be selected and copied, but not edited.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+ public static const READ_SELECT:String = "readSelect";
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/ElementMark.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/ElementMark.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/ElementMark.as
new file mode 100644
index 0000000..e658f1d
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/edit/ElementMark.as
@@ -0,0 +1,86 @@
+// //////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// //////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.edit
+{
+ import org.apache.flex.textLayout.debug.Debugging;
+ import org.apache.flex.textLayout.debug.assert;
+ import org.apache.flex.textLayout.elements.IFlowElement;
+ import org.apache.flex.textLayout.elements.IFlowGroupElement;
+ import org.apache.flex.textLayout.elements.ITextFlow;
+
+ // [ExcludeClass]
+ /** @private - Marks an element by its position in the hierarchy. */
+ public class ElementMark
+ {
+ /** @private */
+ public var _elemStart:int;
+ /** @private */
+ public var _indexChain:Array;
+ CONFIG::debug
+ {
+ private var _originalElement:String; }
+ public function ElementMark(elem:IFlowElement, relativeStartPosition:int)
+ {
+ _elemStart = relativeStartPosition;
+ _indexChain = [];
+
+ CONFIG::debug
+ {
+ var origElem:IFlowElement = elem; }
+ CONFIG::debug
+ {
+ _originalElement = Debugging.getIdentity(origElem); }
+
+ var p:IFlowGroupElement = elem.parent;
+ while (p != null)
+ {
+ _indexChain.splice(0, 0, p.getChildIndex(elem));
+ elem = p;
+ p = p.parent;
+ }
+
+ CONFIG::debug
+ {
+ var foundElem:IFlowElement = findElement(origElem.getTextFlow());
+ assert(origElem == findElement(origElem.getTextFlow()), "Bad ElementMarker");
+ }
+ }
+
+ public function get elemStart():int
+ {
+ return _elemStart;
+ }
+
+ /**
+ * @flexjsignorecoercion org.apache.flex.textLayout.elements.IFlowGroupElement
+ */
+ public function findElement(textFlow:ITextFlow):IFlowElement
+ {
+ var element:IFlowElement = textFlow;
+ for each (var idx:int in _indexChain)
+ element = (element as IFlowGroupElement).getChildAt(idx);
+
+ CONFIG::debug
+ {
+ assert(element != null, "ElementMarker:findElement No element found"); }
+
+ return element;
+ }
+ }
+}
\ No newline at end of file