| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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 flashx.textLayout.operations |
| { |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.edit.ModelEdit; |
| import flashx.textLayout.edit.ParaEdit; |
| import flashx.textLayout.edit.PointFormat; |
| import flashx.textLayout.edit.SelectionState; |
| import flashx.textLayout.edit.TextFlowEdit; |
| import flashx.textLayout.elements.FlowElement; |
| import flashx.textLayout.elements.FlowLeafElement; |
| import flashx.textLayout.elements.LinkElement; |
| import flashx.textLayout.elements.SpanElement; |
| import flashx.textLayout.elements.TCYElement; |
| import flashx.textLayout.formats.ITextLayoutFormat; |
| import flashx.textLayout.formats.TextLayoutFormat; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| |
| /** |
| * The InsertTextOperation class encapsulates a text insertion operation. |
| * |
| * @see flashx.textLayout.edit.EditManager |
| * @see flashx.textLayout.events.FlowOperationEvent |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public class InsertTextOperation extends FlowTextOperation |
| { |
| private var _deleteSelectionState:SelectionState; |
| private var delSelOp:DeleteTextOperation = null; |
| /** @private - this should be private but too late for code changes on Labs */ |
| public var _text:String; |
| |
| private var _pointFormat:ITextLayoutFormat; |
| |
| /** |
| * Creates an InsertTextOperation object. |
| * |
| * @param operationState Describes the insertion point or range of text. |
| * @param text The string to insert. |
| * @param deleteSelectionState Describes the range of text to delete before doing insertion, |
| * if different than the range described by <code>operationState</code>. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function InsertTextOperation(operationState:SelectionState, text:String, deleteSelectionState:SelectionState = null) |
| { |
| super(operationState); |
| |
| _pointFormat = operationState.pointFormat; |
| _text = text; |
| |
| initialize(deleteSelectionState); |
| } |
| |
| private function initialize(deleteSelectionState:SelectionState):void |
| { |
| if (deleteSelectionState == null) |
| deleteSelectionState = originalSelectionState; |
| if (deleteSelectionState.anchorPosition != deleteSelectionState.activePosition) |
| { |
| _deleteSelectionState = deleteSelectionState; |
| delSelOp = new DeleteTextOperation(_deleteSelectionState); |
| } |
| } |
| |
| /** |
| * The text inserted by this operation. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function get text():String |
| { |
| return _text; |
| } |
| public function set text(value:String):void |
| { |
| _text = value; |
| } |
| |
| /** |
| * The text deleted by this operation, if any. |
| * |
| * <p><code>null</code> if no text is deleted.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function get deleteSelectionState():SelectionState |
| { |
| return _deleteSelectionState; |
| } |
| public function set deleteSelectionState(value:SelectionState):void |
| { |
| _deleteSelectionState = value; |
| } |
| |
| /** |
| * The character format applied to the inserted text. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function get characterFormat():ITextLayoutFormat |
| { |
| return _pointFormat; |
| } |
| public function set characterFormat(value:ITextLayoutFormat):void |
| { |
| _pointFormat = new PointFormat(value); |
| } |
| |
| private function doDelete(leaf:FlowLeafElement):ITextLayoutFormat |
| { |
| // User selected a range of text and is replacing it. We're doing the delete here. |
| // We preserve the format from the deleted text, and apply it to the text insert, |
| // unless the user has specified an alternate format. |
| var deleteFormat:PointFormat = PointFormat.createFromFlowElement(textFlow.findLeaf(absoluteStart)); |
| var beforeDeleteFormat:PointFormat = absoluteStart == leaf.getParagraph().getAbsoluteStart() ? null : PointFormat.createFromFlowElement(textFlow.findLeaf(absoluteStart - 1)); |
| |
| if (delSelOp.doOperation()) // figure out what to do here |
| { |
| //do not change characterFormat if user specified one already, or if its the same as in the surrounding text. |
| // If the surrounding text is the same, forcing the point format requires more composition because it inserts |
| // the text in its own span. |
| if (!_pointFormat && (absoluteStart < absoluteEnd) && PointFormat.isEqual(deleteFormat, beforeDeleteFormat)) |
| deleteFormat = null; |
| else |
| { |
| // If the leaf element is empty, remove it now |
| if (leaf.textLength == 0) |
| leaf.parent.removeChild(leaf); |
| } |
| } |
| return deleteFormat; |
| } |
| |
| private function applyPointFormat(span:SpanElement, pointFormat:ITextLayoutFormat):void |
| { |
| if (!TextLayoutFormat.isEqual(pointFormat, span.format)) |
| { |
| var spanFormat:TextLayoutFormat = new TextLayoutFormat(span.format); |
| spanFormat.apply(pointFormat); |
| span.format = spanFormat; |
| } |
| if (pointFormat is PointFormat) |
| { |
| var pf:PointFormat = pointFormat as PointFormat; |
| if (pf.linkElement) |
| { |
| if (pf.linkElement.href) |
| { |
| TextFlowEdit.makeLink(textFlow, absoluteStart, absoluteStart + _text.length, pf.linkElement.href, pf.linkElement.target); |
| var linkLeaf:FlowLeafElement = textFlow.findLeaf(absoluteStart); |
| var linkElement:FlowElement = linkLeaf.getParentByType(LinkElement); |
| linkElement.format = pf.linkElement.format; |
| } |
| } |
| if (pf.tcyElement) |
| { |
| TextFlowEdit.makeTCY(textFlow, absoluteStart, absoluteStart + _text.length); |
| var tcyLeaf:FlowLeafElement = textFlow.findLeaf(absoluteStart); |
| var tcyElement:FlowElement = tcyLeaf.getParentByType(TCYElement); |
| tcyElement.format = pf.tcyElement.format; |
| } |
| else if (span.getParentByType(TCYElement)) |
| TextFlowEdit.removeTCY(textFlow, absoluteStart, absoluteStart + _text.length); |
| } |
| } |
| private function doInternal():void |
| { |
| var deleteFormat:ITextLayoutFormat; |
| |
| if (delSelOp != null) |
| deleteFormat = doDelete(textFlow.findLeaf(absoluteStart)); |
| |
| var span:SpanElement = ParaEdit.insertText(textFlow, absoluteStart, _text, _pointFormat != null || deleteFormat != null/* createNewSpan */); |
| if (textFlow.interactionManager) |
| textFlow.interactionManager.notifyInsertOrDelete(absoluteStart, _text.length); |
| |
| if (span != null) |
| { |
| if (deleteFormat) |
| { |
| span.format = deleteFormat; |
| applyPointFormat(span, deleteFormat); |
| if ((deleteFormat is PointFormat) && PointFormat(deleteFormat).linkElement && PointFormat(deleteFormat).linkElement.href && originalSelectionState.selectionManagerOperationState && textFlow.interactionManager) |
| { |
| // set pointFormat from leafFormat, to insure link attributes are propagated from replaced text to next insertion |
| // if I select a range of text in a link, and type over it to replace, the new text should be in a link with the same settings. |
| var state:SelectionState = textFlow.interactionManager.getSelectionState(); |
| state.pointFormat = PointFormat.clone(deleteFormat as PointFormat); |
| textFlow.interactionManager.setSelectionState(state); |
| } |
| } |
| if (_pointFormat) |
| applyPointFormat(span, _pointFormat); |
| } |
| } |
| |
| /** @private */ |
| public override function doOperation():Boolean |
| { |
| doInternal(); |
| return true; |
| } |
| |
| /** @private */ |
| public override function undo():SelectionState |
| { |
| ModelEdit.deleteText(textFlow, absoluteStart, absoluteStart + _text.length, false); |
| |
| var newSelectionState:SelectionState = originalSelectionState; |
| if (delSelOp != null) |
| newSelectionState = delSelOp.undo(); |
| |
| return originalSelectionState; |
| } |
| |
| /** |
| * Re-executes the operation after it has been undone. |
| * |
| * <p>This function is called by the edit manager, when necessary.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public override function redo():SelectionState |
| { |
| doInternal(); |
| return new SelectionState(textFlow,absoluteStart+_text.length,absoluteStart+_text.length,null); |
| } |
| |
| /** @private */ |
| tlf_internal override function merge(op2:FlowOperation):FlowOperation |
| { |
| if (absoluteStart < absoluteEnd) |
| return null; |
| if (this.endGeneration != op2.beginGeneration) |
| return null; |
| // We are assuming here that these operations are contiguous, because |
| // SelectionManager doesn't try to merge operations if the selection |
| // has changed |
| var insertOp:InsertTextOperation = null; |
| if (op2 is InsertTextOperation) |
| insertOp = op2 as InsertTextOperation; |
| if (insertOp) |
| { |
| if (insertOp.deleteSelectionState != null || deleteSelectionState != null) |
| return null; |
| if ((insertOp.originalSelectionState.pointFormat == null) && (originalSelectionState.pointFormat != null)) |
| return null; |
| if ((originalSelectionState.pointFormat == null) && (insertOp.originalSelectionState.pointFormat != null)) |
| return null; |
| if (originalSelectionState.absoluteStart + _text.length != insertOp.originalSelectionState.absoluteStart) |
| return null; |
| if (((originalSelectionState.pointFormat == null) && (insertOp.originalSelectionState.pointFormat == null)) || |
| (PointFormat.isEqual(originalSelectionState.pointFormat, insertOp.originalSelectionState.pointFormat))) |
| { |
| _text += insertOp.text; |
| setGenerations(beginGeneration,insertOp.endGeneration); |
| } |
| else |
| return null; |
| setGenerations(beginGeneration,insertOp.endGeneration); |
| return this; |
| } |
| |
| if (op2 is SplitParagraphOperation) |
| return new CompositeOperation([this,op2]); |
| |
| return null; |
| } |
| } |
| } |