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 * <s:RichEditableText>Hello <s:span fontWeight="bold">World!</s:span></s:RichEditableText> 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><p></code> and <code><br/></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><s:RichEditableText></code> tag inherits all of the tag 385 * attributes of its superclass and adds the following tag attributes:</p> 386 * 387 * <pre> 388 * <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 * /> 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 * <s:RichEditableText>Hello <s:span fontWeight="bold"/>World</s:span></s:RichEditableText> 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