blob: c078df36568b551268b1a9db5bc6f60ba7e428dc [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 and DivElement objects as children. A div (DivElement object)
* represents a group of paragraphs (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;
/**
* 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".
*
* @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 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);
}
/** @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
{ _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);
}
} // 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;
}
}