You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ha...@apache.org on 2017/03/16 13:37:53 UTC
[33/42] flex-asjs git commit: And here’s TLF…
http://git-wip-us.apache.org/repos/asf/flex-asjs/blob/fd08d137/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
----------------------------------------------------------------------
diff --git a/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
new file mode 100644
index 0000000..3460da5
--- /dev/null
+++ b/frameworks/projects/TLF/src/main/flex/org/apache/flex/textLayout/container/ContainerController.as
@@ -0,0 +1,5222 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+// 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.container
+{
+ import org.apache.flex.textLayout.elements.GlobalSettings;
+ import org.apache.flex.textLayout.elements.IInlineGraphicElement;
+ import org.apache.flex.textLayout.compose.ITextFlowTableBlock;
+ import org.apache.flex.textLayout.elements.ITableRowElement;
+ import org.apache.flex.textLayout.utils.CreateTLFUtil;
+ import org.apache.flex.textLayout.elements.ITableCellElement;
+ import org.apache.flex.textLayout.elements.IParagraphElement;
+ import org.apache.flex.textLayout.elements.IFlowLeafElement;
+ import org.apache.flex.core.IChild;
+ import org.apache.flex.core.IParentIUIBase;
+ import org.apache.flex.core.IUIBase;
+ import org.apache.flex.events.Event;
+ import org.apache.flex.events.IEventDispatcher;
+ import org.apache.flex.events.KeyboardEvent;
+ import org.apache.flex.events.MouseEvent;
+ import org.apache.flex.geom.Matrix;
+ import org.apache.flex.geom.Point;
+ import org.apache.flex.geom.Rectangle;
+ import org.apache.flex.graphics.ICompoundGraphic;
+ import org.apache.flex.graphics.IRect;
+ import org.apache.flex.graphics.SolidColor;
+ import org.apache.flex.text.engine.ITextBlock;
+ import org.apache.flex.text.engine.ITextLine;
+ import org.apache.flex.text.events.IMEEvent;
+ import org.apache.flex.text.events.TextEvent;
+ import org.apache.flex.textLayout.compose.FloatCompositionData;
+ import org.apache.flex.textLayout.compose.FlowDamageType;
+ import org.apache.flex.textLayout.compose.IFlowComposer;
+ import org.apache.flex.textLayout.compose.ITextFlowLine;
+ import org.apache.flex.textLayout.compose.TextLineRecycler;
+ 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.dummy.ContextMenu;
+ import org.apache.flex.textLayout.edit.EditingMode;
+ import org.apache.flex.textLayout.edit.IInteractionEventHandler;
+ import org.apache.flex.textLayout.edit.ISelectionManager;
+ import org.apache.flex.textLayout.edit.SelectionFormat;
+ import org.apache.flex.textLayout.elements.IBackgroundManager;
+ import org.apache.flex.textLayout.elements.CellCoordinates;
+ import org.apache.flex.textLayout.elements.IContainerFormattedElement;
+ import org.apache.flex.textLayout.elements.TableBlockContainer;
+ import org.apache.flex.textLayout.elements.ITextFlow;
+ import org.apache.flex.textLayout.events.ActivateEvent;
+ import org.apache.flex.textLayout.events.ContextMenuEvent;
+ import org.apache.flex.textLayout.events.EditEvent;
+ import org.apache.flex.textLayout.events.FlowElementMouseEventManager;
+ import org.apache.flex.textLayout.events.FocusEvent;
+ import org.apache.flex.textLayout.events.ModelChange;
+ import org.apache.flex.textLayout.events.ScrollEvent;
+ import org.apache.flex.textLayout.events.ScrollEventDirection;
+ import org.apache.flex.textLayout.events.SelectionEvent;
+ import org.apache.flex.textLayout.events.TextLayoutEvent;
+ import org.apache.flex.textLayout.events.UpdateCompleteEvent;
+ import org.apache.flex.textLayout.formats.BlockProgression;
+ import org.apache.flex.textLayout.formats.Float;
+ import org.apache.flex.textLayout.formats.FormatValue;
+ import org.apache.flex.textLayout.formats.ITextLayoutFormat;
+ import org.apache.flex.textLayout.formats.TextLayoutFormat;
+ import org.apache.flex.textLayout.formats.TextLayoutFormatBase;
+
+ import org.apache.flex.textLayout.utils.Twips;
+ import org.apache.flex.utils.DisplayUtils;
+ import org.apache.flex.utils.ObjectMap;
+ import org.apache.flex.utils.PointUtils;
+ import org.apache.flex.utils.Timer;
+
+
+
+ /**
+ * The ContainerController class defines the relationship between a TextFlow object and a container.
+ * A TextFlow may have one or more rectangular areas that can hold text; the text is said to be flowing
+ * through the containers. Each container is a Sprite that is the parent DisplayObject for the TextLines.
+ * Each container has a ContainerController that manages the container; the controller holds the target
+ * width and height for the text area, populates the container with TextLines, and handles scrolling. A
+ * controller also has a format associated with it that allows some formatting attributes to be applied
+ * to the text in the container. This allows, for instance, a TextFlow to have one container where the
+ * text appears in a single column, and a second container in the same TextFlow with two column text. Not
+ * all formatting attributes that can be applied to the container will affect the text; only the ones that
+ * affect container-level layout. The diagram below illustrates the relationship between the TextFlow,
+ * its flowComposer, and the display list.
+ *
+ * <p><img src="../../../images/textLayout_multiController.gif" alt="IContainerController"></img></p>
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.compose.IFlowComposer
+ * @see org.apache.flex.textLayout.elements.TextFlow
+ * @see org.apache.flex.text.engine.ITextLine
+ */
+ public class ContainerController extends TextLayoutFormatBase implements IInteractionEventHandler, ISandboxSupport, IContainerController
+ {
+
+ private var _textFlowCache:ITextFlow;
+ private var _rootElement:IContainerFormattedElement;
+
+ private var _absoluteStart:int;
+ private var _textLength:int;
+
+ private var _container:IParentIUIBase;
+ private var _mouseEventManager:FlowElementMouseEventManager;
+
+ // note must be protected - subclass sets or gets this variable but can't be public
+ /** computed container attributes. @private */
+ protected var _computedFormat:TextLayoutFormat;
+
+ // Generated column information
+ // Generated column information
+ private var _columnState:ColumnState;
+
+ /** Container size to be composed */
+ private var _compositionWidth:Number = 0;
+ private var _compositionHeight:Number = 0;
+ private var _measureWidth:Boolean; // true if we're measuring (isNaN(compositionWidth) optimization so we don't call isNaN too much
+ private var _measureHeight:Boolean; // true if we're measuring (isNaN(compositionHeight) optimization so we don't call isNaN too much
+
+ /* Text bounds after composition */
+ private var _contentLeft:Number;
+ private var _contentTop:Number;
+ private var _contentWidth:Number;
+ private var _contentHeight:Number;
+
+ private var _uncomposedTextLength:int; // 0 if composition was complete when contentHeight, etc registered, greater than one otherwise
+ private var _finalParcelStart:int;
+
+ // Scroll policy -- determines whether scrolling is enabled or not
+ private var _horizontalScrollPolicy:String;
+ private var _verticalScrollPolicy:String;
+
+ // x, y location of the text in the container relative to the underlying scrollable area
+ private var _xScroll:Number;
+ private var _yScroll:Number;
+
+ /** Are event listeners attached to the container */
+ private var _minListenersAttached:Boolean = false;
+ private var _allListenersAttached:Boolean = false;
+ private var _selectListenersAttached:Boolean = false;
+ public var _mouseWheelListenerAttached:Boolean = false;
+
+ /** @private */
+ public function get allListenersAttached():Boolean
+ { return _allListenersAttached; }
+
+ /** Are the displayed shapes out of date? */
+ private var _shapesInvalid:Boolean = false;
+
+ private var _backgroundShape:IUIBase;
+
+ private var _scrollTimer:Timer = null;
+
+ /**
+ * @private use this boolean to determine if container.scrollRect is set. Accessing scrollRect when null changes the rendering behavior of flash player.
+ */
+ protected var _hasScrollRect:Boolean;
+
+ private var _linesInView:Array; // lines that were in view according to the previous compose(). Empty if the lines have already been posted to the display list.
+ private var _updateStart:int;
+ private var _tableBlocksInView:Array; // // table blocks that were in view according to the previous compose(). Empty if the lines have already been posted to the display list.
+
+ private var _composedFloats:Array; // floats that were composed into the controller -- array of FloatCompositionData
+ private var _floatsInContainer:Array; // floats are currently in view -- array of DisplayObject
+
+ /** Interactive Objects **/
+ private var _interactiveObjects:ObjectMap = new ObjectMap(true);
+ private var _oldInteractiveObjects:Array = new Array();
+
+ public function get interactiveObjects():ObjectMap
+ {
+ return _interactiveObjects ;
+ }
+
+ public function get oldInteractiveObjects():Array
+ {
+ return _oldInteractiveObjects ;
+ }
+
+ /**
+ * @private
+ *
+ * <p>This property enables a client to test for a ScrollRect object without accessing
+ * the DisplayObject.scrollRect property, which can have side effects in some cases.</p>
+ *
+ * @return true if the controller has attached a ScrollRect instance.
+ */
+ public function get hasScrollRect():Boolean
+ { return _hasScrollRect; }
+
+ CONFIG::debug
+ {
+ protected var id:String;
+ private static var contCount:int = 0;
+ }
+
+ private var _shapeChildren:Array;
+
+ private var _containerRoot:IParentIUIBase;
+
+ /* Controller have a non-zero default width and height so that if you construct a text example with a container and don't
+ * specify width and height you will still see some text so that you can then have a clue what to do to correct its appearance.
+ */
+
+ /**
+ * Constructor - creates a ContainerController instance. The ContainerController has a default <code>compositionWidth</code>
+ * and <code>compositionHeight</code> so that some text appears in the container if you don't specify its width
+ * height.
+ *
+ * @param container The DisplayObjectContainer in which to manage the text lines.
+ * @param compositionWidth The initial width for composing text in the container.
+ * @param compositionHeight The initial height for composing text in the container.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function ContainerController(container:IParentIUIBase,compositionWidth:Number=100,compositionHeight:Number=100)
+ {
+ initialize(container,compositionWidth,compositionHeight);
+ }
+
+ private function initialize(container:IParentIUIBase,compositionWidth:Number,compositionHeight:Number):void
+ {
+ _container = container;
+ _containerRoot = null;
+
+ _textLength = 0;
+ _absoluteStart = -1;
+
+ _columnState = new ColumnState(null/*blockProgression*/, null/*columnDirection*/, null/*controller*/, 0/*compositionWidth*/, 0/*compositionHeight*/);
+ //_visibleRect = new Rectangle();
+ _xScroll = _yScroll = 0;
+ _contentWidth = _contentHeight = 0;
+ _uncomposedTextLength = 0;
+
+ // We have to set the flag so that we will get double click events. This
+ // is a change to the container we are given, but a minor one.
+
+//TODO doubleClickEnabled
+// _container.doubleClickEnabled = true;
+
+ _horizontalScrollPolicy = _verticalScrollPolicy = String(ScrollPolicy.scrollPolicyPropertyDefinition.defaultValue);
+ _hasScrollRect = false;
+
+ CONFIG::debug { id = contCount.toString(); ++contCount; }
+
+ _shapeChildren = [ ];
+ _linesInView = [ ];
+ _tableBlocksInView = [];
+
+ setCompositionSize(compositionWidth, compositionHeight);
+ format = _containerControllerInitialFormat;
+ }
+
+ /** @private */
+ public function get effectiveBlockProgression():String
+ {
+ return _rootElement ? _rootElement.computedFormat.blockProgression : BlockProgression.TB;
+ }
+
+//TODO we probably need platform specific methods to attach events to the root
+ /** @private Determine containerRoot in case the stage is not accessible. Normally the root is the stage. */
+// public function getContainerRoot():DisplayObject
+// {
+// // safe to test for stage existence
+// if (_containerRoot == null && _container && _container.stage)
+// {
+// // if the stage is accessible lets use it.
+// // trace("BEFORE COMPUTING CONTAINERROOT");
+// try
+// {
+// var x:int = _container.stage.numChildren;
+// _containerRoot = _container.stage;
+// }
+// catch(e:Error)
+// {
+// // TODO: some way to find the highest level accessible root???
+// _containerRoot = _container.root;
+// }
+// // trace("AFTER COMPUTING CONTAINERROOT");
+// }
+// return _containerRoot;
+// }
+
+ /**
+ * Returns the flow composer object that composes and highlights text into the container that this
+ * controller manages.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.compose.IFlowComposer
+ */
+
+ public function get flowComposer():IFlowComposer
+ { return textFlow ? textFlow.flowComposer : null; }
+
+ /** @private */
+ public function get shapesInvalid():Boolean
+ { return _shapesInvalid; }
+ /** @private */
+ public function set shapesInvalid(val:Boolean):void
+ { _shapesInvalid = val; }
+
+ /**
+ * Returns a ColumnState object, which describes the number and characteristics of columns in
+ * the container. These values are updated when the text is recomposed, either as a result
+ * of <code>IFlowComposer.compose()</code> or <code>IFlowComposer.updateAllControllers()</code>.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see ColumnState
+ */
+
+ public function get columnState():ColumnState
+ {
+ if (_rootElement == null)
+ return null;
+
+ if (_computedFormat == null)
+ calculateComputedFormat();
+
+ _columnState.computeColumns();
+
+ return _columnState;
+ }
+
+ /**
+ * Returns the container display object that holds the text lines for this ContainerController instance.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see #ContainerController()
+ */
+
+ public function get container():IParentIUIBase
+ { return _container; }
+
+ /**
+ * Returns the horizontal extent allowed for text inside the container. The value is specified in pixels.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see #setCompositionSize()
+ */
+
+ public function get compositionWidth():Number
+ { return _compositionWidth; }
+
+ /**
+ * Returns the vertical extent allowed for text inside the container. The value is specified in pixels.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see #setCompositionSize()
+ */
+
+ public function get compositionHeight():Number
+ { return _compositionHeight; }
+
+ /** @private */
+ public function get measureWidth():Boolean
+ { return _measureWidth; }
+
+ /** @private */
+ public function get measureHeight():Boolean
+ { return _measureHeight; }
+
+ /**
+ * Sets the width and height allowed for text in the container. Width and height can be specified in pixels or <code>NaN</code> can be used for either value. <code>NaN</code> indicates measure that value.
+ * This can be used to find the widest line and/or the total height of all the content. When NaN is specified as the width lines are broken with a maximum width of <code>ITextLine.MAX_LINE_WIDTH</code>.
+ * When <code>NaN</code> is specified as the height the container is assumed to have unlimited height. The actual measured values can be read back in <code>getContentBounds</code>.
+ * When the computed <code>blockProgression</code> property of <code>TextFlow</code>
+ * is <code>BlockProgression.RL</code> the meanings of width and height are exchanged.
+ *
+ * @param w The width in pixels that's available for text in the container. <code>NaN</code> indicates no specified width.
+ * @param h The height in pixels that's available for text in the container. <code>NaN</code> indicates no specified height.
+ *
+ *
+ * @see org.apache.flex.text.engine.ITextLine#MAX_LINE_WIDTH
+ * @see org.apache.flex.textLayout.formats.BlockProgression
+ * @see #getContentBounds()
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function setCompositionSize(w:Number, h:Number):void
+ {
+ // trace("setCompositionSize(" + w + ", " + h + ")");
+
+ // note: NaN == NaN is always false
+ var widthChanged:Boolean = !(_compositionWidth == w || (isNaN(_compositionWidth) && isNaN(w)));
+ var heightChanged:Boolean = !(_compositionHeight == h || (isNaN(_compositionHeight) && isNaN(h)));
+
+ if (widthChanged || heightChanged)
+ {
+ _compositionHeight = h;
+ _measureHeight = isNaN(_compositionHeight);
+ _compositionWidth = w;
+ _measureWidth = isNaN(_compositionWidth);
+ // otherwise the reset will happen when the cascade is done
+ if (_computedFormat)
+ resetColumnState();
+ // Invalidate all the lines, forcing FTE rebuild if they changed in the logical width direction
+ if (effectiveBlockProgression == BlockProgression.TB ? widthChanged : heightChanged)
+ {
+ if (textFlow && _textLength)
+ textFlow.damage(absoluteStart, _textLength, "invalid", false);
+ }
+ else
+ invalidateContents(); // don't need to rebuild FTE lines, just reflow them
+ attachTransparentBackgroundForHit(false);
+ }
+ }
+
+ /**
+ * Returns the TextFlow object whose content appears in the container. Either the <code>textFlow</code> and
+ * <code>rootElement</code> values are the same, or this is the root element's TextFlow object. For example,
+ * if the container's root element is a DivElement, the value would be the TextFlow object to which the
+ * DivElement belongs.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.elements.TextFlow TextFlow
+ */
+ public function get textFlow():ITextFlow
+ {
+ if (!_textFlowCache && _rootElement)
+ _textFlowCache = _rootElement.getTextFlow();
+ return _textFlowCache;
+ }
+
+ // Reserve possibility for future use as a IContainerFormattedElement within the TextFlow.
+
+ /**
+ * Returns the root element that appears in the container. The root element could be a DivElement or TextFlow
+ * instance, for example.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.elements.IContainerFormattedElement
+ * @see org.apache.flex.textLayout.elements.DivElement
+ * @see org.apache.flex.textLayout.elements.TextFlow
+ */
+
+ public function get rootElement():IContainerFormattedElement
+ { return _rootElement; }
+
+ /** Protected method used when updating the rootElement.
+ * @param value new container to be controlled
+ *
+ * @private
+ */
+ public function setRootElement(value:IContainerFormattedElement):void
+ {
+ if (_rootElement != value)
+ {
+ if (_mouseEventManager)
+ _mouseEventManager.stopHitTests();
+ if (!value)
+ _mouseEventManager = null;
+ else if (!_mouseEventManager)
+ {
+ // Currently, the manager listens to all events itself.
+ // TODO: forward at least mouseOver and mouseDown events without
+ // causing side effects
+ _mouseEventManager = new FlowElementMouseEventManager(container, null);
+ // [MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_UP, MouseEvent.MOUSE_MOVE, MouseEvent.MOUSE_DOWN, MouseEvent.MOUSE_OUT,
+ // KeyboardEvent.KEY_DOWN, KeyboardEvent.KEY_UP]);
+ }
+
+ clearCompositionResults();
+ detachContainer();
+ _rootElement = value;
+ _textFlowCache = null;
+ _textLength = 0;
+ _absoluteStart = -1;
+ attachContainer();
+ if (_rootElement)
+ formatChanged();
+
+ if (GlobalSettings.playerEnablesSpicyFeatures)
+ _container["needsSoftKeyboard"] = (interactionManager && interactionManager.editingMode == EditingMode.READ_WRITE);
+ }
+ }
+
+ /**
+ * @copy org.apache.flex.textLayout.elements.TextFlow#interactionManager
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.textLayout.elements.TextFlow#interactionManager
+ */
+
+ public function get interactionManager():ISelectionManager
+ {
+ return textFlow ? textFlow.interactionManager : null;
+ }
+
+
+ /** @private */
+ public function get uncomposedTextLength():int
+ { return _uncomposedTextLength; }
+
+ /** @private */
+ public function get finalParcelStart():int
+ { return _finalParcelStart; }
+
+ /** @private */
+ public function set finalParcelStart(val:int):void
+ { _finalParcelStart = val; }
+
+ //--------------------------------------------------------------------------
+ //
+ // Start and length
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * Returns the first character in the container. If this is not the first container in the flow,
+ * this value is updated when the text is composed, that is when the IFlowComposer's <code>compose()</code> or
+ * <code>updateAllControllers()</code> methods are called.
+ *
+ * @see org.apache.flex.textLayout.compose.IFlowComposer
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get absoluteStart():int
+ {
+ if (_absoluteStart != -1)
+ return _absoluteStart;
+
+ var rslt:int = 0;
+ var composer:IFlowComposer = flowComposer;
+ if (composer)
+ {
+ var stopIdx:int = composer.getControllerIndex(this);
+ if (stopIdx != 0)
+ {
+ var prevController:IContainerController = composer.getControllerAt(stopIdx-1);
+ rslt = prevController.absoluteStart + prevController.textLength;
+ }
+ }
+ _absoluteStart = rslt;
+
+ return rslt;
+ }
+
+ /** Returns the total number of characters in the container. This can include text that is not currently in view,
+ * if the container is scrollable. This value is updated when the text is composed (when the IFlowComposer's <code>compose()</code>
+ * or <code>updateAllControllers()</code> methods are called).
+ *
+ * @see org.apache.flex.textLayout.compose.IFlowComposer
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get textLength():int
+ {
+ return _textLength;
+ }
+ /**
+ * @private
+ * @flexjsignorecoercion org.apache.flex.textLayout.container.ContainerController
+ */
+ public function setTextLengthOnly(numChars:int):void
+ {
+ if (_textLength != numChars)
+ {
+ _textLength = numChars;
+ _uncomposedTextLength = 0;
+ // all following containers must have absoluteStart invalidated
+ if (_absoluteStart != -1)
+ {
+ var composer:IFlowComposer = flowComposer;
+ if (composer)
+ {
+ var idx:int = composer.getControllerIndex(this)+1;
+ while (idx < flowComposer.numControllers)
+ {
+ var controller:ContainerController = composer.getControllerAt(idx++) as ContainerController;
+ if (controller._absoluteStart == -1)
+ break;
+ controller._absoluteStart = -1;
+ controller._uncomposedTextLength = 0;
+ }
+ }
+ }
+ }
+ }
+
+ /** @private */
+ public function setTextLength(numChars:int):void
+ {
+ CONFIG::debug { assert(numChars >= 0,"bad set textLength"); }
+
+ // If its a scrollable container, and it is the last one, then it gets all the characters even though we might not have composed them all
+ var uncomposedTextLength:int = 0;
+ if (textFlow)
+ {
+ var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+ var flowComposer:IFlowComposer = textFlow.flowComposer;
+ if (numChars != 0 && flowComposer.getControllerIndex(this) == flowComposer.numControllers - 1 &&
+ ((!verticalText && _verticalScrollPolicy != ScrollPolicy.OFF)||
+ (verticalText && _horizontalScrollPolicy != ScrollPolicy.OFF)))
+ {
+ var containerAbsoluteStart:int = absoluteStart;
+ CONFIG::debug { assert(textFlow.textLength >= containerAbsoluteStart,"ContainerController.setTextLength bad absoluteStart"); }
+ uncomposedTextLength = textFlow.textLength-(numChars+containerAbsoluteStart);
+ // _composeCompleteRatio = (textFlow.textLength-containerAbsoluteStart) == numChars ? 1 : 1.1;
+ // var scaledContentHeight:Number = _composeCompleteRatio * _contentHeight;
+ // trace("composeCompleteRatio:",_composeCompleteRatio,"composedContentHeight",_contentHeight,"scaledContentHeight",scaledContentHeight,"textLength",textFlow.textLength,"numChars",numChars);
+ // include all remaining characters in this container when scroll enabled
+ numChars = textFlow.textLength - containerAbsoluteStart;
+ }
+ }
+
+ // this call clears uncomposedTextLength - set it immediately afterwards
+ setTextLengthOnly(numChars);
+ _uncomposedTextLength = uncomposedTextLength;
+
+ CONFIG::debug
+ {
+ if (Debugging.debugOn && textFlow)
+ assert(Math.min(textFlow.textLength, absoluteStart)+_textLength <= textFlow.textLength, "container textLength may not extend past end of root element!");
+ }
+ }
+
+ /**
+ * Determines whether the container has text that requires composing.
+ *
+ * @return true if the container requires composing.
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function isDamaged():Boolean
+ {
+ return flowComposer.isPotentiallyDamaged(absoluteStart + _textLength);
+ }
+
+ /** called whenever the container attributes are changed. Mark computed attributes and columnstate as out of date.
+ * @private
+ */
+ public override function formatChanged(notifyModelChanged:Boolean = true):void
+ {
+ //has no meaning in ContainerController, but we're inheriting from TextLayoutFormatBase
+ // The associated container, if there is one, inherits its container
+ // attributes from here. So we need to tell it that these attributes
+ // have changed.
+ _computedFormat = null;
+ invalidateContents();
+ }
+
+ /** This gets called when an element has changed its style selection related attributes. This may happen because an
+ * ancestor element changed it attributes.
+ * @private
+ */
+ public override function styleSelectorChanged():void
+ {
+ modelChanged(ModelChange.STYLE_SELECTOR_CHANGED,this,0,this._textLength);
+ _computedFormat = null;
+ }
+
+ /** @private */
+ public function modelChanged(changeType:String, element:ContainerController, changeStart:int, changeLen:int, needNormalize:Boolean = true, bumpGeneration:Boolean = true):void
+ {
+ var tf:ITextFlow = _rootElement.getTextFlow();
+ if (tf)
+ tf.processModelChanged(changeType, element, absoluteStart+changeStart, changeLen, needNormalize, bumpGeneration);
+ }
+
+ /** @private */
+ public function gatherVisibleLines(wmode:String, createShape:Boolean):void
+ {
+ CONFIG::debug { assert(_linesInView.length == 0,"gatherVisibleLines: bad _linesInView"); }
+ if (_textLength != 0)
+ {
+ // Similar computations also done in BaseCompose.getControllerVisibleBounds
+ var width:Number = _measureWidth ? _contentWidth : _compositionWidth;
+ var height:Number = _measureHeight ? _contentHeight : _compositionHeight;
+ var adjustX:Number = (wmode == BlockProgression.RL) ? _xScroll - width : _xScroll;
+ var adjustY:Number = _yScroll;
+ var scrollAdjustXTW:int = Twips.roundTo(adjustX);
+ var scrollAdjustYTW:int = Twips.roundTo(adjustY);
+ var scrollAdjustWidthTW:int = Twips.to(width);
+ var scrollAdjustHeightTW:int = Twips.to(height);
+
+ var flowComposer:IFlowComposer = this.flowComposer;
+
+ // Iterate over the lines in the container, setting the x and y positions and
+ // adding them to the list to go into the container. Keep track of the width
+ // and height of the actual text in the container.
+ var firstLine:int = flowComposer.findLineIndexAtPosition(absoluteStart);
+ var lastLine:int = flowComposer.findLineIndexAtPosition(absoluteStart + _textLength - 1);
+
+ var curLine:ITextFlowLine;
+ var textLine:ITextLine;
+ var lineIndex:int;
+ var testRslt:*;
+
+ //Use binary search when there is one single column
+ if(columnCount == 1)
+ {
+ // First just test the firstLine - normal unscrolled case
+ var testPos:int = firstLine;
+ curLine = flowComposer.getLineAt(testPos++);
+ while(curLine && curLine is ITextFlowTableBlock)
+ curLine = flowComposer.getLineAt(testPos++);
+
+ testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+ textLine = testRslt as ITextLine;
+ firstLine++; // its been tested
+ if (textLine)
+ {
+ if (createShape)
+ curLine.createShape(wmode, textLine);
+ _linesInView.push(textLine);
+ }
+ else
+ {
+ var hi:int = lastLine;
+ while (firstLine <= hi)
+ {
+ var mid:int = (firstLine+hi)/2;
+ CONFIG::debug { assert(mid != 0,"ContainerController:gatherVisibleLines: bad mid"); }
+ curLine = flowComposer.getLineAt(mid);
+ testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+
+ if (testRslt && testRslt is ITextLine)
+ {
+ textLine = testRslt as ITextLine;
+ // note that we tested firstLine above so going to mid-1 is always valid
+ var tempLine:ITextFlowLine = flowComposer.getLineAt(mid-1);
+ if (!(testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, tempLine, null) is ITextLine))
+ {
+ // Got the start
+ if (createShape)
+ curLine.createShape(wmode, textLine);
+ _linesInView.push(textLine);
+ firstLine = mid+1;
+ break;
+ }
+ testRslt = -1; // past the start
+ }
+ // need to deal with TextFlowTableBlocks
+
+ if (testRslt < 0 || testRslt == 2)
+ hi = mid-1;
+ else
+ firstLine = mid+1;
+ }
+ }
+
+ for (lineIndex = firstLine; lineIndex <= lastLine; lineIndex++)
+ {
+ curLine = flowComposer.getLineAt(lineIndex);
+ testRslt = testLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+
+ if(testRslt is TableBlockContainer)
+ continue;
+ textLine = testRslt as ITextLine;
+ if (!textLine)
+ break;
+
+ if (createShape)
+ curLine.createShape(wmode, textLine);
+ _linesInView.push(textLine);
+ }
+ }
+ else //multiple columns
+ {
+ for (lineIndex = firstLine; lineIndex <= lastLine; lineIndex++)
+ {
+ curLine = flowComposer.getLineAt(lineIndex);
+ if (curLine == null || curLine.controller != this)
+ continue;
+
+ textLine = oldTestLineVisible(wmode, scrollAdjustXTW, scrollAdjustYTW, scrollAdjustWidthTW, scrollAdjustHeightTW, curLine, null);
+ if (textLine)
+ {
+ if (createShape)
+ curLine.createShape(wmode, textLine);
+ _linesInView.push(textLine);
+ }
+ }
+ }
+ }
+ _updateStart = absoluteStart; // we collected all lines from the start of the container
+ }
+
+ /** determines the shapechildren in the container and applies VJ. @private */
+ public function fillShapeChildren():void
+ {
+ if (_textLength == 0)
+ return; // none
+
+ var wmode:String = effectiveBlockProgression;
+
+ if (_linesInView.length == 0) // no preexisting concpetion of what lines are in view: recalculate
+ gatherVisibleLines(wmode, true);
+
+ // If scrolling is turned off, and flow is vertical, then we need to adjust the positions of all the lines. With
+ // scrolling turned on, we don't need to do this because the adjustment is done in the Player when the scrollRect
+ // is set up correctly. But with the scrollRect, we also get clipping, and if scrolling is turned off we want to
+ // have the clipping turned off as well. So in this case we do the adjustment manually so the scrollRect can be null.
+ // NOTE: similar adjustments are made in TextContainerManager
+ var adjustLines:Boolean = (wmode == BlockProgression.RL) &&
+ (_horizontalScrollPolicy == ScrollPolicy.OFF &&
+ _verticalScrollPolicy == ScrollPolicy.OFF);
+
+ if (adjustLines)
+ {
+ var width:Number = _measureWidth ? _contentWidth : _compositionWidth;
+// var height:Number = _measureHeight ? _contentHeight : _compositionHeight;
+ var adjustX:Number = _xScroll - width; // vertical text: blockProgression is rl
+ var adjustY:Number = _yScroll;
+
+ // Iterate over the lines in the container, setting the x and y positions. Keep track of the width
+ // and height of the actual text in the container.
+ if (adjustX != 0 || adjustY != 0)
+ {
+ for each (var textLine:ITextLine in _linesInView)
+ {
+ if (!textLine)
+ continue;
+
+ if (adjustLines)
+ {
+ textLine.x -= adjustX;
+ textLine.y -= adjustY;
+ }
+ }
+ _contentLeft -= adjustX;
+ _contentTop -= adjustY;
+ }
+ }
+
+
+ }
+
+ //--------------------------------------------------------------------------
+ //
+ // Scrolling
+ //
+ //--------------------------------------------------------------------------
+
+ /**
+ * Specifies the horizontal scrolling policy, which you can set by assigning one of the constants of
+ * the ScrollPolicy class: ON, OFF, or AUTO.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see ScrollPolicy
+ */
+
+ public function get horizontalScrollPolicy():String
+ {
+ return _horizontalScrollPolicy;
+ }
+ public function set horizontalScrollPolicy(scrollPolicy:String):void
+ {
+ var newScrollPolicy:String = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_horizontalScrollPolicy, scrollPolicy) as String;
+
+ if (newScrollPolicy != _horizontalScrollPolicy)
+ {
+ _horizontalScrollPolicy = newScrollPolicy;
+ if (_horizontalScrollPolicy == ScrollPolicy.OFF)
+ horizontalScrollPosition = 0;
+ formatChanged(); // scroll policy affects composition
+ }
+ }
+
+ /** @private */
+ public function checkScrollBounds():void
+ {
+ var newHeight:Number;
+ var visibleHeight:Number;
+ var measuring:Boolean;
+
+ // If we've either grown the content past the composition bounds in the logical vertical direction,
+ // or shrunk it down under the composition bounds, signal a scrolling change
+ // If we're measuring we never scroll.
+ if (effectiveBlockProgression == BlockProgression.RL)
+ {
+ newHeight = _contentWidth;
+ visibleHeight = compositionWidth;
+ measuring = _measureWidth;
+ }
+ else
+ {
+ newHeight = _contentHeight;
+ visibleHeight = compositionHeight;
+ measuring = _measureHeight;
+ }
+
+ // Called when the bounds have changed and they now exceed the composition area, to see if we need to attach a mouse wheel listener for scrolling
+ if (textFlow && !_minListenersAttached)
+ {
+ var needToScroll:Boolean = !measuring && newHeight > visibleHeight;
+ if (needToScroll != _mouseWheelListenerAttached)
+ {
+ if (_mouseWheelListenerAttached)
+ removeMouseWheelListener();
+ else
+ addMouseWheelListener();
+ }
+ }
+
+ }
+
+ /** Specifies the vertical scrolling policy, which you can set by assigning one of the constants of the ScrollPolicy
+ * class: ON, OFF, or, AUTO.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see ScrollPolicy
+ */
+
+ public function get verticalScrollPolicy():String
+ {
+ return _verticalScrollPolicy;
+ }
+ public function set verticalScrollPolicy(scrollPolicy:String):void
+ {
+ var newScrollPolicy:String = ScrollPolicy.scrollPolicyPropertyDefinition.setHelper(_verticalScrollPolicy, scrollPolicy) as String;
+ if (newScrollPolicy != _verticalScrollPolicy)
+ {
+ _verticalScrollPolicy = newScrollPolicy;
+ if (_verticalScrollPolicy == ScrollPolicy.OFF)
+ verticalScrollPosition = 0;
+ formatChanged(); // scroll policy affects composition
+ }
+ }
+
+ /** Specifies the current horizontal scroll location on the stage. The value specifies the number of
+ * pixels from the left.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get horizontalScrollPosition():Number
+ {
+ return _xScroll;
+ }
+
+ public function set horizontalScrollPosition(x:Number):void
+ {
+ if (!_rootElement)
+ return;
+
+ if (_horizontalScrollPolicy == ScrollPolicy.OFF)
+ {
+ _xScroll = 0;
+ return;
+ }
+ var oldScroll:Number = _xScroll;
+ var newScroll:Number = computeHorizontalScrollPosition(x,true);
+
+ if (newScroll != oldScroll)
+ {
+ _shapesInvalid = true;
+ _xScroll = newScroll;
+ updateForScroll(ScrollEventDirection.HORIZONTAL, newScroll - oldScroll);
+ }
+ }
+
+ static private function pinValue(value:Number, minimum:Number, maximum:Number):Number
+ {
+ return Math.min(Math.max(value, minimum), maximum);
+ }
+
+ private function computeHorizontalScrollPosition(x:Number,okToCompose:Boolean):Number
+ {
+ var wmode:String = effectiveBlockProgression;
+ var curEstimatedWidth:Number = contentWidth;
+ var newScroll:Number = 0;
+
+ if (curEstimatedWidth > _compositionWidth && !_measureWidth)
+ {
+ // Pin the lower and upper bounds of _x. If we're doing vertical text, then the right edge is 0 and the left edge is negative
+ // We may not have composed all the way to the indicated position. If not, force composition so that we can be sure we're at
+ // a legal position.
+ if (wmode == BlockProgression.RL)
+ {
+ newScroll = pinValue(x, _contentLeft + _compositionWidth, _contentLeft + curEstimatedWidth);
+ if (okToCompose && _uncomposedTextLength != 0 && newScroll != _xScroll)
+ {
+ // in order to compose have to set _xScroll
+ _xScroll = x;
+ if (_xScroll > _contentLeft + _contentWidth)
+ _xScroll = _contentLeft + _contentWidth;
+ flowComposer.composeToController(flowComposer.getControllerIndex(this));
+ newScroll = pinValue(x, _contentLeft + _compositionWidth, _contentLeft + _contentWidth);
+ }
+ }
+ else
+ newScroll = pinValue(x, _contentLeft, (_contentLeft + curEstimatedWidth) - _compositionWidth);
+ }
+ return newScroll;
+ }
+
+
+ /** Specifies the current vertical scroll location on the stage. The value specifies the number of
+ * pixels from the top.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get verticalScrollPosition():Number
+ {
+ return _yScroll;
+ }
+
+ public function set verticalScrollPosition(y:Number):void
+ {
+ if (!_rootElement)
+ return;
+
+ if (_verticalScrollPolicy == ScrollPolicy.OFF)
+ {
+ _yScroll = 0;
+ return;
+ }
+
+ var oldScroll:Number = _yScroll;
+ var newScroll:Number = computeVerticalScrollPosition(y,true);
+
+ if (newScroll != oldScroll)
+ {
+ _shapesInvalid = true;
+ _yScroll = newScroll;
+ updateForScroll(ScrollEventDirection.VERTICAL, newScroll - oldScroll);
+ }
+ }
+
+ private function computeVerticalScrollPosition(y:Number,okToCompose:Boolean):Number
+ {
+ var newScroll:Number = 0;
+ var curcontentHeight:Number = contentHeight;
+ var wmode:String = effectiveBlockProgression;
+
+ // Only try to scroll if the content height is greater than the composition height, then there is text that is not visible to scroll to
+ if (curcontentHeight > _compositionHeight)
+ {
+ // new scroll value is somewhere between the topmost content, and the top of the last containerfull
+ newScroll = pinValue(y, _contentTop, _contentTop + (curcontentHeight - _compositionHeight));
+
+ // if we're not composed to the end, compose further so we can scroll to it. Sets the scroll position and then
+ // recomposes the container, which will compose through the end of the screenfull that starts at the requested position.
+ if (okToCompose && _uncomposedTextLength != 0 && wmode == BlockProgression.TB)
+ {
+ _yScroll = y;
+ if (_yScroll < _contentTop)
+ _yScroll = _contentTop;
+ flowComposer.composeToController(flowComposer.getControllerIndex(this));
+ newScroll = pinValue(y, _contentTop, _contentTop + (curcontentHeight - _compositionHeight));
+ }
+ }
+ return newScroll;
+ }
+
+ /**
+ * Returns the area that the text occupies, as reflected by the last compose or update operation.
+ * The width and the height might be estimated, if the container is scrollable and the text exceeds the
+ * visible area.
+ *
+ * @return describes the area that the text occupies.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ *
+ * @see org.apache.flex.geom.Rectangle Rectangle
+ */
+ public function getContentBounds():Rectangle
+ {
+ return new Rectangle(_contentLeft, _contentTop, contentWidth, contentHeight);
+ }
+
+ /**
+ * @private
+ */
+
+ public function get contentLeft():Number
+ {
+ return _contentLeft;
+ }
+
+ /**
+ * @private
+ */
+
+ public function get contentTop():Number
+ {
+ return _contentTop;
+ }
+
+ /** @private */
+ public function computeScaledContentMeasure(measure:Number):Number
+ {
+ CONFIG::debug { assert(_finalParcelStart != -1 && _finalParcelStart >= this.absoluteStart && _finalParcelStart <= textFlow.textLength-_uncomposedTextLength,"computeScaledContentMeasure bad _finalParcelStart"); }
+ var charsInFinalParcel:int = textFlow.textLength-_finalParcelStart;
+ var composeCompleteRatio:Number = charsInFinalParcel / (charsInFinalParcel-_uncomposedTextLength);
+ // trace(measure*composeCompleteRatio,charsInFinalParcel,_uncomposedTextLength,measure,composeCompleteRatio);
+ return measure * composeCompleteRatio;
+ }
+
+ /**
+ * @private
+ *
+ * Returns the vertical extent of the text. For horizontal text, it includes space taken for descenders on the last line.
+ * If not all the text is composed, this returns an estimated value based on how much text is already composed; the
+ * more text that is composed, the more accurate s the estimate. To get a completely accurate value, recompose
+ * with the rootElement's flowComposer before accessing contentHeight.
+ * You can get the composed bounds of the text by getting the contentLeft, contentTop, contentWidth, contentHeight properties.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get contentHeight():Number
+ {
+ if (_uncomposedTextLength == 0 || effectiveBlockProgression != BlockProgression.TB)
+ return _contentHeight;
+ return computeScaledContentMeasure(_contentHeight);
+ }
+
+ /**
+ * @private
+ *
+ * Returns the horizontal extent of the text. For vertical text, it includes space taken for descenders on the last line.
+ * If not all the text is composed, this returns an estimated value based on how much text is already composed; the
+ * more text that is composed, the more accurate is the estimate. To get a completely accurate value, recompose
+ * with the rootElement's flowComposer before accessing contentWidth.
+ * You can get the composed bounds of the text by getting the contentLeft, contentTop, contentWidth, contentHeight properties.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function get contentWidth():Number
+ {
+ if (_uncomposedTextLength == 0 || effectiveBlockProgression != BlockProgression.RL)
+ return _contentWidth;
+ return computeScaledContentMeasure(_contentWidth);
+ }
+
+ /** @private */
+ public function setContentBounds(contentLeft:Number, contentTop:Number, contentWidth:Number, contentHeight:Number):void
+ {
+ _contentWidth = contentWidth;
+ _contentHeight = contentHeight;
+ _contentLeft = contentLeft;
+ _contentTop = contentTop;
+ checkScrollBounds();
+ }
+
+ private function updateForScroll(direction:String, delta:Number):void
+ {
+ _linesInView.length = 0; // zero out array of previously gathered up lines; its invalid because we've changed the visible area
+ var flowComposer:IFlowComposer = textFlow.flowComposer;
+ flowComposer.updateToController(flowComposer.getControllerIndex(this));
+
+ attachTransparentBackgroundForHit(false);
+
+ // notify client that we scrolled.
+ if (textFlow.hasEventListener(TextLayoutEvent.SCROLL))
+ textFlow.dispatchEvent(new ScrollEvent(TextLayoutEvent.SCROLL, false, false, direction, delta));
+
+ // trace("contentHeight", contentHeight, "contentWidth", contentWidth);
+ // trace("contentHeight", contentHeight, "contentWidth", contentWidth);
+ }
+
+ /** @private */
+ CONFIG::debug public function validateLines():void
+ {
+ if (!Debugging.containerLineValidation)
+ return;
+
+ // Optimally we would recalculate which lines are in view and make sure they are parented
+ // to the container... but that causes side-effects (like creating TextLines) that affect
+ // regular execution. So we don't try that.
+
+ // Check all the children of the container. Verify that adornments go before or after lines, that lines are at the expected z-order position
+ // And that extraneous lines are not parented to the container.
+ var firstLineIndex:int = -1;
+ var lastLineIndex:int = -1;
+ var numChildren:int = _container.numElements;
+ for (var childIndex:int = 0; childIndex < numChildren; ++childIndex)
+ {
+ var child:IChild = _container.getElementAt(childIndex);
+ if (_shapeChildren.indexOf(child) < 0 && (!_floatsInContainer || _floatsInContainer.indexOf(child) < 0))
+ {
+ // the very last thing can be the selection sprite
+ if (childIndex == numChildren - 1)
+ assert(child == getSelectionSprite(false),"expected selectionsprite but not found");
+
+ assert(firstLineIndex == -1 || lastLineIndex == childIndex - 1, "Found adornment in the middle of ITextLine children");
+ continue; // it's an adornment: skip
+ }
+ else
+ {
+ if (firstLineIndex == -1)
+ firstLineIndex = childIndex;
+ lastLineIndex = childIndex;
+ }
+ if (_floatsInContainer && _floatsInContainer.indexOf(child) >= 0) // it's a float
+ continue;
+ assert(child is ITextLine, "Expected child to be a ITextLine");
+
+ // Check that the line comes after previous lines, in z-order
+ var lineIndex:int = _shapeChildren.indexOf(child);
+ if (lineIndex > 0)
+ assert(_container.getElementIndex(_shapeChildren[lineIndex - 1]) < childIndex, "Line is visible but not at expected z-order position: earlier line is later in z-order");
+ else if (lineIndex < 0)
+ assert(false, "Found line that should not be in the container (its not considered visible)");
+ }
+ }
+
+ private function get containerScrollRectLeft():Number
+ {
+ var rslt:Number;
+ if (horizontalScrollPolicy == ScrollPolicy.OFF && verticalScrollPolicy == ScrollPolicy.OFF)
+ rslt = 0;
+ else
+ rslt= effectiveBlockProgression == BlockProgression.RL ? horizontalScrollPosition - compositionWidth : horizontalScrollPosition;
+ //CONFIG::debug { assert(container.scrollRect == null && rslt == 0 || int(rslt) == container.scrollRect.left,"Bad containerScrollRectLeft"); }
+ return rslt;
+ }
+
+ private function get containerScrollRectRight():Number
+ {
+ var rslt:Number = containerScrollRectLeft+compositionWidth;
+ //CONFIG::debug { assert(container.scrollRect == null && rslt == compositionWidth || int(rslt) == container.scrollRect.right,"Bad containerScrollRectRight"); }
+ return rslt;
+ }
+
+ private function get containerScrollRectTop():Number
+ {
+ var rslt:Number;
+ if (horizontalScrollPolicy == ScrollPolicy.OFF && verticalScrollPolicy == ScrollPolicy.OFF)
+ rslt = 0;
+ else
+ rslt = verticalScrollPosition;
+ //CONFIG::debug { assert(container.scrollRect == null && rslt == 0 || int(rslt) == container.scrollRect.top,"Bad containerScrollRectTop"); }
+ return rslt;
+ }
+
+ private function get containerScrollRectBottom():Number
+ {
+ var rslt:Number = containerScrollRectTop+compositionHeight;
+ //CONFIG::debug { assert(container.scrollRect == null && rslt == compositionHeight || int(rslt) == container.scrollRect.bottom,"Bad containerScrollRectBottom"); }
+ return rslt;
+ }
+
+ /**
+ * Scrolls so that the text range is visible in the container.
+ *
+ * @param activePosition The end of the selection that is changed when you extend the selection. It can be
+ * either the start or the end of the selection, expressed as an offset from the start of the text flow.
+ * @param anchorPosition The stable end of the selection when you extend the selection. It can be either
+ * the start or the end of the selection.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function scrollToRange(activePosition:int,anchorPosition:int):void
+ {
+
+ // return if we're not scrolling, or if it's not the last controller
+ if (!_hasScrollRect || !flowComposer || flowComposer.getControllerAt(flowComposer.numControllers-1) != this)
+ return;
+
+ // clamp values to range absoluteStart,absoluteStart+_textLength
+ var controllerStart:int = absoluteStart;
+ var lastPosition:int = Math.min(controllerStart+_textLength, textFlow.textLength - 1);
+ activePosition = Math.max(controllerStart,Math.min(activePosition,lastPosition));
+ anchorPosition = Math.max(controllerStart,Math.min(anchorPosition,lastPosition));
+
+ var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+ var begPos:int = Math.min(activePosition,anchorPosition);
+ var endPos:int = Math.max(activePosition,anchorPosition);
+
+ // is part of the selection in view?
+ var begLineIndex:int = flowComposer.findLineIndexAtPosition(begPos,(begPos == textFlow.textLength));
+ var endLineIndex:int = flowComposer.findLineIndexAtPosition(endPos,(endPos == textFlow.textLength));
+
+ // no scrolling if any part of the selection is in view
+ var scrollRectLeft:Number = containerScrollRectLeft;
+ var scrollRectTop:Number = containerScrollRectTop;
+ var scrollRectRight:Number = containerScrollRectRight;
+ var scrollRectBottom:Number = containerScrollRectBottom;
+
+ if (flowComposer.damageAbsoluteStart <= endPos)
+ {
+ endPos = Math.min(begPos + 100, endPos + 1);
+ flowComposer.composeToPosition(endPos);
+ begLineIndex = flowComposer.findLineIndexAtPosition(begPos,(begPos == textFlow.textLength));
+ endLineIndex = flowComposer.findLineIndexAtPosition(endPos,(endPos == textFlow.textLength));
+ }
+ var rect:Rectangle = rangeToRectangle(begPos, endPos, begLineIndex, endLineIndex);
+ if (rect)
+ {
+// var lastVisibleLine:ITextFlowLine;
+ var horizontalScrollOK:Boolean;
+ var verticalScrollOK:Boolean;
+
+ // vertical scroll
+ if (verticalText) {
+ // horizontal scroll
+ horizontalScrollOK = (rect.left < scrollRectLeft || rect.right > scrollRectLeft);
+ if (horizontalScrollOK)
+ {
+ if (rect.left < scrollRectLeft)
+ horizontalScrollPosition = rect.left + _compositionWidth;
+ if (rect.right > scrollRectRight)
+ horizontalScrollPosition = rect.right;
+ }
+
+ // If we're showing a blinking insertion point, we need to scroll far enough that
+ // we can see the insertion point, and it comes just after the character.
+ if (rect.top < scrollRectTop)
+ verticalScrollPosition = rect.top;
+ if (activePosition == anchorPosition)
+ rect.bottom += 2;
+ // vertical scroll
+ if (rect.bottom > scrollRectBottom)
+ verticalScrollPosition = rect.bottom - _compositionHeight;
+ }
+ else
+ {
+ // vertical scroll
+
+ // Don't scroll if the range extends both above and below
+ verticalScrollOK = (rect.top > scrollRectTop || rect.bottom < scrollRectBottom);
+
+ // vertical scroll
+ if (verticalScrollOK)
+ {
+ if (rect.top < scrollRectTop)
+ verticalScrollPosition = rect.top;
+
+ if (rect.bottom > scrollRectBottom)
+ verticalScrollPosition = rect.bottom - _compositionHeight;
+ }
+
+ // horizontal scroll
+
+ // If we're showing a blinking insertion point, we need to scroll far enough to see the
+ // insertion point, and it comes up to the right
+ if (activePosition == anchorPosition)
+ rect.right += 2;
+
+ // Don't scroll if range extends both to the left and right
+ horizontalScrollOK = (rect.left > scrollRectLeft || rect.right < scrollRectRight);
+ if (horizontalScrollOK && rect.left < scrollRectLeft)
+ horizontalScrollPosition = rect.left - _compositionWidth / 2;
+ if (horizontalScrollOK && rect.right > scrollRectRight)
+ horizontalScrollPosition = rect.right - _compositionWidth / 2;
+ }
+ }
+ }
+
+ private function rangeToRectangle(start:int, end:int, startLineIndex:int, endLineIndex:int):Rectangle
+ {
+ var bbox:Rectangle;
+ var blockProgression:String = effectiveBlockProgression;
+ var flowComposer:IFlowComposer = textFlow.flowComposer;
+
+ if (!container || !flowComposer)
+ return null;
+
+ if (startLineIndex == endLineIndex)
+ {
+ var line:ITextFlowLine = flowComposer.getLineAt(startLineIndex);
+ if (line.isDamaged())
+ return null;
+ var textLine:ITextLine = line.getTextLine(true);
+ var paragraphStart:int = line.paragraph.getAbsoluteStart();
+
+ var isTCY:Boolean = false;
+ if (blockProgression == BlockProgression.RL)
+ {
+ var leafElement:IFlowLeafElement = _rootElement.getTextFlow().findLeaf(start);
+ isTCY = leafElement.getParentByType("TCYElement") != null;
+ }
+
+ var minAtomIndex:int = textLine.atomCount;
+ var maxAtomIndex:int = 0;
+ if (start == end)
+ {
+ minAtomIndex = textLine.getAtomIndexAtCharIndex(start - paragraphStart);
+ maxAtomIndex = minAtomIndex;
+ }
+ else
+ {
+ var atomIndex:int;
+ var lastPosition:int = end - paragraphStart;
+ for (var pos:int = start - paragraphStart; pos < lastPosition; ++pos)
+ {
+ atomIndex = textLine.getAtomIndexAtCharIndex(pos);
+ if (atomIndex < minAtomIndex)
+ minAtomIndex = atomIndex;
+ if (atomIndex > maxAtomIndex)
+ maxAtomIndex = atomIndex;
+ }
+ }
+ bbox = atomToRectangle(minAtomIndex, line, textLine, blockProgression, isTCY);
+ if (minAtomIndex != maxAtomIndex)
+ bbox = bbox.union(atomToRectangle(maxAtomIndex, line, textLine, blockProgression, isTCY));
+ }
+ else
+ {
+ bbox = new Rectangle(_contentLeft, _contentTop, _contentWidth, _contentHeight);
+ var startLine:ITextFlowLine = flowComposer.getLineAt(startLineIndex);
+ var endLine:ITextFlowLine = flowComposer.getLineAt(endLineIndex);
+ if (blockProgression == BlockProgression.TB)
+ {
+ bbox.top = startLine.y;
+ bbox.bottom = endLine.y + endLine.textHeight;
+ }
+ else
+ {
+ bbox.right = startLine.x + startLine.textHeight;
+ bbox.left = endLine.x;
+ }
+ }
+ return bbox;
+ }
+
+
+ private function atomToRectangle(atomIdx:int, line:ITextFlowLine, textLine:ITextLine, blockProgression:String, isTCY:Boolean):Rectangle
+ {
+ var atomBounds:Rectangle;
+ CONFIG::debug { assert(atomIdx > -1, "How'd we get here?"); }
+ if (atomIdx > -1)
+ atomBounds = textLine.getAtomBounds(atomIdx);
+
+ // special handling for TCY - no line height adjustments TCY is perpendicular to the height direction
+ if (blockProgression == BlockProgression.RL)
+ {
+ if (isTCY)
+ return new Rectangle(line.x+atomBounds.x,line.y+atomBounds.y,atomBounds.width,atomBounds.height);
+ return new Rectangle(line.x, line.y + atomBounds.y, line.height, atomBounds.height);
+ }
+ return new Rectangle(line.x + atomBounds.x, line.y-line.height+line.ascent, atomBounds.width, line.height+textLine.descent);
+ }
+
+ /**
+ * @private
+ */
+
+ public function resetColumnState():void
+ {
+ if (_rootElement)
+ _columnState.updateInputs(effectiveBlockProgression, _rootElement.computedFormat.direction, this, _compositionWidth, _compositionHeight);
+ }
+
+ /**
+ * Marks all the text in this container as needing composing.
+ *
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+
+ public function invalidateContents():void
+ {
+ if (textFlow)
+ textFlow.damage(absoluteStart, Math.min(_textLength, 1), FlowDamageType.GEOMETRY, false);
+ }
+
+ /** @private */
+ private var _transparentBGX:Number;
+ /** @private */
+ private var _transparentBGY:Number;
+ /** @private */
+ private var _transparentBGWidth:Number;
+ /** @private */
+ private var _transparentBGHeight:Number;
+
+ /** No mouse clicks or moves will be generated for the container unless it has a background covering its area. Text Layout Framework
+ * wants those events so that clicking on a container will select the text in it. This code
+ * adds or updates (on size change) that background for Sprite containers only. This may cause clients problems
+ * - definitely no hits is a problem - add this code to explore the issues - expect feedback.
+ * We may have to make this configurable. @private */
+
+
+ public function attachTransparentBackgroundForHit(justClear:Boolean):void
+ {
+ if ((_minListenersAttached || _mouseWheelListenerAttached) && attachTransparentBackground)
+ {
+ var s:IParentIUIBase = _container;
+ if (justClear)
+ {
+//TODO deal with graphics
+// s.graphics.clear();
+ CONFIG::debug { Debugging.traceFTECall(null,s,"clearTransparentBackground()"); }
+ _transparentBGX = _transparentBGY = _transparentBGWidth = _transparentBGHeight = NaN;
+ }
+ else
+ {
+ var bgwidth:Number = _measureWidth ? _contentWidth : _compositionWidth;
+ var bgheight:Number = _measureHeight ? _contentHeight : _compositionHeight;
+
+ var adjustHorizontalScroll:Boolean = effectiveBlockProgression == BlockProgression.RL && _horizontalScrollPolicy != ScrollPolicy.OFF;
+ var bgx:Number = adjustHorizontalScroll ? _xScroll - bgwidth : _xScroll;
+ var bgy:Number = _yScroll;
+
+ CONFIG::debug { assert(!isNaN(bgx) && !isNaN(bgy) && !isNaN(bgwidth) && ! isNaN(bgheight),"Bad background rectangle"); }
+
+ if (bgx != _transparentBGX || bgy != _transparentBGY || bgwidth != _transparentBGWidth || bgheight != _transparentBGHeight)
+ {
+//TODO graphics
+// s.graphics.clear();
+// CONFIG::debug { Debugging.traceFTECall(null,s,"clearTransparentBackground()"); }
+// if (bgwidth != 0 && bgheight != 0 )
+// {
+// s.graphics.beginFill(0, 0);
+// s.graphics.drawRect(bgx, bgy, bgwidth, bgheight);
+// s.graphics.endFill();
+// CONFIG::debug { Debugging.traceFTECall(null,s,"drawTransparentBackground",bgx, bgy, bgwidth, bgheight); }
+// }
+ _transparentBGX = bgx;
+ _transparentBGY = bgy;
+ _transparentBGWidth = bgwidth;
+ _transparentBGHeight = bgheight;
+ }
+ }
+ }
+ }
+
+ /** @private */
+ public function interactionManagerChanged(newInteractionManager:ISelectionManager):void
+ {
+ if (!newInteractionManager)
+ detachContainer();
+ attachContainer();
+ checkScrollBounds();
+ // Need to forward whether the Ctrl key is needed to have
+ // hit-tested FlowElements emit events
+ if (_mouseEventManager)
+ _mouseEventManager.needsCtrlKey =
+ (interactionManager != null && interactionManager.editingMode == EditingMode.READ_WRITE);
+
+ // We have to tell the Player to bring up the soft keyboard on a
+ // keyboard edit gesture. Note that needsSoftKeyboard is new with 10.2, so
+ // have to check for it. This is a change to the container, but unavoidable
+ if (GlobalSettings.playerEnablesSpicyFeatures)
+ _container["needsSoftKeyboard"] = (interactionManager && interactionManager.editingMode == EditingMode.READ_WRITE);
+ }
+
+ //--------------------------------------------------------------------------
+ // Event handlers for editing
+ // Listeners are attached on first compose
+ //--------------------------------------------------------------------------
+
+ /** @private */
+ public function attachContainer():void
+ {
+ if (!_minListenersAttached && textFlow && textFlow.interactionManager)
+ {
+ _minListenersAttached = true;
+
+ _container.addEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);
+ _container.addEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
+
+ attachTransparentBackgroundForHit(false);
+
+//TODO deal with reference to stage
+ // If the container already has focus, we have to attach all listeners
+// if (_container.stage && _container.stage.focus == _container)
+// attachAllListeners();
+ }
+ }
+
+ /** @private */
+ public function attachInteractionHandlers():void
+ {
+ // the receiver is either this or another class that is going to handle the methods.
+ var receiver:IInteractionEventHandler = getInteractionHandler();
+
+ // the required handlers are implemented here and forwarded to the receiver
+ _container.addEventListener(MouseEvent.MOUSE_DOWN, requiredMouseDownHandler);
+ _container.addEventListener(FocusEvent.FOCUS_OUT, requiredFocusOutHandler);
+ _container.addEventListener(MouseEvent.DOUBLE_CLICK, receiver.mouseDoubleClickHandler);
+ _container.addEventListener(ActivateEvent.ACTIVATE, receiver.activateHandler);
+ _container.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, receiver.focusChangeHandler);
+ _container.addEventListener(FocusEvent.KEY_FOCUS_CHANGE, receiver.focusChangeHandler);
+ _container.addEventListener(TextEvent.TEXT_INPUT, receiver.textInputHandler);
+ _container.addEventListener(MouseEvent.MOUSE_OUT, receiver.mouseOutHandler);
+ addMouseWheelListener();
+ _container.addEventListener(ActivateEvent.DEACTIVATE, receiver.deactivateHandler);
+ // attach by literal event name to avoid Argo dependency
+ // normally this would be IMEEvent.START_COMPOSITION
+ _container.addEventListener("imeStartComposition", receiver.imeStartCompositionHandler);
+
+//TODO context menu
+// if (_container.contextMenu)
+// _container.contextMenu.addEventListener(ContextMenuEvent.MENU_SELECT, receiver.menuSelectHandler);
+ _container.addEventListener(EditEvent.COPY, receiver.editHandler);
+ _container.addEventListener(SelectionEvent.SELECT_ALL, receiver.editHandler);
+ _container.addEventListener(EditEvent.CUT, receiver.editHandler);
+ _container.addEventListener(EditEvent.PASTE, receiver.editHandler);
+ _container.addEventListener(EditEvent.CLEAR, receiver.editHandler);
+ }
+
+ /** @private */
+ public function removeInteractionHandlers():void
+ {
+ var receiver:IInteractionEventHandler = getInteractionHandler();
+
+ _container.removeEventListener(MouseEvent.MOUSE_DOWN, requiredMouseDownHandler);
+ _container.removeEventListener(FocusEvent.FOCUS_OUT, requiredFocusOutHandler);
+ _container.removeEventListener(MouseEvent.DOUBLE_CLICK, receiver.mouseDoubleClickHandler);
+ _container.removeEventListener(ActivateEvent.ACTIVATE, receiver.activateHandler);
+ _container.removeEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, receiver.focusChangeHandler);
+ _container.removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, receiver.focusChangeHandler);
+ _container.removeEventListener(TextEvent.TEXT_INPUT, receiver.textInputHandler);
+ _container.removeEventListener(MouseEvent.MOUSE_OUT, receiver.mouseOutHandler);
+ removeMouseWheelListener();
+ _container.removeEventListener(ActivateEvent.DEACTIVATE, receiver.deactivateHandler);
+ // _container.removeEventListener(IMEEvent.IME_START_COMPOSITION, receiver.imeStartCompositionHandler);
+ // attach by literal event name to avoid Argo dependency
+ _container.removeEventListener("imeStartComposition", receiver.imeStartCompositionHandler);
+
+//TODO context menu
+// if (_container.contextMenu)
+// _container.contextMenu.removeEventListener(ContextMenuEvent.MENU_SELECT, receiver.menuSelectHandler);
+ _container.removeEventListener(EditEvent.COPY, receiver.editHandler);
+ _container.removeEventListener(SelectionEvent.SELECT_ALL, receiver.editHandler);
+ _container.removeEventListener(EditEvent.CUT, receiver.editHandler);
+ _container.removeEventListener(EditEvent.PASTE, receiver.editHandler);
+ _container.removeEventListener(EditEvent.CLEAR, receiver.editHandler);
+
+ clearSelectHandlers();
+ }
+
+ /** @private */
+ private function detachContainer():void
+ {
+ if (_minListenersAttached)
+ {
+ _container.removeEventListener(FocusEvent.FOCUS_IN, requiredFocusInHandler);
+ _container.removeEventListener(MouseEvent.MOUSE_OVER, requiredMouseOverHandler);
+
+ if(_allListenersAttached)
+ {
+ removeInteractionHandlers();
+ removeContextMenu();
+
+ attachTransparentBackgroundForHit(true);
+ _allListenersAttached = false;
+ }
+ _minListenersAttached = false;
+ }
+ removeMouseWheelListener();
+ }
+
+
+ private function attachAllListeners():void
+ {
+ if (!_allListenersAttached && textFlow && textFlow.interactionManager)
+ {
+ CONFIG::debug { assert(_minListenersAttached,"Bad call to attachAllListeners - won't detach"); }
+ _allListenersAttached = true;
+ attachContextMenu();
+ attachInteractionHandlers();
+ }
+ }
+
+ /** @private */
+ public function addMouseWheelListener():void
+ {
+ if (!_mouseWheelListenerAttached)
+ {
+ _container.addEventListener(MouseEvent.WHEEL, getInteractionHandler().mouseWheelHandler);
+ _mouseWheelListenerAttached = true;
+ }
+ }
+
+ /** @private */
+ public function removeMouseWheelListener():void
+ {
+ if (_mouseWheelListenerAttached)
+ {
+ _container.removeEventListener(MouseEvent.WHEEL, getInteractionHandler().mouseWheelHandler);
+ _mouseWheelListenerAttached = false;
+ }
+ }
+
+ /** @private */
+ public function attachContextMenu():void
+ {
+//TODO deal with context menu
+// _container.contextMenu = createContextMenu();
+ }
+
+ /** @private */
+ public function removeContextMenu():void
+ {
+//TODO deal with context menu
+// _container.contextMenu = null;
+ }
+
+
+ /** @private
+ *
+ * Shared so that TextContainerManager can create the same ContextMenu.
+ */
+ static public function createDefaultContextMenu():ContextMenu
+ {
+ var contextMenu:ContextMenu = new ContextMenu();
+ contextMenu.clipboardMenu = true;
+ contextMenu.clipboardItems.clear = true;
+ contextMenu.clipboardItems.copy = true;
+ contextMenu.clipboardItems.cut = true;
+ contextMenu.clipboardItems.paste = true;
+ contextMenu.clipboardItems.selectAll = true;
+ return contextMenu;
+ }
+
+ /**
+ * Creates a context menu for the ContainerController. Use the methods of the ContextMenu class to
+ * add items to the menu.
+ * <p>You can override this method to define a custom context menu.</p>
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see flash.ui.ContextMenu ContextMenu
+ */
+ protected function createContextMenu():ContextMenu
+ {
+ return createDefaultContextMenu();
+ }
+
+ public function dispose():void
+ {
+ stopMouseSelectionScrolling();
+ clearSelectionShapes();
+ setRootElement(null);
+ }
+
+ private function stopMouseSelectionScrolling(containerRoot:IEventDispatcher = null):void
+ {
+ if(_scrollTimer)
+ {
+ _scrollTimer.stop();
+ _scrollTimer.removeEventListener(Timer.TIMER, scrollTimerHandler);
+
+//TODO fix this for FlexJS
+// if(!containerRoot)
+// {
+// containerRoot = getContainerRoot();
+// }
+
+ if(containerRoot)
+ {
+ containerRoot.removeEventListener(MouseEvent.MOUSE_UP, scrollTimerHandler);
+ }
+
+ _scrollTimer = null;
+ }
+ }
+ /** @private */
+ public function scrollTimerHandler(event:Event):void
+ {
+ // trace("BEGIN scrollTimerHandler");
+ if (!_scrollTimer)
+ return;
+
+ // shut it down if not in this container
+ if (textFlow.interactionManager == null || textFlow.interactionManager.activePosition < absoluteStart || textFlow.interactionManager.activePosition > absoluteStart+textLength)
+ event = null;
+
+
+ // We're listening for MOUSE_UP so we can cancel autoscrolling
+ if (event is MouseEvent)
+ {
+ stopMouseSelectionScrolling(event.currentTarget as IEventDispatcher);
+// CONFIG::debug { assert(_container.stage == null || getContainerRoot() == event.currentTarget,"scrollTimerHandler bad target"); }
+ }
+ else if (!event)
+ {
+ stopMouseSelectionScrolling();
+ }
+//TODO deal with stage
+// else if (_container.stage)
+// {
+// var containerPoint:Point = new Point(_container.stage.mouseX, _container.stage.mouseY);
+// containerPoint = _container.globalToLocal(containerPoint);
+// var scrollChange:int = autoScrollIfNecessaryInternal(containerPoint);
+// if (scrollChange != 0 && interactionManager) // force selection update if we actually scrolled and we have a selection manager
+// {
+// var mouseEvent:MouseEvent = new PsuedoMouseEvent(MouseEvent.MOUSE_MOVE,false,false,_container.stage.mouseX, _container.stage.mouseY,_container.stage,false,false,false,true);
+// var stashedScrollTimer:Timer = _scrollTimer;
+// try
+// {
+// _scrollTimer = null;
+// interactionManager.mouseMoveHandler(mouseEvent);
+// }
+// catch (e:Error)
+// {
+// throw(e);
+// }
+// finally
+// {
+// _scrollTimer = stashedScrollTimer;
+// }
+// }
+// }
+ // trace("AFTER scrollTimerHandler");
+ }
+
+ /**
+ * Handle a scroll event during a "drag" selection.
+ *
+ * @param mouseX The horizontal position of the mouse cursor on the stage.
+ * @param mouseY The vertical position of the mouse cursor on the stage.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function autoScrollIfNecessary(mouseX:int, mouseY:int):void
+ {
+ if (flowComposer.getControllerAt(flowComposer.numControllers-1) != this)
+ {
+ var verticalText:Boolean = (effectiveBlockProgression == BlockProgression.RL);
+ var lastController:IContainerController = flowComposer.getControllerAt(flowComposer.numControllers - 1);
+ if ((verticalText && _horizontalScrollPolicy == ScrollPolicy.OFF) ||
+ (!verticalText && _verticalScrollPolicy == ScrollPolicy.OFF))
+ return;
+ var r:Rectangle = DisplayUtils.getScreenBoundingRect(lastController.container);// lastController.container.getBounds(_container.stage);
+ if (verticalText)
+ {
+ if (mouseY >= r.top && mouseY <= r.bottom)
+ lastController.autoScrollIfNecessary(mouseX, mouseY);
+ }
+ else
+ {
+ if (mouseX >= r.left && mouseX <= r.right)
+ lastController.autoScrollIfNecessary(mouseX, mouseY);
+ }
+ }
+
+ // even if not the last container - may scroll if there are explicit linebreaks
+ if (!_hasScrollRect)
+ return;
+ var containerPoint:Point = new Point(mouseX, mouseY);
+ containerPoint = PointUtils.globalToLocal(containerPoint, _container);// _container.globalToLocal(containerPoint);
+ autoScrollIfNecessaryInternal(containerPoint);
+ }
+
+ /**
+ * Handle a scroll event during a "drag" selection.
+ *
+ * @param mouseX The horizontal position of the mouse cursor on the stage.
+ * @param mouseY The vertical position of the mouse cursor on the stage.
+ * @returns positive number if scroll went forward in reading order, negative number if it went backwards, and 0 if no scroll
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ private function autoScrollIfNecessaryInternal(extreme:Point):int
+ {
+ CONFIG::debug
+ {
+ assert(_hasScrollRect, "internal scrolling function called on non-scrollable container");
+ }
+
+
+ var scrollDirection:int = 0;
+
+ if (extreme.y - containerScrollRectBottom > 0) {
+ verticalScrollPosition += textFlow.configuration.scrollDragPixels;
+ scrollDirection = 1;
+ }
+ else if (extreme.y - containerScrollRectTop < 0) {
+ verticalScrollPosition -= textFlow.configuration.scrollDragPixels;
+ scrollDirection = -1;
+ }
+
+ if (extreme.x - containerScrollRectRight > 0) {
+ horizontalScrollPosition += textFlow.configuration.scrollDragPixels;
+ scrollDirection = -1;
+ }
+ else if (extreme.x - containerScrollRectLeft < 0) {
+ horizontalScrollPosition -= textFlow.configuration.scrollDragPixels;
+ scrollDirection = 1;
+ }
+
+ // we need a timer so that the mouse doesn't have to continue moving when the mouse is outside the content area
+ if (scrollDirection != 0 && !_scrollTimer)
+ {
+ _scrollTimer = new Timer(textFlow.configuration.scrollDragDelay); // 35 ms is the default auto-repeat interval for ScrollBars.
+ _scrollTimer.addEventListener(Timer.TIMER, scrollTimerHandler, false, 0, true);
+//TODO deal with platform specific events
+// if (getContainerRoot())
+// {
+// getContainerRoot().addEventListener(MouseEvent.MOUSE_UP, scrollTimerHandler, false, 0, true);
+// beginMouseCapture(); // TELL CLIENTS WE WANT mouseUpSomewhere events
+// }
+ _scrollTimer.start();
+ }
+
+ return scrollDirection;
+ }
+
+ /** @private */
+ public function getFirstVisibleLine():ITextFlowLine
+ { return _shapeChildren.length ? _shapeChildren[0].userData : null; }
+ /** @private */
+ public function getLastVisibleLine():ITextFlowLine
+ { return _shapeChildren.length ? _shapeChildren[_shapeChildren.length-1].userData : null; }
+
+ /**
+ * Figure out the scroll distance required to scroll up or down by the specified number of lines.
+ * Negative numbers scroll upward, bringing more of the top of the TextFlow into view. Positive numbers
+ * scroll downward, bringing the next line from the bottom into full view.
+ *
+ * <p>When scrolling up, for example, the method makes the next line fully visible. If the next line is partially
+ * obscured and the number of lines specified is 1, the partially obscured line becomes fully visible.</p>
+ *
+ * @param nLines The number of lines to scroll.
+ *
+ * @return the delta amount of space to scroll
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ */
+
+ public function getScrollDelta(numLines:int):Number
+ {
+ var flowComposer:IFlowComposer = textFlow.flowComposer;
+
+ if (flowComposer.numLines == 0)
+ return 0;
+
+ // Now we want to calculate the top & bottom lines within the scrollRect. It's ok if they're just partially
+ // visible. Once we determine these lines, we figure out how much we need to scroll in order to bring the
+ // lines completely into view.
+
+ var firstVisibleLine:ITextFlowLine = getFirstVisibleLine();
+ if (!firstVisibleLine)
+ return 0;
+
+ var lastVisibleLine:ITextFlowLine = getLastVisibleLine();
+ CONFIG::debug { assert(lastVisibleLine != null,"Expect lastVisibleLine when there is a firstVisibleLine"); }
+
+ // trace(" // findFirstAndLastVisibleLine ",flowComposer.findLineIndexAtPosition(firstVisibleLine.absoluteStart),flowComposer.findLineIndexAtPosition(lastVisibleLine.absoluteStart));
+
+ var newLineIndex:int;
+ var lineIndex:int;
+ if (numLines > 0)
+ {
+ lineIndex = flowComposer.findLineIndexAtPosition(lastVisibleLine.absoluteStart);
+ // If the last visible line is only partly visible, don't count it as visible. But make sure it overlaps by
+ // at least two pixels, otherwise it doesn't look like its clipped.
+
+ var lastTextLine:ITextLine = lastVisibleLine.getTextLine(true);
+ if (effectiveBlockProgression == BlockProgression.TB)
+ {
+ if ((lastTextLine.y + lastTextLine.descent) - containerScrollRectBottom > 2)
+ --lineIndex;
+ }
+ else if (containerScrollRectLeft - (lastTextLine.x - lastTextLine.descent) > 2)
+ --lineIndex;
+
+ // if we hit the end, force composition so that we get more lines - I picked a random amount to scroll forward, if its not enough, it will keep going
+ while (lineIndex + numLines > flowComposer.numLines - 1 && flowComposer.damageAbsoluteStart < textFlow.textLength)
+ {
+ var previousDamageStart:int = flowComposer.damageAbsoluteStart;
+ flowComposer.composeToPosition(flowComposer.damageAbsoluteStart + 1000);
+ // if we've made no progress, abort
+ if (flowComposer.damageAbsoluteStart == previousDamageStart)
+ return 0;
+ }
+ newLineIndex = Math.min(flowComposer.numLines-1, lineIndex + numLines);
+ }
+ if (numLines < 0)
+ {
+ lineIndex = flowComposer.findLineIndexAtPosition(firstVisibleLine.absoluteStart);
+
+ // If the first visible line is only partly visible, don't count it as visible. But make sure it overlaps by
+ // at least two pixels, otherwise it doesn't look like its clipped.
+ if (effectiveBlockProgression == BlockProgression.TB)
+ {
+ if (firstVisibleLine.y + 2 < containerScrollRectTop)
+ ++lineIndex;
+ }
+ else if (firstVisibleLine.x + firstVisibleLine.ascent > containerScrollRectRight + 2)
+ ++lineIndex;
+
+ newLineIndex = Math.max(0, lineIndex + numLines);
+ }
+
+ var line:ITextFlowLine = flowComposer.getLineAt(newLineIndex);
+ if (line.absoluteStart < absoluteStart) // don't scroll past the start of this controller -- previous text is in previous controller
+ return 0;
+ if (line.validity != "valid")
+ {
+ var leaf:IFlowLeafElement = textFlow.findLeaf(line.absoluteStart);
+ var paragraph:IParagraphElement = leaf.getParagraph();
+ textFlow.flowComposer.composeToPosition(paragraph.getAbsoluteStart() + paragraph.textLength);
+ line = flowComposer.getLineAt(newLineIndex);
+ CONFIG::debug { assert(line.validity == "valid", "expected valid line after recomposing"); }
+ }
+
+ var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+
+ var newScrollPosition:Number;
+ if (verticalText)
+ {
+
+ newScrollPosition = numLines < 0 ? line.x + line.textHeight : line.x - line.descent + _compositionWidth;
+ return newScrollPosition - horizontalScrollPosition;
+ }
+
+ newScrollPosition = numLines < 0 ? line.y : line.y + line.textHeight - _compositionHeight;
+ return newScrollPosition - verticalScrollPosition;
+ }
+
+ /**
+ * Processes the <code>MouseEvent.MOUSE_OVER</code> event when the client manages events.
+ *
+ * @param event The MouseEvent object.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ *
+ * @see org.apache.flex.events.MouseEvent#MOUSE_OVER MouseEvent.MOUSE_OVER
+ */
+
+ public function mouseOverHandler(event:MouseEvent):void
+ {
+ if (interactionManager && !event.isDefaultPrevented())
+ interactionManager.mouseOverHandler(event);
+ }
+
+ /** @private Does required mouseOver handling. Calls mouseOverHandler. @see #mouseOverHandler */
+ public function requiredMouseOverHandler(event:MouseEvent):void
+ {
+ attachAllListeners();
+ getInteractionHandler().mouseOverHandler(event);
+ }
+
+ /** Processes the <code>MouseEvent.MOUSE_OUT</code> event when the client manages events.
+ *
+ * @param event The MouseEvent object.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.events.MouseEvent#MOUSE_OUT MouseEvent.MOUSE_OUT
+ */
+ public function mouseOutHandler(event:MouseEvent):void
+ {
+ if (interactionManager && !event.isDefaultPrevented())
+ interactionManager.mouseOutHandler(event);
+ }
+
+ /** Processes the <code>MouseEvent.MOUSE_WHEEL</code> event when the client manages events.
+ *
+ * @param event The MouseEvent object.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.events.MouseEvent#MOUSE_WHEEL MouseEvent.MOUSE_WHEEL
+ */
+ public function mouseWheelHandler(event:MouseEvent):void
+ {
+ if (event.isDefaultPrevented())
+ return;
+
+ // Do the scroll and call preventDefault only if the there is enough text to scroll. Otherwise
+ // we let the event bubble up and cause scrolling at the next level up in the client's container hierarchy.
+ var verticalText:Boolean = effectiveBlockProgression == BlockProgression.RL;
+ if (verticalText)
+ {
+ if (contentWidth > _compositionWidth && !_measureWidth)
+ {
+ horizontalScrollPosition += event.delta * textFlow.configuration.scrollMouseWheelMultiplier;
+ event.preventDefault();
+ }
+ }
+ else if (contentHeight > _compositionHeight && !_measureHeight)
+ {
+ verticalScrollPosition -= event.delta * textFlow.configuration.scrollMouseWheelMultiplier;
+ event.preventDefault();
+ }
+ }
+
+
+ /** Processes the <code>MouseEvent.MOUSE_DOWN</code> event when the client manages events.
+ *
+ * @param event The MouseEvent object.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.events.MouseEvent#MOUSE_DOWN MouseEvent.MOUSE_DOWN
+ */
+
+ public function mouseDownHandler(event:MouseEvent):void
+ {
+ if (interactionManager && !event.isDefaultPrevented())
+ {
+ interactionManager.mouseDownHandler(event);
+ // grab the focus - alternative is to listen to keyevents on the Application
+ // is this necessary?
+ if ( interactionManager.hasSelection())
+ setFocus();
+ }
+ }
+
+ /** @private Does required mouseDown handling. Calls mouseDownHandler. @see #mouseDownHandler */
+ public function requiredMouseDownHandler(event:MouseEvent):void
+ {
+ if (!_selectListenersAttached)
+ {
+//TODO fix root events
+// var containerRoot:DisplayObject = getContainerRoot();
+// if (containerRoot)
+// {
+// containerRoot.addEventListener(MouseEvent.MOUSE_MOVE, rootMouseMoveHandler, false, 0, true);
+// containerRoot.addEventListener(MouseEvent.MOUSE_UP, rootMouseUpHandler, false, 0, true);
+//
+// beginMouseCapture(); // TELL CLIENTS THAT WE WANT moueUpSomewhere EVENTS
+//
+//
+// _selectListenersAttached = true;
+// }
+ }
+ getInteractionHandler().mouseDownHandler(event);
+ }
+
+ /**
+ * Processes the <code>MouseEvent.MOUSE_UP</code> event when the client manages events.
+ *
+ * @param event The MouseEvent object.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ * @see org.apache.flex.events.MouseEvent#MOUSE_UP MouseEvent.MOUSE_UP
+ *
+ */
+ public function mouseUpHandler(event:MouseEvent):void
+ {
+ if (interactionManager && event && !event.isDefaultPrevented())
+ {
+ interactionManager.mouseUpHandler(event);
+ }
+ }
+
+ /** @private */
+ public function rootMouseUpHandler(event:MouseEvent):void
+ {
+ clearSelectHandlers();
+ getInteractionHandler().mouseUpHandler(event);
+ }
+
+
+ private function clearSelectHandlers():void
+ {
+ if (_selectListenersAttached)
+ {
+//TODO fix root events
+// CONFIG::debug { assert(getContainerRoot() != null,"No container root"); }
+// getContainerRoot().removeEventListener(MouseEvent.MOUSE_MOVE, rootMouseMoveHandler);
+// getContainerRoot().removeEventListener(MouseEvent.MOUSE_UP, rootMouseUpHandler);
+// endMouseCapture(); // TELL CLIENTS WE NO LONGER WANT mouseUpSomewhere EVENTS
+// _selectListenersAttached = false;
+ }
+ }
+
+ /**
+ * Called to request clients to begin the forwarding of mouseup and mousemove events from outside a security sandbox.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function beginMouseCapture():void
+ {
+ // trace("BEGIN MOUSECAPTURE");
+ var sandboxManager:ISandboxSupport = getInteractionHandler() as ISandboxSupport;
+ if (sandboxManager && sandboxManager != this)
+ sandboxManager.beginMouseCapture();
+ }
+ /**
+ * Called to inform clients that the the forwarding of mouseup and mousemove events from outside a security sandbox is no longer needed.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function endMouseCapture():void
+ {
+ // trace("END MOUSECAPTURE");
+ var sandboxManager:ISandboxSupport = getInteractionHandler() as ISandboxSupport;
+ if (sandboxManager && sandboxManager != this)
+ sandboxManager.endMouseCapture();
+ }
+ /** Client call to forward a mouseUp event from outside a security sandbox. Coordinates of the mouse up are not needed.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function mouseUpSomewhere(event:Event):void
+ {
+ rootMouseUpHandler(null);
+ scrollTimerHandler(null);
+ }
+ /** Client call to forward a mouseMove event from outside a security sandbox. Coordinates of the mouse move are not needed.
+ *
+ * @playerversion Flash 10
+ * @playerversion AIR 1.5
+ * @langversion 3.0
+ *
+ */
+ public function mouseMoveSomewhere(event:Event):void
+ {
+ return; // do nothing right now
+ }
+
+ // What'd I hit???
+ private function hitOnMyFlowExceptLastContainer(event:MouseEvent):Boolean
+ {
+ if (event.target is ITextLine)
+ {
+ var tfl:ITextFlowLine = ITextLine(event.target).userData as ITextFlowLine;
+ if (tfl)
+ {
+ var para:IParagraphElement = tfl.paragraph;
+ if(para.getTextFlow() == textFlow)
+ return true;
+ }
+ }
+//TODO how to properly check
<TRUNCATED>