//////////////////////////////////////////////////////////////////////////////// // // 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.edit.ElementRange; import flashx.textLayout.edit.ParaEdit; import flashx.textLayout.edit.SelectionState; import flashx.textLayout.elements.FlowLeafElement; import flashx.textLayout.elements.InlineGraphicElement; import flashx.textLayout.elements.ParagraphElement; 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 adjustedForInsert:Boolean = false; private var _characterFormat: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 operationState. * * @playerversion Flash 10 * @playerversion AIR 1.5 * @langversion 3.0 */ public function InsertTextOperation(operationState:SelectionState, text:String, deleteSelectionState:SelectionState = null) { super(operationState); _characterFormat = 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. * *

null if no text is deleted.

* * @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 _characterFormat; } public function set characterFormat(value:ITextLayoutFormat):void { _characterFormat = new TextLayoutFormat(value); } private function doInternal():void { var leafEl:FlowLeafElement = textFlow.findLeaf(absoluteStart); var tcyEl:TCYElement = null; if(leafEl is InlineGraphicElement && leafEl.parent is TCYElement) { tcyEl = leafEl.parent as TCYElement; } if (delSelOp != null) { var deleteFormat:ITextLayoutFormat = new TextLayoutFormat(textFlow.findLeaf(absoluteStart).format); if (delSelOp.doOperation()) // figure out what to do here { //do not change characterFormat if user specified one already if ((characterFormat == null) && (absoluteStart < absoluteEnd)) { _characterFormat = deleteFormat; } else { if (leafEl.textLength == 0) { var pos:int = leafEl.parent.getChildIndex(leafEl); leafEl.parent.replaceChildren(pos, pos + 1, null); } } if(tcyEl && tcyEl.numChildren == 0) { leafEl = new SpanElement(); tcyEl.replaceChildren(0,0,leafEl); } } } // Wasteful, but it gives us the leanLeft logic for insert - which we only want to do if this is a point selection. var range:ElementRange; var useExistingLeaf:Boolean = false; // favor using leaf we have if it's valid (i.e., it has a paragraph in its parent chain and it is still inside a TextFlow) if (absoluteStart >= absoluteEnd || leafEl.getParagraph() == null || leafEl.getTextFlow() == null) { range = ElementRange.createElementRange(textFlow,absoluteStart, absoluteStart); } else { range = new ElementRange(); range.firstParagraph = leafEl.getParagraph(); range.firstLeaf = leafEl; useExistingLeaf = true; } var paraSelBegIdx:int = absoluteStart-range.firstParagraph.getAbsoluteStart(); // force insert to use the leaf given if we have a good one ParaEdit.insertText(range.firstParagraph, range.firstLeaf, paraSelBegIdx, _text, useExistingLeaf); if (textFlow.interactionManager) textFlow.interactionManager.notifyInsertOrDelete(absoluteStart, _text.length); if (_characterFormat && !TextLayoutFormat.isEqual(_characterFormat, range.firstLeaf.format)) ParaEdit.applyTextStyleChange(textFlow,absoluteStart,absoluteStart+_text.length,_characterFormat,null); } /** @private */ public override function doOperation():Boolean { doInternal(); if (originalSelectionState.selectionManagerOperationState && textFlow.interactionManager) { var state:SelectionState = textFlow.interactionManager.getSelectionState(); if (state.pointFormat) { state.pointFormat = null; textFlow.interactionManager.setSelectionState(state); } } return true; } /** @private */ public override function undo():SelectionState { var para:ParagraphElement = textFlow.findAbsoluteParagraph(absoluteStart); // paragraph relative offset - into the store var paraSelBegIdx:int = absoluteStart-para.getAbsoluteStart(); ParaEdit.deleteText(para, paraSelBegIdx, _text.length); if (textFlow.interactionManager) textFlow.interactionManager.notifyInsertOrDelete(absoluteStart, -_text.length); var newSelectionState:SelectionState = originalSelectionState; if (delSelOp != null) { newSelectionState = delSelOp.undo(); } if (adjustedForInsert) { var newBegIdx:int = newSelectionState.anchorPosition; var newEndIdx:int = newSelectionState.activePosition; if (newEndIdx > newBegIdx) newEndIdx--; else newBegIdx--; if (absoluteStart < absoluteEnd) { return new SelectionState(textFlow, newBegIdx, newEndIdx, newSelectionState.pointFormat); } else { return new SelectionState(textFlow, newBegIdx, newEndIdx, originalSelectionState.pointFormat); } } return originalSelectionState; } /** * Re-executes the operation after it has been undone. * *

This function is called by the edit manager, when necessary.

* * @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)) || (TextLayoutFormat.isEqual(originalSelectionState.pointFormat, insertOp.originalSelectionState.pointFormat))) _text += insertOp.text; else return null; setGenerations(beginGeneration,insertOp.endGeneration); return this; } if (op2 is SplitParagraphOperation) return new CompositeOperation([this,op2]); return null; } } }