1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2008 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package spark.components
13{
14
15    import flash.display.DisplayObject;
16    import flash.display.DisplayObjectContainer;
17    import flash.events.Event;
18    import flash.events.FocusEvent;
19    import flash.events.KeyboardEvent;
20    import flash.events.MouseEvent;
21    import flash.geom.Rectangle;
22    import flash.system.IME;
23    import flash.system.IMEConversionMode;
24    import flash.text.engine.ElementFormat;
25    import flash.text.engine.FontDescription;
26    import flash.text.engine.FontLookup;
27    import flash.text.engine.TextBlock;
28    import flash.text.engine.TextElement;
29    import flash.text.engine.TextLine;
30    import flash.ui.Keyboard;
31
32    import flashx.textLayout.compose.ISWFContext;
33    import flashx.textLayout.container.TextContainerManager;
34    import flashx.textLayout.conversion.ConversionType;
35    import flashx.textLayout.conversion.ITextExporter;
36    import flashx.textLayout.conversion.ITextImporter;
37    import flashx.textLayout.conversion.TextConverter;
38    import flashx.textLayout.edit.EditManager;
39    import flashx.textLayout.edit.EditingMode;
40    import flashx.textLayout.edit.IEditManager;
41    import flashx.textLayout.edit.ISelectionManager;
42    import flashx.textLayout.edit.SelectionState;
43    import flashx.textLayout.elements.Configuration;
44    import flashx.textLayout.elements.GlobalSettings;
45    import flashx.textLayout.elements.InlineGraphicElement;
46    import flashx.textLayout.elements.InlineGraphicElementStatus;
47    import flashx.textLayout.elements.TextFlow;
48    import flashx.textLayout.events.CompositionCompleteEvent;
49    import flashx.textLayout.events.DamageEvent;
50    import flashx.textLayout.events.FlowOperationEvent;
51    import flashx.textLayout.events.SelectionEvent;
52    import flashx.textLayout.events.StatusChangeEvent;
53    import flashx.textLayout.factory.StringTextLineFactory;
54    import flashx.textLayout.factory.TextFlowTextLineFactory;
55    import flashx.textLayout.formats.BlockProgression;
56    import flashx.textLayout.formats.Category;
57    import flashx.textLayout.formats.ITextLayoutFormat;
58    import flashx.textLayout.formats.TextLayoutFormat;
59    import flashx.textLayout.operations.CopyOperation;
60    import flashx.textLayout.operations.CutOperation;
61    import flashx.textLayout.operations.DeleteTextOperation;
62    import flashx.textLayout.operations.FlowOperation;
63    import flashx.textLayout.operations.FlowTextOperation;
64    import flashx.textLayout.operations.InsertTextOperation;
65    import flashx.textLayout.operations.PasteOperation;
66    import flashx.textLayout.tlf_internal;
67    import flashx.undo.IUndoManager;
68
69    import mx.core.FlexVersion;
70    import mx.core.IFlexModuleFactory;
71    import mx.core.IIMESupport;
72    import mx.core.ISystemCursorClient;
73    import mx.core.UIComponent;
74    import mx.core.mx_internal;
75    import mx.events.FlexEvent;
76    import mx.managers.IFocusManager;
77    import mx.managers.IFocusManagerComponent;
78    import mx.resources.ResourceManager;
79    import mx.utils.StringUtil;
80
81    import spark.components.supportClasses.RichEditableTextContainerManager;
82    import spark.core.CSSTextLayoutFormat;
83    import spark.core.IEditableText;
84    import spark.core.IViewport;
85    import spark.core.NavigationUnit;
86    import spark.events.TextOperationEvent;
87    import spark.utils.TextUtil;
88
89    use namespace mx_internal;
90    use namespace tlf_internal;
91
92    //--------------------------------------
93    //  Events
94    //--------------------------------------
95
96    /**
97     *  Dispached after the <code>selectionAnchorPosition</code> and/or
98     *  <code>selectionActivePosition</code> properties have changed
99     *  for any reason.
100     *
101     *  @eventType mx.events.FlexEvent.SELECTION_CHANGE
102     *
103     *  @langversion 3.0
104     *  @playerversion Flash 10
105     *  @playerversion AIR 1.5
106     *  @productversion Flex 4
107     */
108    [Event(name="selectionChange", type="mx.events.FlexEvent")]
109
110    /**
111     *  Dispatched before a user editing operation occurs.
112     *  You can alter the operation, or cancel the event
113     *  to prevent the operation from being processed.
114     *
115     *  @eventType spark.events.TextOperationEvent.CHANGING
116     *
117     *  @langversion 3.0
118     *  @playerversion Flash 10
119     *  @playerversion AIR 1.5
120     *  @productversion Flex 4
121     */
122    [Event(name="changing", type="spark.events.TextOperationEvent")]
123
124    /**
125     *  Dispatched after a user editing operation is complete.
126     *
127     *  @eventType spark.events.TextOperationEvent.CHANGE
128     *
129     *  @langversion 3.0
130     *  @playerversion Flash 10
131     *  @playerversion AIR 1.5
132     *  @productversion Flex 4
133     */
134    [Event(name="change", type="spark.events.TextOperationEvent")]
135
136    /**
137     *  Dispatched when the user presses the Enter key,
138     *  if the <code>multiline</code> property is false.
139     *
140     *  @eventType mx.events.FlexEvent.ENTER
141     *
142     *  @langversion 3.0
143     *  @playerversion Flash 10
144     *  @playerversion AIR 1.5
145     *  @productversion Flex 4
146     */
147    [Event(name="enter", type="mx.events.FlexEvent")]
148
149    //--------------------------------------
150    //  Styles
151    //--------------------------------------
152
153    include "../styles/metadata/BasicInheritingTextStyles.as"
154    include "../styles/metadata/BasicNonInheritingTextStyles.as"
155    include "../styles/metadata/AdvancedInheritingTextStyles.as"
156    include "../styles/metadata/AdvancedNonInheritingTextStyles.as"
157    include "../styles/metadata/SelectionFormatTextStyles.as"
158
159    /**
160     *  The alpha level of the color defined by
161     *  the <code>backgroundColor</code> style.
162     *  Valid values range from 0.0 to 1.0.
163     *
164     *  @default 1.0
165     *
166     *  @langversion 3.0
167     *  @playerversion Flash 10
168     *  @playerversion AIR 1.5
169     *  @productversion Flex 4
170     */
171    [Style(name="backgroundAlpha", type="Number", inherit="no")]
172
173    /**
174     *  The color of the background of the entire
175     *  bounding rectangle of this component.
176     *  If this style is <code>undefined</code>,
177     *  no background is drawn.
178     *  Otherwise, this RGB color is drawn with an alpha level
179     *  determined by the <code>backgroundAlpha</code> style.
180     *
181     *  @default undefined
182     *
183     *  @langversion 3.0
184     *  @playerversion Flash 10
185     *  @playerversion AIR 1.5
186     *  @productversion Flex 4
187     */
188    [Style(name="backgroundColor", type="uint", format="Color", inherit="no")]
189
190    //--------------------------------------
191    //  Excluded APIs
192    //--------------------------------------
193
194    [Exclude(name="chromeColor", kind="style")]
195
196    //--------------------------------------
197    //  Other metadata
198    //--------------------------------------
199
200    [AccessibilityClass(implementation="spark.accessibility.RichEditableTextAccImpl")]
201
202    [DefaultProperty("content")]
203
204    [IconFile("RichEditableText.png")]
205
206    [DefaultTriggerEvent("change")]
207
208    [DiscouragedForProfile("mobileDevice")]
209
210    /**
211     *  RichEditableText is a low-level UIComponent for displaying,
212     *  scrolling, selecting, and editing richly-formatted text.
213     *
214     *  <p>The rich text can contain clickable hyperlinks and inline graphics
215     *  that are either embedded or loaded from URLs.</p>
216     *
217     *  <p>RichEditableText does not have scrollbars, but it implements
218     *  the IViewport interface for programmatic scrolling so that it
219     *  can be controlled by a Scroller, which does provide scrollbars.
220     *  It also supports vertical scrolling with the mouse wheel.</p>
221     *
222     *  <p>It does not include any user interface for changing
223     *  the formatting of the text.
224     *  But it offers APIs which can do this programmatically;
225     *  these make it possible, for example, for you to create
226     *  a Bold button that makes the selected text bold.</p>
227     *
228     *  <p>This class is used in the skins of the Spark versions
229     *  of TextInput and TextArea.
230     *  (TextInput does not expose its ability to handle rich text,
231     *  but TextArea does.)
232     *  By default, RichEditableText has a transparent background,
233     *  and it does not support drawing a border.</p>
234     *
235     *  <p>RichEditableText uses the Text Layout Framework (TLF) library,
236     *  which in turn builds on the new Flash Text Engine (FTE)
237     *  in Flash Player 10. In combination, these layers provide text editing with
238     *  high-quality international typography and layout.</p>
239     *
240     *  <p>The Spark architecture provides three text "primitives" --
241     *  Label, RichText, and RichEditableText.
242     *  Label is the fastest and most lightweight
243     *  because it uses only FTE, not TLF,
244     *  but it is limited in its capabilities: no rich text,
245     *  no scrolling, no selection, and no editing.
246     *  RichText adds the ability to display rich text
247     *  with complex layout, but is still completely non-interactive.
248     *  RichEditableText is the heaviest-weight,
249     *  but offers most of what TLF can do.
250     *  In general, use the fastest text primitive that meets your needs.</p>
251     *
252     *  <p>RichEditableText is similar to the UITextField class
253     *  used in MX components. This class did not use FTE or TLF
254     *  but rather extended the older TextField class.</p>
255     *
256     *  <p>The most important differences between UITextField and RichEditableText are:
257     *  <ul>
258     *    <li>RichEditableText offers better typography, better support
259     *        for international languages, and better text layout.</li>
260     *    <li>RichEditableText has an object-oriented model of rich text,
261     *        while UITextField does not.</li>
262     *    <li>RichEditableText has better support for displaying
263     *        large amounts of text.</li>
264     *    <li>RichEditableText requires that fonts be embedded
265     *        differently than UITextField.
266     *        Consult the documentation regarding how to use the
267     *        <code>embedAsCFF</code> attribute when you embed a font.</li>
268     *  </ul></p>
269     *
270     *  <p>RichEditableText uses TLF's object-oriented model of rich text,
271     *  in which text layout elements such as divisions, paragraphs, spans,
272     *  hyperlinks, and images are represented at runtime by ActionScript
273     *  objects which can be programmatically accessed and manipulated.
274     *  The central object in TLF for representing rich text is a
275     *  TextFlow, so you specify rich text for a RichEditableText control to display
276     *  by setting its <code>textFlow</code> property to a TextFlow instance.
277     *  See the description of the <code>textFlow</code>
278     *  property for information about how to create one,
279     *  such as by importing TLF markup.
280     *  If you don't need to display text that has multiple formats,
281     *  you can use the <code>text</code> property to set a "plain text" String.
282     *  See the description of the <code>text</code> and <code>textFlow</code>
283     *  properties for information about how they interact;
284     *  for example, you can set one and get the other.</p>
285     *
286     *  <p>At compile time, you can put TLF markup tags inside
287     *  the RichEditableText tag, as the following example shows:
288     *  <pre>
289     *  &lt;s:RichEditableText&gt;Hello &lt;s:span fontWeight="bold"&gt;World!&lt;/s:span&gt;&lt;/s:RichEditableText&gt;
290     *  </pre>
291     *  In this case, the MXML compiler sets the <code>content</code>
292     *  property, causing a TextFlow to be automatically created
293     *  from the FlowElements that you specify.</p>
294     *
295     *  <p>The default text formatting is determined by CSS styles
296     *  such as <code>fontFamily</code>, <code>fontSize</code>.
297     *  Any formatting information in the TextFlow overrides
298     *  the default formatting provided by the CSS styles.</p>
299     *
300     *  <p>You can control the spacing between lines with the
301     *  <code>lineHeight</code> style and the spacing between
302     *  paragraphs with the <code>paragraphSpaceBefore</code>
303     *  and <code>paragraphSpaceAfter</code> styles.
304     *  You can align or justify the text using the <code>textAlign</code>
305     *  and <code>textAlignLast</code> styles.
306     *  You can inset the text from the component's edges using the
307     *  <code>paddingLeft</code>, <code>paddingTop</code>,
308     *  <code>paddingRight</code>, and <code>paddingBottom</code> styles.</p>
309     *
310     *  <p>By default, a RichEditableText "autosizes": it starts out very
311     *  small if it has no text, and grows in width up to
312     *  <code>maxWidth</code> as you type. It grows in height when you
313     *  press the Enter key to start a new line.</p>
314     *
315     *  <p>The <code>widthInChars</code> and <code>heightInLines</code>
316     *  properties provide a convenient way to specify the width and height
317     *  in a way that scales with the font size.
318     *  You can use the <code>typicalText</code> property as well.
319     *  Note that if you use <code>typicalText</code>, the
320     *  <code>widthInChars</code> and <code>heightInLines</code>
321     *  are ignored.
322     *  You can also specify an explicit width or height in pixels,
323     *  or use a percent width and height, or use constraints such as
324     *  <code>left</code> and <code>right</code>
325     *  or <code>top</code> and <code>bottom</code>.</p>
326     *
327     *  <p>When you specify some kind of width -- whether an explicit or
328     *  percent width, a <code>maxWidth</code> or <code>left</code>
329     *  and <code>right</code> constraints -- the text wraps at the right
330     *  edge of the component and the text becomes vertically scrollable
331     *  when there is more text than fits.
332     *  If you set the <code>lineBreak</code> style to <code>explicit</code>,
333     *  new lines will start only at explicit lines breaks, such as
334     *  if you use CR (<code>\r</code>), LF (<code>\n</code>),
335     *  or CR+LF (<code>\r\n</code>) in <code>text</code>
336     *  or if you use <code>&lt;p&gt;</code> and <code>&lt;br/&gt;</code>
337     *  in TLF markup.
338     *  In that case, the text becomes horizontally scrollable
339     *  if any lines are wider than the control.</p>
340     *
341     *  <p>You can use the <code>maxChars</code> property to limit the number
342     *  of character that the user can enter, and the <code>restrict</code>
343     *  to limit which characters the user can enter.</p>
344     *
345     *  <p>The <code>multiline</code> property determines what happens
346     *  when you press the Enter key.
347     *  If it is <code>true</code>, the Enter key starts a new paragraph.
348     *  If it is <code>false</code>, it causes a <code>FlexEvent.ENTER</code>
349     *  event to be dispatched.</p>
350     *
351     *  <p>If you don't want the text to be editable,
352     *  set the <code>editable</code> property to <code>false</code>.
353     *  If you don't even want the text to be selectable,
354     *  set the <code>selectable</code> property to <code>false</code>.</p>
355     *
356     *  <p>Because RichEditableText uses TLF,
357     *  it supports displaying left-to-right (LTR) text such as French,
358     *  right-to-left (RTL) text such as Arabic, and bidirectional text
359     *  such as a French phrase inside of an Arabic one.
360     *  If the predominant text direction is right-to-left,
361     *  set the <code>direction</code> style to <code>rtl</code>.
362     *  The <code>textAlign</code> style defaults to <code>"start"</code>,
363     *  which makes the text left-aligned when <code>direction</code>
364     *  is <code>ltr</code> and right-aligned when <code>direction</code>
365     *  is <code>rtl</code>.
366     *  To get the opposite alignment, set <code>textAlign</code> to <code>end</code>.</p>
367     *
368     *  <p>As a result of using TLF, the RichEditableText supports
369     *  unlimited undo/redo within one editing session.
370     *  An editing session starts when the component gets keyboard focus
371     *  and ends when it loses focus.</p>
372     *
373     *  <p>RichEditableText uses TLF's TextContainerManager class
374     *  to handle its text display, scrolling, selection, editing and context menu.</p>
375     *
376     *  <p>To use this component in a list-based component, such as a List or DataGrid,
377     *  create an item renderer.
378     *  For information about creating an item renderer, see
379     *  <a href="http://help.adobe.com/en_US/flex/using/WS4bebcd66a74275c3-fc6548e124e49b51c4-8000.html">
380     *  Custom Spark item renderers</a>. </p>
381     *
382     *  @mxml
383     *
384     *  <p>The <code>&lt;s:RichEditableText&gt;</code> tag inherits all of the tag
385     *  attributes of its superclass and adds the following tag attributes:</p>
386     *
387     *  <pre>
388     *  &lt;s:RichEditableText
389     *    <strong>Properties</strong>
390     *    clipAndEnableScrolling="false|true"
391     *    contentHeight="<i>Calculated default</i>"
392     *    contentWidth="<i>Calculated default</i>"
393     *    displayAsPassword="false"
394     *    editable="true"
395     *    heightInLines="NaN"
396     *    horizontalScrollPosition="0"
397     *    imeMode="null"
398     *    maxChars="0"
399     *    multiline="true"
400     *    restrict="null"
401     *    selectable="true"
402     *    selectionActivePosition="-1"
403     *    selectionAnchorPosition="-1"
404     *    selectionHighlighting="TextSelectionHighlighting.WHEN_FOCUSED"
405     *    text=""
406     *    textFlow="<i>TextFlow</i>"
407     *    typicalText=null
408     *    verticalScrollPosition="0"
409     *    widthInChars="NaN"
410     *
411     *    <strong>Events</strong>
412     *    change="<i>No default</i>"
413     *    changing="<i>No default</i>"
414     *    enter="<i>No default</i>"
415     *    selectionChange="<i>No default</i>"
416     *  /&gt;
417     *  </pre>
418     *
419     *  @includeExample examples/RichEditableTextExample.mxml
420     *  @includeExample examples/externalTextFlow.xml -noswf
421     *
422     *  @langversion 3.0
423     *  @playerversion Flash 10
424     *  @playerversion AIR 1.5
425     *  @productversion Flex 4
426     *
427     *  @see spark.components.Label
428     *  @see spark.components.RichText
429     *  @see spark.utils.TextFlowUtil
430     *  @see flashx.textLayout.container.TextContainerManager
431     */
432    public class RichEditableText extends UIComponent
433        implements IFocusManagerComponent, IIMESupport, ISystemCursorClient,
434                   IViewport, IEditableText
435    {
436        include "../core/Version.as";
437
438        //--------------------------------------------------------------------------
439        //
440        //  Class mixins
441        //
442        //--------------------------------------------------------------------------
443
444        /**
445         *  @private
446         *  Placeholder for mixin by RichEditableTextAccImpl.
447         */
448        mx_internal static var createAccessibilityImplementation:Function;
449
450        //--------------------------------------------------------------------------
451        //
452        //  Class initialization
453        //
454        //--------------------------------------------------------------------------
455
456        /**
457         *  @private
458         *  This method initializes the static vars of this class.
459         *  Rather than calling it at static initialization time,
460         *  we call it in the constructor to do the class initialization
461         *  when the first instance is created.
462         *  (It does an immediate return if it has already run.)
463         *  By doing so, we avoid any static initialization issues
464         *  related to whether this class or the TLF classes
465         *  that it uses are initialized first.
466         */
467        private static function initClass():void
468        {
469            if (classInitialized)
470                return;
471
472            // Set the TLF hook used for localizing runtime error messages.
473            // TLF itself has English-only messages,
474            // but higher layers like Flex can provide localized versions.
475            GlobalSettings.resourceStringFunction = TextUtil.getResourceString;
476
477            // Set the TLF hook used to specify the callback used for changing
478            // the FontLookup based on SWFContext.
479            GlobalSettings.resolveFontLookupFunction = TextUtil.resolveFontLookup;
480
481            // Pre-FP10.1, set default tab stops in TLF.  Without this, if there
482            // is a tab and TLF is measuring width, the tab will
483            // measure as the rest of the remaining width up to 10000.
484            GlobalSettings.enableDefaultTabStops =
485                !Configuration.playerEnablesArgoFeatures;
486
487            staticPlainTextImporter =
488                TextConverter.getImporter(TextConverter.PLAIN_TEXT_FORMAT);
489
490            // Throw import errors rather than return a null textFlow.
491            // Alternatively, the error strings are in the Vector, importer.errors.
492            staticPlainTextImporter.throwOnError = true;
493
494            staticPlainTextExporter =
495                TextConverter.getExporter(TextConverter.PLAIN_TEXT_FORMAT);
496
497            classInitialized = true;
498        }
499
500        //--------------------------------------------------------------------------
501        //
502        //  Class variables
503        //
504        //--------------------------------------------------------------------------
505
506        /**
507         *  @private
508         */
509        private static var classInitialized:Boolean = false;
510
511        /**
512         *  @private
513         *  This TLF object composes TextLines from a text String.
514         *  We use it when the 'typicalText' property is set to a String
515         *  that doesn't contain linebreaks.
516         */
517        private static var staticStringFactory:StringTextLineFactory;
518
519        /**
520         *  @private
521         *  This TLF object composes TextLines from a TextFlow.
522         *  We use it when the 'typicalText is set to a String
523         *  that contains linebreaks (and therefore is interpreted
524         *  as multiple paragraphs).
525         */
526        private static var staticTextFlowFactory:TextFlowTextLineFactory;
527
528        /**
529         *  @private
530         *  This TLF object is used to import a 'text' String
531         *  containing linebreaks to create a multiparagraph TextFlow.
532         */
533        private static var staticPlainTextImporter:ITextImporter;
534
535        /**
536         *  @private
537         *  This TLF object is used to export a TextFlow as plain 'text',
538         *  by walking the leaf FlowElements in the TextFlow.
539         */
540        private static var staticPlainTextExporter:ITextExporter;
541
542        /**
543         *  @private
544         *  Regular expression which matches all newlines in the text.  Used
545         *  to strip newlines when pasting text when multiline is false.
546         */
547        private static const ALL_NEWLINES_REGEXP:RegExp = /\n/g;
548
549        //--------------------------------------------------------------------------
550        //
551        //  Class methods
552        //
553        //--------------------------------------------------------------------------
554        /**
555         *  @private
556         */
557        public static function getNumberOrPercentOf(value:Object,
558                                                    n:Number):Number
559        {
560            // If 'value' is a Number like 10.5, return it.
561            if (value is Number)
562                return Number(value);
563
564            // If 'value' is a percentage String like "10.5%",
565            // return that percentage of 'n'.
566            if (value is String)
567            {
568                var len:int = String(value).length;
569                if (len >= 1 && value.charAt(len - 1) == "%")
570                {
571                    var percent:Number = Number(value.substring(0, len - 1));
572                    return percent / 100 * n;
573                }
574            }
575
576            // Otherwise, return NaN.
577            return NaN;
578        }
579
580        /**
581         *  @private
582         */
583        private static function splice(str:String, start:int, end:int,
584                                       strToInsert:String):String
585        {
586            return str.substring(0, start) +
587                strToInsert +
588                str.substring(end, str.length);
589        }
590
591        //--------------------------------------------------------------------------
592        //
593        //  Constructor
594        //
595        //--------------------------------------------------------------------------
596
597        /**
598         *  Constructor.
599         *
600         *  @langversion 3.0
601         *  @playerversion Flash 10
602         *  @playerversion AIR 1.5
603         *  @productversion Flex 4
604         */
605        public function RichEditableText()
606        {
607            super();
608
609            initClass();
610
611            // Use the setter.
612            text = "";
613
614            // Create the TLF TextContainerManager, using this component
615            // as the DisplayObjectContainer for its TextLines.
616            // This TextContainerManager instance persists for the lifetime
617            // of the component.
618            _textContainerManager = createTextContainerManager();
619
620            // Add event listeners on this component.
621            addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
622
623            // The focusInHandler is called by the TCMContainer focusInHandler.
624            // The focusOutHandler is called by the TCMContainer focusOutHandler.
625            // The keyDownHandler is called by the TCMContainer keyDownHandler.
626
627            // Add event listeners on its TextContainerManager.
628
629            _textContainerManager.addEventListener(
630                CompositionCompleteEvent.COMPOSITION_COMPLETE,
631                textContainerManager_compositionCompleteHandler);
632
633            _textContainerManager.addEventListener(
634                DamageEvent.DAMAGE, textContainerManager_damageHandler);
635
636            _textContainerManager.addEventListener(
637                Event.SCROLL, textContainerManager_scrollHandler);
638
639            _textContainerManager.addEventListener(
640                SelectionEvent.SELECTION_CHANGE,
641                textContainerManager_selectionChangeHandler);
642
643            _textContainerManager.addEventListener(
644                FlowOperationEvent.FLOW_OPERATION_BEGIN,
645                textContainerManager_flowOperationBeginHandler);
646
647            _textContainerManager.addEventListener(
648                FlowOperationEvent.FLOW_OPERATION_END,
649                textContainerManager_flowOperationEndHandler);
650
651            _textContainerManager.addEventListener(
652                FlowOperationEvent.FLOW_OPERATION_COMPLETE,
653                textContainerManager_flowOperationCompleteHandler);
654
655            _textContainerManager.addEventListener(
656                StatusChangeEvent.INLINE_GRAPHIC_STATUS_CHANGE,
657                textContainerManager_inlineGraphicStatusChangeHandler);
658        }
659
660        //--------------------------------------------------------------------------
661        //
662        //  Variables
663        //
664        //--------------------------------------------------------------------------
665
666        /**
667         *  @private
668         *  The composition bounds used when creating the TextLines.
669         */
670        mx_internal var unbounded:Rectangle = new Rectangle(0, 0, NaN, NaN);
671
672        /**
673         *  @private
674         *  The hostFormat object in the _textContainerManager determines the
675         *  default text formatting used by this component, based on its CSS styles.
676         *  This flag is used by updateStylesIfChanged() determine when the object
677         *  must be reinitialized.  It is set in stylesInitialized() and
678         *  styleChanged(), and cleared in commitProperties().  After
679         *  initialization, the hostFormat object should never be null.
680         */
681        private var hostFormatChanged:Boolean;
682
683        /**
684         *  @private
685         *  It is set to NaN by stylesInitialized() and styleChanged(),
686         *  and recreated whenever necessary in calculateFontMetrics().
687         */
688        private var ascent:Number = NaN;
689
690        /**
691         *  @private
692         *  It is set to NaN by stylesInitialized() and styleChanged(),
693         *  and recreated whenever necessary in calculateFontMetrics().
694         */
695        private var descent:Number = NaN;
696
697        /**
698         *  @private
699         *  Source of text: one of "text", "textFlow" or "content".
700         */
701        private var source:String = "text";
702
703        /**
704         *  @private
705         *  Holds the last recorded value of the textFlow generation.  Used to
706         *  determine whether to return immediately from damage event if there
707         *  have been no changes.
708         */
709        private var lastGeneration:uint = 0;    // 0 means not set
710
711        /**
712         *  @private
713         *  The generation of the text flow that last reported its content
714         *  bounds.
715         */
716        private var lastContentBoundsGeneration:int = 0;  // 0 means not set
717
718        /**
719         *  @private
720         *  True if TextOperationEvent.CHANGING and TextOperationEvent.CHANGE
721         *  events should be dispatched.
722         */
723        private var dispatchChangeAndChangingEvents:Boolean = true;
724
725        /**
726         *  @private
727         */
728        private var inMeasureMethod:Boolean = false;
729
730        /**
731         *  @private
732         */
733        private var inUpdateDLMethod:Boolean = false;
734
735        /**
736         *  @private
737         */
738        private var remeasuringText:Boolean = false;
739
740        /**
741         *  @private
742         */
743        mx_internal var preserveSelectionOnSetText:Boolean = false;
744
745        /**
746         *  @private
747         */
748        mx_internal var passwordChar:String = "*";
749
750        /**
751         *  @private
752         */
753        mx_internal var undoManager:IUndoManager;
754
755        /**
756         *  @private
757         */
758        mx_internal var clearUndoOnFocusOut:Boolean = true;
759
760        /**
761         *  @private
762         *  Holds the last recorded value of the module factory used to create the
763         *  font.
764         */
765        mx_internal var embeddedFontContext:IFlexModuleFactory;
766
767        /**
768         *  @private
769         *  The TLF edit manager will batch all inserted text until the next
770         *  enter frame event.  This includes text inserted via the GUI as well
771         *  as api calls to EditManager.insertText().  Set this to false if you
772         *  want every keystroke to be inserted into the text immediately which will
773         *  result in a TextOperationEvent.CHANGE event for each character.  One
774         *  place this is needed is for the type-ahead feature of the editable combo
775         *  box.
776         */
777        mx_internal var batchTextInput:Boolean = true;
778
779        /**
780         *  @private
781         *  True if we've seen a MOUSE_DOWN event and haven't seen the
782         *  corresponding MOUSE_UP event.
783         */
784        private var mouseDown:Boolean = false;
785
786        /**
787         *  @private
788         *  Hold the previous editingMode while using a specific instance manager
789         *  so that the editingMode can be restored when the instance manager is
790         *  released.
791         */
792        private var priorEditingMode:String;
793
794        /**
795         *  @private
796         *  Cache the width constraint as set by the layout in setLayoutBoundsSize()
797         *  so that text reflow can be calculated during a subsequent measure pass.
798         */
799        private var widthConstraint:Number = NaN;
800
801        /**
802         *  @private
803         *  Cache the height constraint as set by the layout in setLayoutBoundsSize()
804         *  so that text reflow can be calculated during a subsequent measure pass.
805         */
806        private var heightConstraint:Number = NaN;
807
808        /**
809         *  @private
810         *  If the selection was via the selectRange() or selectAll() api, remember
811         *  that until the next selection is set, either interactively or via the
812         *  API.
813         */
814        private var hasProgrammaticSelectionRange:Boolean = false;
815
816        /**
817         *  @private
818         *  True if this component sizes itself based on its actual
819         *  contents.
820         */
821        mx_internal var autoSize:Boolean = false;
822
823        /**
824         *  @private
825         */
826        private var lastMeasuredWidth:Number = NaN;
827
828        /**
829         *  @private
830         */
831        private var lastMeasuredHeight:Number = NaN;
832
833        /**
834         *  @private
835         */
836        private var lastUnscaledWidth:Number;
837        /**
838         *  @private
839         */
840        private var lastUnscaledHeight:Number;
841
842        //--------------------------------------------------------------------------
843        //
844        //  Overridden properties: UIComponent
845        //
846        //--------------------------------------------------------------------------
847
848        //----------------------------------
849        //  baselinePosition
850        //----------------------------------
851
852        /**
853         *  @private
854         */
855        override public function get baselinePosition():Number
856        {
857            return getStyle("paddingTop") + ascent;
858        }
859
860        //----------------------------------
861        //  enabled
862        //----------------------------------
863
864        /**
865         *  @private
866         */
867        private var enabledChanged:Boolean = false;
868
869        /**
870         *  @private
871         */
872        override public function set enabled(value:Boolean):void
873        {
874            if (value == super.enabled)
875                return;
876
877            super.enabled = value;
878            enabledChanged = true;
879
880            invalidateProperties();
881            invalidateDisplayList();
882        }
883
884        //----------------------------------
885        // explicitHeight
886        //----------------------------------
887
888        /**
889         *  @private
890         */
891        override public function set explicitHeight(value:Number):void
892        {
893            super.explicitHeight = value;
894
895            heightConstraint = NaN;
896
897            // Because of autoSizing, the size and display might be impacted.
898            invalidateSize();
899            invalidateDisplayList();
900        }
901
902        //----------------------------------
903        // explicitWidth
904        //----------------------------------
905
906        /**
907         *  @private
908         */
909        override public function set explicitWidth(value:Number):void
910        {
911            super.explicitWidth = value;
912
913            widthConstraint = NaN;
914
915            // Because of autoSizing, the size and display might be impacted.
916            invalidateSize();
917            invalidateDisplayList();
918        }
919
920        //----------------------------------
921        // isTruncated
922        //----------------------------------
923
924        /**
925         *  @private
926         */
927        public function get isTruncated():Boolean
928        {
929            // This class does not support truncation
930            return false;
931        }
932
933        //----------------------------------
934        // percentHeight
935        //----------------------------------
936
937        /**
938         *  @private
939         */
940        override public function set percentHeight(value:Number):void
941        {
942            super.percentHeight = value;
943
944            heightConstraint = NaN;
945
946            // If we were autoSizing and now we are not we need to remeasure.
947            invalidateSize();
948            invalidateDisplayList();
949        }
950
951        //----------------------------------
952        // percentWidth
953        //----------------------------------
954
955        /**
956         *  @private
957         */
958        override public function set percentWidth(value:Number):void
959        {
960            super.percentWidth = value;
961
962            widthConstraint = NaN;
963
964            // If we were autoSizing and now we are not we need to remeasure.
965            invalidateSize();
966            invalidateDisplayList();
967        }
968
969        //--------------------------------------------------------------------------
970        //
971        //  Properties: ISystemCursorClient
972        //
973        //--------------------------------------------------------------------------
974
975        /**
976         *  True if the system cursor should always be shown when the mouse
977         *  moves over the component.  If false, the custom cursor will be shown.
978         *
979         *  @langversion 3.0
980         *  @playerversion Flash 10
981         *  @playerversion AIR 1.5
982         *  @productversion Flex 4
983         */
984        public function get showSystemCursor():Boolean
985        {
986            return editable;
987        }
988
989        //--------------------------------------------------------------------------
990        //
991        //  Properties: IViewport
992        //
993        //--------------------------------------------------------------------------
994
995        //----------------------------------
996        //  clipAndEnableScrolling
997        //----------------------------------
998
999        /**
1000         *  @private
1001         */
1002        private var _clipAndEnableScrolling:Boolean = false;
1003
1004        /**
1005         *  @private
1006         */
1007        private var clipAndEnableScrollingChanged:Boolean = false;
1008
1009        /**
1010         *  @copy spark.core.IViewport#clipAndEnableScrolling
1011         *
1012         *  @default false
1013         *
1014         *  @langversion 3.0
1015         *  @playerversion Flash 10
1016         *  @playerversion AIR 1.5
1017         *  @productversion Flex 4
1018         */
1019        public function get clipAndEnableScrolling():Boolean
1020        {
1021            return _clipAndEnableScrolling;
1022        }
1023
1024        /**
1025         *  @private
1026         *  Set to true by a scroller when it installs this as a viewport.
1027         *  Set to false by a scroller when it uninstalls this as a viewport.
1028         */
1029        public function set clipAndEnableScrolling(value:Boolean):void
1030        {
1031            if (value == _clipAndEnableScrolling)
1032                return;
1033
1034            _clipAndEnableScrolling = value;
1035            clipAndEnableScrollingChanged = true;
1036
1037            invalidateProperties();
1038        }
1039
1040        //----------------------------------
1041        //  contentHeight
1042        //----------------------------------
1043
1044        /**
1045         *  @private
1046         */
1047        private var _contentHeight:Number = 0;
1048
1049        [Bindable("propertyChange")]
1050
1051        /**
1052         *  The height of the text.
1053         *
1054         *  <p>Due to the fact that the Text Layout Framework
1055         *  virtualizes TextLines for performance,
1056         *  this height will initially be an estimate
1057         *  if the component cannot display all of the text.
1058         *  If you scroll to the end of the text,
1059         *  all the TextLines will get composed
1060         *  and the <code>contentHeight</code> will be exact.</p>
1061         *
1062         *  <p>To scroll over the text vertically, vary the
1063         *  <code>verticalScrollPosition</code> between 0 and
1064         *  <code>contentHeight - height</code>.</p>
1065         *
1066         *  @langversion 3.0
1067         *  @playerversion Flash 10
1068         *  @playerversion AIR 1.5
1069         *  @productversion Flex 4
1070         */
1071        public function get contentHeight():Number
1072        {
1073            return _contentHeight;
1074        }
1075
1076        //----------------------------------
1077        //  contentWidth
1078        //----------------------------------
1079
1080        /**
1081         *  @private
1082         */
1083        private var _contentWidth:Number = 0;
1084
1085        [Bindable("propertyChange")]
1086
1087        /**
1088         *  The width of the text.
1089         *
1090         *  <p>Due to the fact that the Text Layout Framework
1091         *  virtualizes TextLines for performance,
1092         *  this width will initially be an estimate
1093         *  if the component cannot display all of the text.
1094         *  If you scroll to the end of the text,
1095         *  all the TextLines will get composed
1096         *  and the <code>contentWidth</code> will be exact.</p>
1097         *
1098         *  <p>To scroll over the text horizontally, vary the
1099         *  <code>horizontalScrollPosition</code> between 0 and
1100         *  <code>contentWidth - width</code>.</p>
1101         *
1102         *  @langversion 3.0
1103         *  @playerversion Flash 10
1104         *  @playerversion AIR 1.5
1105         *  @productversion Flex 4
1106         */
1107        public function get contentWidth():Number
1108        {
1109            return _contentWidth;
1110        }
1111
1112        //----------------------------------
1113        //  horizontalScrollPosition
1114        //----------------------------------
1115
1116        /**
1117         *  @private
1118         */
1119        private var _horizontalScrollPosition:Number = 0;
1120
1121        /**
1122         *  @private
1123         */
1124        private var horizontalScrollPositionChanged:Boolean = false;
1125
1126        [Bindable("propertyChange")]
1127        [Inspectable(defaultValue="0", minValue="0.0")]
1128
1129        /**
1130         *  The number of pixels by which the text is scrolled horizontally.
1131         *
1132         *  <p>To scroll over the text horizontally, vary the
1133         *  <code>horizontalScrollPosition</code> between 0 and
1134         *  <code>contentWidth - width</code>.</p>
1135         *
1136         *  @default 0
1137         *
1138         *  @langversion 3.0
1139         *  @playerversion Flash 10
1140         *  @playerversion AIR 1.5
1141         *  @productversion Flex 4
1142         */
1143        public function get horizontalScrollPosition():Number
1144        {
1145            return _horizontalScrollPosition;
1146        }
1147
1148        /**
1149         *  @private
1150         */
1151        public function set horizontalScrollPosition(value:Number):void
1152        {
1153            // Convert NaN to 0 to keep TCM happy.
1154            if (isNaN(value))
1155                value = 0;
1156
1157            if (value == _horizontalScrollPosition)
1158                return;
1159
1160            _horizontalScrollPosition = value;
1161            horizontalScrollPositionChanged = true;
1162
1163            invalidateProperties();
1164
1165            // Note:  TLF takes care of updating the container when the scroll
1166            // position is set so there is no need for us to invalidate the
1167            // display list.
1168        }
1169
1170        //----------------------------------
1171        //  verticalScrollPosition
1172        //----------------------------------
1173
1174        /**
1175         *  @private
1176         */
1177        private var _verticalScrollPosition:Number = 0;
1178
1179        /**
1180         *  @private
1181         */
1182        private var verticalScrollPositionChanged:Boolean = false;
1183
1184        [Bindable("propertyChange")]
1185        [Inspectable(defaultValue="0", minValue="0.0")]
1186
1187        /**
1188         *  The number of pixels by which the text is scrolled vertically.
1189         *
1190         *  <p>To scroll over the text vertically, vary the
1191         *  <code>verticalScrollPosition</code> between 0 and
1192         *  <code>contentHeight - height</code>.</p>
1193         *
1194         *  @default 0
1195         *
1196         *  @langversion 3.0
1197         *  @playerversion Flash 10
1198         *  @playerversion AIR 1.5
1199         *  @productversion Flex 4
1200         */
1201        public function get verticalScrollPosition():Number
1202        {
1203            return _verticalScrollPosition;
1204        }
1205
1206        /**
1207         *  @private
1208         */
1209        public function set verticalScrollPosition(value:Number):void
1210        {
1211            // Convert NaN to 0 to keep TCM happy.
1212            if (isNaN(value))
1213                value = 0;
1214
1215            if (value == _verticalScrollPosition)
1216                return;
1217
1218            _verticalScrollPosition = value;
1219            verticalScrollPositionChanged = true;
1220
1221            invalidateProperties();
1222
1223            // Note:  TLF takes care of updating the container when the scroll
1224            // position is set so there is no need for us to invalidate the
1225            // display list.
1226        }
1227
1228        //--------------------------------------------------------------------------
1229        //
1230        //  Properties
1231        //
1232        //--------------------------------------------------------------------------
1233
1234        //----------------------------------
1235        //  content
1236        //----------------------------------
1237
1238        /**
1239         *  @private
1240         */
1241        private var _content:Object;
1242
1243        /**
1244         *  @private
1245         */
1246        private var contentChanged:Boolean = false;
1247
1248        /**
1249         *  @private
1250         *  This metadata tells the MXML compiler to disable some of its default
1251         *  interpretation of the value specified for the 'content' property.
1252         *  Normally, for properties of type Object, it assumes that things
1253         *  looking like numbers are numbers and things looking like arrays
1254         *  are arrays. But <content>1</content> should generate code to set the
1255         *  content to  the String "1", not the int 1, and <content>[1]</content>
1256         *  should set it to the String "[1]", not the Array [ 1 ].
1257         *  However, {...} continues to be interpreted as a databinding
1258         *  expression, and @Resource(...), @Embed(...), etc.
1259         *  as compiler directives.
1260         *  Similar metadata on TLF classes causes the same rules to apply
1261         *  within <p>, <span>, etc.
1262         */
1263        [RichTextContent]
1264
1265        /**
1266         *  This property is intended for use in MXML at compile time;
1267         *  to get or set rich text content at runtime,
1268         *  please use the <code>textFlow</code> property instead.
1269         *
1270         *  <p>The <code>content</code> property is the default property
1271         *  for RichEditableText, so that you can write MXML such as
1272         *  <pre>
1273         *  &lt;s:RichEditableText&gt;Hello &lt;s:span fontWeight="bold"/&gt;World&lt;/s:span&gt;&lt;/s:RichEditableText&gt;
1274         *  </pre>
1275         *  and have the String and SpanElement that you specify
1276         *  as the content be used to create a TextFlow.</p>
1277         *
1278         *  <p>This property is typed as Object because you can set it to
1279         *  to a String, a FlowElement, or an Array of Strings and FlowElements.
1280         *  In the example above, you are specifying the content
1281         *  to be a 2-element Array whose first element is the String
1282         *  "Hello" and whose second element is a SpanElement with the text
1283         *  "World" in boldface.</p>
1284         *
1285         *  <p>No matter how you specify the content, it gets converted
1286         *  into a TextFlow, and when you get this property, you will get
1287         *  the resulting TextFlow.</p>
1288         *
1289         *  <p>Adobe recommends using <code>textFlow</code> property
1290         *  to get and set rich text content at runtime,
1291         *  because it is strongly typed as a TextFlow
1292         *  rather than as an Object.
1293         *  A TextFlow is the canonical representation
1294         *  for rich text content in the Text Layout Framework.</p>
1295         *
1296         *  @langversion 3.0
1297         *  @playerversion Flash 10
1298         *  @playerversion AIR 1.5
1299         *  @productversion Flex 4
1300         */
1301        public function get content():Object
1302        {
1303            return textFlow;
1304        }
1305
1306        /**
1307         *  @private
1308         */
1309        public function set content(value:Object):void
1310        {
1311            // Treat setting the 'content' to null
1312            // as if 'text' were being set to the empty String
1313            // (which is the default state).
1314            if (value == null)
1315            {
1316                text = "";
1317                return;
1318            }
1319
1320            if (value == _content)
1321                return;
1322
1323            _content = value;
1324            contentChanged = true;
1325            source = "content";
1326
1327            // Of 'text', 'textFlow', and 'content', the last one set wins.
1328            textChanged = false;
1329            textFlowChanged = false;
1330
1331            // The other two are now invalid and must be recalculated when needed.
1332            _text = null;
1333            _textFlow = null;
1334
1335            invalidateProperties();
1336            invalidateSize();
1337            invalidateDisplayList();
1338
1339            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
1340        }
1341
1342        //----------------------------------
1343        //  displayAsPassword
1344        //----------------------------------
1345
1346        /**
1347         *  @private
1348         */
1349        private var _displayAsPassword:Boolean = false;
1350
1351        /**
1352         *  @private
1353         */
1354        private var displayAsPasswordChanged:Boolean = false;
1355
1356        [Inspectable(category="General", defaultValue="false")]
1357
1358        /**
1359         *  @copy flash.text.TextField#displayAsPassword
1360         *
1361         *  @langversion 3.0
1362         *  @playerversion Flash 10
1363         *  @playerversion AIR 1.5
1364         *  @productversion Flex 4
1365         */
1366        public function get displayAsPassword():Boolean
1367        {
1368            return _displayAsPassword;
1369        }
1370
1371        /**
1372         *  @private
1373         */
1374        public function set displayAsPassword(value:Boolean):void
1375        {
1376            if (value == _displayAsPassword)
1377                return;
1378
1379            _displayAsPassword = value;
1380            displayAsPasswordChanged = true;
1381
1382            invalidateProperties();
1383            invalidateSize();
1384            invalidateDisplayList();
1385        }
1386
1387        //----------------------------------
1388        //  editable
1389        //----------------------------------
1390
1391        /**
1392         *  @private
1393         */
1394        private var _editable:Boolean = true;
1395
1396        /**
1397         *  @private
1398         */
1399        private var editableChanged:Boolean = false;
1400
1401        [Inspectable(category="General", defaultValue="true")]
1402
1403        /**
1404         *  A flag indicating whether the user is allowed
1405         *  to edit the text in this control.
1406         *
1407         *  <p>If <code>true</code>, the mouse cursor will change to an i-beam
1408         *  when over the bounds of this control.
1409         *  If <code>false</code>, the mouse cursor will remain an arrow.</p>
1410         *
1411         *  <p>If this property is <code>true</code>,
1412         *  the <code>selectable</code> property is ignored.</p>
1413         *
1414         *  @default true
1415         *
1416         *  @see spark.components.RichEditableText#selectable
1417         *
1418         *  @langversion 3.0
1419         *  @playerversion Flash 10
1420         *  @playerversion AIR 1.5
1421         *  @productversion Flex 4
1422         */
1423        public function get editable():Boolean
1424        {
1425            return _editable;
1426        }
1427
1428        /**
1429         *  @private
1430         */
1431        public function set editable(value:Boolean):void
1432        {
1433            if (value == _editable)
1434                return;
1435
1436            _editable = value;
1437            editableChanged = true;
1438
1439            invalidateProperties();
1440            invalidateDisplayList();
1441        }
1442
1443        //----------------------------------
1444        //  editingMode
1445        //----------------------------------
1446
1447        [Inspectable(category="General", defaultValue="readWrite", enumeration="readOnly,readWrite,readSelect")]
1448
1449        /**
1450         *  @private
1451         *  The editingMode of this component's TextContainerManager.
1452         *  Note that this is not a public property
1453         *  and does not use the invalidation mechanism.
1454         */
1455        private function get editingMode():String
1456        {
1457            // Note: this could be called before all properties are committed.
1458
1459            if (enabledChanged || editableChanged || selectableChanged)
1460            {
1461                updateEditingMode();
1462
1463                enabledChanged = false;
1464                editableChanged = false;
1465                selectableChanged = false;
1466            }
1467
1468            return _textContainerManager.editingMode;
1469        }
1470
1471        /**
1472         *  @private
1473         */
1474        private function set editingMode(value:String):void
1475        {
1476            var lastEditingMode:String = _textContainerManager.editingMode;
1477
1478            if (lastEditingMode == value)
1479                return;
1480
1481            _textContainerManager.editingMode = value;
1482
1483            // Make sure the selection manager selection is in sync with the
1484            // current selection.
1485            if (value != EditingMode.READ_ONLY &&
1486                _selectionAnchorPosition != -1 && _selectionActivePosition != -1)
1487            {
1488                var selectionManager:ISelectionManager =
1489                    _textContainerManager.beginInteraction();
1490
1491                selectionManager.selectRange(
1492                    _selectionAnchorPosition, _selectionActivePosition);
1493
1494                _textContainerManager.endInteraction();
1495            }
1496        }
1497
1498        //----------------------------------
1499        //  enableIME
1500        //----------------------------------
1501
1502        /**
1503         *  A flag that indicates whether the IME should
1504         *  be enabled when the component receives focus.
1505         *
1506         *  @returns true if the component is editable and it is not displaying a password.
1507         *
1508         *  @langversion 3.0
1509         *  @playerversion Flash 10
1510         *  @playerversion AIR 1.5
1511         *  @productversion Flex 4
1512         */
1513        public function get enableIME():Boolean
1514        {
1515            return editable && !displayAsPassword;
1516        }
1517
1518        //----------------------------------
1519        //  heightInLines
1520        //----------------------------------
1521
1522        /**
1523         *  @private
1524         */
1525        private var _heightInLines:Number = NaN;
1526
1527        /**
1528         *  @private
1529         */
1530        private var heightInLinesChanged:Boolean = false;
1531
1532        [Inspectable(category="General", minValue="0.0")]
1533
1534        /**
1535         *  The default height of the control, measured in lines.
1536         *
1537         *  <p>The control's formatting styles, such as <code>fontSize</code>
1538         *  and <code>lineHeight</code>, are used to calculate the line height
1539         *  in pixels.</p>
1540         *
1541         *  <p>You would, for example, set this property to 5 if you want
1542         *  the height of the RichEditableText to be sufficient
1543         *  to display five lines of text.</p>
1544         *
1545         *  <p>If this property is <code>NaN</code> (the default),
1546         *  then the component's default height will be determined
1547         *  from the text to be displayed.</p>
1548         *
1549         *  <p>This property will be ignored if you specify an explicit height,
1550         *  a percent height, or both <code>top</code> and <code>bottom</code>
1551         *  constraints.</p>
1552         *
1553         *  <p>This property will also be ignored if the <code>typicalText</code>
1554         *  property is specified.</p>
1555         *
1556         *  <p>RichEditableText's <code>measure()</code> method uses
1557         *  <code>widthInChars</code> and <code>heightInLines</code>
1558         *  to determine the <code>measuredWidth</code>
1559         *  and <code>measuredHeight</code>.
1560         *  These are similar to the <code>cols</code> and <code>rows</code>
1561         *  of an HTML TextArea.</p>
1562         *
1563         *  <p>Since both <code>widthInChars</code> and <code>heightInLines</code>
1564         *  default to <code>NaN</code>, RichTextEditable "autosizes" by default:
1565         *  it starts out very small if it has no text, grows in width as you
1566         *  type, and grows in height when you press Enter to start a new line.</p>
1567         *
1568         *  @default NaN
1569         *
1570         *  @see spark.components.RichEditableText#widthInChars
1571         *
1572         *  @langversion 3.0
1573         *  @playerversion Flash 10
1574         *  @playerversion AIR 1.5
1575         *  @productversion Flex 4
1576         */
1577        public function get heightInLines():Number
1578        {
1579            return _heightInLines;
1580        }
1581
1582        /**
1583         *  @private
1584         */
1585        public function set heightInLines(value:Number):void
1586        {
1587            if (value == _heightInLines)
1588                return;
1589
1590            _heightInLines = value;
1591            heightInLinesChanged = true;
1592
1593            heightConstraint = NaN;
1594
1595            invalidateProperties();
1596            invalidateSize();
1597            invalidateDisplayList();
1598        }
1599
1600        //----------------------------------
1601        //  imeMode
1602        //----------------------------------
1603
1604        /**
1605         *  @private
1606         */
1607        private var _imeMode:String = null;
1608
1609        /**
1610         *  Specifies the IME (input method editor) mode.
1611         *  The IME enables users to enter text in Chinese, Japanese, and Korean.
1612         *  Flex sets the specified IME mode when the control gets the focus,
1613         *  and sets it back to the previous value when the control loses the focus.
1614         *
1615         *  <p>The flash.system.IMEConversionMode class defines constants for the
1616         *  valid values for this property.
1617         *  You can also specify <code>null</code> to specify no IME.</p>
1618         *
1619         *  @default null
1620         *
1621         *  @see flash.system.IMEConversionMode
1622         *
1623         *  @langversion 3.0
1624         *  @playerversion Flash 10
1625         *  @playerversion AIR 1.5
1626         *  @productversion Flex 4
1627         */
1628        public function get imeMode():String
1629        {
1630            return _imeMode;
1631        }
1632
1633        /**
1634         *  @private
1635         */
1636        public function set imeMode(value:String):void
1637        {
1638            _imeMode = value;
1639        }
1640
1641        //----------------------------------
1642        //  lineBreak
1643        //----------------------------------
1644
1645        [Inspectable(environment="none")]
1646
1647        /**
1648         *  @private
1649         *
1650         *  This property is only defined to implement the IEditableText
1651         *  interface. The lineBreak style should be used instead of this
1652         *  property.
1653         */
1654        public function get lineBreak():String
1655        {
1656            return getStyle("lineBreak");
1657        }
1658
1659        /**
1660         *  @private
1661         */
1662        public function set lineBreak(value:String):void
1663        {
1664            setStyle("lineBreak", value);
1665        }
1666
1667        //----------------------------------
1668        //  maxChars
1669        //----------------------------------
1670
1671        /**
1672         *  @private
1673         */
1674        private var _maxChars:int = 0;
1675
1676        [Inspectable(category="General", defaultValue="0")]
1677
1678        /**
1679         *  @copy flash.text.TextField#maxChars
1680         *
1681         *  @default 0
1682         *
1683         *  @langversion 3.0
1684         *  @playerversion Flash 10
1685         *  @playerversion AIR 1.5
1686         *  @productversion Flex 4
1687         */
1688        public function get maxChars():int
1689        {
1690            return _maxChars;
1691        }
1692
1693        /**
1694         *  @private
1695         */
1696        public function set maxChars(value:int):void
1697        {
1698            _maxChars = value;
1699        }
1700
1701        //----------------------------------
1702        //  multiline
1703        //----------------------------------
1704
1705        /**
1706         *  @private
1707         */
1708        private var _multiline:Boolean = true;
1709
1710        [Inspectable(category="General", defaultValue="true")]
1711
1712        /**
1713         *  Determines whether the user can enter multiline text.
1714         *
1715         *  <p>If <code>true</code>, the Enter key starts a new paragraph.
1716         *  If <code>false</code>, the Enter key doesn't affect the text
1717         *  but causes the RichEditableText to dispatch an <code>"enter"</code>
1718         *  event.  If you paste text into the RichEditableText with a multiline
1719         *  value of <code>false</code>, newlines are stripped out of the text. </p>
1720         *
1721         *  @default true
1722         *
1723         *  @langversion 3.0
1724         *  @playerversion Flash 10
1725         *  @playerversion AIR 1.5
1726         *  @productversion Flex 4
1727         */
1728        public function get multiline():Boolean
1729        {
1730            return _multiline;
1731        }
1732
1733        /**
1734         *  @private
1735         */
1736        public function set multiline(value:Boolean):void
1737        {
1738            _multiline = value;
1739        }
1740
1741        //----------------------------------
1742        //  restrict
1743        //----------------------------------
1744
1745        /**
1746         *  @private
1747         */
1748        private var _restrict:String = null;
1749
1750        [Inspectable(category="General", defaultValue="null")]
1751
1752        /**
1753         *  @copy flash.text.TextField#restrict
1754         *
1755         *  @default null
1756         *
1757         *  @langversion 3.0
1758         *  @playerversion Flash 10
1759         *  @playerversion AIR 1.5
1760         *  @productversion Flex 4
1761         */
1762        public function get restrict():String
1763        {
1764            return _restrict;
1765        }
1766
1767        /**
1768         *  @private
1769         */
1770        public function set restrict(value:String):void
1771        {
1772            _restrict = value;
1773        }
1774
1775        //----------------------------------
1776        //  selectable
1777        //----------------------------------
1778
1779        /**
1780         *  @private
1781         */
1782        private var _selectable:Boolean = true;
1783
1784        /**
1785         *  @private
1786         */
1787        private var selectableChanged:Boolean = false;
1788
1789        [Inspectable(category="General", defaultValue="true")]
1790
1791        /**
1792         *  A flag indicating whether the content is selectable
1793         *  with the mouse, or with the keyboard when the control
1794         *  has the keyboard focus.
1795         *
1796         *  <p>Making the text selectable lets you copy text from the control.</p>
1797         *
1798         *  <p>This property is ignored if the <code>editable</code>
1799         *  property is <code>true</code>.</p>
1800         *
1801         *  @default true
1802         *
1803         *  @langversion 3.0
1804         *  @playerversion Flash 10
1805         *  @playerversion AIR 1.5
1806         *  @productversion Flex 4
1807         */
1808        public function get selectable():Boolean
1809        {
1810            return _selectable;
1811        }
1812
1813        /**
1814         *  @private
1815         */
1816        public function set selectable(value:Boolean):void
1817        {
1818            if (value == _selectable)
1819                return;
1820
1821            _selectable = value;
1822            selectableChanged = true;
1823
1824            invalidateProperties();
1825            invalidateDisplayList();
1826        }
1827
1828        //----------------------------------
1829        //  selectionActivePosition
1830        //----------------------------------
1831
1832        /**
1833         *  @private
1834         */
1835        private var _selectionActivePosition:int = -1;
1836
1837        [Bindable("selectionChange")]
1838        [Inspectable(category="General", defaultValue="-1")]
1839
1840        /**
1841         *  A character position, relative to the beginning of the
1842         *  <code>text</code> String, specifying the end of the selection
1843         *  that moves when the selection is extended with the arrow keys.
1844         *
1845         *  <p>The active position may be either the start
1846         *  or the end of the selection.</p>
1847         *
1848         *  <p>For example, if you drag-select from position 12 to position 8,
1849         *  then <code>selectionAnchorPosition</code> will be 12
1850         *  and <code>selectionActivePosition</code> will be 8,
1851         *  and when you press Left-Arrow <code>selectionActivePosition</code>
1852         *  will become 7.</p>
1853         *
1854         *  <p>A value of -1 indicates "not set".</p>
1855         *
1856         *  @default -1
1857         *
1858         *  @see spark.components.RichEditableText#selectionAnchorPosition
1859         *
1860         *  @langversion 3.0
1861         *  @playerversion Flash 10
1862         *  @playerversion AIR 1.5
1863         *  @productversion Flex 4
1864         */
1865        public function get selectionActivePosition():int
1866        {
1867            return _selectionActivePosition;
1868        }
1869
1870        //----------------------------------
1871        //  selectionAnchorPosition
1872        //----------------------------------
1873
1874        /**
1875         *  @private
1876         */
1877        private var _selectionAnchorPosition:int = -1;
1878
1879        [Bindable("selectionChange")]
1880        [Inspectable(category="General", defaultValue="-1")]
1881
1882        /**
1883         *  A character position, relative to the beginning of the
1884         *  <code>text</code> String, specifying the end of the selection
1885         *  that stays fixed when the selection is extended with the arrow keys.
1886         *
1887         *  <p>The anchor position may be either the start
1888         *  or the end of the selection.</p>
1889         *
1890         *  <p>For example, if you drag-select from position 12 to position 8,
1891         *  then <code>selectionAnchorPosition</code> will be 12
1892         *  and <code>selectionActivePosition</code> will be 8,
1893         *  and when you press Left-Arrow <code>selectionActivePosition</code>
1894         *  will become 7.</p>
1895         *
1896         *  <p>A value of -1 indicates "not set".</p>
1897         *
1898         *  @default -1
1899         *
1900         *  @see spark.components.RichEditableText#selectionActivePosition
1901         *
1902         *  @langversion 3.0
1903         *  @playerversion Flash 10
1904         *  @playerversion AIR 1.5
1905         *  @productversion Flex 4
1906         */
1907        public function get selectionAnchorPosition():int
1908        {
1909            return _selectionAnchorPosition;
1910        }
1911
1912        //----------------------------------
1913        //  selectionHighlighting
1914        //----------------------------------
1915
1916        /**
1917         *  @private
1918         */
1919        private var _selectionHighlighting:String =
1920            TextSelectionHighlighting.WHEN_FOCUSED;
1921
1922        /**
1923         *  @private
1924         *  To indicate either selection highlighting or selection styles have
1925         *  changed.
1926         */
1927        private var selectionFormatsChanged:Boolean = false;
1928
1929        [Inspectable(category="General", enumeration="always,whenActive,whenFocused", defaultValue="whenFocused")]
1930
1931        /**
1932         *  Determines when the text selection is highlighted.
1933         *
1934         *  <p>The allowed values are specified by the
1935         *  spark.components.TextSelectionHighlighting class.
1936         *  Possible values are <code>TextSelectionHighlighting.WHEN_FOCUSED</code>,
1937         *  <code>TextSelectionHighlighting.WHEN_ACTIVE</code>,
1938         *  and <code>TextSelectionHighlighting.ALWAYS</code>.</p>
1939         *
1940         *  <p><code>WHEN_FOCUSED</code> shows the text selection
1941         *  only when the component has keyboard focus.</p>
1942         *
1943         *  <p><code>WHEN_ACTIVE</code> shows the text selection whenever
1944         *  the component's window is active, even if the component
1945         *  doesn't have the keyboard focus.</p>
1946         *
1947         *  <p><code>ALWAYS</code> shows the text selection,
1948         *  even if the component doesn't have the keyboard focus
1949         *  or if the component's window isn't the active window.</p>
1950         *
1951         *  @default TextSelectionHighlighting.WHEN_FOCUSED
1952         *
1953         *  @see spark.components.TextSelectionHighlighting
1954         *
1955         *  @langversion 3.0
1956         *  @playerversion Flash 10
1957         *  @playerversion AIR 1.5
1958         *  @productversion Flex 4
1959         */
1960        public function get selectionHighlighting():String
1961        {
1962            return _selectionHighlighting;
1963        }
1964
1965        /**
1966         *  @private
1967         */
1968        public function set selectionHighlighting(value:String):void
1969        {
1970            if (value == _selectionHighlighting)
1971                return;
1972
1973            _selectionHighlighting = value;
1974            selectionFormatsChanged = true;
1975
1976            invalidateProperties();
1977            invalidateDisplayList();
1978        }
1979
1980        //----------------------------------
1981        //  text
1982        //----------------------------------
1983
1984        /**
1985         *  @private
1986         */
1987        private var _text:String = "";
1988
1989        /**
1990         *  @private
1991         */
1992        private var textChanged:Boolean = false;
1993
1994        [Bindable("change")]
1995        [Inspectable(category="General", defaultValue="")]
1996
1997        /**
1998         *  The text String displayed by this component.
1999         *
2000         *  <p>Setting this property affects the <code>textFlow</code> property
2001         *  and vice versa.</p>
2002         *
2003         *  <p>If you set the <code>text</code> to a String such as
2004         *  <code>"Hello World"</code> and get the <code>textFlow</code>,
2005         *  it will be a TextFlow containing a single ParagraphElement
2006         *  with a single SpanElement.</p>
2007         *
2008         *  <p>If you set the <code>text</code> to null, it will be
2009         *  set to the default value which is an empty string.</p>
2010         *
2011         *  <p>If the text contains explicit line breaks --
2012         *  CR ("\r"), LF ("\n"), or CR+LF ("\r\n") --
2013         *  then the content will be set to a TextFlow
2014         *  which contains multiple paragraphs, each with one span.</p>
2015         *
2016         *  <p>If you set the <code>textFlow</code> and get the <code>text</code>,
2017         *  the text in each paragraph will be separated by a single
2018         *  LF ("\n").</p>
2019         *
2020         *  <p>Setting this property also affects the properties
2021         *  specifying the control's scroll position and the text selection.
2022         *  It resets the <code>horizontalScrollPosition</code>
2023         *  and <code>verticalScrollPosition</code> to 0.
2024         *  Starting with Flex 4.6, the <code>selectionAnchorPosition</code> and
2025         *  <code>selectionActivePosition</code> are preserved.
2026         *  Previously, the <code>selectionAnchorPosition</code>
2027         *  and <code>selectionActivePosition</code> were set
2028         *  to -1 to clear the selection.</p>
2029         *
2030         *  @default ""
2031         *
2032         *  @see spark.components.RichEditableText#textFlow
2033         *  @see spark.components.RichEditableText#horizontalScrollPosition
2034         *  @see spark.components.RichEditableText#verticalScrollPosition
2035         *  @see spark.components.RichEditableText#selectionAnchorPosition
2036         *  @see spark.components.RichEditableText#selectionActivePosition
2037         *
2038         *  @langversion 3.0
2039         *  @playerversion Flash 10
2040         *  @playerversion AIR 1.5
2041         *  @productversion Flex 4
2042         */
2043        public function get text():String
2044        {
2045            // Note: if displayAsPassword, _text will contain the actual text and the text flow will
2046            // contain the same number of passwordChars.
2047
2048            // Go to the source if there isn't a pending change.  getText has its own buffering and
2049            // only extracts the text from the TextFlow when it is damaged.
2050            if (_textContainerManager && !textChanged && !textFlowChanged && !contentChanged && !displayAsPassword)
2051                return _textContainerManager.getText("\n");
2052
2053            // Extracting the plaintext from a TextFlow is somewhat expensive,
2054            // as it involves iterating over the leaf FlowElements in the TextFlow.
2055            // Therefore we do this extraction only when necessary, namely when
2056            // you first set the 'content' or the 'textFlow'
2057            // (or mutate the TextFlow), and then get the 'text'.
2058            if (_text == null)
2059            {
2060                // If 'content' was last set,
2061                // we have to first turn that into a TextFlow.
2062                if (_content != null)
2063                    _textFlow = createTextFlowFromContent(_content);
2064
2065                // Once we have a TextFlow, we can export its plain text.
2066                _text = staticPlainTextExporter.export(
2067                    _textFlow, ConversionType.STRING_TYPE) as String;
2068            }
2069
2070            return _text;
2071        }
2072
2073        /**
2074         *  @private
2075         *  This will create a TextFlow with a single paragraph with a single span
2076         *  with exactly the text specified.  If there is whitespace and line
2077         *  breaks in the text, they will remain, regardless of the settings of
2078         *  the lineBreak and whiteSpaceCollapse styles.
2079         */
2080        public function set text(value:String):void
2081        {
2082            // Treat setting the 'text' to null
2083            // as if it were set to the empty String
2084            // (which is the default state).
2085            if (value == null)
2086                value = "";
2087
2088            // If value is the same as _text, make sure if was not produced from
2089            // setting 'textFlow' or 'content'.  For example, if you set a TextFlow
2090            // corresponding to "Hello <span color="OxFF0000">World</span>"
2091            // and then get the 'text', it will be the String "Hello World"
2092            // But if you then set the 'text' to "Hello World"
2093            // this represents a change: the "World" should no longer be red.
2094            //
2095            // Note: this is needed to stop two-binding from recursing.
2096            if (source == "text" && text == value)
2097                return;
2098
2099            _text = value;
2100            textChanged = true;
2101            source = "text";
2102
2103            // Of 'text', 'textFlow', and 'content', the last one set wins.
2104            textFlowChanged = false;
2105            contentChanged = false;
2106
2107            // The other two are now invalid and must be recalculated when needed.
2108            _textFlow = null;
2109            _content = null;
2110
2111            invalidateProperties();
2112            invalidateSize();
2113            invalidateDisplayList();
2114
2115            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
2116        }
2117
2118        //----------------------------------
2119        //  textContainerManager
2120        //----------------------------------
2121
2122        /**
2123         *  @private
2124         */
2125        private var _textContainerManager:RichEditableTextContainerManager;
2126
2127        /**
2128         *  @private
2129         *  The TLF TextContainerManager instance that displays,
2130         *  scrolls, and edits the text in this component.
2131         */
2132        mx_internal function get textContainerManager():TextContainerManager
2133        {
2134            return _textContainerManager;
2135        }
2136
2137        //----------------------------------
2138        //  textFlow
2139        //----------------------------------
2140
2141        /**
2142         *  @private
2143         *  Storage for the textFlow property.
2144         */
2145        private var _textFlow:TextFlow;
2146
2147        /**
2148         *  @private
2149         */
2150        private var textFlowChanged:Boolean = false;
2151
2152        /**
2153         *  The TextFlow representing the rich text displayed by this component.
2154         *
2155         *  <p>A TextFlow is the most important class
2156         *  in the Text Layout Framework (TLF).
2157         *  It is the root of a tree of FlowElements
2158         *  representing rich text content.</p>
2159         *
2160         *  <p>You normally create a TextFlow from TLF markup
2161         *  using the <code>TextFlowUtil.importFromString()</code>
2162         *  or <code>TextFlowUtil.importFromXML()</code> methods.
2163         *  Alternately, you can use TLF's TextConverter class
2164         *  (which can import a subset of HTML) or build a TextFlow
2165         *  using methods like <code>addChild()</code> on TextFlow.</p>
2166         *
2167         *  <p>Setting this property affects the <code>text</code> property
2168         *  and vice versa.</p>
2169         *
2170         *  <p>If you set the <code>textFlow</code> and get the <code>text</code>,
2171         *  the text in each paragraph will be separated by a single
2172         *  LF ("\n").</p>
2173         *
2174         *  <p>If you set the <code>text</code> to a String such as
2175         *  <code>"Hello World"</code> and get the <code>textFlow</code>,
2176         *  it will be a TextFlow containing a single ParagraphElement
2177         *  with a single SpanElement.</p>
2178         *
2179         *  <p>If the text contains explicit line breaks --
2180         *  CR ("\r"), LF ("\n"), or CR+LF ("\r\n") --
2181         *  then the content will be set to a TextFlow
2182         *  which contains multiple paragraphs, each with one span.</p>
2183         *
2184         *  <p>Setting this property also affects the properties
2185         *  specifying the control's scroll position and the text selection.
2186         *  It resets the <code>horizontalScrollPosition</code>
2187         *  and <code>verticalScrollPosition</code> to 0,
2188         *  and it sets the <code>selectionAnchorPosition</code>
2189         *  and <code>selectionActivePosition</code>
2190         *  to -1 to clear the selection.</p>
2191         *
2192         *  <p>To turn a TextFlow object into TLF markup,
2193         *  use the <code>TextFlowUtil.export()</code> markup.</p>
2194         *
2195         *  <p>A single TextFlow cannot be shared by multiple instances
2196         *  of RichEditableText.
2197         *  To display the same text in a second instance, you must create
2198         *  a second TextFlow, either by using <code>TextFlowUtil.export()</code>
2199         *  and <code>TextFlowUtil.importFromXML()</code> or by using
2200         *  the <code>deepCopy()</code> method on TextFlow.</p>
2201         *
2202         *  @see spark.utils.TextFlowUtil#importFromString()
2203         *  @see spark.utils.TextFlowUtil#importFromXML()
2204         *  @see spark.components.RichEditableText#text
2205         *
2206         *  @langversion 3.0
2207         *  @playerversion Flash 10
2208         *  @playerversion AIR 2.5
2209         *  @productversion Flex 4.5
2210         */
2211        public function get textFlow():TextFlow
2212        {
2213            // Note: this could be called before all properties are committed.
2214
2215            // We might not have a valid _textFlow for two reasons:
2216            // either because the 'text' was set (which is the state
2217            // after construction) or because the 'content' was set.
2218            if (!_textFlow)
2219            {
2220                if (_content != null)
2221                {
2222                    _textFlow = createTextFlowFromContent(_content);
2223                    _content = null;
2224                }
2225                else
2226                {
2227                    _textFlow = staticPlainTextImporter.importToFlow(_text);
2228                }
2229                textFlowChanged = true;
2230            }
2231
2232            // Make sure the interactionManager and controller are added to this textFlow.
2233            if (textChanged || contentChanged || textFlowChanged)
2234            {
2235                _textContainerManager.setTextFlow(_textFlow);
2236                textChanged = contentChanged = textFlowChanged = false;
2237            }
2238
2239            // If not read-only, make sure the textFlow has a composer in
2240            // place so that it can be modified by the caller if desired.
2241            if (editingMode != EditingMode.READ_ONLY)
2242            {
2243                _textContainerManager.beginInteraction();
2244                _textContainerManager.endInteraction();
2245            }
2246
2247            return _textFlow;
2248        }
2249
2250        /**
2251         *  @private
2252         */
2253        public function set textFlow(value:TextFlow):void
2254        {
2255            // Treat setting the 'textFlow' to null
2256            // as if 'text' were being set to the empty String
2257            // (which is the default state).
2258            if (value == null)
2259            {
2260                text = "";
2261                return;
2262            }
2263
2264            if (value == _textFlow)
2265                return;
2266
2267            _textFlow = value;
2268            textFlowChanged = true;
2269            source = "textFlow";
2270
2271            // Of 'text', 'textFlow', and 'content', the last one set wins.
2272            textChanged = false;
2273            contentChanged = false;
2274
2275            // The other two are now invalid and must be recalculated when needed.
2276            _text = null
2277            _content = null;
2278
2279            invalidateProperties();
2280            invalidateSize();
2281            invalidateDisplayList();
2282
2283            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
2284        }
2285
2286        //----------------------------------
2287        //  typicalText
2288        //----------------------------------
2289
2290        /**
2291         *  @private
2292         */
2293        private var typicalTextChanged:Boolean;
2294
2295        /**
2296         *  @private
2297         */
2298        private var _typicalText:String;
2299
2300        /**
2301         *  @private
2302         *  Used when _typicalText is multiline
2303         */
2304        private var _typicalTextFlow:TextFlow;
2305
2306        [Inspectable(category="General", defaultValue="null")]
2307
2308        /**
2309         *  Text that is used to determine
2310         *  the default width and height of the control,
2311         *  without actually being displayed.
2312         *
2313         *  <p>This property will be ignored if you specify an explicit width,
2314         *  a percent width, or both <code>left</code> and <code>right</code>
2315         *  constraints.</p>
2316         *
2317         *  <p>Use of this property causes the <code>widthInChars</code>
2318         *  and <code>heightInLines</code> properties to be ignored. </p>
2319         *
2320         *  @default null
2321         *
2322         *  @see spark.primitives.heightInLines
2323         *  @see spark.primitives.widthInChars
2324         *
2325         *  @langversion 3.0
2326         *  @playerversion Flash 10.2
2327         *  @playerversion AIR 2.0
2328         *  @productversion Flex 4.5
2329         */
2330        public function get typicalText():String
2331        {
2332            return _typicalText;
2333        }
2334
2335        /**
2336         *  @private
2337         */
2338        public function set typicalText(value:String):void
2339        {
2340            if (value == _typicalText)
2341                return;
2342
2343            _typicalText = value;
2344
2345            typicalTextChanged = true;
2346
2347            invalidateProperties();
2348            invalidateSize();
2349            invalidateDisplayList();
2350        }
2351
2352        //----------------------------------
2353        //  widthInChars
2354        //----------------------------------
2355
2356        /**
2357         *  @private
2358         *  These are measured in ems.
2359         */
2360        private var _widthInChars:Number = NaN;
2361
2362        /**
2363         *  @private
2364         */
2365        private var widthInCharsChanged:Boolean = true;
2366
2367        [Inspectable(category="General", minValue="0.0")]
2368
2369        /**
2370         *  The default width of the control, measured in em units.
2371         *
2372         *  <p>An em is a unit of typographic measurement
2373         *  equal to the point size.
2374         *  It is not necessarily exactly the width of the "M" character,
2375         *  but in many fonts the "M" is about one em wide.
2376         *  The control's <code>fontSize</code> style is used,
2377         *  to calculate the em unit in pixels.</p>
2378         *
2379         *  <p>You would, for example, set this property to 20 if you want
2380         *  the width of the RichEditableText to be sufficient
2381         *  to display about 20 characters of text.</p>
2382         *
2383         *  <p>If this property is <code>NaN</code> (the default),
2384         *  then the component's default width will be determined
2385         *  from the text to be displayed.</p>
2386         *
2387         *  <p>This property will be ignored if you specify an explicit width,
2388         *  a percent width, or both <code>left</code> and <code>right</code>
2389         *  constraints.</p>
2390         *
2391         *  <p>This property will also be ignored if the <code>typicalText</code>
2392         *  property is specified.</p>
2393         *
2394         *  <p>RichEditableText's <code>measure()</code> method uses
2395         *  <code>widthInChars</code> and <code>heightInLines</code>
2396         *  to determine the <code>measuredWidth</code>
2397         *  and <code>measuredHeight</code>.
2398         *  These are similar to the <code>cols</code> and <code>rows</code>
2399         *  of an HTML TextArea.</p>
2400         *
2401         *  <p>Since both <code>widthInChars</code> and <code>heightInLines</code>
2402         *  default to <code>NaN</code>, RichTextEditable "autosizes" by default:
2403         *  it starts out very small if it has no text, grows in width as you
2404         *  type, and grows in height when you press Enter to start a new line.</p>
2405         *
2406         *  @default NaN
2407         *
2408         *  @see spark.primitives.heightInLines
2409         *
2410         *  @langversion 3.0
2411         *  @playerversion Flash 10
2412         *  @playerversion AIR 1.5
2413         *  @productversion Flex 4
2414         */
2415        public function get widthInChars():Number
2416        {
2417            return _widthInChars;
2418        }
2419
2420        /**
2421         *  @private
2422         */
2423        public function set widthInChars(value:Number):void
2424        {
2425            if (value == _widthInChars)
2426                return;
2427
2428            _widthInChars = value;
2429            widthInCharsChanged = true;
2430
2431            widthConstraint = NaN;
2432
2433            invalidateProperties();
2434            invalidateSize();
2435            invalidateDisplayList();
2436        }
2437
2438        //--------------------------------------------------------------------------
2439        //
2440        //  Overridden Methods: UIComponent
2441        //
2442        //--------------------------------------------------------------------------
2443
2444        /**
2445         *  @private
2446         */
2447        override protected function initializeAccessibility():void
2448        {
2449            if (RichEditableText.createAccessibilityImplementation != null)
2450                RichEditableText.createAccessibilityImplementation(this);
2451        }
2452
2453        /**
2454         *  @private
2455         */
2456        override public function parentChanged(p:DisplayObjectContainer):void
2457        {
2458            if (focusManager)
2459            {
2460                focusManager.removeEventListener(FlexEvent.FLEX_WINDOW_ACTIVATE,
2461                    _textContainerManager.activateHandler)
2462                focusManager.removeEventListener(FlexEvent.FLEX_WINDOW_DEACTIVATE,
2463                    _textContainerManager.deactivateHandler)
2464            }
2465
2466            super.parentChanged(p);
2467
2468            if (focusManager)
2469            {
2470                addActivateHandlers();
2471            }
2472            else
2473            {
2474                // if no focusmanager yet, add capture phase to detect when it
2475                // gets added
2476                if (systemManager)
2477                    systemManager.getSandboxRoot().addEventListener(FlexEvent.ADD_FOCUS_MANAGER,
2478                        addFocusManagerHandler, true, 0, true)
2479                else
2480                    // no systemManager yet?  Check again when added to stage
2481                    addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
2482            }
2483
2484        }
2485
2486        /**
2487         *  @private
2488         */
2489        override public function removeChild(child:DisplayObject):DisplayObject
2490        {
2491            // not sure why this happens but it does if you just change
2492            // the embeddedFont context
2493            if (!child.parent)
2494                return child;
2495
2496            if (child.parent == this)
2497                return super.removeChild(child);
2498
2499            return child.parent.removeChild(child);
2500        }
2501
2502        /**
2503         *  @private
2504         */
2505        override protected function commitProperties():void
2506        {
2507            super.commitProperties();
2508
2509            updateStylesIfChanged();
2510
2511            var oldAnchorPosition:int = _selectionAnchorPosition;
2512            var oldActivePosition:int = _selectionActivePosition;
2513
2514            // EditingMode needs to be current before attempting to set a
2515            // selection below.
2516            if (enabledChanged || selectableChanged || editableChanged)
2517            {
2518                updateEditingMode();
2519
2520                enabledChanged = false;
2521                editableChanged = false;
2522                selectableChanged = false;
2523            }
2524
2525            // Only one of textChanged, textFlowChanged, and contentChanged
2526            // will be true; the other two will be false because each setter
2527            // guarantees this.
2528
2529            if (textChanged)
2530            {
2531                if (FlexVersion.compatibilityVersion > FlexVersion.VERSION_4_5)
2532                    preserveSelectionOnSetText = true;
2533
2534                // If the text has linebreaks (CR, LF, or CF+LF)
2535                // create a multi-paragraph TextFlow from it
2536                // and use the TextFlowTextLineFactory to render it.
2537                // Otherwise the StringTextLineFactory will put
2538                // all of the lines into a single paragraph
2539                // and FTE performance will degrade on a large paragraph.
2540                if (_text.indexOf("\n") != -1 || _text.indexOf("\r") != -1)
2541                {
2542                    _textFlow = staticPlainTextImporter.importToFlow(_text);
2543                    _textContainerManager.setTextFlow(_textFlow);
2544                }
2545                else
2546                {
2547                    _textContainerManager.setText(_text);
2548                }
2549            }
2550            else if (textFlowChanged)
2551            {
2552                _textContainerManager.setTextFlow(_textFlow);
2553            }
2554            else if (contentChanged)
2555            {
2556                _textFlow = createTextFlowFromContent(_content);
2557                _textContainerManager.setTextFlow(_textFlow);
2558
2559                // Content converted to textFlow.
2560                _content = null;
2561            }
2562
2563            if (textChanged || textFlowChanged || contentChanged)
2564            {
2565                lastGeneration = _textFlow ? _textFlow.generation : 0;
2566                lastContentBoundsGeneration = 0;
2567
2568                // Handle the case where the initial text, textFlow or content
2569                // is displayed as a password.
2570                if (displayAsPassword)
2571                    displayAsPasswordChanged = true;
2572
2573                // New text so remove any leftover constraints.
2574                // Used if an item renderer is being recycled.
2575                widthConstraint = NaN;
2576                heightConstraint = NaN;
2577
2578                textChanged = false;
2579                textFlowChanged = false;
2580                contentChanged = false;
2581                invalidateSize();
2582                invalidateDisplayList();
2583            }
2584
2585            // If displayAsPassword changed, it only applies to the display,
2586            // not the underlying text.
2587            if (displayAsPasswordChanged)
2588            {
2589                preserveSelectionOnSetText = true;
2590
2591                // If there is any text, convert it to the passwordChar.
2592                if (displayAsPassword)
2593                {
2594                    // Make sure _text is set with the actual text before we
2595                    // change the displayed text.
2596                    _text = _textContainerManager.getText("\n");
2597
2598                    // Paragraph terminators are lost during this substitution.
2599                    var textToDisplay:String = StringUtil.repeat(
2600                        passwordChar, _text.length);
2601
2602                    _textContainerManager.setText(textToDisplay);
2603                }
2604                else
2605                {
2606                    // Text was displayed as password.  Now display as plain text.
2607                    _textContainerManager.setText(_text);
2608                }
2609
2610                // When TLF text is set above, TLF's textFlow is recreated so reset
2611                // our copy of the textFlow and the generation.
2612                _textFlow = null;
2613                lastGeneration = 0;
2614                lastContentBoundsGeneration = 0;
2615
2616                displayAsPasswordChanged = false;
2617            }
2618
2619            if (preserveSelectionOnSetText)
2620            {
2621                preserveSelectionOnSetText = false;
2622
2623                if (oldAnchorPosition != -1)
2624                {
2625                    // This will return null if editingMode = readOnly which is true when either
2626                    // editable is false and/or enabled is false.
2627                    var selManager:ISelectionManager = _textContainerManager.beginInteraction();
2628
2629                    // The visible selection will be refreshed during the update.
2630                    if (selManager)
2631                    {
2632                        selManager.selectRange(oldAnchorPosition, oldActivePosition);
2633                        _textContainerManager.endInteraction();
2634                    }
2635                }
2636            }
2637
2638            if (clipAndEnableScrollingChanged)
2639            {
2640                // The TLF code seems to check for !off.
2641                _textContainerManager.horizontalScrollPolicy = "auto";
2642                _textContainerManager.verticalScrollPolicy = "auto";
2643
2644                clipAndEnableScrollingChanged = false;
2645            }
2646
2647            if (horizontalScrollPositionChanged)
2648            {
2649                var oldHorizontalScrollPosition:Number =
2650                    _textContainerManager.horizontalScrollPosition;
2651
2652                _textContainerManager.horizontalScrollPosition =
2653                    _horizontalScrollPosition;
2654
2655                dispatchPropertyChangeEvent("horizontalScrollPosition",
2656                    oldHorizontalScrollPosition, _horizontalScrollPosition);
2657
2658                horizontalScrollPositionChanged = false;
2659            }
2660
2661            if (verticalScrollPositionChanged)
2662            {
2663                var oldVerticalScrollPosition:Number =
2664                    _textContainerManager.verticalScrollPosition;
2665
2666                _textContainerManager.verticalScrollPosition =
2667                    _verticalScrollPosition;
2668
2669                dispatchPropertyChangeEvent("verticalScrollPosition",
2670                    oldVerticalScrollPosition, _verticalScrollPosition);
2671
2672                verticalScrollPositionChanged = false;
2673            }
2674        }
2675
2676        /**
2677         *  @private
2678         */
2679        override protected function canSkipMeasurement():Boolean
2680        {
2681            autoSize = false;
2682            return super.canSkipMeasurement();
2683        }
2684
2685        /**
2686         *  @private
2687         */
2688        override protected function measure():void
2689        {
2690            var bounds:Rectangle;
2691
2692            // If the damage handler is called while measuring text, this means
2693            // the text lines are damaged and the display needs to be updated.
2694            // This flag tells the handler to invalidate just the display list.
2695            inMeasureMethod = true;
2696
2697            lastMeasuredWidth = measuredWidth;
2698            lastMeasuredHeight = measuredHeight;
2699
2700            super.measure();
2701
2702            // Styles can be changed in event handlers while in the middle
2703            // of the component lifecycle.  Make sure they are not stale when
2704            // composing text.
2705            updateStylesIfChanged();
2706
2707            // percentWidth and/or percentHeight will come back in as constraints
2708            // on the remeasure if we're autoSizing.
2709
2710            // TODO:(cframpto) implement blockProgression rl for autoSize
2711
2712            if (isMeasureFixed())
2713            {
2714                autoSize = false;
2715
2716                if (typicalText)
2717                {
2718                    if (typicalTextChanged)
2719                    {
2720                        // If the text has linebreaks (CR, LF, or CF+LF)
2721                        // create a multi-paragraph TextFlow from it
2722                        // and use the TextFlowTextLineFactory to render it.
2723                        // Otherwise the StringTextLineFactory will put
2724                        // all of the lines into a single paragraph
2725                        // and FTE performance will degrade on a large paragraph.
2726                        if (_typicalText.indexOf("\n") != -1 || _typicalText.indexOf("\r") != -1)
2727                        {
2728                            _typicalTextFlow = staticPlainTextImporter.importToFlow(_typicalText);
2729                            // this helped get the factory to generate the same bounds as the
2730                            // composer
2731                            _typicalTextFlow.hostFormat = _textContainerManager.hostFormat;
2732                        }
2733                        else
2734                            _typicalTextFlow = null;
2735                        typicalTextChanged = false;
2736                    }
2737                    // if multiline...
2738                    if (_typicalTextFlow)
2739                    {
2740                        // create the factory if needed
2741                        if (!staticTextFlowFactory)
2742                        {
2743                            staticTextFlowFactory = new TextFlowTextLineFactory();
2744                            // set bounds to natural bounds
2745                            staticTextFlowFactory.compositionBounds = unbounded;
2746                        }
2747                        if (_typicalTextFlow.flowComposer)
2748                        {
2749                            _typicalTextFlow.flowComposer.swfContext =
2750                                ISWFContext(embeddedFontContext);
2751                        }
2752                        staticTextFlowFactory.swfContext = ISWFContext(embeddedFontContext);
2753                        // create the textlines
2754                        staticTextFlowFactory.createTextLines(tossTextLine, _typicalTextFlow);
2755                        // get the bounds
2756                        bounds = staticTextFlowFactory.getContentBounds();
2757                    }
2758                    else // single line
2759                    {
2760                        // create the factory if needed
2761                        if (!staticStringFactory)
2762                        {
2763                            staticStringFactory = new StringTextLineFactory();
2764                            // set bounds to natural bounds
2765                            staticStringFactory.compositionBounds = unbounded;
2766                        }
2767                        // create the textlines
2768                        staticStringFactory.text = _typicalText;
2769                        staticStringFactory.textFlowFormat = _textContainerManager.hostFormat;
2770                        staticStringFactory.swfContext = ISWFContext(embeddedFontContext);
2771                        staticStringFactory.createTextLines(tossTextLine);
2772                        // get the bounds
2773                        bounds = staticStringFactory.getContentBounds();
2774                    }
2775
2776                    measuredWidth = Math.ceil(bounds.width);
2777                    measuredHeight = Math.ceil(bounds.height);
2778                }
2779                else
2780                {
2781                    // Go large.  For performance reasons, want to avoid a scrollRect
2782                    // whenever possible in drawBackgroundAndSetScrollRect().  This is
2783                    // particularly true for 1 line TextInput components.
2784                    measuredWidth = !isNaN(explicitWidth) ? explicitWidth :
2785                        Math.ceil(calculateWidthInChars());
2786                    measuredHeight = !isNaN(explicitHeight) ? explicitHeight :
2787                        Math.ceil(calculateHeightInLines());
2788                }
2789            }
2790            else
2791            {
2792                var composeWidth:Number;
2793                var composeHeight:Number;
2794
2795                // If we're here, then at one or both of the width and height can
2796                // grow to fit the text.  It is important to figure out whether
2797                // or not autoSize should be allowed to continue.  If in
2798                // updateDisplayList(), autoSize is true, then the
2799                // compositionHeight is NaN to allow the text to grow.
2800                autoSize = true;
2801
2802                if (!isNaN(widthConstraint) || !isNaN(explicitWidth) ||
2803                    !isNaN(widthInChars))
2804                {
2805                    // width specified but no height
2806                    // if no text, start at one line high and grow
2807
2808                    if (!isNaN(widthConstraint))
2809                        composeWidth = widthConstraint;
2810                    else if (!isNaN(explicitWidth))
2811                        composeWidth = explicitWidth;
2812                    else
2813                        composeWidth = Math.ceil(calculateWidthInChars());
2814
2815                    // The composeWidth may be adjusted for minWidth/maxWidth
2816                    // except if we're using the explicitWidth.
2817                    bounds = measureTextSize(composeWidth);
2818
2819                    // The measured width shouldn’t be pinned to the composeWidth if
2820                    // the composeWidth is set by %, otherwise the measuredWidth
2821                    // can keep stretching
2822                    if (!isNaN(explicitWidth) || !isNaN(widthInChars))
2823                        measuredWidth = textContainerManager.compositionWidth;
2824                    else
2825                        measuredWidth = Math.ceil(bounds.width);
2826                    measuredHeight = Math.ceil(bounds.bottom);
2827                }
2828                else if (!isNaN(heightConstraint) || !isNaN(explicitHeight) ||
2829                    !isNaN(_heightInLines))
2830                {
2831                    // if no text, 1 char wide with specified height and grow
2832
2833                    if (!isNaN(heightConstraint))
2834                        composeHeight = heightConstraint;
2835                    else if (!isNaN(explicitHeight))
2836                        composeHeight = explicitHeight;
2837                    else
2838                        composeHeight = calculateHeightInLines();
2839
2840                    // The composeWidth may be adjusted for minWidth/maxWidth.
2841                    bounds = measureTextSize(NaN, composeHeight);
2842
2843                    measuredWidth = Math.ceil(bounds.right);
2844                    measuredHeight = composeHeight;
2845
2846                    // Have we already hit the limit with the existing text?  If we
2847                    // are beyond the composeHeight we can assume we've maxed out on
2848                    // the compose width as well (or the composeHeight isn't
2849                    // large enough for even one line of text).
2850                    if (bounds.bottom > composeHeight)
2851                        autoSize = false;
2852                }
2853                else
2854                {
2855                    // The composeWidth may be adjusted for minWidth/maxWidth.
2856                    bounds = measureTextSize(NaN);
2857
2858                    measuredWidth = Math.ceil(bounds.right);
2859                    measuredHeight = Math.ceil(bounds.bottom);
2860                }
2861
2862                // Clamp the height, except if we're using the explicitHeight.
2863                if (isNaN(explicitHeight))
2864                {
2865                    if (!isNaN(explicitMinHeight) && measuredHeight < explicitMinHeight)
2866                        measuredHeight = explicitMinHeight;
2867
2868                    // Reached max height so can't grow anymore.
2869                    if (!isNaN(explicitMaxHeight) && measuredHeight > explicitMaxHeight)
2870                    {
2871                        measuredHeight = explicitMaxHeight;
2872                        autoSize = false;
2873                    }
2874                }
2875
2876                // Make sure we weren't previously scrolled.
2877                if (autoSize && getStyle("lineBreak") == "toFit")
2878                {
2879                    _textContainerManager.horizontalScrollPosition = 0;
2880                    _textContainerManager.verticalScrollPosition = 0;
2881                }
2882
2883                // If we remeasured, we composed and cleared the display.  We need to update the
2884                // display if the size didn't change, since validateSize will not do it for us.
2885                // This code path can be used by itemRenderer's since setLayoutBounds(), which is
2886                // where the constraints are set, is not always called.
2887                if (remeasuringText &&
2888                    lastMeasuredWidth == measuredWidth && lastMeasuredHeight == measuredHeight)
2889                {
2890                    _textContainerManager.updateContainer();
2891                }
2892            }
2893
2894            remeasuringText = false;
2895            inMeasureMethod = false;
2896
2897            //trace("measure", measuredWidth, measuredHeight, "autoSize", autoSize);
2898        }
2899
2900        /**
2901         *  @private
2902         */
2903        override protected function updateDisplayList(unscaledWidth:Number,
2904                                                      unscaledHeight:Number):void
2905        {
2906            inUpdateDLMethod = true;
2907
2908            //trace("updateDisplayList", unscaledWidth, unscaledHeight, "autoSize", autoSize);
2909
2910            // Styles can be changed in event handlers while in the middle
2911            // of the component lifecycle.  Make sure they are not stale when
2912            // composing text.
2913            updateStylesIfChanged();
2914
2915            // Check if the auto-size text is constrained in some way and needs
2916            // to be remeasured.  If one of the dimension changes, the text may
2917            // compose differently and have a different size which the layout
2918            // manager needs to know.
2919            // Don't exit early if we have changed size.  We may have to run
2920            // drawBackgroundAndSetScrollRect
2921            if (autoSize &&
2922                lastUnscaledHeight == unscaledHeight && lastUnscaledWidth == unscaledWidth &&
2923                remeasureText(unscaledWidth, unscaledHeight))
2924            {
2925                inUpdateDLMethod = false;
2926                return;
2927            }
2928
2929            super.updateDisplayList(unscaledWidth, unscaledHeight);
2930
2931            // If we're autoSizing we're telling the layout manager one set of
2932            // values and TLF another set of values so there is room for the text
2933            // to grow.
2934
2935            // TODO:(cframpto) compositionWidth can be NaN when
2936            // autoSize for blockProgression=="rl" is implemented
2937            if (!autoSize)
2938            {
2939                _textContainerManager.compositionWidth = unscaledWidth;
2940                _textContainerManager.compositionHeight = unscaledHeight;
2941            }
2942
2943            // If scrolling, always compose with the composer so we get consistent
2944            // measurements.  The factory and the composer produce slightly
2945            // different results which can confuse the scroller.  If there isn't a
2946            // composer, this calls updateContainer so do it here now that the
2947            // composition sizes are set so the results can be used.
2948            if (clipAndEnableScrolling &&
2949                _textContainerManager.composeState !=
2950                TextContainerManager.COMPOSE_COMPOSER)
2951            {
2952                _textContainerManager.convertToTextFlowWithComposer();
2953            }
2954
2955            // The EditManager calls updateAllControllers() directly when there
2956            // is interactive input such as typing or cut/paste. This bypasses
2957            // our update cycle which matters if we are auto-sizing.
2958            // compositionWidth/Height are NaN and the background is drawn with
2959            // the old width/height because measureTextSize hasn't had a
2960            // chance to update the layout manager yet.  Once the layoutManager
2961            // has been updated, the compositionWidth/Height are still NaN
2962            // so TLF doesn't think there is anything to compose.  The text
2963            // hasn't changed shape, but the background has.
2964            if (autoSize && !isNaN(lastUnscaledWidth) &&
2965                (lastUnscaledWidth != unscaledWidth ||
2966                    lastUnscaledHeight != unscaledHeight))
2967            {
2968                if (_textContainerManager.composeState == TextContainerManager.COMPOSE_COMPOSER)
2969                     _textContainerManager.getTextFlow().flowComposer.getControllerAt(0).shapesInvalid = true;
2970                else if (!_textContainerManager.isDamaged())
2971                    _textContainerManager.drawBackgroundAndSetScrollRect(0,0);
2972            }
2973
2974            _textContainerManager.updateContainer();
2975
2976            lastUnscaledWidth = unscaledWidth;
2977            lastUnscaledHeight = unscaledHeight;
2978
2979            inUpdateDLMethod = false;
2980         }
2981
2982        /**
2983         *  @private
2984         *  This is called by the layout manager the first time this
2985         *  component is measured, or later if its size changes. This
2986         *  is not always called before updateDisplayList().  For example,
2987         *  for recycled item renderers this is not called if the measured
2988         *  size doesn't change.
2989         *
2990         *  width and height are NaN unless there are constraints on them.
2991         */
2992        override public function setLayoutBoundsSize(
2993                                    width:Number, height:Number,
2994                                    postLayoutTransform:Boolean = true):void
2995        {
2996            //trace("setLayoutBoundsSize", width, height);
2997
2998            // Save these so when we are auto-sizing we know which dimensions
2999            // are constrained.  Without this it is not possible to differentiate
3000            // between a measured width/height that is the same as the
3001            // constrained width/height to know whether that dimension can
3002            // be sized or must be fixed at the constrained value.
3003            heightConstraint = height;
3004
3005            super.setLayoutBoundsSize(width, height, postLayoutTransform);
3006
3007            // Did we already constrain the width?
3008            if (widthConstraint == width)
3009                return;
3010
3011            // No reflow for explicit lineBreak
3012            if (getStyle("lineBreak") == "explicit")
3013                return;
3014
3015            // If we don't measure.
3016            // Call super so we don't call the override
3017            // and set autoSize to false;
3018            if (super.canSkipMeasurement())
3019                return;
3020
3021            if (!isNaN(explicitHeight))
3022                return;
3023
3024            // We support reflow only in the case of constrained width and
3025            // unconstrained height. Note that we compare with measuredWidth,
3026            // as for example the RichEditableText can be
3027            // constrained by the layout with "left" and "right", but the
3028            // container width itself may not be constrained and it would depend
3029            // on the element's measuredWidth.
3030            var constrainedWidth:Boolean = !isNaN(width) && (width != measuredWidth) && (width != 0);
3031            if (!constrainedWidth)
3032                return;
3033
3034            // We support reflow only when we don't have a transform.
3035            // We could add support for scale, but not skew or rotation.
3036            if (postLayoutTransform && hasComplexLayoutMatrix)
3037                return;
3038
3039            widthConstraint = width;
3040
3041            invalidateSize();
3042
3043        }
3044
3045        /**
3046         *  @inheritDoc
3047         *
3048         *  @langversion 3.0
3049         *  @playerversion Flash 10
3050         *  @playerversion AIR 1.5
3051         *  @productversion Flex 4
3052         */
3053        override public function stylesInitialized():void
3054        {
3055            super.stylesInitialized();
3056
3057            ascent = NaN;
3058            descent = NaN;
3059
3060            hostFormatChanged = true;
3061        }
3062
3063        /**
3064         *  @inheritDoc
3065         *
3066         *  @langversion 3.0
3067         *  @playerversion Flash 10
3068         *  @playerversion AIR 1.5
3069         *  @productversion Flex 4
3070         */
3071        override public function styleChanged(styleProp:String):void
3072        {
3073            super.styleChanged(styleProp);
3074
3075            // If null or "styleName" is passed it indicates that
3076            // multiple styles may have changed.  Otherwise it is a single style
3077            // so mark whether it is the selectionFormat that changed or the
3078            // hostFormat that changed.
3079            if (styleProp == null || styleProp == "styleName")
3080            {
3081                hostFormatChanged = true;
3082                selectionFormatsChanged = true;
3083                ascent = NaN;
3084                descent = NaN;
3085            }
3086            else if (styleProp == "focusedTextSelectionColor" ||
3087                styleProp == "unfocusedTextSelectionColor" ||
3088                styleProp == "inactiveTextSelectionColor")
3089            {
3090                selectionFormatsChanged = true;
3091            }
3092            else
3093            {
3094                hostFormatChanged = true;
3095
3096                if (styleProp.indexOf("font") == 0 || styleProp == "cffHinting")
3097                {
3098                    // Regenerate font swfContext and metrics as well.
3099                    ascent = NaN;
3100                    descent = NaN;
3101                }
3102            }
3103
3104            // Need to create new format(s).
3105            invalidateProperties();
3106        }
3107
3108        /**
3109         *  @private
3110         */
3111        override public function setFocus():void
3112        {
3113            // We are about to set focus on this component.  If it is due to
3114            // a programmatic focus change we have to programatically do what the
3115            // mouseOverHandler and the mouseDownHandler do so that the user can
3116            // type in this component without using the mouse first.  We need to
3117            // put a textFlow with a composer in place.
3118            if (editingMode != EditingMode.READ_ONLY &&
3119                _textContainerManager.composeState !=
3120                TextContainerManager.COMPOSE_COMPOSER)
3121            {
3122                _textContainerManager.beginInteraction();
3123                _textContainerManager.endInteraction();
3124            }
3125
3126            super.setFocus();
3127        }
3128
3129        /**
3130         *  @private
3131         */
3132        override public function drawFocus(isFocused:Boolean):void
3133        {
3134            if (isFocused)
3135            {
3136                // For some composite components, the focused object may not
3137                // be "this". If so, we don't want to draw the focus.  This
3138                // replaces the parentDrawsFocus variable used in halo.
3139                var fm:IFocusManager = focusManager;
3140                if (fm && fm.getFocus() != this)
3141                    return;
3142            }
3143
3144            super.drawFocus(isFocused);
3145        }
3146
3147        //--------------------------------------------------------------------------
3148        //
3149        //  Methods: IViewport
3150        //
3151        //--------------------------------------------------------------------------
3152
3153        //----------------------------------
3154        //  horizontalScrollPositionDelta
3155        //----------------------------------
3156
3157        /**
3158         *  @inheritDoc
3159         *
3160         *  @langversion 3.0
3161         *  @playerversion Flash 10
3162         *  @playerversion AIR 1.5
3163         *  @productversion Flex 4
3164         */
3165        public function getHorizontalScrollPositionDelta(navigationUnit:uint):Number
3166        {
3167            var scrollR:Rectangle = scrollRect;
3168            if (!scrollR)
3169                return 0;
3170
3171            // maxDelta is the horizontalScrollPosition delta required
3172            // to scroll to the RIGHT and minDelta scrolls to LEFT.
3173            var maxDelta:Number = contentWidth - scrollR.right;
3174            var minDelta:Number = -scrollR.left;
3175
3176            // Scroll by a "character" which is 1 em (matches widthInChars()).
3177            var em:Number = getStyle("fontSize");
3178
3179            switch (navigationUnit)
3180            {
3181                case NavigationUnit.LEFT:
3182                    return (scrollR.left <= 0) ? 0 : Math.max(minDelta, -em);
3183
3184                case NavigationUnit.RIGHT:
3185                    return (scrollR.right >= contentWidth) ? 0 : Math.min(maxDelta, em);
3186
3187                case NavigationUnit.PAGE_LEFT:
3188                    return Math.max(minDelta, -scrollR.width);
3189
3190                case NavigationUnit.PAGE_RIGHT:
3191                    return Math.min(maxDelta, scrollR.width);
3192
3193                case NavigationUnit.HOME:
3194                    return minDelta;
3195
3196                case NavigationUnit.END:
3197                    return maxDelta;
3198
3199                default:
3200                    return 0;
3201            }
3202        }
3203
3204        //----------------------------------
3205        //  verticalScrollPositionDelta
3206        //----------------------------------
3207
3208        /**
3209         *  @inheritDoc
3210         *
3211         *  @langversion 3.0
3212         *  @playerversion Flash 10
3213         *  @playerversion AIR 1.5
3214         *  @productversion Flex 4
3215         */
3216        public function getVerticalScrollPositionDelta(navigationUnit:uint):Number
3217        {
3218            var scrollR:Rectangle = scrollRect;
3219            if (!scrollR)
3220                return 0;
3221
3222            // maxDelta is the horizontalScrollPosition delta required
3223            // to scroll to the END and minDelta scrolls to HOME.
3224            var maxDelta:Number = contentHeight - scrollR.bottom;
3225            var minDelta:Number = -scrollR.top;
3226
3227            switch (navigationUnit)
3228            {
3229                case NavigationUnit.UP:
3230                    return _textContainerManager.getScrollDelta(-1);
3231
3232                case NavigationUnit.DOWN:
3233                    return _textContainerManager.getScrollDelta(1);
3234
3235                case NavigationUnit.PAGE_UP:
3236                    return Math.max(minDelta, -scrollR.height);
3237
3238                case NavigationUnit.PAGE_DOWN:
3239                    return Math.min(maxDelta, scrollR.height);
3240
3241                case NavigationUnit.HOME:
3242                    return minDelta;
3243
3244                case NavigationUnit.END:
3245                    return maxDelta;
3246
3247                default:
3248                    return 0;
3249            }
3250        }
3251
3252        //--------------------------------------------------------------------------
3253        //
3254        //  Methods
3255        //
3256        //--------------------------------------------------------------------------
3257
3258        /**
3259         *  Inserts the specified text into the RichEditableText
3260         *  as if you had typed it.
3261         *
3262         *  <p>If a range was selected, the new text replaces the selected text.
3263         *  If there was an insertion point, the new text is inserted there.</p>
3264         *
3265         *  <p>An insertion point is then set after the new text.
3266         *  If necessary, the text will scroll to ensure
3267         *  that the insertion point is visible.</p>
3268         *
3269         *  @param text The text to be inserted.
3270         *
3271         *  @langversion 3.0
3272         *  @playerversion Flash 10
3273         *  @playerversion AIR 1.5
3274         *  @productversion Flex 4
3275         */
3276        public function insertText(text:String):void
3277        {
3278            handleInsertText(text);
3279        }
3280
3281        /**
3282         *  Appends the specified text to the end of the RichEditableText,
3283         *  as if you had clicked at the end and typed.
3284         *
3285         *  <p>An insertion point is then set after the new text.
3286         *  If necessary, the text will scroll to ensure
3287         *  that the insertion point is visible.</p>
3288         *
3289         *  @param text The text to be appended.
3290         *
3291         *  @langversion 3.0
3292         *  @playerversion Flash 10
3293         *  @playerversion AIR 1.5
3294         *  @productversion Flex 4
3295         */
3296        public function appendText(text:String):void
3297        {
3298            handleInsertText(text, true);
3299        }
3300
3301        /**
3302         *  @copy flashx.textLayout.container.ContainerController#scrollToRange()
3303         *
3304         *  @langversion 3.0
3305         *  @playerversion Flash 10
3306         *  @playerversion AIR 1.5
3307         *  @productversion Flex 4
3308         */
3309        public function scrollToRange(anchorPosition:int, activePosition:int):void
3310        {
3311            // Make sure the properties are commited since the text could change.
3312            validateProperties();
3313
3314            // Scrolls so that the text position is visible in the container.
3315            textContainerManager.scrollToRange(anchorPosition, activePosition);
3316        }
3317
3318        /**
3319         *  Selects a specified range of characters.
3320         *
3321         *  <p>If either position is negative, it will deselect the text range.</p>
3322         *
3323         *  @param anchorPosition The character position specifying the end
3324         *  of the selection that stays fixed when the selection is extended.
3325         *
3326         *  @param activePosition The character position specifying the end
3327         *  of the selection that moves when the selection is extended.
3328         *
3329         *  @langversion 3.0
3330         *  @playerversion Flash 10
3331         *  @playerversion AIR 1.5
3332         *  @productversion Flex 4
3333         */
3334        public function selectRange(anchorPosition:int,
3335                                    activePosition:int):void
3336        {
3337            // Make sure the properties are commited since the text could change.
3338            validateProperties();
3339
3340            if (editingMode == EditingMode.READ_ONLY)
3341            {
3342                var selectionState:SelectionState =
3343                    new SelectionState(textFlow, anchorPosition, activePosition);
3344
3345                var selectionEvent:SelectionEvent =
3346                    new SelectionEvent(SelectionEvent.SELECTION_CHANGE,
3347                        false, false, selectionState);
3348
3349                textContainerManager_selectionChangeHandler(selectionEvent);
3350            }
3351            else
3352            {
3353                var im:ISelectionManager = _textContainerManager.beginInteraction();
3354
3355                im.selectRange(anchorPosition, activePosition);
3356
3357                // Refresh the selection.  This does not cause a damage event.
3358                im.refreshSelection();
3359
3360                _textContainerManager.endInteraction();
3361            }
3362
3363            // Remember if the current selection is a range which was set
3364            // programatically.
3365            hasProgrammaticSelectionRange = (anchorPosition != activePosition);
3366        }
3367
3368        /**
3369         *  Selects all of the text. This does not include the final paragraph
3370         *  terminator.
3371         *
3372         *  @langversion 3.0
3373         *  @playerversion Flash 10
3374         *  @playerversion AIR 1.5
3375         *  @productversion Flex 4
3376         */
3377        public function selectAll():void
3378        {
3379            selectRange(0, int.MAX_VALUE);
3380        }
3381
3382        /**
3383         *  Returns a TextLayoutFormat object specifying the computed formats
3384         *  for the specified range of characters.
3385         *
3386         *  <p>If a format is not consistently set across the entire range,
3387         *  its value will be <code>undefined</code>.</p>
3388         *
3389         *  <p>You can specify a Vector of Strings containing the names of the
3390         *  formats that you care about; if you don't, all formats
3391         *  will be computed.</p>
3392         *
3393         *  <p>If you don't specify a range, the selected range is used.</p>
3394         *
3395         *  @param requestedFormats A Vector of Strings specifying the names
3396         *  of the requested formats, or <code>null</code> to request all formats.
3397         *
3398         *  @param anchorPosition A character position specifying
3399         *  the fixed end of the selection.
3400         *
3401         *  @param activePosition A character position specifying
3402         *   the movable end of the selection.
3403         *
3404         *  @return A TextLayoutFormat object.
3405         *
3406         *  @langversion 3.0
3407         *  @playerversion Flash 10
3408         *  @playerversion AIR 1.5
3409         *  @productversion Flex 4
3410         */
3411        public function getFormatOfRange(requestedFormats:Vector.<String> = null,
3412                                         anchorPosition:int = -1,
3413                                         activePosition:int = -1):TextLayoutFormat
3414        {
3415            var format:TextLayoutFormat = new TextLayoutFormat();
3416
3417            // Make sure all properties are committed.
3418            validateProperties();
3419
3420            // This internal TLF object maps the names of format properties
3421            // to Property instances.
3422            // Each Property instance has a category property which tells
3423            // whether it is container-, paragraph-, or character-level.
3424            var description:Object = TextLayoutFormat.description;
3425
3426            var p:String;
3427            var category:String;
3428
3429            // Based on which formats have been requested, determine which
3430            // of the getCommonXXXFormat() methods we need to call.
3431
3432            var needContainerFormat:Boolean = false;
3433            var needParagraphFormat:Boolean = false;
3434            var needCharacterFormat:Boolean = false;
3435
3436            if (!requestedFormats)
3437            {
3438                requestedFormats = new Vector.<String>;
3439                for (p in description)
3440                    requestedFormats.push(p);
3441
3442                needContainerFormat = true;
3443                needParagraphFormat = true;
3444                needCharacterFormat = true;
3445            }
3446            else
3447            {
3448                for each (p in requestedFormats)
3449                {
3450                    if (!(p in description))
3451                        continue;
3452
3453                    category = description[p].category;
3454
3455                    if (category == Category.CONTAINER)
3456                        needContainerFormat = true;
3457                    else if (category == Category.PARAGRAPH)
3458                        needParagraphFormat = true;
3459                    else if (category == Category.CHARACTER)
3460                        needCharacterFormat = true;
3461                }
3462            }
3463
3464            // Get the common formats.
3465
3466            var containerFormat:ITextLayoutFormat;
3467            var paragraphFormat:ITextLayoutFormat;
3468            var characterFormat:ITextLayoutFormat;
3469
3470            if (anchorPosition == -1 && activePosition == -1)
3471            {
3472                anchorPosition = _selectionAnchorPosition;
3473                activePosition = _selectionActivePosition;
3474            }
3475
3476            if (needContainerFormat)
3477            {
3478                containerFormat =
3479                    _textContainerManager.getCommonContainerFormat();
3480            }
3481
3482            if (needParagraphFormat)
3483            {
3484                paragraphFormat =
3485                    _textContainerManager.getCommonParagraphFormat(
3486                        anchorPosition, activePosition);
3487            }
3488
3489            if (needCharacterFormat)
3490            {
3491                characterFormat =
3492                    _textContainerManager.getCommonCharacterFormat(
3493                        anchorPosition, activePosition);
3494            }
3495
3496            // Extract the requested formats to return.
3497            for each (p in requestedFormats)
3498            {
3499                if (!(p in description))
3500                    continue;
3501
3502                category = description[p].category;
3503
3504                if (category == Category.CONTAINER && containerFormat)
3505                    format[p] = containerFormat[p];
3506                else if (category == Category.PARAGRAPH && paragraphFormat)
3507                    format[p] = paragraphFormat[p];
3508                else if (category == Category.CHARACTER && characterFormat)
3509                    format[p] = characterFormat[p];
3510            }
3511
3512            return format;
3513        }
3514
3515        /**
3516         *  Applies the specified formats to each element in the specified
3517         *  range that correspond to the given format.
3518         *  It applies the character formats to the text in the specified range
3519         *  (no change is made if the specified range is a single point).
3520         *  It applies the paragraph formats to any paragraphs at least
3521         *  partially within the range (or a single paragraph if the range is a
3522         *  single point).
3523         *  It applies the container formats to the container.
3524         *
3525         *  <p>The supported formats are those in TextLayoutFormat.
3526         *  A value of <code>undefined</code> does not get applied.
3527         *  If you don't specify a range, the selected range is used.</p>
3528         *
3529         *  <p>The following example sets the <code>fontSize</code> and <code>color</code> of the selection:
3530         *  <pre>
3531         *  var textLayoutFormat:TextLayoutFormat = new TextLayoutFormat();
3532         *  textLayoutFormat.fontSize = 12;
3533         *  textLayoutFormat.color = 0xFF0000;
3534         *  myRET.setFormatOfRange(textLayoutFormat);
3535         *  </pre>
3536         *  </p>
3537         *
3538         *  <p>If you use the results of <code>getFormatOfRange()</code> to
3539         *  specify the <code>format</code>, note that every format in the
3540         *  <code>TextLayoutFormat</code> has a
3541         *  computed value, which will be applied to each element that
3542         *  corresponds to the given format.</p>
3543         *
3544         *  <p>If you would like to specify a format to be applied to all the text
3545         *  it would be better to use <code>setStyle(format, value)</code>
3546         *  on the component itself.</p>
3547         *
3548         *  <p>The following example sets the <code>fontSize</code> and <code>color</code> of all the text:
3549         *  <pre>
3550         *  myRET.setStyle("fontSize", 12);
3551         *  myRET.setStyle("color", 0xFF0000);
3552         *  </pre>
3553         *  </p>
3554         *
3555         *  @param format The TextLayoutFormat to apply to the selection.
3556         *
3557         *  @param anchorPosition A character position, relative to the beginning of the
3558         *  text String, specifying the end of the selection that stays fixed when the
3559         *  selection is extended with the arrow keys.
3560         *
3561         *  @param activePosition A character position, relative to the beginning of the
3562         *  text String, specifying the end of the selection that moves when the
3563         *  selection is extended with the arrow keys.
3564         *
3565         *  @langversion 3.0
3566         *  @playerversion Flash 10
3567         *  @playerversion AIR 1.5
3568         *  @productversion Flex 4
3569         */
3570        public function setFormatOfRange(format:TextLayoutFormat,
3571                                         anchorPosition:int=-1,
3572                                         activePosition:int=-1):void
3573        {
3574            // Make sure all properties are committed.  The damage handler for the
3575            // applyTextFormat op will cause the remeasure and display update.
3576            validateProperties();
3577
3578            // Assign each specified attribute to one of three format objects,
3579            // depending on whether it is container-, paragraph-,
3580            // or character-level. Note that these can remain null.
3581            var containerFormat:TextLayoutFormat;
3582            var paragraphFormat:TextLayoutFormat;
3583            var characterFormat:TextLayoutFormat;
3584
3585            // This internal TLF object maps the names of format properties
3586            // to Property instances.
3587            // Each Property instance has a category property which tells
3588            // whether it is container-, paragraph-, or character-level.
3589            var description:Object = TextLayoutFormat.description;
3590
3591            for (var p:String in description)
3592            {
3593                if (format[p] === undefined)
3594                    continue;
3595
3596                var category:String = description[p].category;
3597
3598                if (category == Category.CONTAINER)
3599                {
3600                    if (!containerFormat)
3601                        containerFormat =  new TextLayoutFormat();
3602                    containerFormat[p] = format[p];
3603                }
3604                else if (category == Category.PARAGRAPH)
3605                {
3606                    if (!paragraphFormat)
3607                        paragraphFormat =  new TextLayoutFormat();
3608                    paragraphFormat[p] = format[p];
3609                }
3610                else if (category == Category.CHARACTER)
3611                {
3612                    if (!characterFormat)
3613                        characterFormat =  new TextLayoutFormat();
3614                    characterFormat[p] = format[p];
3615                }
3616            }
3617
3618            // If the selection isn't specified, use the current one.
3619            if (anchorPosition == -1 && activePosition == -1)
3620            {
3621                anchorPosition = _selectionAnchorPosition;
3622                activePosition = _selectionActivePosition;
3623            }
3624
3625            // Apply the three format objects to the current selection if
3626            // selectionState is null, else the specified selection.
3627            _textContainerManager.applyFormatOperation(
3628                characterFormat, paragraphFormat, containerFormat,
3629                anchorPosition, activePosition);
3630        }
3631
3632        /**
3633         *  @private
3634         */
3635        mx_internal function createTextContainerManager():RichEditableTextContainerManager
3636        {
3637            return new RichEditableTextContainerManager(this);
3638        }
3639
3640        /**
3641         *  @private
3642         */
3643        private function updateStylesIfChanged():void
3644        {
3645
3646            if (hostFormatChanged)
3647            {
3648                // Side-effect is it marks the text as damaged.
3649                _textContainerManager.hostFormat = new CSSTextLayoutFormat(this);
3650
3651                hostFormatChanged = false;
3652            }
3653
3654            if (isNaN(ascent) || isNaN(descent))
3655            {
3656                // If the CSS styles for this component specify an embedded font,
3657                // embeddedFontContext will be set to the module factory that
3658                // should create TextLines (since they must be created in the
3659                // SWF where the embedded font is.)
3660                // Otherwise, this will be null.
3661                embeddedFontContext = getEmbeddedFontContext();
3662
3663                _textContainerManager.swfContext =
3664                    ISWFContext(embeddedFontContext);
3665
3666                // Note: CSSTextLayoutFormat has special processing
3667                // for the fontLookup style. If it is "auto",
3668                // the fontLookup format is set to either
3669                // "device" or "embedded" depending on whether
3670                // embeddedFontContext is null or non-null.
3671
3672                // Recalcuate the ascent and descent.
3673                calculateFontMetrics();
3674            }
3675
3676            if (selectionFormatsChanged)
3677            {
3678                _textContainerManager.invalidateSelectionFormats();
3679
3680                selectionFormatsChanged = false;
3681            }
3682        }
3683
3684        /**
3685         *  @private
3686         *  Uses the component's CSS styles to determine the module factory
3687         *  that should creates its TextLines.
3688         */
3689        private function getEmbeddedFontContext():IFlexModuleFactory
3690        {
3691            var fontContext:IFlexModuleFactory;
3692
3693            var fontLookup:String = getStyle("fontLookup");
3694            if (fontLookup != FontLookup.DEVICE)
3695            {
3696                var font:String = getStyle("fontFamily");
3697                var bold:Boolean = getStyle("fontWeight") == "bold";
3698                var italic:Boolean = getStyle("fontStyle") == "italic";
3699
3700                fontContext = getFontContext(font, bold, italic, true);
3701            }
3702
3703            return fontContext;
3704        }
3705
3706        /**
3707         *  @private
3708         *  Return true if there is a width and height to use for the measure.
3709         */
3710        mx_internal function isMeasureFixed():Boolean
3711        {
3712            // This can be called from RET EditManager when hostFormat is null
3713            // because a style changed and commitProperties hasn't run yet.
3714            if (!_textContainerManager.hostFormat)
3715                updateStylesIfChanged();
3716
3717            if (_textContainerManager.hostFormat.blockProgression !=
3718                BlockProgression.TB)
3719            {
3720                return true;
3721            }
3722
3723            if (typicalText != null)
3724                return true;
3725
3726            // Is there some sort of width and some sort of height?
3727            return  (!isNaN(explicitWidth) || !isNaN(_widthInChars) ||
3728                !isNaN(widthConstraint)) &&
3729                    (!isNaN(explicitHeight) || !isNaN(_heightInLines) ||
3730                        !isNaN(heightConstraint));
3731        }
3732
3733        /**
3734         *  @private
3735         *  Returns the bounds of the measured text.  The initial composeWidth may
3736         *  be adjusted for minWidth or maxWidth.  The value used for the compose
3737         *  is in _textContainerManager.compositionWidth.
3738         */
3739        private function measureTextSize(composeWidth:Number,
3740                                         composeHeight:Number=NaN):Rectangle
3741        {
3742            // Adjust for explicit min/maxWidth so the measurement is accurate.
3743            // If no explicitWidth or widthInChars
3744            if (isNaN(explicitWidth) && isNaN(widthInChars))
3745            {
3746                // then if there is an explicit minWidth and
3747                // a specified composeWidth and the composeWidth
3748                // is less than the minWidth, use the minWidth
3749                if (!isNaN(explicitMinWidth) &&
3750                    !isNaN(composeWidth) && composeWidth < minWidth)
3751                {
3752                    composeWidth = minWidth;
3753                }
3754                // if composeWidth is NaN, just compose and see what happens
3755                // and fix up the measurements afterwards.  See final check
3756                // at the end of the method
3757
3758                // On the other hand, if there is an explicit maxWidth and
3759                // no specified composeWidth then use maxWidth,
3760                // or if there is a a specified composeWidth
3761                // and the composeWidth is greater than maxWidth, use the maxWidth.
3762                if (!isNaN(explicitMaxWidth) &&
3763                    isNaN(composeWidth) ||
3764                    composeWidth > maxWidth)
3765                {
3766                    composeWidth = maxWidth;
3767                }
3768            }
3769
3770            // If the width is NaN it can grow up to TextLine.MAX_LINE_WIDTH wide.
3771            // If the height is NaN it can grow to allow all the text to fit.
3772            _textContainerManager.compositionWidth = composeWidth;
3773            _textContainerManager.compositionHeight = composeHeight;
3774
3775            // If scrolling, always compose with the composer so we get consistent
3776            // measurements.  The factory and the composer produce slightly
3777            // different results which can confuse the scroller.  If there isn't a
3778            // composer, this calls updateContainer so do it here now that the
3779            // composition sizes are set so the results can be used.
3780            if (clipAndEnableScrolling &&
3781                _textContainerManager.composeState !=
3782                TextContainerManager.COMPOSE_COMPOSER)
3783            {
3784                _textContainerManager.convertToTextFlowWithComposer();
3785            }
3786
3787            // Compose only.  The display should not be updated.
3788            _textContainerManager.compose();
3789
3790            // Adjust width and height for text alignment.
3791            var bounds:Rectangle = _textContainerManager.getContentBounds();
3792
3793            // If it's an empty text flow, there is one line with one
3794            // character so the height is good for the line but we
3795            // need to give it some width other than optional padding.
3796
3797            if (_textContainerManager.getText("\n").length == 0)
3798            {
3799                // Empty text flow.  One Em wide so there
3800                // is a place to put the insertion cursor.
3801                bounds.width = bounds.width + getStyle("fontSize");
3802            }
3803
3804            //trace("measureTextSize", composeWidth, "->", bounds.width, composeHeight, "->", bounds.height);
3805
3806            // one final check:  If there is no explicitWidth...
3807            if (isNaN(explicitWidth) && isNaN(widthInChars))
3808            {
3809                // but there is a minWidth, and no specified composeWidth
3810                // and we measure out to be less than the minWidth
3811                // report the minWidth anyway.
3812                if (!isNaN(explicitMinWidth) &&
3813                    isNaN(composeWidth) &&
3814                    bounds.width < minWidth)
3815                {
3816                    bounds.width = minWidth;
3817                }
3818            }
3819            return bounds;
3820        }
3821
3822
3823        /**
3824         *  @private
3825         *  If auto-sizing text, it may need to be remeasured if it is
3826         *  constrained in one dimension by the layout manager.  If it is
3827         *  constrained in both dimensions there is no need to remeasure.
3828         *  Changing one dimension may change the size of the measured text
3829         *  and the layout manager needs to know this.
3830         */
3831        private function remeasureText(width:Number, height:Number):Boolean
3832        {
3833            // Neither dimensions changed.  If auto-sizing we're still auto-sizing.
3834            if (width == measuredWidth && height == measuredHeight)
3835                return false;
3836
3837            // Either constraints are preventing auto-sizing or we need to
3838            // remeasure which will reset autoSize.
3839            autoSize = false;
3840
3841            // If no width or height, there is nothing to remeasure since
3842            // there is no room for text.
3843            if (width == 0 || height == 0)
3844                return false;
3845
3846            // If we're using typical text, no need to remeasure.
3847            if (typicalText != null)
3848                return false;
3849
3850            // No reflow for explicit lineBreak
3851            if (_textContainerManager.hostFormat.lineBreak == "explicit")
3852                return false;
3853
3854            if (!isNaN(widthConstraint))
3855            {
3856                // Do we have a constrained width and an explicit height?
3857                // If so, the sizes are set so no need to remeasure now.
3858                if (!isNaN(explicitHeight) || !isNaN(_heightInLines) ||
3859                    !isNaN(heightConstraint))
3860                {
3861                    return false;
3862                }
3863            }
3864
3865            if (!isNaN(heightConstraint))
3866            {
3867                // Do we have a constrained height and an explicit width?
3868                // If so, the sizes are set so no need to remeasure now.
3869                if (!isNaN(explicitWidth) || !isNaN(_widthInChars))
3870                    return false;
3871            }
3872
3873            // Width or height is different than what was measured.  Since we're
3874            // auto-sizing, need to remeasure, so the layout manager leaves the
3875            // correct amount of space for the component.
3876            invalidateSize();
3877
3878            // Need to make sure the container is updated after the text is re-composed
3879            // to measure it.
3880            remeasuringText = true;
3881
3882            return true;
3883        }
3884
3885        /**
3886         *  @private
3887         *  This method is called when anything affecting the
3888         *  default font, size, weight, etc. changes.
3889         *  It calculates the 'ascent', 'descent', and
3890         *  instance variables, which are used in measure().
3891         */
3892        private function calculateFontMetrics():void
3893        {
3894            var fontDescription:FontDescription = new FontDescription();
3895
3896            var s:String;
3897
3898            s = getStyle("cffHinting");
3899            if (s != null)
3900                fontDescription.cffHinting = s;
3901
3902            s = getStyle("fontFamily");
3903            if (s != null)
3904                fontDescription.fontName = s;
3905
3906            s = getStyle("fontLookup");
3907            if (s != null)
3908            {
3909                // FTE understands only "device" and "embeddedCFF"
3910                // for fontLookup. But Flex allows this style to be
3911                // set to "auto", in which case we automatically
3912                // determine it based on whether the CSS styles
3913                // specify an embedded font.
3914                if (s != "device")
3915                {
3916                    s = TextUtil.resolveFontLookup(
3917                        _textContainerManager.swfContext,
3918                        _textContainerManager.hostFormat);
3919                }
3920                fontDescription.fontLookup = s;
3921            }
3922
3923            s = getStyle("fontStyle");
3924            if (s != null)
3925                fontDescription.fontPosture = s;
3926
3927            s = getStyle("fontWeight");
3928            if (s != null)
3929                fontDescription.fontWeight = s;
3930
3931            var elementFormat:ElementFormat = new ElementFormat();
3932            elementFormat.fontDescription = fontDescription;
3933            elementFormat.fontSize = getStyle("fontSize");
3934
3935            var textElement:TextElement = new TextElement();
3936            textElement.elementFormat = elementFormat;
3937            textElement.text = "M";
3938
3939            var textBlock:TextBlock = new TextBlock();
3940            textBlock.content = textElement;
3941
3942            var swfContext:ISWFContext = ISWFContext(embeddedFontContext);
3943
3944            var textLine:TextLine;
3945            if (swfContext)
3946            {
3947                textLine = swfContext.callInContext(
3948                    textBlock.createTextLine, textBlock,
3949                    [ null, 1000 ]);
3950            }
3951            else
3952                textLine = textBlock.createTextLine(null, 1000);
3953
3954            ascent = textLine.ascent;
3955            descent = textLine.descent;
3956        }
3957
3958        /**
3959         *  @private
3960         */
3961        private function calculateWidthInChars():Number
3962        {
3963            var em:Number = getStyle("fontSize");
3964
3965            var effectiveWidthInChars:int;
3966
3967            // If both height and width are NaN use 10 chars.  Otherwise if only
3968            // width is NaN, use 1.
3969            if (isNaN(_widthInChars))
3970                effectiveWidthInChars = isNaN(_heightInLines) ? 10 : 1;
3971            else
3972                effectiveWidthInChars = _widthInChars;
3973
3974            // Without the explicit casts, if padding values are non-zero, the
3975            // returned width is a very large number.
3976            return getStyle("paddingLeft") +
3977                effectiveWidthInChars * em +
3978                getStyle("paddingRight");
3979        }
3980
3981        /**
3982         *  @private
3983         *  Calculates the height needed for heightInLines lines using the default
3984         *  font.
3985         */
3986        private function calculateHeightInLines():Number
3987        {
3988            var height:Number = getStyle("paddingTop") + getStyle("paddingBottom");
3989
3990            if (_heightInLines == 0)
3991                return height;
3992
3993            var effectiveHeightInLines:int;
3994
3995            // If both height and width are NaN use 10 lines.  Otherwise if
3996            // only height is NaN, use 1.
3997            if (isNaN(_heightInLines))
3998                effectiveHeightInLines = isNaN(_widthInChars) ? 10 : 1;
3999            else
4000                effectiveHeightInLines = _heightInLines;
4001
4002            // Position of the baseline of first line in the container.
4003            value = getStyle("firstBaselineOffset");
4004            if (value == lineHeight)
4005                height += lineHeight;
4006            else if (value is Number)
4007                height += Number(value);
4008            else
4009                height += ascent;
4010
4011            // Distance from baseline to baseline.  Can be +/- number or
4012            // or +/- percent (in form "120%") or "undefined".
4013            if (effectiveHeightInLines > 1)
4014            {
4015                var value:Object = getStyle("lineHeight");
4016                var lineHeight:Number =
4017                    RichEditableText.getNumberOrPercentOf(value, getStyle("fontSize"));
4018
4019                // Default is 120%
4020                if (isNaN(lineHeight))
4021                    lineHeight = getStyle("fontSize") * 1.2;
4022
4023                height += (effectiveHeightInLines - 1) * lineHeight;
4024            }
4025
4026            // Add in descent of last line.
4027            height += descent;
4028
4029            return height;
4030        }
4031
4032        /**
4033         *  @private
4034         */
4035        private function createTextFlowFromContent(content:Object):TextFlow
4036        {
4037            var textFlow:TextFlow ;
4038
4039            if (content is TextFlow)
4040            {
4041                textFlow = content as TextFlow;
4042            }
4043            else if (content is Array)
4044            {
4045                textFlow = new TextFlow();
4046                textFlow.whiteSpaceCollapse = getStyle("whiteSpaceCollapse");
4047                textFlow.mxmlChildren = content as Array;
4048                textFlow.whiteSpaceCollapse = undefined;
4049            }
4050            else
4051            {
4052                textFlow = new TextFlow();
4053                textFlow.whiteSpaceCollapse = getStyle("whiteSpaceCollapse");
4054                textFlow.mxmlChildren = [ content ];
4055                textFlow.whiteSpaceCollapse = undefined;
4056            }
4057
4058            return textFlow;
4059        }
4060
4061        /**
4062         *  @private
4063         */
4064        private function updateEditingMode():void
4065        {
4066            var newEditingMode:String = EditingMode.READ_ONLY;
4067
4068            if (enabled)
4069            {
4070                if (_editable)
4071                    newEditingMode = EditingMode.READ_WRITE;
4072                else if (_selectable)
4073                    newEditingMode = EditingMode.READ_SELECT;
4074            }
4075
4076            editingMode = newEditingMode;
4077        }
4078
4079        /**
4080         *  @private
4081         *
4082         *  This is used when text is either inserted or appended via the API.
4083         */
4084        private function handleInsertText(newText:String, isAppend:Boolean=false):void
4085        {
4086            // Make sure all properties are committed.  The damage handler for the
4087            // insert will cause the remeasure and display update.
4088            validateProperties();
4089
4090            if (isAppend)
4091            {
4092                // Set insertion pt to the end of the current text.
4093                _selectionAnchorPosition = text.length;
4094                _selectionActivePosition = _selectionAnchorPosition;
4095            }
4096            else
4097            {
4098                // Insert requires a selection, or it is a noop.
4099                if (_selectionAnchorPosition == -1 || _selectionActivePosition == -1)
4100                    return;
4101            }
4102
4103            // This will update the selection after the operation is done.
4104            var success:Boolean =
4105                _textContainerManager.insertTextOperation(
4106                    newText, _selectionAnchorPosition, _selectionActivePosition);
4107
4108            if (success)
4109                dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
4110        }
4111
4112        /**
4113         *  @private
4114         */
4115        private function handlePasteOperation(op:PasteOperation):void
4116        {
4117            var hasConstraints:Boolean =
4118                restrict || maxChars || displayAsPassword;
4119
4120            // If there are no constraints and multiline text is allowed
4121            // there is nothing that needs to be done.
4122            if (!hasConstraints && multiline)
4123                return;
4124
4125            // Make sure the clipboard has something to paste.
4126            if (op.textScrap == null || op.textScrap.textFlow == null)
4127                return;
4128
4129            // If copied/cut from displayAsPassword field the pastedText
4130            // is '*' characters but this is correct.
4131            var pastedText:String = staticPlainTextExporter.export(
4132                op.textScrap.textFlow, ConversionType.STRING_TYPE) as String;
4133
4134            // If there are no constraints and no newlines there is nothing
4135            // more to do.
4136            if (!hasConstraints && pastedText.indexOf("\n") == -1)
4137                return;
4138
4139            // Save this in case we modify the pasted text.  We need to know
4140            // how much text to delete.
4141            var textLength:int = pastedText.length;
4142
4143            // Keep _text in sync with the text flow.  If there was a selection
4144            // for the paste, delete that text, then insert the pasted
4145            // text.
4146            if (_displayAsPassword)
4147            {
4148                _text = splice(_text, op.absoluteStart, op.absoluteEnd, "");
4149                _text = splice(_text, op.absoluteStart, op.absoluteStart, pastedText);
4150            }
4151
4152            // If multiline is false, strip newlines out of pasted text
4153            // This will not strip newlines out of displayAsPassword fields
4154            // since the text is the passwordChar and newline won't be found.
4155            if (!multiline)
4156                pastedText = pastedText.replace(ALL_NEWLINES_REGEXP, "");
4157
4158            // We know it's an EditManager or we wouldn't have gotten here.
4159            var editManager:IEditManager =
4160                EditManager(_textContainerManager.beginInteraction());
4161
4162            // Generate a CHANGING event for the PasteOperation but not for the
4163            // DeleteTextOperation or the InsertTextOperation which are also part
4164            // of the paste.
4165            dispatchChangeAndChangingEvents = false;
4166
4167            // Replace the same text, the same place where the paste was done.
4168            // This will go thru the InsertPasteOperation and do the right
4169            // things with restrict, maxChars, displayAsPassword and _text.
4170            var selectionState:SelectionState = new SelectionState(
4171                  op.textFlow, op.absoluteStart, op.absoluteStart + textLength);
4172            editManager.insertText(pastedText, selectionState);
4173
4174            // All done with the edit manager.
4175            _textContainerManager.endInteraction();
4176
4177            dispatchChangeAndChangingEvents = true;
4178        }
4179
4180        //--------------------------------------------------------------------------
4181        //
4182        //  Event handlers
4183        //
4184        //--------------------------------------------------------------------------
4185
4186        /**
4187         *  @private
4188         *  find the right time to listen to the focusmanager
4189         */
4190        private function addedToStageHandler(event:Event):void
4191        {
4192            if (event.target == this)
4193            {
4194                removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
4195                callLater(addActivateHandlers);
4196            }
4197        }
4198
4199        /**
4200         *  @private
4201         *  add listeners to focusManager
4202         */
4203        private function addActivateHandlers():void
4204        {
4205            if (!focusManager)
4206                return;
4207
4208            focusManager.addEventListener(FlexEvent.FLEX_WINDOW_ACTIVATE,
4209                _textContainerManager.activateHandler, false, 0, true)
4210            focusManager.addEventListener(FlexEvent.FLEX_WINDOW_DEACTIVATE,
4211                _textContainerManager.deactivateHandler, false, 0, true)
4212        }
4213
4214        /**
4215         *  @private
4216         *  Called when a FocusManager is added to an IFocusManagerContainer.
4217         *  We need to check that it belongs
4218         *  to us before listening to it.
4219         *  Because we listen to sandboxroot, you cannot assume the type of
4220         *  the event.
4221         */
4222        private function addFocusManagerHandler(event:Event):void
4223        {
4224            if (focusManager == event.target["focusManager"])
4225            {
4226                systemManager.getSandboxRoot().removeEventListener(FlexEvent.ADD_FOCUS_MANAGER,
4227                    addFocusManagerHandler, true)
4228                addActivateHandlers();
4229            }
4230        }
4231
4232        /**
4233         *  @private
4234         *  RichEditableTextContainerManager overrides focusInHandler and calls
4235         *  this before executing its own focusInHandler.
4236         */
4237        mx_internal function focusInHandler(event:FocusEvent):void
4238        {
4239            // When TCM is simulating a focusIn event, event will be null.
4240            // Ignore these and wait for the actual focus in event.
4241            if (event == null)
4242                return;
4243
4244            //trace("focusIn handler");
4245
4246            var fm:IFocusManager = focusManager;
4247            if (fm && editingMode == EditingMode.READ_WRITE)
4248                fm.showFocusIndicator = true;
4249
4250            // showFocusIndicator must be set before this is called.
4251            super.focusInHandler(event);
4252
4253            if (editingMode == EditingMode.READ_WRITE)
4254            {
4255                // If the focusIn was because of a mouseDown event, let TLF
4256                // handle the selection.  Otherwise it was because we tabbed in
4257                // or we programatically set the focus.
4258                if (!mouseDown)
4259                {
4260                    var selectionManager:ISelectionManager =
4261                        _textContainerManager.beginInteraction();
4262
4263                    if (multiline)
4264                    {
4265                        if (!selectionManager.hasSelection())
4266                            selectionManager.selectRange(0, 0);
4267                    }
4268                    else if (!hasProgrammaticSelectionRange)
4269                    {
4270                        selectionManager.selectAll();
4271                    }
4272
4273                    selectionManager.refreshSelection();
4274
4275                    _textContainerManager.endInteraction();
4276                }
4277
4278                if (_imeMode != null)
4279                {
4280                    // When IME.conversionMode is unknown it cannot be
4281                    // set to anything other than unknown(English)
4282                    try
4283                    {
4284                        if (IME.conversionMode != IMEConversionMode.UNKNOWN)
4285                        {
4286                            IME.conversionMode = _imeMode;
4287                        }
4288                    }
4289                    catch(e:Error)
4290                    {
4291                        // on Windows, setting a Japanese IME mode when in
4292                        // english throws an error (on Mac it doesn't)
4293                        // so ignore errors we get.
4294                    }
4295                }
4296            }
4297
4298            if (focusManager && multiline)
4299                focusManager.defaultButtonEnabled = false;
4300        }
4301
4302        /**
4303         *  @private
4304         *  RichEditableTextContainerManager overrides focusOutHandler and calls
4305         *  this before executing its own focusOutHandler.
4306         */
4307        mx_internal function focusOutHandler(event:FocusEvent):void
4308        {
4309            //trace("focusOut handler");
4310
4311            super.focusOutHandler(event);
4312
4313            // By default, we clear the undo history when a RichEditableText loses
4314            // focus.
4315            if (clearUndoOnFocusOut && undoManager)
4316                undoManager.clearAll();
4317
4318            if (focusManager)
4319                focusManager.defaultButtonEnabled = true;
4320
4321
4322            dispatchEvent(new FlexEvent(FlexEvent.VALUE_COMMIT));
4323        }
4324
4325        /**
4326         *  @private
4327         *  RichEditableTextContainerManager overrides keyDownHandler and calls
4328         *  this before executing its own keyDownHandler.
4329         */
4330        mx_internal function keyDownHandler(event:KeyboardEvent):void
4331        {
4332            if (editingMode != EditingMode.READ_WRITE)
4333                return;
4334
4335            // Handle the ENTER key here, if multiline text is not allowed or
4336            // if there isn't enough room for more characters to be added.
4337            if (event.keyCode == Keyboard.ENTER)
4338            {
4339                if (!multiline)
4340                {
4341                    dispatchEvent(new FlexEvent(FlexEvent.ENTER));
4342                    event.preventDefault();
4343                    return;
4344                }
4345
4346                // Multiline.  Make sure there is room before acting on it.
4347                if (_maxChars != 0 && text.length >= _maxChars)
4348                {
4349                    event.preventDefault();
4350                    return;
4351                }
4352
4353                // Let the TLF EditManager handle the ENTER key.  If editing
4354                // a list, depending on the position, it will either add a new
4355                // list item or close off the list.  Otherwise it will split
4356                // the paragraph.
4357            }
4358        }
4359
4360        /**
4361         *  @private
4362         */
4363        mx_internal function mouseDownHandler(event:MouseEvent):void
4364        {
4365            mouseDown = true;
4366
4367            // Need to get called even if mouse events are dispatched
4368            // outside of this component.  For example, when the user does
4369            // a mouse down in RET, drags the mouse outside of the
4370            /// component, and then releases the mouse.
4371            systemManager.getSandboxRoot().addEventListener(
4372                MouseEvent.MOUSE_UP,
4373                systemManager_mouseUpHandler, true /*useCapture*/);
4374        }
4375
4376        /**
4377         *  @private
4378         */
4379        private function systemManager_mouseUpHandler(event:MouseEvent):void
4380        {
4381            mouseDown = false;
4382
4383            systemManager.getSandboxRoot().removeEventListener(
4384                MouseEvent.MOUSE_UP,
4385                         systemManager_mouseUpHandler, true /*useCapture*/);
4386        }
4387
4388        /**
4389         *  @private
4390         *  call a TLF method so we don't leak
4391         */
4392        private function removedFromStageHandler(event:Event):void
4393        {
4394            if (event.target == this)
4395            {
4396                TextContainerManager.releaseReferences();
4397            }
4398        }
4399
4400        /**
4401         *  @private
4402         *  If the textFlow hasn't changed the generation remains the same.
4403         *  Changing the composition width and/or height does not change the
4404         *  generation.  The bounds can change as a result of different
4405         *  composition dimensions or as a result of more of the text flow
4406         *  being composed.  Only as much of the text flow as is displayed is
4407         *  composed.  If not all of the text flow is composed, its content height
4408         *  is estimated.  Until the entire text flow is composed its content
4409         *  height can increase or decrease while scrolling thru the flow.
4410         *
4411         *  If the following conditions are met with the contentWidth and the
4412         *  contentHeight reported to the scroller, the scroller can avoid the
4413         *  situation we've seen where it tries to add a scroll bar which causes the
4414         *  text to reflow, which changes the content bounds, which causes the
4415         *  scroller to react, and potentially loop indefinately.
4416         *
4417         *       if width is reduced the height should grow or stay the same
4418         *       if height is reduced the width should grow or stay the same
4419         *       if width and height are reduced then either the width or height
4420         *           should grow or stay the same.
4421         *
4422         *  toFit
4423         *      width       height
4424         *
4425         *      smaller     smaller     height pinned to old height
4426         *      smaller     larger      ok
4427         *      larger      larger      ok
4428         *      larger      smaller     ok
4429         *
4430         *  explicit
4431         *      width       height
4432         *      smaller     smaller     width pinned to old width
4433         *      smaller     larger      width pinned to old width
4434         *      larger      larger      ok
4435         *      larger      smaller     ok
4436         */
4437        private function adjustContentBoundsForScroller(bounds:Rectangle):void
4438        {
4439            // Already reported bounds at least once for this generation of
4440            // the text flow so we have to be careful to mantain consistency
4441            // for the scroller.
4442            if (_textFlow.generation == lastContentBoundsGeneration)
4443            {
4444                if (bounds.width <= _contentWidth)
4445                {
4446                    if (_textContainerManager.hostFormat.lineBreak == "toFit")
4447                    {
4448                        if (bounds.height < _contentHeight)
4449                            bounds.height = _contentHeight;
4450                    }
4451                    else
4452                    {
4453                        // The width may get smaller if the compose height is
4454                        // reduced and fewer lines are composed.  Use the old
4455                        // content width which is more accurate.
4456                        bounds.width = _contentWidth;
4457                    }
4458                }
4459            }
4460
4461            lastContentBoundsGeneration = _textFlow.generation;
4462        }
4463
4464        /**
4465         *  @private
4466         *  Called when the TextContainerManager dispatches a 'compositionComplete'
4467         *  event when it has recomposed the text into TextLines.
4468         */
4469        private function textContainerManager_compositionCompleteHandler(
4470            event:CompositionCompleteEvent):void
4471        {
4472            //trace("compositionComplete");
4473
4474            var oldContentWidth:Number = _contentWidth;
4475            var oldContentHeight:Number = _contentHeight;
4476
4477            var newContentBounds:Rectangle =
4478                _textContainerManager.getContentBounds();
4479
4480            // If x and/or y are not 0, adjust for what is visible.  For example, if there is an
4481            // image which is wider than the composeWidth and float="right", x will be negative
4482            // and the part of the image between x and 0 will not be visible so it should
4483            // not be included in the reported width.  This will avoid a scrollbar that does
4484            // nothing.
4485            newContentBounds.width += newContentBounds.x;
4486            newContentBounds.height += newContentBounds.y;
4487
4488            // Try to prevent the scroller from getting into a loop while
4489            // adding/removing scroll bars.
4490            if (_textFlow && clipAndEnableScrolling)
4491                adjustContentBoundsForScroller(newContentBounds);
4492
4493            var newContentWidth:Number = newContentBounds.width;
4494            var newContentHeight:Number = newContentBounds.height;
4495
4496            // TODO:(cframpto) handle blockProgression == RL
4497
4498            if (newContentWidth != oldContentWidth)
4499            {
4500                _contentWidth = newContentWidth;
4501
4502                //trace("composeWidth", _textContainerManager.compositionWidth, "contentWidth", oldContentWidth, newContentWidth);
4503
4504                // If there is a scroller, this triggers the scroller layout.
4505                dispatchPropertyChangeEvent(
4506                    "contentWidth", oldContentWidth, newContentWidth);
4507            }
4508
4509            if (newContentHeight != oldContentHeight)
4510            {
4511                _contentHeight = newContentHeight;
4512
4513                //trace("composeHeight", _textContainerManager.compositionHeight, "contentHeight", oldContentHeight, newContentHeight);
4514
4515                // If there is a scroller, this triggers the scroller layout.
4516                dispatchPropertyChangeEvent(
4517                    "contentHeight", oldContentHeight, newContentHeight);
4518            }
4519        }
4520
4521        /**
4522         *  @private
4523         *  Called when the TextContainerManager dispatches a 'damage' event.
4524         *  The TextFlow could have been modified interactively or programatically.
4525         */
4526        private function textContainerManager_damageHandler(event:DamageEvent):void
4527        {
4528            if (event.damageLength == 0)
4529                return;
4530
4531            if (inUpdateDLMethod)
4532                return;
4533
4534            // Text that is being measured is damaged so update the display.
4535            if (inMeasureMethod)
4536            {
4537                invalidateDisplayList();
4538                return;
4539            }
4540
4541            //trace("damageHandler", "generation", _textFlow ? _textFlow.generation : -1, "lastGeneration", lastGeneration);
4542
4543            // The following textContainerManager functions can trigger a damage
4544            // event:
4545            //    setText/setTextFlow
4546            //    set hostFormat
4547            //    set compositionWidth/compositionHeight
4548            //    set horizontalScrollPosition/veriticalScrollPosition
4549            //    set swfContext
4550            //    updateContainer or compose: always if TextFlowFactory, sometimes
4551            //        if flowComposer
4552            // or the textFlow can be modified directly.
4553
4554            // If no changes, don't recompose/update.  The TextFlowFactory
4555            // createTextLines dispatches damage events every time the textFlow
4556            // is composed, even if there are no changes.
4557            if (_textFlow && _textFlow.generation == lastGeneration)
4558                return;
4559
4560            // If there are pending changes, don't wipe them out.  We have
4561            // not gotten to commitProperties() yet.
4562            if (textChanged || textFlowChanged || contentChanged)
4563                return;
4564
4565            // In this case we always maintain _text with the underlying text and
4566            // display the appropriate number of passwordChars.  If there are any
4567            // interactive editing operations _text is updated during the operation.
4568            if (!displayAsPassword)
4569                _text = null;
4570
4571            _content = null;
4572            _textFlow = _textContainerManager.getTextFlow();
4573
4574            lastGeneration = _textFlow.generation;
4575
4576            // We don't need to call invalidateProperties()
4577            // because the hostFormat and the _textFlow are still valid.
4578
4579            // If the textFlow content is modified directly or if there is a style
4580            // change by changing the textFlow directly the size could change.
4581            invalidateSize();
4582
4583            invalidateDisplayList();
4584        }
4585
4586        /**
4587         *  @private
4588         *  Called when the TextContainerManager dispatches a 'scroll' event
4589         *  as it autoscrolls.
4590         */
4591        private function textContainerManager_scrollHandler(event:Event):void
4592        {
4593            var oldHorizontalScrollPosition:Number = _horizontalScrollPosition;
4594            var newHorizontalScrollPosition:Number =
4595                _textContainerManager.horizontalScrollPosition;
4596
4597            if (newHorizontalScrollPosition != oldHorizontalScrollPosition)
4598            {
4599                //trace("hsp scroll", oldHorizontalScrollPosition, "->", newHorizontalScrollPosition);
4600
4601                _horizontalScrollPosition = newHorizontalScrollPosition;
4602
4603                dispatchPropertyChangeEvent("horizontalScrollPosition",
4604                    oldHorizontalScrollPosition, newHorizontalScrollPosition);
4605            }
4606
4607            var oldVerticalScrollPosition:Number = _verticalScrollPosition;
4608            var newVerticalScrollPosition:Number =
4609                _textContainerManager.verticalScrollPosition;
4610
4611            if (newVerticalScrollPosition != oldVerticalScrollPosition)
4612            {
4613                //trace("vsp scroll", oldVerticalScrollPosition, "->", newVerticalScrollPosition);
4614
4615                _verticalScrollPosition = newVerticalScrollPosition;
4616
4617                dispatchPropertyChangeEvent("verticalScrollPosition",
4618                    oldVerticalScrollPosition, newVerticalScrollPosition);
4619            }
4620        }
4621
4622        /**
4623         *  @private
4624         *  Called when the TextContainerManager dispatches a 'selectionChange' event.
4625         */
4626        private function textContainerManager_selectionChangeHandler(
4627            event:SelectionEvent):void
4628        {
4629            if (preserveSelectionOnSetText)
4630                return;
4631
4632            var oldAnchor:int = _selectionAnchorPosition;
4633            var oldActive:int = _selectionActivePosition;
4634
4635            var selectionState:SelectionState = event.selectionState;
4636
4637            if (selectionState)
4638            {
4639                _selectionAnchorPosition = selectionState.anchorPosition;
4640                _selectionActivePosition = selectionState.activePosition;
4641            }
4642            else
4643            {
4644                _selectionAnchorPosition = -1;
4645                _selectionActivePosition = -1;
4646            }
4647
4648            // Selection changed so reset.
4649            hasProgrammaticSelectionRange = false;
4650
4651            // Only dispatch the event if the selection has really changed.
4652            var changed:Boolean = oldAnchor != _selectionAnchorPosition ||
4653                oldActive != _selectionActivePosition;
4654
4655            if (changed)
4656            {
4657                //trace("selectionChangeHandler", _selectionAnchorPosition, _selectionActivePosition);
4658                dispatchEvent(new FlexEvent(FlexEvent.SELECTION_CHANGE));
4659            }
4660        }
4661
4662        /**
4663         *  @private
4664         *  Called when the TextContainerManager dispatches an 'operationBegin'
4665         *  event before an editing operation.
4666         */
4667        private function textContainerManager_flowOperationBeginHandler(
4668            event:FlowOperationEvent):void
4669        {
4670            //trace("flowOperationBegin", "level", event.level);
4671
4672            var op:FlowOperation = event.operation;
4673
4674            // The text flow's generation will be incremented if the text flow
4675            // is modified in any way by this operation.
4676
4677            if (op is InsertTextOperation)
4678            {
4679                var insertTextOperation:InsertTextOperation =
4680                    InsertTextOperation(op);
4681
4682                var textToInsert:String = insertTextOperation.text;
4683
4684                // Note: Must process restrict first, then maxChars,
4685                // then displayAsPassword last.
4686
4687                // The text deleted by this operation.  If we're doing our
4688                // own manipulation of the textFlow or thisis a result of a paste operation
4689                // we have to take the deleted text into account as well as the inserted text.
4690                var delSelOp:SelectionState =
4691                    insertTextOperation.deleteSelectionState;
4692
4693                var delLen:int = (delSelOp == null) ? 0 :
4694                    delSelOp.absoluteEnd - delSelOp.absoluteStart;
4695
4696                if (_restrict != null)
4697                {
4698                    textToInsert = StringUtil.restrict(textToInsert, restrict);
4699                    // If restrict is the result of a paste the text has already been inserted
4700                    // into the buffer and has to be removed so don't short-circuit the operation.
4701                    if (textToInsert.length == 0 && delLen == 0)
4702                    {
4703                        event.preventDefault();
4704                        return;
4705                    }
4706                }
4707
4708                if (maxChars != 0)
4709                {
4710                    var length1:int = text.length - delLen;
4711                    var length2:int = textToInsert.length;
4712                    if (length1 + length2 > maxChars)
4713                        textToInsert = textToInsert.substr(0, maxChars - length1);
4714                }
4715
4716                if (_displayAsPassword)
4717                {
4718                    // Remove deleted text.
4719                    if (delLen > 0)
4720                    {
4721                        _text = splice(_text, delSelOp.absoluteStart,
4722                                       delSelOp.absoluteEnd, "");
4723                    }
4724
4725                    // Add in the inserted text.
4726                    if (textToInsert.length > 0)
4727                    {
4728                        _text = splice(_text, insertTextOperation.absoluteStart,
4729                            insertTextOperation.absoluteStart, textToInsert);
4730
4731                        // Display the passwordChar rather than the actual text.
4732                        textToInsert = StringUtil.repeat(passwordChar,
4733                            textToInsert.length);
4734                    }
4735                }
4736
4737                insertTextOperation.text = textToInsert;
4738            }
4739            else if (op is CopyOperation)
4740            {
4741                if (_displayAsPassword)
4742                {
4743                    // For security, don't allow passwords to be copied.
4744                    event.preventDefault();
4745                    return;
4746                }
4747            }
4748            else if (op is PasteOperation)
4749            {
4750                // Paste is implemented in operationEnd.  The basic idea is to allow
4751                // the paste to go through unchanged, but group it together with a
4752                // second operation that modifies text as part of the same
4753                // transaction. This is vastly simpler for TLF to manage.
4754            }
4755            else if (op is DeleteTextOperation || op is CutOperation)
4756            {
4757                var flowTextOperation:FlowTextOperation =
4758                    FlowTextOperation(op);
4759
4760                // Eat 0-length deletion.  This can happen when insertion point is
4761                // at start of container when a backspace is entered
4762                // or when the insertion point is at the end of the
4763                // container and a delete key is entered.
4764                if (flowTextOperation.absoluteStart ==
4765                    flowTextOperation.absoluteEnd)
4766                {
4767                    event.preventDefault();
4768                    return;
4769                }
4770
4771                if (_displayAsPassword)
4772                {
4773                    if (op is DeleteTextOperation)
4774                    {
4775                        _text = splice(_text, flowTextOperation.absoluteStart,
4776                            flowTextOperation.absoluteEnd, "");
4777                    }
4778                    else
4779                    {
4780                        // For security, don't allow passwords to be cut.
4781                        event.preventDefault();
4782                        return;
4783                    }
4784                }
4785            }
4786
4787            // Dispatch a 'changing' event from the RichEditableText
4788            // as notification that an editing operation is about to occur.
4789            // The level will be 0 for single operations, and at the start
4790            // of a composite operation.
4791            if (dispatchChangeAndChangingEvents && event.level == 0)
4792            {
4793                var newEvent:TextOperationEvent =
4794                    new TextOperationEvent(TextOperationEvent.CHANGING);
4795                newEvent.operation = op;
4796                dispatchEvent(newEvent);
4797
4798                // If the event dispatched from this RichEditableText is canceled,
4799                // cancel the one from the EditManager, which will prevent
4800                // the editing operation from being processed.
4801                if (newEvent.isDefaultPrevented())
4802                    event.preventDefault();
4803            }
4804        }
4805
4806        /**
4807         *  @private
4808         *  Called when the TextContainerManager dispatches an 'operationEnd' event
4809         *  after an editing operation.
4810         */
4811        private function textContainerManager_flowOperationEndHandler(
4812            event:FlowOperationEvent):void
4813        {
4814            //trace("flowOperationEnd", "level", event.level);
4815
4816            // Paste is a special case.  Any mods have to be made to the text
4817            // which includes what was pasted.
4818            if (event.operation is PasteOperation)
4819                handlePasteOperation(PasteOperation(event.operation));
4820        }
4821
4822        /**
4823         *  @private
4824         *  Called when the TextContainerManager dispatches an 'operationComplete'
4825         *  event after an editing operation.
4826         */
4827        private function textContainerManager_flowOperationCompleteHandler(
4828            event:FlowOperationEvent):void
4829        {
4830            //trace("flowOperationComplete", "level", event.level);
4831
4832            // Dispatch a 'change' event from the RichEditableText
4833            // as notification that an editing operation has occurred.
4834            // The flow is now in a state that it can be manipulated.
4835            // The level will be 0 for single operations, and at the end
4836            // of a composite operation.
4837            if (dispatchChangeAndChangingEvents && event.level == 0)
4838            {
4839                var newEvent:TextOperationEvent =
4840                    new TextOperationEvent(TextOperationEvent.CHANGE);
4841                newEvent.operation = event.operation;
4842                dispatchEvent(newEvent);
4843            }
4844        }
4845
4846        /**
4847         *  @private
4848         *  Called when an InlineGraphicElement is asynchronously loaded.
4849         *  The event status could be "sizePending", "loaded" or "error".
4850         *  Must call updateAllContainers() to cause transition to the next
4851         *  graphic status.
4852         */
4853        private function textContainerManager_inlineGraphicStatusChangeHandler (
4854            event:StatusChangeEvent):void
4855        {
4856            if (event.status == InlineGraphicElementStatus.READY)
4857            {
4858                // Now that the actual size of the graphic is available need to
4859                // optionally remeasure and updateContainer.
4860                if (autoSize)
4861                    invalidateSize();
4862            }
4863
4864            // Call updateAllContainers().
4865            invalidateDisplayList();
4866        }
4867
4868        /**
4869         *  @private
4870         *  release the textline as it is temporary.
4871         */
4872        private function tossTextLine(textLine:DisplayObject):void
4873        {
4874            TextUtil.recycleTextLine(textLine as TextLine);
4875        }
4876    }
4877
4878}
4879