flex-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ha...@apache.org
Subject [36/42] flex-asjs git commit: And here’s TLF…
Date Thu, 16 Mar 2017 13:37:56 GMT
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/compose/TextFlowLine.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/compose/TextFlowLine.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/compose/TextFlowLine.as
new file mode 100644
index 0000000..6f417c1
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/compose/TextFlowLine.as
@@ -0,0 +1,2552 @@
+// //////////////////////////////////////////////////////////////////////////////
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// //////////////////////////////////////////////////////////////////////////////
+package org.apache.flex.textLayout.compose
+{
+	import org.apache.flex.core.IParentIUIBase;
+	import org.apache.flex.core.UIBase;
+	import org.apache.flex.geom.Point;
+	import org.apache.flex.geom.Rectangle;
+	import org.apache.flex.graphics.ICompoundGraphic;
+	import org.apache.flex.graphics.PathBuilder;
+	import org.apache.flex.graphics.SolidColor;
+	import org.apache.flex.text.engine.Constants;
+	import org.apache.flex.text.engine.ElementFormat;
+	import org.apache.flex.text.engine.FontMetrics;
+	import org.apache.flex.text.engine.ITextBlock;
+	import org.apache.flex.text.engine.ITextLine;
+	import org.apache.flex.text.engine.TextRotation;
+	import org.apache.flex.textLayout.compose.utils.AdornmentUtils;
+	import org.apache.flex.textLayout.compose.utils.TextLineUtil;
+	import org.apache.flex.textLayout.container.IContainerController;
+	import org.apache.flex.textLayout.debug.Debugging;
+	import org.apache.flex.textLayout.debug.assert;
+	import org.apache.flex.textLayout.dummy.BoundsUtil;
+	import org.apache.flex.textLayout.edit.ISelectionManager;
+	import org.apache.flex.textLayout.edit.SelectionFormat;
+	import org.apache.flex.textLayout.elements.ElementConstants;
+	import org.apache.flex.textLayout.elements.IBackgroundManager;
+	import org.apache.flex.textLayout.elements.IContainerFormattedElement;
+	import org.apache.flex.textLayout.elements.IFlowElement;
+	import org.apache.flex.textLayout.elements.IFlowLeafElement;
+	import org.apache.flex.textLayout.elements.IInlineGraphicElement;
+	import org.apache.flex.textLayout.elements.IListItemElement;
+	import org.apache.flex.textLayout.elements.IParagraphElement;
+	import org.apache.flex.textLayout.elements.ISubParagraphGroupElementBase;
+	import org.apache.flex.textLayout.elements.ITableLeafElement;
+	import org.apache.flex.textLayout.elements.ITextFlow;
+	import org.apache.flex.textLayout.elements.LeadingUtils;
+	import org.apache.flex.textLayout.elements.utils.GeometricElementUtils;
+	import org.apache.flex.textLayout.formats.BlockProgression;
+	import org.apache.flex.textLayout.formats.Direction;
+	import org.apache.flex.textLayout.formats.Float;
+	import org.apache.flex.textLayout.formats.ITextLayoutFormat;
+	import org.apache.flex.textLayout.formats.JustificationRule;
+	import org.apache.flex.textLayout.formats.LeadingModel;
+	import org.apache.flex.textLayout.formats.LineBreak;
+	import org.apache.flex.textLayout.formats.ListStylePosition;
+	import org.apache.flex.textLayout.formats.TextAlign;
+	import org.apache.flex.textLayout.formats.TextLayoutFormat;
+	import org.apache.flex.textLayout.utils.CharacterUtil;
+	import org.apache.flex.textLayout.utils.Twips;
+	import org.apache.flex.utils.ObjectMap;
+	import org.apache.flex.utils.PointUtils;
+
+	/** 
+	 * The TextFlowLine class represents a single line of text in a text flow.
+	 * 
+	 * <p>Use this class to access information about how a line of text has been composed: its position, 
+	 * height, width, and so on. When the text flow (TextFlow) is modified, the lines immediately before and at the  
+	 * site of the modification are marked as invalid because they need to be recomposed. Lines after
+	 * the site of the modification might not be damaged immediately, but they might be regenerated once the
+	 * text is composed. You can access a TextFlowLine that has been damaged, but any values you access
+	 * reflect the old state of the TextFlow. When the TextFlow is recomposed, it generates new lines and you can 
+	 * get the new line for a given position by calling <code>TextFlow.flowComposer.findLineAtPosition()</code>.</p>
+	 *
+	 * @playerversion Flash 10
+	 * @playerversion AIR 1.5
+	 * @langversion 3.0
+	 */
+	public class TextFlowLine implements ITextFlowLine
+	{
+		// TODO temp setter
+		public function set height(value:Number):void
+		{
+		}
+
+		/** @private - the selection block cache */
+		static private var _selectionBlockCache:ObjectMap = new ObjectMap(true);
+
+		public function get composable():Boolean
+		{
+			return true;
+		}
+
+		private var _absoluteStart:int;		// text-offset of start of line - from beginning of the TextFlow
+		private var _textLength:int;		// number of chars to next line (incl trailing spaces, etc.)
+		private var _x:Number = 0;			// left edge of line; left as Number because it is user-settable
+		private var _y:Number = 0;			// top edge of line; left as Number because it is user-settable
+		private var _height:Number = 0;				// y advance
+		private var _outerTargetWidth:Number = 0;	// width line is composed to, excluding indents
+		private var _boundsLeftTW:int = 2;			// text line bounds: logical left
+		private var _boundsRightTW:int = 1;			// text line bounds: logical right (if left > right, then it is not set)
+		private var _para:IParagraphElement;				// owning paragraph
+		private var _controller:IContainerController;	// what frame the line was composed into
+		private var _columnIndex:int;					// column number in the container
+		private var _adornCount:int = 0;
+		static private const VALIDITY_MASK:uint = 7;		// 3 bits
+		static private const ALIGNMENT_SHIFT:uint = 3;
+		static private const ALIGNMENT_MASK:uint = 24; 		// 2 bits
+		static private const NUMBERLINE_MASK:uint = 32;		// 1 bit
+		static private const GRAPHICELEMENT_MASK:uint = 64;	// 1 bit
+		private var _flags:uint;
+		// added to support TextFlowLine when ITextLine not available
+		protected var _ascent:Number;
+		protected var _descent:Number;
+		private var _targetWidth:Number;
+		protected var _lineOffset:Number;
+		private var _lineExtent:Number;	// content bounds logical width for the line
+		private var _accumulatedLineExtent:Number;
+		private var _accumulatedMinimumStart:Number;
+		private var _numberLinePosition:int;
+
+		/** Constructor - creates a new TextFlowLine instance. 
+		 *  <p><strong>Note</strong>: No client should call this. It's exposed for writing your own composer.</p>
+		 *
+		 * @param textLine The ITextLine display object to use for this line.
+		 * @param paragraph The paragraph (ParagraphElement) in which to place the line.
+		 * @param outerTargetWidth The width the line is composed to, excluding indents.
+		 * @param lineOffset The line's offset in pixels from the appropriate container inset (as dictated by paragraph direction and container block progression), prior to alignment of lines in the paragraph. 
+		 * @param absoluteStart	The character position in the text flow at which the line begins.
+		 * @param numChars	The number of characters in the line.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.text.engine.ITextLine
+		 * @see org.apache.flex.textLayout.elements.ParagraphElement
+		 * @see #absoluteStart
+		 */
+		public function TextFlowLine(textLine:ITextLine, paragraph:IParagraphElement, outerTargetWidth:Number = 0, lineOffset:Number = 0, absoluteStart:int = 0, numChars:int = 0)
+		{
+			initialize(paragraph, outerTargetWidth, lineOffset, absoluteStart, numChars, textLine);
+		}
+
+		/** @private */
+		public function initialize(paragraph:IParagraphElement, outerTargetWidth:Number = 0, lineOffset:Number = 0, absoluteStart:int = 0, numChars:int = 0, textLine:ITextLine = null):void
+		{
+			_para = paragraph;
+			_outerTargetWidth = outerTargetWidth;
+			_absoluteStart = absoluteStart;
+			_textLength = numChars;
+
+			_adornCount = 0;
+			_lineExtent = 0;
+			_accumulatedLineExtent = 0;
+			_accumulatedMinimumStart = Constants.MAX_LINE_WIDTH;
+
+			_flags = 0;
+			_controller = null;
+
+			if (textLine)
+			{
+				textLine.userData = this;
+				_targetWidth = textLine.specifiedWidth;
+				_ascent = textLine.ascent;
+				_descent = textLine.descent;
+				// the docs say this is true
+				CONFIG::debug
+				{
+					assert(textLine.textHeight == textLine.ascent + textLine.descent, "bad textheight"); }
+				_lineOffset = lineOffset;
+				setValidity(textLine.validity);
+				CONFIG::debug
+				{
+					assert(textLine.validity == "valid", "Initializing TextFlowLine to invalid ITextLine"); }
+				setFlag(textLine.hasGraphicElement ? GRAPHICELEMENT_MASK : 0, GRAPHICELEMENT_MASK);
+			}
+			else
+				setValidity("invalid");
+		}
+
+		private function setFlag(value:uint, mask:uint):void
+		{
+			CONFIG::debug
+			{
+				assert((value | mask) == mask, "TFL:setFlag bad value"); }
+			_flags = (_flags & ~mask) | value;
+		}
+
+		private function getFlag(mask:uint):uint
+		{
+			return _flags & mask;
+		}
+
+		/** @private */
+		public function get heightTW():int
+		{
+			return Twips.to(_height);
+		}
+
+		/** @private */
+		public function get outerTargetWidthTW():int
+		{
+			return Twips.to(_outerTargetWidth);
+		}
+
+		/** @private */
+		public function get ascentTW():int
+		{
+			return Twips.to(_ascent);
+		}
+
+		/** @private */
+		public function get targetWidthTW():int
+		{
+			return Twips.to(_targetWidth);
+		}
+
+		/** @private */
+		public function get textHeightTW():int
+		{
+			return Twips.to(textHeight);
+		}
+
+		/** @private */
+		public function get lineOffsetTW():int
+		{
+			return Twips.to(_lineOffset);
+		}
+
+		/** @private */
+		public function get lineExtentTW():int
+		{
+			return Twips.to(_lineExtent);
+		}
+
+		/** @private */
+		public function get hasGraphicElement():Boolean
+		{
+			return getFlag(GRAPHICELEMENT_MASK) != 0;
+		}
+
+		/** @private */
+		public function get hasNumberLine():Boolean
+		{
+			return getFlag(NUMBERLINE_MASK) != 0;
+		}
+
+		/** @private */
+		public function get numberLinePosition():Number
+		{
+			return Twips.from(_numberLinePosition);
+		}
+
+		/** @private */
+		public function set numberLinePosition(position:Number):void
+		{
+			CONFIG::debug
+			{
+				assert(Twips.from(Twips.to(position)) == position, "Bad numberLinePosition"); }
+			_numberLinePosition = Twips.to(position);
+		}
+
+		/**
+		 * The height of the text line, which is equal to <code>ascent</code> plus <code>descent</code>. The 
+		 * value is calculated based on the difference between the baselines that bound the line, either 
+		 * ideographic top and bottom or ascent and descent depending on whether the baseline at y=0 
+		 * is ideographic (for example, TextBaseline.IDEOGRAPHIC_TOP) or not. 
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.text.engine.TextBaseline TextBaseline
+		 */
+		public function get textHeight():Number
+		{
+			return _ascent + _descent;
+		}
+
+		/** 
+		 * The horizontal position of the line relative to its container, expressed as the offset in pixels from the 
+		 * left of the container.
+		 * <p><strong>Note: </strong>Although this property is technically <code>read-write</code>, 
+		 * you should treat it as <code>read-only</code>. The setter exists only to satisfy the
+		 * requirements of the IVerticalJustificationLine interface that defines both a getter and setter for this property.
+		 * Use of the setter, though possible, will lead to unpredictable results.
+		 * </p>
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see #y
+		 */
+		public function get x():Number
+		{
+			return _x;
+		}
+
+		/** 
+		 * This comment is ignored, but the setter should not be used and exists only to satisfy
+		 * the IVerticalJustificationLine interface.
+		 * @see org.apache.flex.textLayout.compose.IVerticalJustificationLine 
+		 * @private 
+		 */
+		public function set x(lineX:Number):void
+		{
+			_x = lineX;
+			// invalidate bounds
+			_boundsLeftTW = 2;
+			_boundsRightTW = 1;
+		}
+
+		/** @private */
+		public function get xTW():int
+		{
+			return Twips.to(_x);
+		}
+
+		/** 
+		 * The vertical position of the line relative to its container, expressed as the offset in pixels from the top 
+		 * of the container.
+		 * <p><strong>Note: </strong>Although this property is technically <code>read-write</code>, 
+		 * you should treat it as <code>read-only</code>. The setter exists only to satisfy the
+		 * requirements of the IVerticalJustificationLine interface that defines both a getter and setter for this property.
+		 * Use of the setter, though possible, will lead to unpredictable results.
+		 * </p>
+		 * 
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see #x
+		 */
+		public function get y():Number
+		{
+			return _y;
+		}
+
+		/** @private */
+		public function get yTW():int
+		{
+			return Twips.to(_y);
+		}
+
+		/** This comment is ignored, but the setter should not be used and exists only to satisfy
+		 * the IVerticalJustificationLine interface.
+		 * @see org.apache.flex.textLayout.compose.IVerticalJustificationLine
+		 * @private
+		 */
+		public function set y(lineY:Number):void
+		{
+			_y = lineY;
+			// invalidate bounds
+			_boundsLeftTW = 2;
+			_boundsRightTW = 1;
+		}
+
+		/** @private */
+		public function setXYAndHeight(lineX:Number, lineY:Number, lineHeight:Number):void
+		{
+			_x = lineX;
+			_y = lineY;
+			_height = lineHeight;
+			// invalidate bounds
+			_boundsLeftTW = 2;
+			_boundsRightTW = 1;
+		}
+
+		/** 
+		 * One of the values from TextFlowLineLocation for specifying a line's location within a paragraph.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.textLayout.elements.ParagraphElement
+		 * @see TextFlowLineLocation
+		 */
+		public function get location():int
+		{
+			if (_para)
+			{
+				var lineStart:int;
+
+				// Harbs 8-31-14 added handling of multiple textBlocks might need more work to handle end?
+				var textLine:ITextLine = peekTextLine();
+				if (textLine)
+					lineStart = _absoluteStart - _para.getTextBlockAbsoluteStart(textLine.textBlock);
+				else
+					lineStart = _absoluteStart - _para.getAbsoluteStart();
+				// Initialize settings for location
+				if (lineStart == 0)		// we're at the start of the paragraph
+					return _textLength == _para.textLength ? TextFlowLineLocation.ONLY : TextFlowLineLocation.FIRST;
+				if (lineStart + textLength == _para.textLength)	// we're at the end of the para
+					return TextFlowLineLocation.LAST;
+			}
+			return TextFlowLineLocation.MIDDLE;
+		}
+
+		/** 
+		 * The controller (ContainerController object) for the container in which the line has been placed.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see org.apache.flex.textLayout.container.ContainerController 
+		 */
+		public function get controller():IContainerController
+		{
+			return _controller;
+		}
+
+		/** The number of the column in which the line has been placed, with the first column being 0.
+		 *		
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get columnIndex():int
+		{
+			return _columnIndex;
+		}
+
+		/**
+		 *  @private 
+		 * @flexjsignorecoercion org.apache.flex.textLayout.container.ContainerController
+		 */
+		public function setController(cont:IContainerController, colNumber:int):void
+		{
+			_controller = cont;
+			_columnIndex = colNumber;
+		}
+
+		/** The height of the line in pixels.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 */
+		public function get height():Number
+		{
+			return _height;
+		}
+
+		/** 
+		 * @copy org.apache.flex.text.engine.ITextLine#ascent
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get ascent():Number
+		{
+			return _ascent;
+		}
+
+		/** 
+		 * @copy org.apache.flex.text.engine.ITextLine#descent
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get descent():Number
+		{
+			return _descent;
+		}
+
+		/** 
+		 * The line's offset in pixels from the appropriate container inset (as dictated by paragraph direction and container block progression), 
+		 * prior to alignment of lines in the paragraph.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get lineOffset():Number
+		{
+			return _lineOffset;
+		}
+
+		/** 
+		 * The paragraph (ParagraphElement) in which the line resides.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 * 
+		 * @see org.apache.flex.textLayout.elements.ParagraphElement
+		 */
+		public function get paragraph():IParagraphElement
+		{
+			return _para;
+		}
+
+		/** 
+		 * The location of the line as an absolute character position in the TextFlow object.
+		 * 
+		 * @return 	the character position in the text flow at which the line begins.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.textLayout.elements.TextFlow
+		 */
+		public function get absoluteStart():int
+		{
+			return _absoluteStart;
+		}
+
+		/** @private */
+		public function setAbsoluteStart(val:int):void
+		{
+			_absoluteStart = val;
+		}
+
+		/** 
+		 * The number of characters to the next line, including trailing spaces. 
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get textLength():int
+		{
+			return _textLength;
+		}
+
+		/** @private */
+		public function setTextLength(val:int):void
+		{
+			_textLength = val;
+			// assert(_validity == "invalid", "not already damaged");
+			damage("invalid");
+		}
+
+		/** 
+		 * The amount of space to leave before the line.
+		 * <p>If the line is the first line of a paragraph that has a space-before applied, the line will have
+		 * a <code>spaceBefore</code> value. If the line comes at the top of a column, <code>spaceBefore</code> is ignored. 
+		 * Otherwise, the line follows another line in the column, and it is positioned vertically to insure that there is
+		 * at least this much space left between this line and the last line of the preceding paragraph.</p> 
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.textLayout.formats.TextLayoutFormat#paragraphSpaceBefore TextLayoutFormat.paragraphSpaceBefore
+		 */
+		public function get spaceBefore():Number
+		{
+			return (this.location & TextFlowLineLocation.FIRST) ? _para.computedFormat.paragraphSpaceBefore : 0;
+		}
+
+		/** 
+		 * The amount of space to leave after the line.
+		 * <p>If the line is the last line of a paragraph that has a space-after, the line will have
+		 * a <code>spaceAfter</code> value. If the line comes at the bottom of a column, then the <code>spaceAfter</code>
+		 * is ignored. Otherwise, the line comes before another line in the column, and the following line must be positioned vertically to
+		 * insure that there is at least this much space left between this last line of the paragraph and the first
+		 * line of the following paragraph.</p> 
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.textLayout.formats.TextLayoutFormat#paragraphSpaceAfter TextLayoutFormat.paragraphSpaceAfter
+		 */
+		public function get spaceAfter():Number
+		{
+			return ((this.location & TextFlowLineLocation.LAST) ? _para.computedFormat.paragraphSpaceAfter : 0);
+		}
+
+		/** @private 
+		 * Target width not including paragraph indents */
+		public function get outerTargetWidth():Number
+		{
+			return _outerTargetWidth;
+		}
+
+		/** @private */
+		public function set outerTargetWidth(val:Number):void
+		{
+			_outerTargetWidth = val;
+		}
+
+		/** @private  
+		 * Amount of space used to break the line
+		 * <p>The target width is the amount of space allowed for the line, including the space required for indents.</p>
+		 */
+		public function get targetWidth():Number
+		{
+			return _targetWidth;
+		}
+
+		/** 
+		 * Returns the bounds of the line as a rectangle.
+		 *
+		 * @return a rectangle that represents the boundaries of the line.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function getBounds():Rectangle
+		{
+			var textLine:ITextLine = getTextLine(true);
+			if (!textLine)
+				return new Rectangle();
+
+			// TODO: just use the textLine.x and textLine.y - after all getTextLine now sets them.
+			// not going to change this right now though
+			var bp:String = paragraph.getAncestorWithContainer().computedFormat.blockProgression;
+			var shapeX:Number = this.x;
+			var shapeY:Number = createShapeY(bp);
+			if (bp == BlockProgression.TB)
+				shapeY += descent - textLine.height;
+			return new Rectangle(shapeX, shapeY, textLine.width, textLine.height);
+		}
+
+		private const _validities:Array = ["invalid", "possiblyInvalid", "static", "valid", FlowDamageType.GEOMETRY];
+
+		private function setValidity(value:String):void
+		{
+			CONFIG::debug
+			{
+				assert(_validities.indexOf(value) != -1, "Bad alignment passed to TextFlowLine"); }
+			setFlag(_validities.indexOf(value), VALIDITY_MASK);
+		}
+
+		/** The validity of the line. 
+		 * <p>A line can be invalid if the text, the attributes applied to it, or the controller settings have
+		 * changed since the line was created. An invalid line can still be displayed, and you can use it, but the values
+		 * used will be the values at the time it was created. The line returned by <code>getTextLine()</code> also will be in an
+		 * invalid state. </p>
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see #getTextLine()
+		 * @see org.apache.flex.text.engine.ITextLine#validity ITextLine.validity
+		 * @see FlowDamageType#GEOMETRY
+		 */
+		public function get validity():String
+		{
+			return _validities[getFlag(VALIDITY_MASK)];
+		}
+
+		/** 
+		 * The width of the line if it was not justified. For unjustified text, this value is the same as <code>textLength</code>. 
+		 * For justified text, this value is what the length would have been without justification, and <code>textLength</code> 
+		 * represents the actual line width. For example, when the following String is justified and assigned a width of 500, it 
+		 * has an actual width of 500 but an unjustified width of 268.9921875. 
+		 *
+		 * @internal TBD: add graphic of justified line
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 */
+		public function get unjustifiedTextWidth():Number
+		{
+			// hack - outerTargetWidth holds value from the factory
+			var textLine:ITextLine = getTextLine(true);
+			return textLine.unjustifiedTextWidth + (_outerTargetWidth - targetWidth);
+		}
+
+		/** @private */
+		public function get lineExtent():Number
+		{
+			return _lineExtent;
+		}
+
+		/** @private */
+		public function set lineExtent(value:Number):void
+		{
+			_lineExtent = value;
+		}
+
+		/** @private */
+		public function get accumulatedLineExtent():Number
+		{
+			return _accumulatedLineExtent;
+		}
+
+		/** @private */
+		public function set accumulatedLineExtent(value:Number):void
+		{
+			_accumulatedLineExtent = value;
+		}
+
+		/** @private */
+		public function get accumulatedMinimumStart():Number
+		{
+			return _accumulatedMinimumStart;
+		}
+
+		/** @private */
+		public function set accumulatedMinimumStart(value:Number):void
+		{
+			_accumulatedMinimumStart = value;
+		}
+
+		static private const _alignments:Array = [TextAlign.LEFT, TextAlign.CENTER, TextAlign.RIGHT];
+
+		/** @private */
+		public function get alignment():String
+		{
+			return _alignments[getFlag(ALIGNMENT_MASK) >> ALIGNMENT_SHIFT];
+		}
+
+		/** @private */
+		public function set alignment(value:String):void
+		{
+			CONFIG::debug
+			{
+				assert(_alignments.indexOf(value) != -1, "Bad alignment passed to TextFlowLine"); }
+			setFlag(_alignments.indexOf(value) << ALIGNMENT_SHIFT, ALIGNMENT_MASK);
+		}
+
+		/** @private 
+		 * True if the line needs composing. */
+		public function isDamaged():Boolean
+		{
+			return (validity != "valid");
+		}
+
+		/** @private
+		 * Mark the line as valid */
+		public function clearDamage():void
+		{
+			CONFIG::debug
+			{
+				assert(validity == FlowDamageType.GEOMETRY, "can't clear damage other than geometry"); }
+			setValidity("valid");
+		}
+
+		/** @private
+		 * Mark the line as damaged */
+		public function damage(damageType:String):void
+		{
+			// trace("TextFlowLine.damage ", this.start.toString(), this.textLength.toString());
+			var current:String = validity;
+			if (current == damageType || current == "invalid")
+				return;	// totally damaged
+			setValidity(damageType);
+		}
+
+		/** @private */
+		/**
+		 * Check if the line is visible by comparing a set rectangle to the supplied
+		 * rectangle (all values in Twips).
+		 * -1 BEFORE visible bounds
+		 * 0 Visible
+		 * 1 AFTER visible bounds
+		 * @private
+		 */
+		public function testLineVisible(wmode:String, x:int, y:int, w:int, h:int):int
+		{
+			CONFIG::debug
+			{
+				assert(hasLineBounds(), "Bad call to TextFlowLine.isLineVisible"); }
+
+			if (wmode == BlockProgression.RL)
+			{
+				if (_boundsRightTW >= x && _boundsLeftTW < x + w)
+					return 0;
+				return x < _boundsRightTW ? 1 : -1;
+			}
+
+			if (_boundsRightTW >= y && _boundsLeftTW < y + h)
+				return 0;
+			return y < _boundsRightTW ? -1 : 1;
+		}
+
+		public function oldTestLineVisible(wmode:String, x:int, y:int, w:int, h:int):Boolean
+		{
+			CONFIG::debug
+			{
+				assert(hasLineBounds(), "Bad call to TextFlowLine.isLineVisible"); }
+
+			if (wmode == BlockProgression.RL)
+				return _boundsRightTW >= x && _boundsLeftTW < x + w;
+
+			return _boundsRightTW >= y && _boundsLeftTW < y + h;
+		}
+
+		/** @private
+		 * Set the text line bounds rectangle, all values in Twips.
+		 * If left > right, the rectangle is considered not to be set.
+		 * @private
+		 */
+		public function cacheLineBounds(wmode:String, bndsx:Number, bndsy:Number, bndsw:Number, bndsh:Number):void
+		{
+			if (wmode == BlockProgression.RL)
+			{
+				_boundsLeftTW = Twips.to(bndsx);
+				_boundsRightTW = Twips.to(bndsx + bndsw);
+			}
+			else
+			{
+				_boundsLeftTW = Twips.to(bndsy);
+				_boundsRightTW = Twips.to(bndsy + bndsh);
+			}
+		}
+
+		/** @private
+		 * Check if the text line bounds are set. If the stored left
+		 * value is > the right value, then the rectangle is not set.
+		 * @private
+		 */
+		public function hasLineBounds():Boolean
+		{
+			return (_boundsLeftTW <= _boundsRightTW);
+		}
+		/** @private */
+		CONFIG::debug
+		public function toString():String
+		{
+			return "x:" + x + " y: " + y + " absoluteStart:" + absoluteStart + " textLength:" + textLength + " location: " + location + " validity: " + validity;
+		}
+
+		/** 
+		 * Indicates whether the <code>org.apache.flex.text.engine.ITextLine</code> object for this TextFlowLine exists.  
+		 * The value is <code>true</code> if the ITextLine object has <em>not</em> been garbage collected and 
+		 * <code>false</code> if it has been.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.text.engine.ITextLine ITextLine
+		 */
+		public function get textLineExists():Boolean
+		{
+			return peekTextLine() != null;
+		}
+
+		/** @private
+		 * Returns the associated ITextLine if there is one. Finds it by looking up in the ITextBlock.
+		 */
+		public function peekTextLine():ITextLine
+		{
+			var textLine:ITextLine;
+
+			if (!paragraph)
+				return null;
+
+			// Look it up in the textBlock
+			var textBlocks:Vector.<ITextBlock> = paragraph.getTextBlocks();
+			for each (var textBlock:ITextBlock in textBlocks)
+			{
+				for (textLine = textBlock.firstLine; textLine; textLine = textLine.nextLine)
+				{
+					if (textLine.userData == this) // found it
+						return textLine;
+				}
+			}
+			return null;
+		}
+
+		/** 
+		 * Returns the <code>org.apache.flex.text.engine.ITextLine</code> object for this line, which might be recreated 
+		 * if it does not exist due to garbage collection. Set <code>forceValid</code> to <code>true</code>
+		 * to cause the ITextLine to be regenerated. Returns null if the ITextLine cannot be recreated.
+		 *.
+		 * @param forceValid	if true, the ITextLine is regenerated, if it exists but is invalid.
+		 *
+		 * @return object for this line or <code>null</code> if the ITextLine object cannot be 
+		 * recreated.
+		 *
+		 * @playerversion Flash 10
+		 * @playerversion AIR 1.5
+		 * @langversion 3.0
+		 *
+		 * @see org.apache.flex.text.engine.ITextLine ITextLine
+		 */
+		public function getTextLine(forceValid:Boolean = false):ITextLine
+		{
+			var textLine:ITextLine = peekTextLine();
+			if (textLine && textLine.validity == FlowDamageType.GEOMETRY)
+				createShape(paragraph.getTextFlow().computedFormat.blockProgression, textLine);
+			else if (!textLine || (textLine.validity == "invalid" && forceValid))
+			{
+				/*	if (!textLine)
+				{
+				trace("text line does not exist: regenerating");
+				textLine = peekTextLine();
+				}
+				else
+				trace("textline is invalid: regenerated"); */
+				if (isDamaged() && validity != FlowDamageType.GEOMETRY)
+					return null;
+
+				textLine = getTextLineInternal();
+			}
+
+			return textLine;
+		}
+
+		private function getTextLineInternal():ITextLine
+		{
+			// 8-31-14 Do we need to change this to handle multiple textBlocks?
+			// Look it up in the textBlock
+			var paraAbsStart:int = paragraph.getAbsoluteStart();
+
+			// If we haven't found it yet, we need to regenerate it.
+			// Regenerate the whole paragraph at once, up to the current position.
+			var textBlock:ITextBlock = paragraph.getTextBlockAtPosition(absoluteStart - paraAbsStart);
+			var currentLine:ITextLine = textBlock.firstLine;
+			var flowComposer:IFlowComposer = paragraph.getTextFlow().flowComposer;
+			var lineIndex:int = flowComposer.findLineIndexAtPosition(paraAbsStart);
+			var previousLine:ITextLine = null;
+			var textLine:ITextLine;
+			do
+			{
+				var line:ITextFlowLine = flowComposer.getLineAt(lineIndex);
+				CONFIG::debug
+				{
+					assert(line && line.paragraph == paragraph, "Expecting line in same paragraph"); }
+				if (currentLine != null && currentLine.validity == "valid" && (line != this || currentLine.userData == line))
+				{
+					textLine = currentLine;
+					currentLine = currentLine.nextLine;
+				}
+				else if (!line.composable)// TextFlowTableBlock
+				{
+					textLine = null;
+					currentLine = null;
+				}
+				else
+				{
+					textLine = line.recreateTextLine(textBlock, previousLine);
+					currentLine = null;
+				}
+				previousLine = textLine;
+				++lineIndex;
+			} while (line != this);
+
+			// Put it in the cache, so we can find it there next time
+			// textLineCache[this] = textLine;
+
+			return textLine;
+		}
+
+		/**
+		 *  @private Regenerate the ITextLine -- called when textLine has been gc'ed 
+		 * @flexjsignorecoercion org.apache.flex.textLayout.elements.IListItemElement
+		 */
+		public function recreateTextLine(textBlock:ITextBlock, previousLine:ITextLine):ITextLine
+		{
+			var textLine:ITextLine;
+
+			var textFlow:ITextFlow = _para.getTextFlow();
+			var bp:String = textFlow.computedFormat.blockProgression;
+			var flowComposer:IFlowComposer = textFlow.flowComposer;
+			var swfContext:ISWFContext = flowComposer.swfContext ? flowComposer.swfContext : SWFContext.globalSWFContext;
+
+			var numberLine:ITextLine;
+			var effLineOffset:Number = _lineOffset;
+			if (hasNumberLine)
+			{
+				var boxStartTotalIndent:Number = this._lineOffset - _para.computedFormat.textIndent;
+				numberLine = flowComposer.createNumberLine(_para.getParentByType("ListItemElement") as IListItemElement, _para, flowComposer.swfContext, boxStartTotalIndent);
+				if (numberLine)
+				{
+					if (TextLineUtil.getNumberLineListStylePosition(numberLine) == ListStylePosition.INSIDE)
+						effLineOffset += TextLineUtil.getNumberLineInsideLineWidth(numberLine);
+				}
+			}
+
+			// trace("Recreating line from", absoluteStart, "to", absoluteStart + textLength);
+			textLine = TextLineRecycler.getLineForReuse();
+			if (textLine)
+			{
+				CONFIG::debug
+				{
+					assert(textFlow.backgroundManager == null || textFlow.backgroundManager.getEntry(textLine) === undefined, "Bad ITextLine in recycler cache"); }
+				textLine = swfContext.callInContext(textBlock["recreateTextLine"], textBlock, [textLine, previousLine, _targetWidth, effLineOffset, true]);
+			}
+			else
+				textLine = swfContext.callInContext(textBlock.createTextLine, textBlock, [previousLine, _targetWidth, effLineOffset, true]);
+
+			if (textLine == null)
+				return null;
+
+			textLine.x = this.x;
+			CONFIG::debug
+			{
+				Debugging.traceFTEAssign(textLine, "x", this.x);  }
+			textLine.y = createShapeY(bp);
+			CONFIG::debug
+			{
+				Debugging.traceFTEAssign(textLine, "y", createShapeY(bp));  }
+			textLine.doubleClickEnabled = true;
+			textLine.userData = this;
+
+			// Regenerate adornments (e.g., underline & strikethru)
+			if (_adornCount > 0)
+			{
+				var paraStart:int = _para.getAbsoluteStart();
+				var elem:IFlowLeafElement = _para.findLeaf(this.absoluteStart - paraStart);
+				var elemStart:int = elem.getAbsoluteStart();
+
+				CONFIG::debug
+				{
+					assert(textLine.userData == this, "textLine doesn't point back to TextFlowLine"); }
+				if (numberLine)
+				{
+					var listItemElement:IListItemElement = _para.getParentByType("ListItemElement") as IListItemElement;
+					TextLineUtil.initializeNumberLinePosition(numberLine, listItemElement, _para, textLine.textWidth);
+				}
+
+				createAdornments(_para.getAncestorWithContainer().computedFormat.blockProgression, elem, elemStart, textLine, numberLine);
+
+				if (numberLine && TextLineUtil.getNumberLineListStylePosition(numberLine) == ListStylePosition.OUTSIDE)
+				{
+					if (bp == BlockProgression.TB)
+						numberLine.x = this.numberLinePosition;
+					else
+						numberLine.y = this.numberLinePosition;
+				}
+			}
+
+			return textLine;
+		}
+
+		/** @private */
+		public function createShape(bp:String, textLine:ITextLine):void
+		{
+			var newX:Number = this.x;
+			// if (int(newX*20) != int(textLine.x*20))
+			{
+				textLine.x = newX;
+				CONFIG::debug
+				{
+					Debugging.traceFTEAssign(textLine, "x", newX);  }
+			}
+			var newY:Number = createShapeY(bp);
+			// if (int(newY*20) != int(textLine.y*20))
+			{
+				textLine.y = newY;
+				CONFIG::debug
+				{
+					Debugging.traceFTEAssign(textLine, "y", newY);  }
+			}
+		}
+
+		private function createShapeY(bp:String):Number
+		{
+			return bp == BlockProgression.RL ? y : y + _ascent;
+		}
+
+		/** @private 
+		 * Scan through the format runs within the line, and draw any underline or strikethrough that might need it
+		 */
+		public function createAdornments(blockProgression:String, elem:IFlowLeafElement, elemStart:int, textLine:ITextLine, numberLine:ITextLine):void
+		{
+			CONFIG::debug
+			{
+				assert(elemStart == elem.getAbsoluteStart(), "bad elemStart passed to createAdornments"); }
+			var endPos:int = _absoluteStart + _textLength;
+
+			CONFIG::debug
+			{
+				assert(textLine.validity == "valid", "createAdornments: bad ITextLine validity"); }
+			/*if (textLine.validity != "valid")
+			{
+			// This can happen if we are scrolling through text and lines have been released, 
+			// then scrolled back into view before they're gc'ed. Then we have an invalid line in the TextFlowLine
+			// cache, so we create a valid text line to calculate where the adornment shapes should go. The 
+			// actual shapes will be added to the invalid (original) ITextLine in the TextFlowLine cache.
+			textLine = getTextLineInternal();
+			}*/
+
+			// init adornments back to 0 - may be redoing the line in a new position
+			// This can happen if there was damage earlier in the paragraph, so that the ITextLine was damaged (because all lines in block were damaged)
+			// but the TextFlowLine is still considered OK (didn't cause a line break change).
+			// CONFIG::debug { assert(_adornCount == 0 && _hasNumberLine == false,"createAdornments: adornments applied twice?"); }
+			_adornCount = 0;
+
+			if (numberLine)
+			{
+				_adornCount++;
+				setFlag(NUMBERLINE_MASK, NUMBERLINE_MASK);
+				textLine.addElement(numberLine);
+				CONFIG::debug
+				{
+					Debugging.traceFTECall(null, textLine, "addChildNumberLine", numberLine); }
+
+				// handle background on the numberLine
+				if (TextLineUtil.getNumberLineBackground(numberLine) != null)
+				{
+					var bgm:IBackgroundManager = elem.getTextFlow().getBackgroundManager();
+					if (bgm)
+						bgm.addNumberLine(textLine, numberLine);
+				}
+			}
+			else
+				setFlag(0, NUMBERLINE_MASK);
+
+			for (;;)
+			{
+				_adornCount += AdornmentUtils.updateAdornments(elem, textLine, blockProgression);
+
+				var elemFormat:ITextLayoutFormat = elem.format;
+				var imeStatus:* = elemFormat ? elemFormat.getStyle("imeStatus") : undefined;
+				if (imeStatus)
+				{
+					AdornmentUtils.updateIMEAdornments(elem, textLine, blockProgression, imeStatus as String);
+				}
+				elemStart += elem.textLength;
+				if (elemStart >= endPos)
+					break;
+				elem = elem.getNextLeaf(_para);
+				CONFIG::debug
+				{
+					assert(elem != null, "bad nextLeaf"); }
+			}
+		}
+
+		/** @private 
+		 * Scan through the format runs within the line, and figure out what the leading for the overall line is.
+		 * The line's leading is equal to the maximum leading of any individual run within the line.
+		 * The leading in an individual format run is calculated by looking at the leading attribute in the
+		 * CharacterFormat. If it is set to a value, we just use that value. Otherwise, if it is set to AUTO,
+		 * we calculate the leading based on the point size and the auto leading percentage from the ParagraphFormat.
+		 */
+		public function getLineLeading(bp:String, elem:IFlowLeafElement, elemStart:int):Number
+		{
+			CONFIG::debug
+			{
+				assert(elemStart == elem.getAbsoluteStart(), "bad elemStart passed to getLineLeading"); }
+			var endPos:int = _absoluteStart + _textLength;
+			var totalLeading:Number = 0;
+			CONFIG::debug
+			{
+				assert(elem.getAncestorWithContainer() != null, "element with no container"); }
+			for (;;)
+			{
+				// If there's only one element in the line, and it has 0 leading, try resetting the leading using the default algorithm. Important
+				// for lines that contain only a float (which can otherwise recurse infinitely), and TCY spans in vertical text.
+				var elemLeading:Number = elem.getEffectiveLineHeight(bp);
+				if (!elemLeading && elem.textLength == this.textLength)
+					elemLeading = TextLayoutFormat.lineHeightProperty.computeActualPropertyValue(elem.computedFormat.lineHeight, elem.computedFormat.fontSize);
+				totalLeading = Math.max(totalLeading, elemLeading);
+				elemStart += elem.textLength;
+				if (elemStart >= endPos)
+					break;
+				elem = elem.getNextLeaf(_para);
+				CONFIG::debug
+				{
+					assert(elem != null, "bad nextLeaf"); }
+				if (elem == null)
+					break;
+			}
+			return totalLeading;
+		}
+
+		/** @private 
+		 * Scan through the format runs within the line, and figure out what the typographic ascent (i.e. ascent relative to the 
+		 * Roman baseline) for the overall line is. Normally it is the distance between the Roman and Ascent baselines, 
+		 * but it may be adjusted upwards by the width/height of the GraphicElement.
+		 */
+		public function getLineTypographicAscent(elem:IFlowLeafElement, elemStart:int, textLine:ITextLine):Number
+		{
+			CONFIG::debug
+			{
+				assert(elemStart == elem.getAbsoluteStart(), "bad elemStart passed to getLineTypographicAscent"); }
+			return TextLineUtil.getTextLineTypographicAscent(textLine ? textLine : getTextLine(), elem, elemStart, absoluteStart + textLength);
+		}
+
+		/** @private 
+		 * Get the "line box" for the line as defined by the CSS visual formatting model (http://www.w3.org/TR/CSS2/visuren.html)
+		 * Essentially, the union of all "inline boxes" on the line.
+		 * @return A rectangle representing the line box. Top and Bottom are relative to the Roman baseline. Left and Right are ignored.
+		 * May return null, for example, if the line only contains a float.
+		 */
+		public function getCSSLineBox(bp:String, elem:IFlowLeafElement, elemStart:int, swfContext:ISWFContext, effectiveListMarkerFormat:ITextLayoutFormat = null, numberLine:ITextLine = null):Rectangle
+		{
+			CONFIG::debug
+			{
+				assert(elem.getAncestorWithContainer() != null, "element with no container"); }
+			CONFIG::debug
+			{
+				assert(elemStart == elem.getAbsoluteStart(), "bad elemStart passed to getCSSLineBox"); }
+
+			var lineBox:Rectangle;
+
+			var endPos:int = _absoluteStart + _textLength;
+			var textLine:ITextLine = getTextLine();
+
+			for (;;)
+			{
+				addToLineBox(elem.getCSSInlineBox(bp, textLine, _para, swfContext));
+
+				elemStart += elem.textLength;
+				if (elemStart >= endPos)
+					break;
+
+				elem = elem.getNextLeaf(_para);
+				CONFIG::debug
+				{
+					assert(elem != null, "bad nextLeaf"); }
+			}
+
+			if (effectiveListMarkerFormat && numberLine)
+			{
+				// Para not available for number line, but the methods below handle null para correctly.
+				var para:IParagraphElement = null;
+
+				var ef:ElementFormat = GeometricElementUtils.computeElementFormatHelper(effectiveListMarkerFormat, para, swfContext);
+				var metrics:FontMetrics = swfContext ? swfContext.callInContext(ef.getFontMetrics, ef, null, true) : ef.getFontMetrics();
+
+				addToLineBox(GeometricElementUtils.getCSSInlineBoxHelper(effectiveListMarkerFormat, metrics, numberLine, para));
+			}
+
+			function addToLineBox(inlineBox:Rectangle):void
+			{
+				if (inlineBox)
+					lineBox = lineBox ? lineBox.union(inlineBox) : inlineBox;
+			}
+
+			return lineBox;
+		}
+
+		// helper method to determine which subset of line is underlined
+		// I believe this will be replaced by the eventSink mechanism
+		// private function isTextlineSubsetOfSpan(element:FlowLeafElement): Boolean
+		// {
+		// var spanStart:int = element.getAbsoluteStart();
+		// var spanEnd:int = spanStart + element.textLength;
+		//
+		// var lineStart:int = this.absoluteStart;
+		// var lineEnd:int = this.absoluteStart + this._textLength;
+		//
+		// return spanStart <= lineStart && spanEnd >= lineEnd;
+		// }
+		// TODO remove reference to UIBase
+		/**
+		 *  Create a rectangle for selection
+		 * @flexjsignorecoercion org.apache.flex.core.UIBase
+		 */
+		static private function createSelectionRect(selObj:ICompoundGraphic, color:uint, x:Number, y:Number, width:Number, height:Number):UIBase
+		{
+			// selObj.graphics.beginFill(color);
+			selObj.fill = new SolidColor(color);
+			// var cmds:Vector.<int> = new Vector.<int>();
+			// var pathPoints:Vector.<Number> = new Vector.<Number>();
+
+			var builder:PathBuilder = new PathBuilder(true);
+
+			// set the start point - topLeft
+			builder.moveTo(x, y);
+
+			// line to topRight
+			builder.lineTo(x + width, y);
+
+			// line to botRight
+			builder.lineTo(x + width, y + height);
+
+			// line to botLeft
+			builder.lineTo(x, y + height);
+
+			// line to close the path - topLeft
+			// Should auto-close in the ICompoundGraphic
+
+			selObj.drawPathCommands(builder);
+
+			return selObj as UIBase;
+		}
+
+		/** @private getSelectionShapesCacheEntry
+		 * 
+		 * creates and adds block selection(s) to the text container.  In most circumstances,
+		 * this method will produce and add a single UIBase, but in certain circumstances,
+		 * such as TCY in TTB text, will need to make multiple selection rectangles.
+		 * 
+		 * Examples:
+		 * 1) horizontal - ABCDE
+		 * 2) vertical - ABCDE
+		 * 3) horizontal - ABcdE
+		 * 4) vertical:		A
+		 * 					B
+		 * 				   cde
+		 * 					F
+		 * 
+		 */
+		private function getSelectionShapesCacheEntry(begIdx:int, endIdx:int, prevLine:ITextFlowLine, nextLine:ITextFlowLine, blockProgression:String):SelectionCache
+		{
+			if (isDamaged())
+				return null;
+
+			// 8-31-14 Do we need to adjust this for paras with multiple textBlocks?
+			// get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
+			// var paraAbsStart:int = _para.getAbsoluteStart();
+			var textLine:ITextLine = getTextLine();
+			var paraAbsStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
+
+			// if the indexes are identical and are equal to the start of the line, then
+			// don't draw anything.  This prevents a bar being drawn on a following line when
+			// selecting accross line boundaries
+			// with exception for a selection that includes just the first character of an empty last line in the TextFlow
+			if (begIdx == endIdx && paraAbsStart + begIdx == absoluteStart)
+			{
+				if (absoluteStart != _para.getTextFlow().textLength - 1)
+					return null;
+				endIdx++;
+			}
+
+			// the cached selection bounds and rects
+			var selectionCache:SelectionCache = _selectionBlockCache[this];
+			if (selectionCache && selectionCache.begIdx == begIdx && selectionCache.endIdx == endIdx)
+				return selectionCache;
+
+			var drawRects:Array = new Array();
+			// an array to store any tcy rectangles which need separate processing and may not exist
+			var tcyDrawRects:Array = new Array();
+
+			if (selectionCache == null)
+			{
+				selectionCache = new SelectionCache();
+				_selectionBlockCache[this] = selectionCache;
+			}
+			else
+			{
+				selectionCache.clear();
+			}
+			selectionCache.begIdx = begIdx;
+			selectionCache.endIdx = endIdx;
+
+			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
+			calculateSelectionBounds(textLine, drawRects, begIdx, endIdx, blockProgression, heightAndAdj);
+
+			// iterate the blocks and create DisplayObjects to draw...
+			for each (var drawRect:Rectangle in drawRects)
+			{
+				CONFIG::debug
+				{
+					assert(selectionCache != null, "If we're caching, selectionArray should never be null!"); }
+				// we have to make new rectangles or the convertLineRectToGlobal will alter the cached ones!
+				selectionCache.pushSelectionBlock(drawRect);
+			}
+
+			// allow the atoms to be garbage collected.
+			// if (textLine) {
+			// textLine.flushAtomData(); // Warning: Now does nothing
+			// }
+
+			return selectionCache;
+		}
+
+		/** @private - helper method to calculate all selection blocks within a line.
+		 * @flexjsignorecoercion org.apache.flex.textLayout.elements.ISubParagraphGroupElementBase
+		 * @flexjsignorecoercion org.apache.flex.textLayout.elements.IInlineGraphicElement
+		 */
+		public function calculateSelectionBounds(textLine:ITextLine, rectArray:Array, begIdx:int, endIdx:int, blockProgression:String, heightAndAdj:Array):void
+		{
+			// the direction of the text
+			var direction:String = _para.computedFormat.direction;
+			// get the absolute start of the paragraph.  Calculation is expensive, so just do this once.
+			// var paraAbsStart:int = _para.getAbsoluteStart();
+			var paraAbsStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
+			// the current index.  used to iterate to the next element
+			var curIdx:int = begIdx;
+			// the current FlowLeafElement as determined by curIdx
+			var curElem:IFlowLeafElement = null;
+			// the highest glyph. Needed to normalize the rectangles we'll be building
+			var largestRise:Number = 0;
+
+			// blockRectArray holds each leaf's blocks which could be 1 or more
+			var blockRectArray:Array = [];
+			// floatRectArray holds the selection rects for any floats in the range.
+			var floatRectArray:Array = null;
+			// tcyDrawRects:Array
+			var tcyDrawRects:Array = null;
+
+			// do this loop and only afterwards perform the normalization and addition to the rectArr
+			while (curIdx < endIdx)
+			{
+				curElem = _para.findLeaf(curIdx);
+				// if we somehow got a 0 length element, then increment the index and continue
+				if (curElem.textLength == 0)
+				{
+					++curIdx;
+					continue;
+				}
+				else if (curElem is IInlineGraphicElement && (curElem as IInlineGraphicElement).computedFloat != Float.NONE)
+				{
+					if (floatRectArray == null)
+						floatRectArray = [];
+
+					var ilg:IInlineGraphicElement = curElem as IInlineGraphicElement;
+					var floatInfo:FloatCompositionData = controller.getFloatAtPosition(paraAbsStart + curIdx);
+					if (floatInfo)
+					{
+						var blockRect:Rectangle = new Rectangle(floatInfo.x - textLine.x, floatInfo.y - textLine.y, ilg.elementWidth, ilg.elementHeight);
+						floatRectArray.push(blockRect);
+					}
+					++curIdx;
+					continue;
+				}
+				// the number of potential glyphs to highlight. Could be larger than needed if we are only selecting part of it.
+				var numCharsSelecting:int = curElem.textLength + curElem.getElementRelativeStart(_para) - curIdx;
+				// special handling for TableLeafElements (do nothing)
+				if (curElem is ITableLeafElement)
+				{
+					// if(floatRectArray == null)
+					// floatRectArray = new Array();
+
+					// var block:TextFlowTableBlock = TableElement(TableLeafElement(curElem).parent).getFirstBlock();
+					// var blockRect:Rectangle = new Rectangle(floatInfo.x - textLine.x, floatInfo.y - textLine.y, ilg.elementWidth, ilg.elementHeight);
+					// floatRectArray.push(new Rectangle(0,0,block.width,block.height));
+					++curIdx;
+					continue;
+				}
+				// the index of the last glyph to highlight. If a partial selection, use endIdx
+				var endPos:int = (numCharsSelecting + curIdx) > endIdx ? endIdx : (numCharsSelecting + curIdx);
+
+				// if this is not a TCY in vertical, the blocks should all be running in the same direction
+				if (blockProgression != BlockProgression.RL || (textLine.getAtomTextRotation(textLine.getAtomIndexAtCharIndex(curIdx)) != TextRotation.ROTATE_0))
+				{
+					var leafBlockArray:Array = makeSelectionBlocks(textLine, curIdx, endPos, paraAbsStart, blockProgression, direction, heightAndAdj);
+					// copy all the blocks into the blockRectArray - we'll normalize them later
+					for (var leafBlockIter:int = 0; leafBlockIter < leafBlockArray.length; ++leafBlockIter)
+					{
+						blockRectArray.push(leafBlockArray[leafBlockIter]);
+					}
+				}
+				else
+				{
+					var tcyBlock:IFlowElement = curElem.getParentByType("TCYElement");
+					CONFIG::debug
+					{
+						assert(tcyBlock != null, "What kind of object is this that is ROTATE_0, but not TCY?");}
+
+					// if this element is still encompassed by a SubParagraphGroupElementBase of some kind (either a link or a TCYBlock)
+					// keep moving up to the parent.  Otherwise, the below code will go into an infinite loop.  bug 1905734
+					var tcyParentRelativeEnd:int = tcyBlock.parentRelativeEnd;
+					var subParBlock:ISubParagraphGroupElementBase = tcyBlock.getParentByType("SubParagraphGroupElementBase") as ISubParagraphGroupElementBase;
+					while (subParBlock)
+					{
+						tcyParentRelativeEnd += subParBlock.parentRelativeStart;
+						subParBlock = subParBlock.getParentByType("SubParagraphGroupElementBase") as ISubParagraphGroupElementBase;
+					}
+
+					var largestTCYRise:Number = 0;
+					var lastTCYIdx:int = endIdx < tcyParentRelativeEnd ? endIdx : tcyParentRelativeEnd;
+					var tcyRects:Array = new Array();
+
+					while (curIdx < lastTCYIdx)
+					{
+						curElem = _para.findLeaf(curIdx);
+						numCharsSelecting = curElem.textLength + curElem.getElementRelativeStart(_para) - curIdx;
+						endPos = numCharsSelecting + curIdx > endIdx ? endIdx : numCharsSelecting + curIdx;
+						var tcyRectArray:Array = makeSelectionBlocks(textLine, curIdx, endPos, paraAbsStart, blockProgression, direction, heightAndAdj);
+
+						for (var tcyBlockIter:int = 0; tcyBlockIter < tcyRectArray.length; ++tcyBlockIter)
+						{
+							var tcyRect:Rectangle = tcyRectArray[tcyBlockIter];
+
+							if (tcyRect.height > largestTCYRise)
+							{
+								largestTCYRise = tcyRect.height;
+							}
+
+							tcyRects.push(tcyRect);
+						}
+						curIdx = endPos;
+					}
+
+					if (!tcyDrawRects)
+						tcyDrawRects = new Array();
+
+					normalizeRects(tcyRects, tcyDrawRects, largestTCYRise, BlockProgression.TB, direction);
+					continue;
+				}
+
+				// set the curIdx to the last char in the block
+				curIdx = endPos;
+			}
+
+			// adding check for an empty set of draw rects.  If there are not recangles, skip this.
+			// this can happen is there are ONLY TCY blocks and the whole line is selected.
+			// Watson 2273832. - gak 02.09.09
+			// if the whole line is selected
+			if (blockRectArray.length > 0 && (paraAbsStart + begIdx) == absoluteStart && (paraAbsStart + endIdx) == (absoluteStart + textLength))
+			{
+				curElem = _para.findLeaf(begIdx);
+				// if we have the entire line selected, but the first element is NOT the last, then
+				// we will land up with a selection which is 1 character wider than it should be.
+				if (((curElem.getAbsoluteStart() + curElem.textLength) < (absoluteStart + textLength)) && endPos >= 2)
+				{
+					// make sure that this is a white char and that we aren't deselecting the last
+					// char in a line - esp important for scripts which don't use spaces ie Japanese
+					var charCode:int = _para.getCharCodeAtPosition(endPos - 1);
+					if (charCode != ElementConstants.kParagraphTerminator.charCodeAt(0) && CharacterUtil.isWhitespace(charCode))
+					{
+						var lastElemBlockArray:Array = makeSelectionBlocks(textLine, endPos - 1, endPos - 1, paraAbsStart, blockProgression, direction, heightAndAdj);
+						var lastRect:Rectangle = lastElemBlockArray[lastElemBlockArray.length - 1];
+						var modifyRect:Rectangle = blockRectArray[blockRectArray.length - 1] as Rectangle;
+
+						if (blockProgression != BlockProgression.RL)
+						{
+							// if they have the same width, simply remove the last block
+							if (modifyRect.width == lastRect.width)
+							{
+								blockRectArray.pop();
+							}
+							else
+							{
+								modifyRect.width -= lastRect.width;
+
+								// if this is RTL, we need to shift the selection block over by the amount
+								// we reduced it.
+								if (direction == Direction.RTL)
+									modifyRect.left -= lastRect.width;
+							}
+						}
+						else
+						{
+							// if they have the same height, simply remove the last block
+							if (modifyRect.height == lastRect.height)
+							{
+								blockRectArray.pop();
+							}
+							else
+							{
+								modifyRect.height -= lastRect.height;
+
+								// if this is RTL, we need to shift the selection block down by the amount
+								// we reduced it.
+								if (direction == Direction.RTL)
+									modifyRect.top += lastRect.height;
+							}
+						}
+					}
+				}
+			}
+
+			normalizeRects(blockRectArray, rectArray, largestRise, blockProgression, direction);
+			// add in the TCY Rects
+			if (tcyDrawRects && tcyDrawRects.length > 0)
+			{
+				for (var tcyIter:int = 0; tcyIter < tcyDrawRects.length; ++tcyIter)
+				{
+					rectArray.push(tcyDrawRects[tcyIter]);
+				}
+			}
+
+			// float selections do not normalize, put them into the rect array now
+			if (floatRectArray)
+			{
+				for (var floatIter:int = 0; floatIter < floatRectArray.length; ++floatIter)
+				{
+					rectArray.push(floatRectArray[floatIter]);
+				}
+			}
+		}
+
+		// TODO remove reference to UIBase
+		private function createSelectionShapes(selObj:ICompoundGraphic, selFormat:SelectionFormat, container:IParentIUIBase, begIdx:int, endIdx:int, prevLine:ITextFlowLine, nextLine:ITextFlowLine):void
+		{
+			var contElement:IContainerFormattedElement = _para.getAncestorWithContainer();
+			CONFIG::debug
+			{
+				assert(contElement != null, "para with no container"); }
+			var blockProgression:String = contElement.computedFormat.blockProgression;
+
+			var selCache:SelectionCache = getSelectionShapesCacheEntry(begIdx, endIdx, prevLine, nextLine, blockProgression);
+			if (!selCache)
+				return;
+
+			// iterate the blocks and create DisplayObjects to draw...
+			var drawRect:Rectangle;
+			var color:uint = selFormat.rangeColor;
+
+			if (_para && _para.getTextFlow())
+			{
+				var selMgr:ISelectionManager = _para.getTextFlow().interactionManager;
+				if (selMgr && (selMgr.anchorPosition == selMgr.activePosition))
+					color = selFormat.pointColor;
+			}
+
+			for each (drawRect in selCache.selectionBlocks)
+			{
+				drawRect = drawRect.clone();
+				convertLineRectToContainer(drawRect, true);
+				createSelectionRect(selObj, color, drawRect.x, drawRect.y, drawRect.width, drawRect.height);
+			}
+		}
+
+		/** @private 
+		 * Get the height and vertical adjustment for the line's selection shape, assuming Western typographic rules
+		 * where leading is included in selection.
+		 * @return An array with two elements
+		 * [0] height
+		 * [1] vertical adjustment to counter 'align bottom' behavior. The remainder of the selection code assumes selection shape
+		 * bottom is to be aligned with line descent. If this is not the case, vertical adjustment is set to an appropriate non-zero value. 
+		 */
+		public function getRomanSelectionHeightAndVerticalAdjustment(prevLine:ITextFlowLine, nextLine:ITextFlowLine):Array
+		{
+			var rectHeight:Number = 0;
+			var verticalAdj:Number = 0; // Default to 'align bottom'.
+
+			// This code erroneously assumed that it would only be called with a SPACE justifier and that AUTO would be up.  That is incorrect
+			// because some scripts, like Korean, use an up leading model and the EAST_ASIAN justifier.  New code just performs the check
+			if (LeadingUtils.useUpLeadingDirection(_para.getEffectiveLeadingModel()))
+			{
+				// "Space above, align bottom"
+				// 1) Space above as dictated by first baseline offset for the first line or line leading otherwise (both obtained from the 'height' data member)
+				// 2) Selection rectangle must at least include all of the text area
+				rectHeight = Math.max(height, textHeight);
+
+				// 3) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
+			}
+			else
+			{
+				// TODO-9/4/08-Is this the right way to check for first/last lines?
+				var isFirstLine:Boolean = !prevLine || prevLine.controller != controller || prevLine.columnIndex != columnIndex;
+				var isLastLine:Boolean = !nextLine || nextLine.controller != controller || nextLine.columnIndex != columnIndex || nextLine.paragraph.getEffectiveLeadingModel() == LeadingModel.ROMAN_UP;
+				// I'm removing this line as it makes the assumption that AUTO leading dir is UP only for Roman text, which is incorrect.
+				// Korean also uses UP leading but uses the EastAsian justifier. - gak 01.22.09
+				// ||(nextLine.paragraph.computedFormat.leadingDirection == LeadingDirection.AUTO && nextLine.paragraph.computedFormat.justificationRule == JustificationRule.SPACE);
+
+				if (isLastLine)
+				{
+					// There is no line after this one, or there is one which uses leading UP, so leading DOWN does not apply
+
+					if (!isFirstLine)
+					{
+						// "Space above None, align bottom" (eqivalently, "Space below None, align top"):
+						// 1) Only the text area should be selected
+						rectHeight = textHeight;
+
+						// 2) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
+					}
+					else
+					{
+						// "Space above, align bottom"
+						// 1) Space above as dictated by first baseline offset
+						// 2) Selection rectangle must at least include all of the text area
+						rectHeight = Math.max(height, textHeight);
+						// 3) Selection rectangle's bottom aligned with line descent; verticalAdj remains 0
+					}
+				}
+				else
+				{
+					// There is a line after this one, so leading DOWN applies
+
+					if (!isFirstLine)
+					{
+						// "Space below, align top"
+						// 1) Space below as dictated by line leading (obtained from 'height' member of next line)
+						// 2) Selection rectangle must at least include all of the text area
+						rectHeight = Math.max(nextLine.height, textHeight);
+
+						// 3) Selection rectangle's top to be aligned with line ascent, so its bottom to be at rectHeight - textLine.ascent,
+						// not textLine.descent, set verticalAdj accordingly
+						verticalAdj = rectHeight - textHeight; // same as rectHeight - textLine.ascent - textLine.descent
+					}
+					else
+					{
+						// Union of
+						// 1) first line, leading up: In this case, rectangle height is the larger of line height and text height,
+						// and the rectangle is shifted down by descent amount to align bottoms. So, top of rectangle is at:
+						var top:Number = _descent - Math.max(height, textHeight);
+
+						// 2) interior line, leading down: In this case, rectangle height is the larger of line leading and text height,
+						// and the rectangle is shifted up by ascent amount to align tops. So, bottom of rectangle is at:
+						var bottom:Number = Math.max(nextLine.height, textHeight) - _ascent;
+
+						rectHeight = bottom - top;
+
+						// 3) Selection rectangle's bottom to be at 'bottom', not the line's descent; set verticalAdj accordingly
+						verticalAdj = bottom - _descent;
+					}
+				}
+			}
+
+			// If we don't have a line above us, then we need to pad the line a bit as well as make it shift up.
+			// If we don't, then it overlaps the line below too much OR clips the top of the glyphs.
+			if (!prevLine || prevLine.columnIndex != this.columnIndex || prevLine.controller != this.controller)
+			{
+				// make it taller - this is kinda a fudge, but we have no info to determine a good top.
+				// if we don't do this, the selection rectangle will clip to the top of the glyphs and even
+				// let parts stick out a bit.  So, re-add the descent and offset the rect by 50% so that
+				// it appears to balance the top and bottom.
+				rectHeight += this.descent;
+				verticalAdj = Math.floor(this.descent / 2);
+			}
+			return [rectHeight, verticalAdj];
+		}
+
+		/** @private 
+		 * 
+		 * ? Get a list of rects of the characters in the given textline? Used to show selection? JF 
+		 */
+		private function makeSelectionBlocks(textLine:ITextLine, begIdx:int, endIdx:int, paraAbsStart:int, blockProgression:String, direction:String, heightAndAdj:Array):Array
+		{
+			CONFIG::debug
+			{
+				assert(begIdx <= endIdx, "Selection indexes are reversed!  How can this happen?!"); }
+
+			var blockArray:Array = [];
+			var blockRect:Rectangle = new Rectangle();
+			var startElem:IFlowLeafElement = _para.findLeaf(begIdx);
+			var startMetrics:Rectangle = startElem.getComputedFontMetrics().emBox;
+
+			if (!textLine)
+				textLine = getTextLine(true);
+
+			// ++makeBlockPassCounter;
+			// trace(makeBlockPassCounter + ") direction = " + direction + " blockProgression = " + blockProgression);
+
+			// CNW: removed whole line optimization 5/18/10 - was yielding different results than code below and was no faster
+
+			// trace(makeBlockPassCounter + ") begIdx = " + begIdx.toString() + " endIdx = " +  endIdx.toString());
+			var begAtomIndex:int = textLine.getAtomIndexAtCharIndex(begIdx);
+			var endAtomIndex:int = adjustEndElementForBidi(textLine, begIdx, endIdx, begAtomIndex, direction);
+
+			// trace(makeBlockPassCounter + ") begAtomIndex = " + begAtomIndex.toString() + " endAtomIndex = " +  endAtomIndex.toString());
+			CONFIG::debug
+			{
+				assert(begAtomIndex >= 0, "Invalid start index! begIdx = " + begIdx);}
+			CONFIG::debug
+			{
+				assert(endAtomIndex >= 0, "Invalid end index! begIdx = " + endIdx);}
+
+			if (direction == Direction.RTL && textLine.getAtomBidiLevel(endAtomIndex) % 2 != 0)
+			{
+				// if we are in RTL, anchoring the LTR text gets tricky.  Because the endElement is before the first
+				// element - which is why we're in this code - the result can be a zero-width rectangle if the span of LTR
+				// text breaks across line boundaries.  If that is the case, then the endAtomIndex value will be 0.  As
+				// this is the less common case, assume that it isn't and make all other cases come first
+				if (endAtomIndex == 0 && begIdx < endIdx - 1)
+				{
+					// since the endAtomIndex is 0, meaning that the LTR spans lines,
+					// we want to grab the glyph before the endIdx which represents the last LTR glyph for the selection.
+					// Make a recursive call into makeSelectionBlocks using and endIdx decremented by 1.
+					blockArray = makeSelectionBlocks(textLine, begIdx, endIdx - 1, paraAbsStart, blockProgression, direction, heightAndAdj);
+					var bidiBlock:Array = makeSelectionBlocks(textLine, endIdx - 1, endIdx - 1, paraAbsStart, blockProgression, direction, heightAndAdj);
+					var bidiBlockIter:int = 0;
+					while (bidiBlockIter < bidiBlock.length)
+					{
+						blockArray.push(bidiBlock[bidiBlockIter]);
+						++bidiBlockIter;
+					}
+					return blockArray;
+				}
+			}
+
+			var begIsBidi:Boolean = begAtomIndex != -1 ? isAtomBidi(textLine, begAtomIndex, direction) : false;
+			var endIsBidi:Boolean = endAtomIndex != -1 ? isAtomBidi(textLine, endAtomIndex, direction) : false;
+
+			if (begIsBidi || endIsBidi)
+			{
+				// this code needs to iterate over the glyphs starting at the begAtomIndex and going forward.
+				// It doesn't matter is beg is bidi or not, we need to find a boundary, create a rect on it, then proceded.
+				// use the value of begIsBidi for testing the consistency of the selection.
+
+				// Example bidi text.  Note that the period comes at the left end of the line:
+				//
+				// Bidi state:		f f t t t t t	(true/false)
+				// Element Index:0 1 2 3 4 5 6		(0 is the para terminator)
+				// Chars:			. t o _ b e
+				// Flow Index:	   6 0 1 2 3 4 (5) 	Note that these numbers represent the space between glyphs AND
+				// 5(f)			that index 5 is both the space after the e and before the period.
+				// but, the position 5 is not a valid cursor location.
+
+				// the original code I implemented used the beg and endElement indexes however that fails because when the text
+				// is mixed bidi/non-bidi, the indexes are only 1 char apart. This resulted in, for example, only the period in
+				// a line getting selected when the text was bidi.   Instead, we're going to use the begIdx and endIdx and
+				// recalculate the element indexes each time.  This is expensive, but I don't see an alternative. - gak 09.05.08
+				var curIdx:int = begIdx;
+				var incrementor:int = begIdx != endIdx ? 1 : 0;
+
+				// the indexes used to draw the seleciton.  activeStart/End represent the
+				// beginning of the selection shape atoms, while cur is the one we are testing.
+				var activeStartIndex:int = begAtomIndex;
+				var activeEndIndex:int = begAtomIndex;
+				var curElementIndex:int = begAtomIndex;
+
+				// when activeEndIsBidi no longer matches the bidi setting for the activeStartIndex, we will create the shape
+				var activeEndIsBidi:Boolean = begIsBidi;
+
+				do
+				{
+					// increment the index
+					curIdx += incrementor;
+					// get the next atom index
+					curElementIndex = textLine.getAtomIndexAtCharIndex(curIdx);
+
+					// calculate the bidi level for the - kinda cludgy, but if the bidi-text wraps, curElementIndex == -1
+					// so just set it to false if this is the case.  It will get ignored in the subsequent check and curIdx
+					// will == endIdx as this is the last glyph in the line - which is why the next is -1 - gak 09.12.08
+					var curIsBidi:Boolean = (curElementIndex != -1) ? isAtomBidi(textLine, curElementIndex, direction) : false;
+
+					if (curElementIndex != -1 && curIsBidi != activeEndIsBidi)
+					{
+						blockRect = makeBlock(textLine, curIdx, activeStartIndex, activeEndIndex, startMetrics, blockProgression, direction, heightAndAdj);
+						blockArray.push(blockRect);
+
+						// shift the activeStart/End indexes to the current
+						activeStartIndex = curElementIndex;
+						activeEndIndex = curElementIndex;
+						// update the bidi setting
+						activeEndIsBidi = curIsBidi;
+					}
+					else
+					{
+						// we don't get another chance to make a block, so if this is the last char, make the block before we bail out.
+						// we have to check both equality and equality plus the incrementor because if we don't, then we'll miss a
+						// character in the selection.
+						if (curIdx == endIdx)
+						{
+							blockRect = makeBlock(textLine, curIdx, activeStartIndex, activeEndIndex, startMetrics, blockProgression, direction, heightAndAdj);
+							blockArray.push(blockRect);
+						}
+
+						activeEndIndex = curElementIndex;
+					}
+				} while (curIdx < endIdx);
+			}
+			else
+			{
+				var testILG:IInlineGraphicElement = startElem as IInlineGraphicElement;
+				if (!testILG || testILG.effectiveFloat == Float.NONE || begIdx == endIdx)
+				{
+					blockRect = makeBlock(textLine, begIdx, begAtomIndex, endAtomIndex, startMetrics, blockProgression, direction, heightAndAdj);
+					if (testILG && testILG.elementWidthWithMarginsAndPadding() != testILG.elementWidth)
+					{	// Don't include margins or padding around inlines in the bounds
+						var verticalText:Boolean = testILG.getTextFlow().computedFormat.blockProgression == BlockProgression.RL;
+						var ilgFormat:ITextLayoutFormat = testILG.computedFormat;
+						if (verticalText)
+						{
+							var paddingTop:Number = testILG.getEffectivePaddingTop();
+							blockRect.top += paddingTop;	// don't include the left side indent in the selected area
+							var paddingBottom:Number = testILG.getEffectivePaddingBottom();
+							blockRect.bottom -= paddingBottom;
+						}
+						else
+						{
+							var paddingLeft:Number = testILG.getEffectivePaddingLeft();
+							blockRect.left += paddingLeft;	// don't include the left side indent in the selected area
+							var paddingRight:Number = testILG.getEffectivePaddingRight();
+							blockRect.right -= paddingRight;
+						}
+					}
+				}
+				else
+				{
+					blockRect = BoundsUtil.getBounds(testILG.graphic, textLine);// testILG.graphic.getBounds(textLine);
+				}
+
+				blockArray.push(blockRect);
+			}
+
+			return blockArray;
+		}
+
+		/** @private 
+		 * 
+		 * ? Get the bounds of the supplied range of characters in the given textline? Used to show selection? JF 
+		 */
+		private function makeBlock(textLine:ITextLine, begTextIndex:int, begAtomIndex:int, endAtomIndex:int, startMetrics:Rectangle, blockProgression:String, direction:String, heightAndAdj:Array):Rectangle
+		{
+			var blockRect:Rectangle = new Rectangle();
+			var globalStart:Point = new Point(0, 0);
+
+			if (begAtomIndex > endAtomIndex)
+			{
+				// swap the start and end
+				var tempEndIdx:int = endAtomIndex;
+				endAtomIndex = begAtomIndex;
+				begAtomIndex = tempEndIdx;
+			}
+			if (!textLine)
+				textLine = getTextLine(true);
+
+			// now that we have elements and they are in the right order for drawing, get their rectangles
+			var begCharRect:Rectangle = textLine.getAtomBounds(begAtomIndex);
+			// trace(makeBlockPassCounter + ") begCharRect = " + begCharRect.toString());
+
+			var endCharRect:Rectangle = textLine.getAtomBounds(endAtomIndex);
+			// trace(makeBlockPassCounter + ") endCharRect = " + endCharRect.toString());
+
+			// Calculate the justificationRule value
+			var justRule:String = _para.getEffectiveJustificationRule();
+			// If this is TTB text and NOT TCY, as indicated by TextRotation.rotate0...
+			if (blockProgression == BlockProgression.RL && textLine.getAtomTextRotation(begAtomIndex) != TextRotation.ROTATE_0)
+			{
+				globalStart.y = begCharRect.y;
+				blockRect.height = begAtomIndex != endAtomIndex ? endCharRect.bottom - begCharRect.top : begCharRect.height;
+
+				// re-ordered this code.  EAST_ASIAN is more common in vertical and should be the first option.
+				if (justRule == JustificationRule.EAST_ASIAN)
+				{
+					blockRect.width = begCharRect.width;
+				}
+				else
+				{
+					blockRect.width = heightAndAdj[0];
+					globalStart.x -= heightAndAdj[1];
+				}
+			}
+			else
+			{
+				// given bidi text alternations, the endCharRect could be left of the begCharRect,
+				// use whichever is farther left.
+				globalStart.x = Math.min(begCharRect.x, endCharRect.x);
+				// if we're here and the BlockProgression is TTB, then we're TCY.  Less frequent case, so make non-TCY
+				// the first option...
+				// NB - Never use baseline adjustments for TCY.  They don't make sense here.(I think) - gak 06.03.08
+				if (blockProgression == BlockProgression.RL)
+					globalStart.y = begCharRect.y + (startMetrics.width / 2); // TODO-9/5/8:Behavior for leading down TBD
+
+				if (justRule != JustificationRule.EAST_ASIAN)
+				{
+					blockRect.height = heightAndAdj[0];
+					if (blockProgression == BlockProgression.RL)
+						globalStart.x -= heightAndAdj[1];
+					else
+						globalStart.y += heightAndAdj[1];
+					// changed the width from a default of 2 to use the begCharRect.width so that point seletion
+					// can choose to use the right or left side of the glyph when drawing a caret Watson 1876415/1876953- gak 08.19.09
+					blockRect.width = begAtomIndex != endAtomIndex ? Math.abs(endCharRect.right - begCharRect.left) : begCharRect.width;
+				}
+				else
+				{
+					blockRect.height = begCharRect.height;
+
+					// changed the width from a default of 2 to use the begCharRect.width so that point seletion
+					// can choose to use the right or left side of the glyph when drawing a caret Watson 1876415/1876953- gak 08.19.09
+					blockRect.width = begAtomIndex != endAtomIndex ? Math.abs(endCharRect.right - begCharRect.left) : begCharRect.width;
+				}
+			}
+
+			blockRect.x = globalStart.x;
+			blockRect.y = globalStart.y;
+			if (blockProgression == BlockProgression.RL)
+			{
+				if (textLine.getAtomTextRotation(begAtomIndex) != TextRotation.ROTATE_0)
+					blockRect.x -= textLine.descent;
+				else // it's TCY
+					blockRect.y -= (blockRect.height / 2);
+			}
+			else
+			{
+				blockRect.y += (textLine.descent - blockRect.height);
+			}
+
+			var tfl:ITextFlowLine = textLine.userData as ITextFlowLine;
+			var curElem:IFlowLeafElement = _para.findLeaf(begTextIndex);
+			var rotation:String;
+			if (!curElem)
+			{
+				if (begTextIndex < 0)
+					curElem = _para.getFirstLeaf();
+				else if (begTextIndex >= _para.textLength)
+					curElem = _para.getLastLeaf();
+				rotation = curElem ? curElem.computedFormat.textRotation : TextRotation.ROTATE_0;
+			}
+			else
+				rotation = curElem.computedFormat.textRotation;
+
+			// handle rotation.  For horizontal text, rotations of 90 or 180 cause the text
+			// to draw under the baseline in a cosistent location.  Vertical text is a bit more complicated
+			// in that a 90 rotation puts it immediately to the left of the Em Box, whereas 180 is one quarter
+			// of the way in the Em Box. Fix for Watson 1915930 - gak 02.17.09
+			if (rotation == TextRotation.ROTATE_180 || rotation == TextRotation.ROTATE_90)
+			{
+				if (blockProgression != BlockProgression.RL)
+					blockRect.y += (blockRect.height / 2);
+				else
+				{
+					if (curElem.getParentByType("TCYElement") == null)
+					{
+						if (rotation == TextRotation.ROTATE_90)
+							blockRect.x -= blockRect.width;
+						else
+							blockRect.x -= (blockRect.width * .75);
+					}
+					else
+					{
+						if (rotation == TextRotation.ROTATE_90)
+							blockRect.y += blockRect.height;
+						else
+							blockRect.y += (blockRect.height * .75);
+					}
+				}
+			}
+
+			return blockRect;
+		}
+
+		/** @private
+		 * 
+		 * 
+		 */
+		public function convertLineRectToContainer(rect:Rectangle, constrainShape:Boolean):void
+		{
+			var textLine:ITextLine = getTextLine();
+
+			/* var globalStart:Point = new Point(rect.x, rect.y);
+			
+			// convert to controller coordinates...
+			// //trace(makeBlockPassCounter + ") globalStart = " + globalStart.toString());
+			globalStart = textLine.localToGlobal(globalStart);
+			// //trace(makeBlockPassCounter + ") localToGlobal.globalStart = " + globalStart.toString());
+			globalStart = container.globalToLocal(globalStart);
+			// //trace(makeBlockPassCounter + ") globalToLocal.globalStart = " + globalStart.toString());
+			rect.x = globalStart.x;
+			rect.y = globalStart.y; */
+
+			// this is much simpler and actually more accurate - localToGlobal/globalToLocal does some rounding
+			rect.x += textLine.x;
+			rect.y += textLine.y;
+
+			if (constrainShape)
+			{
+				var tf:ITextFlow = _para.getTextFlow();
+				var columnRect:Rectangle = controller.columnState.getColumnAt(this.columnIndex);
+				constrainRectToColumn(tf, rect, columnRect, controller.horizontalScrollPosition, controller.verticalScrollPosition, controller.compositionWidth, controller.compositionHeight);
+			}
+		}
+
+		/** @private */
+		static public function constrainRectToColumn(tf:ITextFlow, rect:Rectangle, columnRect:Rectangle, hScrollPos:Number, vScrollPos:Number, compositionWidth:Number, compositionHeight:Number):void
+		{
+			if (columnRect == null)
+				return;
+			if (tf.computedFormat.lineBreak == LineBreak.EXPLICIT)
+				return;
+
+			var bp:String = tf.computedFormat.blockProgression;
+			var direction:String = tf.computedFormat.direction;
+
+			if (bp == BlockProgression.TB && !isNaN(compositionWidth))
+			{
+				if (direction == Direction.LTR)
+				{
+					// make sure is doesn't go past the end of the container
+					if (rect.left > (columnRect.x + columnRect.width + hScrollPos))
+						rect.left = (columnRect.x + columnRect.width + hScrollPos);
+
+					// make sure that if this is a selection and not a point selection, that
+					// we don't go beyond the end of the container...
+					if (rect.right > (columnRect.x + columnRect.width + hScrollPos))
+						rect.right = (columnRect.x + columnRect.width + hScrollPos);
+				}
+				else
+				{
+					if (rect.right < (columnRect.x + hScrollPos))
+						rect.right = (columnRect.x + hScrollPos);
+
+					if (rect.left < (columnRect.x + hScrollPos))
+						rect.left = (columnRect.x + hScrollPos);
+				}
+			}
+			else if (bp == BlockProgression.RL && !isNaN(compositionHeight))
+			{
+				if (direction == Direction.LTR)
+				{
+					// make sure is doesn't go past the end of the container
+					if (rect.top > (columnRect.y + columnRect.height + vScrollPos))
+						rect.top = (columnRect.y + columnRect.height + vScrollPos);
+
+					// make sure that if this is a selection and not a point selection, that
+					// we don't go beyond the end of the container...
+					if (rect.bottom > (columnRect.y + columnRect.height + vScrollPos))
+						rect.bottom = (columnRect.y + columnRect.height + vScrollPos);
+				}
+				else
+				{
+					if (rect.bottom < (columnRect.y + vScrollPos))
+						rect.bottom = (columnRect.y + vScrollPos);
+
+					if (rect.top < (columnRect.y + vScrollPos))
+						rect.top = (columnRect.y + vScrollPos);
+				}
+			}
+		}
+
+		// TODO remove reference to UIBase
+		/** @private
+		 * Helper method to hilight the portion of a block selection on this ITextLine.  A selection display is created and added to the line's TextFrame with ContainerController addSelectionShape.
+		 * @param begIdx absolute index of start of selection on this line.
+		 * @param endIdx absolute index of end of selection on this line.
+		 */
+		public function hiliteBlockSelection(selObj:ICompoundGraphic, selFormat:SelectionFormat, container:IParentIUIBase, begIdx:int, endIdx:int, prevLine:ITextFlowLine, nextLine:ITextFlowLine):void
+		{
+			// no container for overflow lines, or lines scrolled out
+			if (isDamaged() || !_controller)
+				return;
+
+			var textLine:ITextLine = peekTextLine();
+			if (!textLine || !textLine.parent)
+				return;
+
+			var paraStart:int = _para.getTextBlockAbsoluteStart(textLine.textBlock);
+			begIdx -= paraStart;
+			endIdx -= paraStart;
+
+			createSelectionShapes(selObj, selFormat, container, begIdx, endIdx, prevLine, nextLine);
+		}
+
+		// TODO remove reference to UIBase
+		/** @private
+		 * Helper method to hilight a point selection on this ITextLine.  x,y,w,h of the selection are calculated and ContainerController.drawPointSelection is called 
+		 * @param idx absolute index of the point selection.
+		 */
+		public function hilitePointSelection(selFormat:SelectionFormat, idx:int, container:IParentIUIBase, prevLine:ITextFlowLine, nextLine:ITextFlowLine):void
+		{
+			var rect:Rectangle = computePointSelectionRectangle(idx, container, prevLine, nextLine, true);
+			if (rect)
+				_controller.drawPointSelection(selFormat, rect.x, rect.y, rect.width, rect.height);
+		}
+
+		static private function setRectangleValues(rect:Rectangle, x:Number, y:Number, width:Number, height:Number):void
+		{
+			rect.x = x;
+			rect.y = y;
+			rect.width = width;
+			rect.height = height;
+		}
+
+		static private const localZeroPoint:Point = new Point(0, 0);
+		static private const localOnePoint:Point = new Point(1, 0);
+		static private const rlLocalOnePoint:Point = new Point(0, 1);
+
+		// TODO generalize this so we're not relying on UIBase
+		/** @private */
+		public function computePointSelectionRectangle(idx:int, container:IParentIUIBase, prevLine:ITextFlowLine, nextLine:ITextFlowLine, constrainSelRect:Boolean):Rectangle
+		{
+			if (isDamaged() || !_controller)
+				return null;
+
+			// no container for overflow lines, or lines scrolled out
+			var textLine:ITextLine = peekTextLine();
+			if (!textLine || !textLine.parent)
+				return null;
+			// adjust to this paragraph's ITextBlock
+			// I'm assuming this needs to be relative to the ITextBlock and not the paragraph -- Harbs
+			idx -= _para.getTextBlockAbsoluteStart(textLine.textBlock);
+			// idx -= _para.getAbsoluteStart();
+
+			textLine = getTextLine(true);
+
+			// endIdx will only differ if idx is altered when detecting TCY bounds
+			var endIdx:int = idx;
+			var elementIndex:int = textLine.getAtomIndexAtCharIndex(idx);
+			CONFIG::debug
+			{
+				assert(elementIndex != -1, "Invalid point selection index! idx = " + idx); }
+
+			var isTCYBounds:Boolean = false;
+			var paraLeadingTCY:Boolean = false;
+
+			var contElement:IContainerFormattedElement = _para.getAncestorWithContainer();
+			CONFIG::debug
+			{
+				assert(contElement != null, "para with no container"); }
+			var blockProgression:String = contElement.computedFormat.blockProgression;
+			var direction:String = _para.computedFormat.direction;
+
+			// need to check for TCY.  TCY cannot take input into it's head, but can in it's tail.
+			if (blockProgression == BlockProgression.RL)
+			{
+				if (idx == 0)
+				{
+					if (textLine.getAtomTextRotation(0) == TextRotation.ROTATE_0)
+						paraLeadingTCY = true;
+				}
+				else
+				{
+					var prevElementIndex:int = textLine.getAtomIndexAtCharIndex(idx - 1);
+					if (prevElementIndex != -1)
+					{
+						// if this elem is TCY, then we need to back up one space and use the right bounds
+						if (textLine.getAtomTextRotation(elementIndex) == TextRotation.ROTATE_0 && textLine.getAtomTextRotation(prevElementIndex) != TextRotation.ROTATE_0)
+						{
+							elementIndex = prevElementIndex;
+							--idx;
+							isTCYBounds = true;
+						}
+						else if (textLine.getAtomTextRotation(prevElementIndex) == TextRotation.ROTATE_0)
+						{
+							elementIndex = prevElementIndex;
+							--idx;
+							isTCYBounds = true;
+						}
+					}
+				}
+			}
+
+			var heightAndAdj:Array = getRomanSelectionHeightAndVerticalAdjustment(prevLine, nextLine);
+			var blockRectArray:Array = makeSelectionBlocks(textLine, idx, endIdx, _para.getTextBlockAbsoluteStart(textLine.textBlock), blockProgression, direction, heightAndAdj);
+			CONFIG::debug
+			{
+				assert(blockRectArray.length == 1, "A point selection should return a single selection rectangle!"); }
+			var rect:Rectangle = blockRectArray[0];
+
+			convertLineRectToContainer(rect, constrainSelRect);
+
+			var drawOnRight:Boolean = (direction == Direction.RTL);
+
+			if ((drawOnRight && textLine.getAtomBidiLevel(elementIndex) % 2 == 0) || (!drawOnRight && textLine.getAtomBidiLevel(elementIndex) % 2 != 0))
+			{
+				drawOnRight = !drawOnRight;
+			}
+
+			// compute a width so that cursor is visually one pixel wide independent of scaling
+			var zeroPoint:Point = PointUtils.localToGlobal(localZeroPoint, container);// container.localToGlobal(localZeroPoint);
+			var cursorWidth:Number;
+
+			if (blockProgression == BlockProgression.RL && textLine.getAtomTextRotation(elementIndex) != TextRotation.ROTATE_0)
+			{
+				var rlOnePoint:Point = PointUtils.localToGlobal(rlLocalOnePoint, container);// container.localToGlobal(rlLocalOnePoint);
+				cursorWidth = zeroPoint.y - rlOnePoint.y;
+				cursorWidth = cursorWidth == 0 ? 1 : Math.abs(1.0 / cursorWidth);
+				// trace(zeroPoint,onePoint,cursorSize);
+				if (!drawOnRight)
+					setRectangleValues(rect, rect.x, !isTCYBounds ? rect.y : rect.y + rect.height, rect.width, cursorWidth);
+				else
+					setRectangleValues(rect, rect.x, !isTCYBounds ? rect.y + rect.height : rect.y, rect.width, cursorWidth);
+			}
+			else
+			{
+				var onePoint:Point = PointUtils.localToGlobal(localOnePoint, container);// container.localToGlobal(localOnePoint);
+				cursorWidth = zeroPoint.x - onePoint.x;
+				cursorWidth = cursorWidth == 0 ? 1 : Math.abs(1.0 / cursorWidth);
+				// trace(zeroPoint,onePoint,cursorSize);
+				// choose to use the right or left side of the glyph based on Direction when drawing a caret Watson 1876415/1876953
+				// if the direction is ltr, then the cursor should be on the left side
+				if (!drawOnRight)
+					setRectangleValues(rect, !isTCYBounds ? rect.x : rect.x + rect.width, rect.y, cursorWidth, rect.height);
+				else // otherwise, it should be on the right, unless it is TCY
+					setRectangleValues(rect, !isTCYBounds ? rect.x + rect.width : rect.x, rect.y, cursorWidth, rect.height);
+			}
+
+			// allow the atoms to be garbage collected.
+			// textLine.flushAtomData(); // Warning: Now does nothing
+
+			return rect;
+		}
+
+		/** @private
+		 * Three states.  Disjoint(0), Intersects(1), HeightContainedIn(2),  
+		 */
+		public function selectionWillIntersectScrollRect(scrollRect:Rectangle, begIdx:int, endIdx:int, prevLine:ITextFlowLine, nextLine:ITextFlowLine):int
+		{
+			var contElement:IContainerFormattedElement = _para.getAncestorWithContainer();
+			CONFIG::debug
+			{
+				assert(contElement != null, "para with no container"); }
+			var blockProgression:String = contElement.computedFormat.blockProgression;
+			var textLine:ITextLine = getTextLine(true);
+
+			if (begIdx == endIdx)
+			{
+				var pointSelRect:Rectangle = computePointSelectionRectangle(begIdx, UIBase(controller.container), prevLine, nextLine, false);
+				if (pointSelRect)
+				{
+					if (scrollRect.containsRect(pointSelRect))
+						return 2;
+					if (scrollRect.intersects(pointSelRect))
+						return 1;
+				}
+			}
+			else
+			{
+				var paraStart:int;
+				// 8-31-14 Assuming this should be from the textBlock. Keeping getAbsoluteStart() in case there's no textLine -- not sure if that's needed
+				if (textLine)
+					paraStart = _para.getTextBlockAbsoluteStart(textLine.textBlock);
+				else
+					paraStart = _para.getAbsoluteStart();
+				var selCache:SelectionCache = this.getSelectionShapesCacheEntry(begIdx - paraStart, endIdx - paraStart, prevLine, nextLine, blockProgression);
+				if (selCache)
+				{
+					// iterate the blocks and check for intersections
+					var drawRect:Rectangle;
+					for each (drawRect in selCache.selectionBlocks)
+					{
+						drawRect = drawRect.clone();
+						// convertLineRectToContainer(container, drawRect);
+						drawRect.x += textLine.x;
+						drawRect.y += textLine.y;
+						if (scrollRect.intersects(drawRect))
+						{
+							if (blockProgression == BlockProgression.RL)
+							{
+								// see if width is entirely contained in scrollRect
+								if (drawRect.left >= scrollRect.left && drawRect.right <= scrollRect.right)
+									return 2;
+							}
+							else
+							{
+								if (drawRect.top >= 

<TRUNCATED>

Mime
View raw message