| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.elements |
| { |
| import flash.events.Event; |
| import flash.events.EventDispatcher; |
| import flash.events.IEventDispatcher; |
| import flash.text.engine.TextLineValidity; |
| import flash.utils.Dictionary; |
| |
| import flashx.textLayout.compose.FlowComposerBase; |
| import flashx.textLayout.compose.IFlowComposer; |
| import flashx.textLayout.compose.ISWFContext; |
| import flashx.textLayout.compose.StandardFlowComposer; |
| import flashx.textLayout.compose.TextFlowLine; |
| import flashx.textLayout.container.ContainerController; |
| import flashx.textLayout.debug.Debugging; |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.edit.ISelectionManager; |
| import flashx.textLayout.events.CompositionCompleteEvent; |
| import flashx.textLayout.events.DamageEvent; |
| import flashx.textLayout.events.ModelChange; |
| import flashx.textLayout.events.StatusChangeEvent; |
| import flashx.textLayout.formats.ITextLayoutFormat; |
| import flashx.textLayout.formats.TextLayoutFormat; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| /** |
| * |
| * @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_BEGIN |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="flowOperationBegin", type="flashx.textLayout.events.FlowOperationEvent")] |
| |
| /** |
| * |
| * @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_END |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="flowOperationEnd", type="flashx.textLayout.events.FlowOperationEvent")] |
| |
| /** |
| * |
| * @eventType flashx.textLayout.events.FlowOperationEvent.FLOW_OPERATION_COMPLETE |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="flowOperationComplete", type="flashx.textLayout.events.FlowOperationEvent")] |
| |
| /** Dispatched whenever the selection is changed. Primarily used to update selection-dependent user interface. |
| * It can also be used to alter the selection, but cannot be used to alter the TextFlow itself. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="selectionChange", type="flashx.textLayout.events.SelectionEvent")] |
| |
| /** Dispatched after every recompose. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="compositionComplete", type="flashx.textLayout.events.CompositionCompleteEvent")] |
| |
| /** Dispatched when the mouse is pressed down over any link. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="mouseDown", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when the mouse is released over any link. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="mouseUp", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when the mouse passes over any link. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="mouseMove", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when the mouse first enters any link. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="rollOver", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when the mouse goes out of any link. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="rollOut", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when any link is clicked. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="click", type="flashx.textLayout.events.FlowElementMouseEvent")] |
| |
| /** Dispatched when a InlineGraphicElement is resized due to having width or height as auto or percent |
| * and the graphic has finished loading. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="inlineGraphicStatusChanged", type="flashx.textLayout.events.StatusChangeEvent")] |
| |
| /** Dispatched by a TextFlow object after text is scrolled within a controller container. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="scroll", type="flashx.textLayout.events.TextLayoutEvent")] |
| |
| /** Dispatched by a TextFlow object each time it is damaged |
| * |
| * You can use this event to find out that the TextFlow has changed, but do not access the TextFlow itself when this event |
| * is sent out. This event is sent when TextFlow changes are partially complete, so it can be in an inconsistent state: |
| * some changes have been mad already, and other changes are still pending. Get the information you need from the event, and make |
| * required changes after control returns to your application. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="damage", type="flashx.textLayout.events.DamageEvent")] |
| |
| /** Dispatched by a TextFlow object each time a container has had new DisplayObjects added or updated as a result of composition. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| [Event(name="updateComplete", type="flashx.textLayout.events.UpdateCompleteEvent")] |
| |
| |
| /** |
| * The TextFlow class is responsible for managing all the text content of a story. In TextLayout, text is stored in a |
| * hierarchical tree of elements. TextFlow is the root object of the element tree. All elements on the tree |
| * derive from the base class, FlowElement. |
| * |
| * <p>A TextFlow object can have ParagraphElement, ListElement and DivElement objects as children. A div (DivElement |
| * object) represents a group of paragraphs (ParagraphElement objects). A ListElement contains ListItemElement objects |
| * which in turn contain one or more ParagraphElement objects. A paragraph can have SpanElement, InlineGraphicElement, |
| * LinkElement, and TCYElement objects as children.</p> |
| * |
| * <p>A span (SpanElement) is a range of text in a paragraph that has the same attributes. An image |
| * (InlineGraphicElement) represents an arbitrary graphic that appears as a single character in a line of text. A |
| * LinkElement represents a hyperlink, or HTML <code>a</code> tag, and it can contain multiple spans. A TCYElement object |
| * is used in Japanese text when there is a small run of text that appears perpendicular to the line, as in a horizontal |
| * run within a vertical line. A TCYElement also can contain multiple spans.</p> |
| |
| * <p>TextFlow also derives from the ContainerFormattedElement class, which is the root class for all container-level block |
| * elements.</p> |
| * <p>The following illustration shows the relationship of other elements, such as spans and paragraphs, to the TextFlow |
| * object.</p> |
| * <p><img src="../../../images/textLayout_textFlowHierarchy.gif" alt="example TextFlow hierarchy"></img></p> |
| * |
| * <p>Each TextFlow object has a corresponding Configuration object that allows you to specify initial character and |
| * paragraph formats and the initial container format. It also allows you to specify attributes for selection, links, |
| * focus, and scrolling. When you supply a Configuration object as parameter to the <code>TextFlow()</code> |
| * constructor, it creates a read-only snapshot that you can access through the <code>TextFlow.configuration</code> |
| * property. After creation, you can't change the TextFlow's configuration. If you do not specify a Configuration, you |
| * can access the default configuration through the <code>TextFlow.defaultConfiguration</code> property.</p> |
| * |
| * @includeExample examples\TextFlowExample.as -noswf |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see #configuration |
| * @see IConfiguration |
| * @see DivElement |
| * @see FlowElement |
| * @see FlowGroupElement |
| * @see FlowLeafElement |
| * @see flashx.textLayout.compose.IFlowComposer IFlowComposer |
| * @see ParagraphElement |
| * @see SpanElement |
| */ |
| |
| public class TextFlow extends ContainerFormattedElement implements IEventDispatcher |
| { |
| private var _flowComposer:IFlowComposer; |
| |
| /** References the Selection manager attached to this TextFlow object. */ |
| private var _interactionManager:ISelectionManager; |
| |
| /** References the Configuration object for this TextFlow object. */ |
| |
| private var _configuration:IConfiguration; |
| |
| /** Manages computing and drawing backgroundColor attribute */ |
| private var _backgroundManager:BackgroundManager; |
| |
| /** Default configuration for all new TextFlow objects if the configuration is not specified. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see Configuration |
| */ |
| |
| public static var defaultConfiguration:Configuration = new Configuration(); |
| |
| // normalize support |
| private var normalizeStart:int = 0; |
| private var normalizeLen:int = 0; |
| |
| // event dispatch support; lazy initialization |
| private var _eventDispatcher:EventDispatcher; |
| |
| // textflow specific generation support - used to validate undo |
| private var _generation:uint; |
| // next generation number to hand out - these just have to be unique so share one. |
| // 0 is reserved to mean "not set" |
| static private var _nextGeneration:uint = 1; |
| |
| // styling support |
| private var _formatResolver:IFormatResolver; |
| |
| // interactive object count - now LinkElements later add in elements with eventMirrors |
| private var _interactiveObjectCount:int; |
| |
| // ILG count |
| private var _graphicObjectCount:int; |
| |
| // nested TextFlow support |
| private var _parentElement:FlowGroupElement; |
| |
| /** |
| * Constructor - creates a new TextFlow instance. |
| * |
| * <p>If you provide a <code>config</code> parameter, the contents of the Configuration object are copied and |
| * you cannot make changes. You can access configuration settings, however, through the |
| * <code>configuration</code> property. If the <code>config</code> parameter is null, you can access the default |
| * configuration settings through the <code>defaultConfiguration</code> property.</p> |
| * |
| * <p>The Configuration object provides a mechanism for setting configurable default attributes on a TextFlow. |
| * While you can't make changes to the Configuration object, you can override default attributes, if necessary, |
| * by setting the attributes of TextFlow and its children.</p> |
| * |
| * @param config Specifies the configuration to use for this TextFlow object. If it's null, use |
| * <code>TextFlow.defaultConfiguration</code> to access configuration values. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see Configuration |
| * @see #configuration |
| * @see #defaultConfiguration |
| * |
| */ |
| |
| public function TextFlow(config:IConfiguration = null) |
| { |
| super(); |
| initializeForConstructor(config); |
| } |
| |
| private function initializeForConstructor(config:IConfiguration):void |
| { |
| if (config == null) |
| config = defaultConfiguration; |
| // read only non changing copy of current state |
| _configuration = Configuration(config).getImmutableClone(); |
| // do not set up the event dispatcher yet |
| |
| format = _configuration.textFlowInitialFormat; |
| |
| // initialize the flowComposer |
| if (_configuration.flowComposerClass) |
| flowComposer = new _configuration.flowComposerClass(); |
| |
| _generation = _nextGeneration++; |
| _interactiveObjectCount = 0; |
| _graphicObjectCount = 0; |
| } |
| |
| /** @private */ |
| public override function shallowCopy(startPos:int = 0, endPos:int = -1):FlowElement |
| { |
| var retFlow:TextFlow = super.shallowCopy(startPos, endPos) as TextFlow; |
| retFlow._configuration = _configuration; |
| retFlow._generation = _nextGeneration++; |
| if (formatResolver) |
| retFlow.formatResolver = formatResolver.getResolverForNewFlow(this,retFlow); |
| // TODO: preserve the hostFormat?? |
| // preserve the swfContext |
| if (retFlow.flowComposer && flowComposer) |
| retFlow.flowComposer.swfContext = flowComposer.swfContext; |
| return retFlow; |
| } |
| |
| /** @private - count of interactive objects */ |
| tlf_internal function get interactiveObjectCount():int |
| { return _interactiveObjectCount; } |
| |
| /** @private - increment the count */ |
| tlf_internal function incInteractiveObjectCount():void |
| { _interactiveObjectCount++; } |
| |
| /** @private - decrement the count */ |
| tlf_internal function decInteractiveObjectCount():void |
| { _interactiveObjectCount--; } |
| |
| /** @private - count of interactive objects */ |
| tlf_internal function get graphicObjectCount():int |
| { return _graphicObjectCount; } |
| |
| /** @private - increment the count */ |
| tlf_internal function incGraphicObjectCount():void |
| { _graphicObjectCount++; } |
| |
| /** @private - decrement the count */ |
| tlf_internal function decGraphicObjectCount():void |
| { _graphicObjectCount--; } |
| |
| /** |
| * The Configuration object for this TextFlow object. The Configuration object specifies the initial character |
| * and paragraph formats, the initial container format, and attributes for selection highlighting, |
| * links, focus, and scrolling. |
| * |
| * <p>If you do not specify a Configuration object, Text Layout Framework uses a default Configuration object, which |
| * is referenced by the <code>defaultConfiguration</code> property.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see Configuration |
| * @see #defaultConfiguration |
| */ |
| public function get configuration():IConfiguration |
| { return _configuration; } |
| |
| /** |
| * The InteractionManager associated with this TextFlow object. |
| * <p>Controls all selection and editing on the text. If the TextFlow is not selectable, |
| * the interactionManager is null. To make the TextFlow editable, assign a interactionManager |
| * that is both an ISelectionManager and an IEditManager. To make a TextFlow that is read-only |
| * and allows selection, assign a interactionManager that is an ISelectionManager only. </p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see flashx.textLayout.edit.ISelectionManager ISelectionManager |
| * @see flashx.textLayout.edit.IEditManager IEditManager |
| */ |
| public function get interactionManager():ISelectionManager |
| { |
| return _interactionManager; |
| } |
| public function set interactionManager(newInteractionManager:ISelectionManager):void |
| { |
| // detatch old interactionManager |
| if (_interactionManager != newInteractionManager) |
| { |
| if (_interactionManager) |
| _interactionManager.textFlow = null; |
| _interactionManager = newInteractionManager; |
| if (_interactionManager) |
| { |
| _interactionManager.textFlow = this; |
| normalize(); |
| } |
| if (flowComposer) |
| flowComposer.interactionManagerChanged(newInteractionManager); |
| } |
| } |
| |
| /** Manages the containers for this element. |
| * |
| * <p>The TextLines that are created from the element appear as children of the container. |
| * The flowComposer manages the containers, and as the text is edited it adds lines to and removes lines |
| * from the containers. The flowComposer also keeps track of some critical attributes, such as the |
| * width and height to compose to, whether scrolling is on, and so on.</p> |
| * |
| * <p>The container and <code>flowComposer</code> are closely related. If you reset <code>flowComposer</code>, |
| * the container is reset to the new flowComposer's container. Likewise if the container is reset, |
| * <code>flowComposer</code> is reset to the container's new flowComposer.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see flashx.textLayout.compose.IFlowComposer FlowComposer |
| */ |
| public override function get flowComposer():IFlowComposer |
| { |
| return _flowComposer; |
| } |
| |
| public function set flowComposer(composer:IFlowComposer):void |
| { |
| changeFlowComposer(composer,true); |
| } |
| |
| /** @private use this function directly if you want to clear the flowcomposer but not unload the graphics. */ |
| tlf_internal function changeFlowComposer(newComposer:IFlowComposer,okToUnloadGraphics:Boolean):void |
| { |
| var origComposer:IFlowComposer = _flowComposer; |
| if (_flowComposer != newComposer) |
| { |
| var oldSWFContext:ISWFContext = FlowComposerBase.computeBaseSWFContext(_flowComposer ? _flowComposer.swfContext : null); |
| var newSWFContext:ISWFContext = FlowComposerBase.computeBaseSWFContext(newComposer ? newComposer.swfContext : null); |
| |
| // Clear out old settings |
| if (_flowComposer) |
| { |
| //hideSelection is no longer on IFlowComposer, so do it manually |
| var containerIter:int = 0; |
| while(containerIter < _flowComposer.numControllers) |
| _flowComposer.getControllerAt(containerIter++).clearSelectionShapes(); |
| |
| _flowComposer.setRootElement(null); // clear event listeners |
| } |
| |
| _flowComposer = newComposer; |
| |
| if (_flowComposer) |
| _flowComposer.setRootElement(this); |
| |
| // Mark flow as damaged |
| if (textLength) |
| damage(getAbsoluteStart(), textLength, TextLineValidity.INVALID, false); |
| |
| if (oldSWFContext != newSWFContext) |
| invalidateAllFormats(); |
| |
| // containers *may* have changed requiring reinherit of columnDirection/blockProgression |
| // but that's only in the case when we have ContainerFormattedElements that can have containers -- if then |
| // this call is really expensive for long flows and needs to be optimized for cases when nothing changes |
| // containerFormatChanged(false); |
| |
| // no longer visible shut down all the InlineGraphicElements |
| if (_flowComposer == null) |
| { |
| if (okToUnloadGraphics) |
| unloadGraphics(); |
| } |
| else if (origComposer == null) |
| prepareGraphicsForLoad(); |
| } |
| } |
| |
| /** @private - use to unload ILGs. Generally TLF manage this for you but TLF errs on the side of letting the graphics run once they are started. There may |
| * be cases where the client will want to unload the graphics. */ |
| tlf_internal function unloadGraphics():void |
| { |
| if (_graphicObjectCount) |
| applyFunctionToElements(function (elem:FlowElement):Boolean{ if (elem is InlineGraphicElement) (elem as InlineGraphicElement).stop(true); return false; }); |
| } |
| |
| /** @private - use to queue ILGs for loading. Generally TLF manage this for you. However, this function exists so that clients may initiate a load in edge cases. |
| * Graphics aren't loaded until the next updateAllControllers call. */ |
| tlf_internal function prepareGraphicsForLoad():void |
| { |
| // queue for start/restart any graphics that are not loaded but have a URL or CLASS source |
| if (_graphicObjectCount) |
| appendElementsForDelayedUpdate(this,null); |
| } |
| |
| /** Returns an element whose <code>id</code> property matches the <code>idName</code> parameter. Provides |
| * the ability to apply a style based on the <code>id</code>. |
| * |
| * <p>For example, the following line sets the style "color" to 0xFF0000 (red), for the |
| * element having the <code>id</code> span1.</p> |
| * |
| * <listing version="3.0" > |
| * textFlow.getElementByID("span1").setStyle("color", 0xFF0000); |
| * </listing> |
| * |
| * <p><strong>Note:</strong> In the following code, <code>p.addChild(s)</code> <em>removes</em> <code>s</code> |
| * from its original parent and adds it to <code>p</code>, the new parent.</p> |
| * |
| * <listing version="3.0" > |
| * var s:SpanElement = new SpanElement(); |
| * var p:ParagraphElement = new ParagraphElement(); |
| * ... |
| * s = textFlow.getElementByID("span3") as SpanElement; |
| * p.addChild(s); |
| * textFlow.addChild(p); |
| * </listing> |
| * |
| * @param idName The <code>id</code> value of the element to find. |
| * |
| * @return The element whose id matches <code>idName</code>. |
| * |
| * @includeExample examples\TextFlow_getElementByIDExample.as -noswf |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see FlowElement#id |
| */ |
| |
| public function getElementByID(idName:String):FlowElement |
| { |
| var rslt:FlowElement; |
| applyFunctionToElements(function (elem:FlowElement):Boolean{ if (elem.id == idName) { rslt = elem; return true; } return false; }); |
| return rslt; |
| } |
| |
| /** Returns all elements that have <code>styleName</code> set to <code>styleNameValue</code>. |
| * |
| * @param styleNameValue The name of the style for which to find elements that have it set. |
| * |
| * @return An array of the elements whose <code>styleName</code> value matches <code>styleNameValue</code>. For example, |
| * all elements that have the style name "color". |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see FlowElement#styleName |
| */ |
| public function getElementsByStyleName(styleNameValue:String):Array |
| { |
| var a:Array = new Array; |
| applyFunctionToElements(function (elem:FlowElement):Boolean{ if (elem.styleName == styleNameValue) a.push(elem); return false; }); |
| return a; |
| } |
| |
| /** Returns all elements that have <code>typeName</code> set to <code>typeNameValue</code>. |
| * |
| * @param styleNameValue The name of the style for which to find elements that have it set. |
| * |
| * @return An array of the elements whose <code>typeName</code> value matches <code>typeNameValue</code>. For example, |
| * all elements that have the type name "foo". A <code>typeName</code> is the TextFlow markup tag (such as the |
| * <code><p></code> tag for ParagraphElements). |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see FlowElement#styleName |
| */ |
| public function getElementsByTypeName(typeNameValue:String):Array |
| { |
| var a:Array = new Array; |
| applyFunctionToElements(function (elem:FlowElement):Boolean{ if (elem.typeName == typeNameValue) a.push(elem); return false; }); |
| return a; |
| } |
| |
| /** @private */ |
| override protected function get abstract():Boolean |
| { return false; } |
| |
| /** @private */ |
| tlf_internal override function get defaultTypeName():String |
| { return "TextFlow"; } |
| |
| /** @private */ |
| tlf_internal override function updateLengths(startIdx:int,len:int,updateLines:Boolean):void |
| { |
| if (normalizeStart != -1) |
| { |
| var newNormalizeStart:int = startIdx < normalizeStart ? startIdx : normalizeStart; |
| |
| if (newNormalizeStart < normalizeStart) |
| normalizeLen += (normalizeStart-newNormalizeStart); |
| normalizeLen += len; |
| normalizeStart = newNormalizeStart; |
| } |
| else |
| { |
| normalizeStart = startIdx; |
| normalizeLen = len; |
| } |
| // never go below zero |
| if (normalizeLen < 0) |
| normalizeLen = 0; |
| |
| // fix the lines |
| if (updateLines && _flowComposer) |
| { |
| _flowComposer.updateLengths(startIdx,len); |
| super.updateLengths(startIdx,len, false); |
| } |
| else |
| super.updateLengths(startIdx,len,updateLines); |
| } |
| |
| [RichTextContent] |
| /** @private NOTE: all FlowElement implementers and overrides of mxmlChildren must specify [RichTextContent] metadata */ |
| public override function set mxmlChildren(array:Array):void |
| { |
| super.mxmlChildren = array; |
| normalize(); |
| applyWhiteSpaceCollapse(null); |
| } |
| |
| /** @private Update any elements that have a delayed updated. Normally used to stop foreignelements when they |
| * are either displayed the first time or removed from the stage |
| */ |
| tlf_internal function applyUpdateElements(okToUnloadGraphics:Boolean):Boolean |
| { |
| if (_elemsToUpdate) |
| { |
| var hasController:Boolean = flowComposer && flowComposer.numControllers != 0; |
| for (var child:Object in _elemsToUpdate) |
| (child as FlowElement).applyDelayedElementUpdate(this,okToUnloadGraphics,hasController); |
| // if there is a controller then done with this list. |
| // if no controller have to keep the list around because they may be in the list waiting for load on a future compose |
| // the scenario that preserving the list fixes is a compose with no controllers followed by a compose with controllers |
| if (hasController) |
| { |
| _elemsToUpdate = null; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** @private */ |
| tlf_internal override function preCompose():void |
| { |
| // normalizes the flow |
| do |
| { |
| normalize(); |
| } |
| // starts or stops any FEs that have been modified, removed or deleted |
| while (applyUpdateElements(true)); |
| |
| // need to call normalize again in case any of the element updates have modified the hierarchy |
| // normalize(); |
| } |
| |
| /** |
| * Mark the a range of text as invalid - needs to be recomposed. |
| * <p>The text classes are self damaging. This is only used when modifying the container chain.</p> |
| * <p>Warning: Plan to evaulate a way to hide this method totally.</p> |
| * @param start text index of first character to marked invalid |
| * @param damageLen number of characters to mark invalid |
| * @param needNormalize optional parameter (true is default) - normalize should include this range. |
| * @private |
| */ |
| tlf_internal function damage(damageStart:int, damageLen:int, damageType:String, needNormalize:Boolean = true):void |
| { |
| // CONFIG::debug { assert(damageLen > 0,"must have at least 1 char in damageLen"); } |
| |
| if (needNormalize) |
| { |
| if (normalizeStart == -1) |
| { |
| normalizeStart = damageStart; |
| normalizeLen = damageLen; |
| } |
| else |
| { |
| if (damageStart < normalizeStart) |
| { |
| var newNormalizeLen:uint = normalizeLen; |
| newNormalizeLen = normalizeStart+normalizeLen - damageStart; |
| if (damageLen > newNormalizeLen) |
| newNormalizeLen = damageLen; |
| normalizeStart = damageStart; |
| normalizeLen = newNormalizeLen; |
| } |
| else if ((normalizeStart+normalizeLen) > damageStart) |
| { |
| if (damageStart+damageLen > normalizeStart+normalizeLen) |
| normalizeLen = damageStart+damageLen-normalizeStart; |
| } |
| else |
| normalizeLen = damageStart+damageLen-normalizeStart; |
| } |
| |
| // clamp to textLength |
| CONFIG::debug { assert(normalizeStart <= textLength,"damage bad length"); } |
| if (normalizeStart+normalizeLen > textLength) |
| normalizeLen = textLength-normalizeStart; |
| // trace("damage damageStart:" + damageStart.toString() + " damageLen:" + damageLen.toString() + " textLength:" + this.textLength + " normalizeStart:" + normalizeStart.toString() + " normalizeLen:" + normalizeLen.toString() ); |
| } |
| |
| if (_flowComposer) |
| _flowComposer.damage(damageStart, damageLen, damageType); |
| |
| if (hasEventListener(DamageEvent.DAMAGE)) |
| dispatchEvent(new DamageEvent(DamageEvent.DAMAGE,false,false,this,damageStart,damageLen)); |
| } |
| |
| /** |
| * Find the paragraph at the specified absolute position |
| * @private |
| */ |
| tlf_internal function findAbsoluteParagraph(pos:int):ParagraphElement |
| { |
| var elem:FlowElement = findLeaf(pos); |
| return elem ? elem.getParagraph() : null; |
| } |
| |
| /** |
| * Find the FlowGroupElement at the absolute position, |
| * could be synonymous with the paragraph OR a subBlockElement |
| * @private |
| */ |
| tlf_internal function findAbsoluteFlowGroupElement(pos:int):FlowGroupElement |
| { |
| var elem:FlowElement = findLeaf(pos); |
| return elem.parent; |
| } |
| |
| /** @private */ |
| CONFIG::debug public override function debugCheckFlowElement(depth:int = 0, extraData:String = ""):int |
| { |
| // debugging function that asserts if the flow element tree is in an invalid state |
| |
| var rslt:int = super.debugCheckFlowElement(depth,extraData); |
| |
| // describe the lines |
| if (Debugging.verbose && flowComposer) |
| { |
| for ( var lineIdx:int = 0; lineIdx < flowComposer.numLines; lineIdx++) |
| { |
| var workLine:TextFlowLine = flowComposer.getLineAt(lineIdx); |
| var containerIdx:int = flowComposer.getControllerIndex(workLine.controller); |
| trace("line:",lineIdx,"controller:",containerIdx,workLine.toString()); |
| } |
| |
| for (var idx:int = 0; idx < flowComposer.numControllers; idx++) |
| { |
| var controller:ContainerController = flowComposer.getControllerAt(idx); |
| trace("controller:",idx,Debugging.getIdentity(controller),controller.absoluteStart,controller.textLength,controller.compositionWidth,controller.compositionHeight,controller.getContentBounds()); |
| } |
| } |
| |
| rslt += assert(parent == null, "TextFlow should not have a parent"); |
| rslt += assert(parentRelativeStart == 0, "TextFlow start not zero"); |
| |
| return rslt; |
| } |
| |
| /** |
| * Check the internal consistency of the flow's FlowElement tree. |
| * @private |
| * Asserts if the data structures in the flow are invalid. |
| */ |
| CONFIG::debug public function debugCheckTextFlow(validateControllers:Boolean=true):int |
| { |
| if (!Debugging.debugCheckTextFlow) |
| return 0; |
| |
| var rslt:int = debugCheckFlowElement(); |
| |
| if (_flowComposer && validateControllers) |
| { |
| var idx:int; |
| var endPrevController:int = 0; |
| for (idx = 0; idx < flowComposer.numControllers; idx++) |
| { |
| var controller:ContainerController = flowComposer.getControllerAt(idx); |
| if (Debugging.verbose) |
| { |
| trace("controller:",idx,"absoluteStart:",controller.absoluteStart,"textLength:",controller.textLength); |
| } |
| |
| rslt += assert(controller.absoluteStart == endPrevController, "controller has bad start"); |
| rslt += assert(controller.textLength >= 0, "controller has bad textLength"); |
| endPrevController = controller.absoluteStart+controller.textLength; |
| rslt += assert(endPrevController <= textLength, "textLength may not extend past end of root element!"); |
| } |
| } |
| |
| if (_flowComposer is StandardFlowComposer) |
| rslt += StandardFlowComposer(_flowComposer).debugCheckTextFlowLines(validateControllers); |
| return rslt; |
| } |
| |
| /** |
| * Called after an import to validate that the entire flow textLength needs normalize. |
| * @private |
| */ |
| CONFIG::debug public function debugCheckNormalizeAll():void |
| { |
| assert(normalizeStart == 0,"normalizeStart: bad normailzeStart"); |
| assert(normalizeLen == textLength,"debugCheckNormalizeAll: bad normalizeLen"); |
| } |
| |
| /** |
| * @copy flash.events.IEventDispatcher#addEventListener() |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false): void |
| { |
| if (!_eventDispatcher) |
| _eventDispatcher = new EventDispatcher(this); |
| _eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference); |
| } |
| |
| /** |
| * @copy flash.events.IEventDispatcher#dispatchEvent() |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function dispatchEvent(event:Event):Boolean |
| { |
| if (!_eventDispatcher) |
| return true; |
| return _eventDispatcher.dispatchEvent(event); |
| } |
| |
| /** |
| * @copy flash.events.IEventDispatcher#hasEventListener() |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function hasEventListener(type:String):Boolean |
| { |
| if (!_eventDispatcher) |
| return false; |
| return _eventDispatcher.hasEventListener(type); |
| } |
| |
| /** |
| * @copy flash.events.IEventDispatcher#removeEventListener(). |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function removeEventListener(type:String, listener:Function, useCapture:Boolean = false): void |
| { |
| if (!_eventDispatcher) |
| return; |
| |
| _eventDispatcher.removeEventListener(type, listener, useCapture); |
| } |
| |
| /** |
| * @copy flash.events.IEventDispatcher#willTrigger() |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public function willTrigger(type:String):Boolean |
| { |
| if (!_eventDispatcher) |
| return false; |
| return _eventDispatcher.willTrigger(type); |
| } |
| |
| // elements which are images or blocks that *may* have children requiring activation or deactivation |
| private var _elemsToUpdate:Dictionary; |
| |
| /** @private */ |
| tlf_internal function appendOneElementForUpdate(elem:FlowElement):void |
| { |
| if (_elemsToUpdate == null) |
| _elemsToUpdate = new Dictionary(); |
| _elemsToUpdate[elem] = null; |
| } |
| |
| /** @private */ |
| tlf_internal function mustUseComposer():Boolean |
| { |
| if (_interactiveObjectCount != 0) |
| return true; |
| |
| if (_elemsToUpdate == null || _elemsToUpdate.length == 0) |
| return false; |
| |
| normalize(); |
| |
| // anything that doesn't normalize completely forces use of the compser |
| var rslt:Boolean = false; |
| for (var elem:Object in _elemsToUpdate) |
| { |
| if ((elem as FlowElement).updateForMustUseComposer(this)) |
| rslt = true; |
| } |
| |
| return rslt; |
| } |
| |
| /** @private */ |
| tlf_internal function processModelChanged(changeType:String, elem:Object, changeStart:int, changeLen:int, needNormalize:Boolean, bumpGeneration:Boolean):void |
| { |
| // track elements that may need an update before the next compose |
| if (elem is FlowElement) |
| (elem as FlowElement).appendElementsForDelayedUpdate(this,changeType); |
| |
| if (bumpGeneration) |
| _generation = _nextGeneration++; |
| |
| if (changeLen > 0 || changeType == ModelChange.ELEMENT_ADDED) |
| damage(changeStart, changeLen, TextLineValidity.INVALID, needNormalize); |
| |
| if (formatResolver) |
| { |
| switch(changeType) |
| { |
| case ModelChange.ELEMENT_REMOVAL: |
| case ModelChange.ELEMENT_ADDED: |
| case ModelChange.STYLE_SELECTOR_CHANGED: |
| formatResolver.invalidate(elem); |
| elem.formatChanged(false); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * The generation number for this TextFlow object. The undo and redo operations use the generation number to validate that |
| * it's legal to undo or redo an operation. The generation numbers must match. |
| * |
| * <p>Each model change increments <code>generation</code> so if the generation number changes, you know the |
| * TextFlow model has changed.</p> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| |
| public function get generation():uint |
| { |
| return _generation; |
| } |
| |
| /** used to reset the number backwards after an undo or redo. @private */ |
| tlf_internal function setGeneration(num:uint):void |
| { |
| _generation = num; |
| } |
| |
| /** @private */ |
| tlf_internal function processAutoSizeImageLoaded(elem:InlineGraphicElement):void |
| { |
| if (flowComposer) |
| elem.appendElementsForDelayedUpdate(this,null); |
| } |
| |
| /** |
| * Examine the damaged textLength of the TextFlow and put it in a normal form. This includes adding spans to empty paragraph and |
| * merging sibling spans that have the same attributes. |
| * @private |
| */ |
| tlf_internal function normalize():void |
| { |
| //trace("NORMALIZE"); |
| |
| if (normalizeStart != -1) |
| { |
| var normalizeEnd:int = normalizeStart + (normalizeLen==0?1:normalizeLen); |
| normalizeRange(normalizeStart==0?normalizeStart:normalizeStart-1,normalizeEnd); |
| |
| normalizeStart = -1; |
| normalizeLen = 0; |
| } |
| CONFIG::debug { debugCheckTextFlow(false); } |
| } |
| |
| private var _hostFormatHelper:HostFormatHelper; |
| |
| /** The TextLayoutFormat object for this TextFlow object. This enables several optimizations for reusing |
| * host formats. For example; |
| * |
| * <listing> |
| * textFlowA.hostFormat = textFlowB.hostFormat |
| * </listing> |
| * |
| * You must set format values before assigning the TextLayoutFormat object to <code>hostFormat</code>. |
| * For example, the following lines do <em>not</em> set the font size to 24 because |
| * the font size is set <em>after</em> the TextLayoutFormat object has been assigned to <code>hostFormat</code>. |
| * |
| * <listing> |
| * format = new TextLayoutFormat() |
| * textFlow.hostFormat = format |
| * format.fontSize = 24; |
| * </listing> |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see flashx.textLayout.formats.ITextLayoutFormat ITextLayoutFormat |
| */ |
| |
| public function get hostFormat():ITextLayoutFormat |
| { return _hostFormatHelper ? _hostFormatHelper.format : null; } |
| public function set hostFormat(value:ITextLayoutFormat):void |
| { |
| if (value == null) |
| _hostFormatHelper = null; |
| else |
| { |
| if (_hostFormatHelper == null) |
| _hostFormatHelper = new HostFormatHelper(); |
| _hostFormatHelper.format = value; |
| } |
| formatChanged(); |
| } |
| |
| /** @private */ |
| tlf_internal override function doComputeTextLayoutFormat():TextLayoutFormat |
| { |
| var parentPrototype:TextLayoutFormat = _hostFormatHelper ? _hostFormatHelper.getComputedPrototypeFormat() : null; |
| return FlowElement.createTextLayoutFormatPrototype(formatForCascade,parentPrototype); |
| } |
| |
| |
| /** Use the formatResolver to get the character "before style" of an Object. |
| * @param elem is either a FlowElement or a ContainerController(doesn't happen for characterformat) |
| * @return any styled CharacterFormat for that element |
| * @private |
| */ |
| tlf_internal function getTextLayoutFormatStyle(elem:Object):TextLayoutFormat |
| { |
| if (_formatResolver == null) |
| return null; |
| var rslt:ITextLayoutFormat = _formatResolver.resolveFormat(elem); |
| if (rslt == null) |
| return null; |
| // optimization! |
| var tlfvh:TextLayoutFormat = rslt as TextLayoutFormat; |
| return tlfvh ? tlfvh : new TextLayoutFormat(rslt); |
| } |
| |
| /** Use the formatResolver to get the character "after style" of an Object. |
| * @param elem is either a FlowElement or a ContainerController(doesn't happen for characterformat) |
| * @return any styled CharacterFormat for that element |
| * @private |
| */ |
| tlf_internal function getExplicitStyle(elem:Object):TextLayoutFormat |
| { |
| if (_formatResolver == null) |
| return null; |
| if(_formatResolver is IExplicitFormatResolver) |
| { |
| var rslt:ITextLayoutFormat = (_formatResolver as IExplicitFormatResolver).resolveExplicitFormat(elem); |
| if (rslt == null) |
| return null; |
| |
| var tlfvh:TextLayoutFormat = rslt as TextLayoutFormat; |
| return tlfvh ? tlfvh : new TextLayoutFormat(rslt); |
| } |
| return null; |
| } |
| |
| /** @private This API peeks at the background manager. Use when removing data as things go out of view or are recomposed. */ |
| tlf_internal function get backgroundManager():BackgroundManager |
| { return _backgroundManager; } |
| |
| /** @private */ |
| tlf_internal function clearBackgroundManager():void |
| { |
| //store cache for being deleted BackgroundManager |
| if(_backgroundManager) |
| { |
| if(!BackgroundManager.BACKGROUND_MANAGER_CACHE) |
| BackgroundManager.BACKGROUND_MANAGER_CACHE = new Dictionary(); |
| BackgroundManager.BACKGROUND_MANAGER_CACHE[this] = _backgroundManager.getShapeRectArray().concat(); |
| } |
| //end |
| _backgroundManager = null; |
| } |
| |
| /** @private. Returns the existing backgroundManager - creating it if it doesn't exist. Use when adding backgrounds to draw. */ |
| tlf_internal function getBackgroundManager():BackgroundManager |
| { |
| if (!_backgroundManager && (flowComposer is StandardFlowComposer)) |
| _backgroundManager = (flowComposer as StandardFlowComposer).createBackgroundManager() |
| return _backgroundManager; |
| } |
| |
| /** A callback function for resolving element styles. You can use this to provide styling using CSS or |
| * named styles, for example. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see IFormatResolver |
| */ |
| |
| public function get formatResolver(): IFormatResolver |
| { |
| return _formatResolver; |
| } |
| public function set formatResolver(val:IFormatResolver):void |
| { |
| if (_formatResolver != val) |
| { |
| if (_formatResolver) |
| _formatResolver.invalidateAll(this); |
| _formatResolver = val; |
| if (_formatResolver) |
| _formatResolver.invalidateAll(this); |
| |
| formatChanged(true); |
| } |
| } |
| |
| /** Invalidates all formatting information for the TextFlow, forcing it to be recomputed. |
| * Call this method when styles have changed. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| * @see IFormatResolver#invalidateAll() |
| */ |
| |
| public function invalidateAllFormats():void |
| { |
| if (_formatResolver) |
| _formatResolver.invalidateAll(this); |
| formatChanged(true); |
| } |
| |
| /** The parent element is the element that the TextFlow is nested inside (such as a TableCellElement). |
| * This property is for support of nested TextFlows to handle things like selection and editing. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| * |
| */ |
| public function get parentElement():FlowGroupElement
|
| { |
| return _parentElement; |
| } |
| |
| public function set parentElement(value:FlowGroupElement):void
|
| { |
| _parentElement = value; |
| } |
| |
| public function nestedInTable():Boolean{ |
| return parentElement && parentElement is TableCellElement; |
| } |
| |
| } // end TextFlow class |
| } |
| import flash.utils.Dictionary; |
| |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.elements.FlowElement; |
| import flashx.textLayout.formats.ITextLayoutFormat; |
| import flashx.textLayout.formats.TextLayoutFormat; |
| import flashx.textLayout.formats.TextLayoutFormat; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| /** @private. Expected usage is that all values are set. */ |
| class HostFormatHelper |
| { |
| private var _format:ITextLayoutFormat; |
| private var _computedPrototypeFormat:TextLayoutFormat; |
| |
| public function get format():ITextLayoutFormat |
| { return _format; } |
| public function set format(value:ITextLayoutFormat):void |
| { _format = value; _computedPrototypeFormat = null; } |
| |
| public function getComputedPrototypeFormat():TextLayoutFormat |
| { |
| if (_computedPrototypeFormat == null) |
| { |
| var useFormat:ITextLayoutFormat; |
| if (_format is TextLayoutFormat || _format is TextLayoutFormat) |
| useFormat = _format; |
| else |
| useFormat = new TextLayoutFormat(_format); |
| _computedPrototypeFormat = FlowElement.createTextLayoutFormatPrototype(useFormat,null); |
| } |
| return _computedPrototypeFormat; |
| } |
| |
| } |