1//////////////////////////////////////////////////////////////////////////////// 2// 3// ADOBE SYSTEMS INCORPORATED 4// Copyright 2003-2007 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 mx.controls 13{ 14 15import flash.display.DisplayObject; 16import flash.display.DisplayObjectContainer; 17import flash.display.Graphics; 18import flash.display.Shape; 19import flash.display.Sprite; 20import flash.events.Event; 21import flash.events.FocusEvent; 22import flash.events.KeyboardEvent; 23import flash.events.MouseEvent; 24import flash.geom.Point; 25import flash.ui.Keyboard; 26import flash.utils.Dictionary; 27import flash.utils.describeType; 28 29import mx.collections.CursorBookmark; 30import mx.collections.ICollectionView; 31import mx.collections.ItemResponder; 32import mx.collections.Sort; 33import mx.collections.SortField; 34import mx.collections.errors.ItemPendingError; 35import mx.controls.dataGridClasses.DataGridBase; 36import mx.controls.dataGridClasses.DataGridColumn; 37import mx.controls.dataGridClasses.DataGridDragProxy; 38import mx.controls.dataGridClasses.DataGridHeader; 39import mx.controls.dataGridClasses.DataGridItemRenderer; 40import mx.controls.dataGridClasses.DataGridListData; 41import mx.controls.listClasses.IDropInListItemRenderer; 42import mx.controls.listClasses.IListItemRenderer; 43import mx.controls.listClasses.ListBaseContentHolder; 44import mx.controls.listClasses.ListBaseSeekPending; 45import mx.controls.listClasses.ListRowInfo; 46import mx.controls.scrollClasses.ScrollBar; 47import mx.core.ContextualClassFactory; 48import mx.core.EdgeMetrics; 49import mx.core.EventPriority; 50import mx.core.FlexShape; 51import mx.core.FlexSprite; 52import mx.core.FlexVersion; 53import mx.core.IFactory; 54import mx.core.IFlexDisplayObject; 55import mx.core.IFlexModuleFactory; 56import mx.core.IIMESupport; 57import mx.core.IInvalidating; 58import mx.core.IPropertyChangeNotifier; 59import mx.core.IRectangularBorder; 60import mx.core.IUIComponent; 61import mx.core.ScrollPolicy; 62import mx.core.UIComponent; 63import mx.core.UIComponentGlobals; 64import mx.core.mx_internal; 65import mx.events.CollectionEvent; 66import mx.events.CollectionEventKind; 67import mx.events.DataGridEvent; 68import mx.events.DataGridEventReason; 69import mx.events.DragEvent; 70import mx.events.IndexChangedEvent; 71import mx.events.ListEvent; 72import mx.events.SandboxMouseEvent; 73import mx.events.ScrollEvent; 74import mx.events.ScrollEventDetail; 75import mx.managers.IFocusManager; 76import mx.managers.IFocusManagerComponent; 77import mx.skins.halo.ListDropIndicator; 78import mx.styles.ISimpleStyleClient; 79import mx.styles.StyleManager; 80import mx.utils.ObjectUtil; 81import mx.utils.StringUtil; 82 83use namespace mx_internal; 84 85//-------------------------------------- 86// Events 87//-------------------------------------- 88 89/** 90 * Dispatched when the user releases the mouse button while over an item 91 * renderer, tabs to the DataGrid control or within the DataGrid control, 92 * or in any other way attempts to edit an item. 93 * 94 * @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGINNING 95 */ 96[Event(name="itemEditBeginning", type="mx.events.DataGridEvent")] 97 98/** 99 * Dispatched when the <code>editedItemPosition</code> property has been set 100 * and the item can be edited. 101 * 102 * @eventType mx.events.DataGridEvent.ITEM_EDIT_BEGIN 103 */ 104[Event(name="itemEditBegin", type="mx.events.DataGridEvent")] 105 106/** 107 * Dispatched when the item editor has just been instantiated. 108 * 109 * @eventType mx.events.DataGridEvent.ITEM_EDITOR_CREATE 110 */ 111[Event(name="itemEditorCreate", type="mx.events.DataGridEvent")] 112 113/** 114 * Dispatched when an item editing session ends for any reason. 115 * 116 * @eventType mx.events.DataGridEvent.ITEM_EDIT_END 117 */ 118[Event(name="itemEditEnd", type="mx.events.DataGridEvent")] 119 120/** 121 * Dispatched when an item renderer gets focus, which can occur if the user 122 * clicks on an item in the DataGrid control or navigates to the item using 123 * a keyboard. Only dispatched if the item is editable. 124 * 125 * @eventType mx.events.DataGridEvent.ITEM_FOCUS_IN 126 */ 127[Event(name="itemFocusIn", type="mx.events.DataGridEvent")] 128 129/** 130 * Dispatched when an item renderer loses focus, which can occur if the user 131 * clicks another item in the DataGrid control or clicks outside the control, 132 * or uses the keyboard to navigate to another item in the DataGrid control 133 * or outside the control. 134 * Only dispatched if the item is editable. 135 * 136 * @eventType mx.events.DataGridEvent.ITEM_FOCUS_OUT 137 */ 138[Event(name="itemFocusOut", type="mx.events.DataGridEvent")] 139 140/** 141 * Dispatched when a user changes the width of a column, indicating that the 142 * amount of data displayed in that column may have changed. 143 * If <code>horizontalScrollPolicy</code> is <code>"off"</code>, other 144 * columns shrink or expand to compensate for the columns' resizing, 145 * and they also dispatch this event. 146 * 147 * @eventType mx.events.DataGridEvent.COLUMN_STRETCH 148 */ 149[Event(name="columnStretch", type="mx.events.DataGridEvent")] 150 151/** 152 * Dispatched when the user releases the mouse button on a column header 153 * to request the control to sort 154 * the grid contents based on the contents of the column. 155 * Only dispatched if the column is sortable and the data provider supports 156 * sorting. The DataGrid control has a default handler for this event that implements 157 * a single-column sort. Multiple-column sort can be implemented by calling the 158 * <code>preventDefault()</code> method to prevent the single column sort and setting 159 * the <code>sort</code> property of the data provider. 160 * <p> 161 * <b>Note</b>: The sort arrows are defined by the default event handler for 162 * the headerRelease event. If you call the <code>preventDefault()</code> method 163 * in your event handler, the arrows are not drawn. 164 * </p> 165 * 166 * @eventType mx.events.DataGridEvent.HEADER_RELEASE 167 */ 168[Event(name="headerRelease", type="mx.events.DataGridEvent")] 169 170/** 171 * Dispatched when the user releases the mouse button on a column header after 172 * having dragged the column to a new location resulting in shifting the column 173 * to a new index. 174 * 175 * @eventType mx.events.IndexChangedEvent.HEADER_SHIFT 176 */ 177[Event(name="headerShift", type="mx.events.IndexChangedEvent")] 178 179//-------------------------------------- 180// Styles 181//-------------------------------------- 182 183include "../styles/metadata/IconColorStyles.as" 184 185/** 186 * A flag that indicates whether to show vertical grid lines between 187 * the columns. 188 * If <code>true</code>, shows vertical grid lines. 189 * If <code>false</code>, hides vertical grid lines. 190 * @default true 191 */ 192[Style(name="verticalGridLines", type="Boolean", inherit="no")] 193 194/** 195 * A flag that indicates whether to show horizontal grid lines between 196 * the rows. 197 * If <code>true</code>, shows horizontal grid lines. 198 * If <code>false</code>, hides horizontal grid lines. 199 * @default false 200 */ 201[Style(name="horizontalGridLines", type="Boolean", inherit="no")] 202 203/** 204 * The color of the vertical grid lines. 205 * @default 0x666666 206 */ 207[Style(name="verticalGridLineColor", type="uint", format="Color", inherit="yes")] 208 209/** 210 * The color of the horizontal grid lines. 211 */ 212[Style(name="horizontalGridLineColor", type="uint", format="Color", inherit="yes")] 213 214/** 215 * An array of two colors used to draw the header background gradient. 216 * The first color is the top color. 217 * The second color is the bottom color. 218 * @default [0xFFFFFF, 0xE6E6E6] 219 */ 220[Style(name="headerColors", type="Array", arrayType="uint", format="Color", inherit="yes")] 221 222/** 223 * The color of the row background when the user rolls over the row. 224 * @default 0xE3FFD6 225 */ 226[Style(name="rollOverColor", type="uint", format="Color", inherit="yes")] 227 228/** 229 * The color of the background for the row when the user selects 230 * an item renderer in the row. 231 * @default 0xCDFFC1 232 */ 233[Style(name="selectionColor", type="uint", format="Color", inherit="yes")] 234 235/** 236 * The name of a CSS style declaration for controlling other aspects of 237 * the appearance of the column headers. 238 * @default "dataGridStyles" 239 */ 240[Style(name="headerStyleName", type="String", inherit="no")] 241 242/** 243 * The class to use as the skin for a column that is being resized. 244 * @default mx.skins.halo.DataGridColumnResizeSkin 245 */ 246[Style(name="columnResizeSkin", type="Class", inherit="no")] 247 248 249/** 250 * The class to use as the skin that defines the appearance of the 251 * background of the column headers in a DataGrid control. 252 * @default mx.skins.halo.DataGridHeaderSeparator 253 */ 254[Style(name="headerBackgroundSkin", type="Class", inherit="no")] 255 256/** 257 * The class to use as the skin that defines the appearance of the 258 * separator between column headers in a DataGrid control. 259 * @default mx.skins.halo.DataGridHeaderSeparator 260 */ 261[Style(name="headerSeparatorSkin", type="Class", inherit="no")] 262 263/** 264 * The class to use as the skin that defines the appearance of the 265 * separator between rows in a DataGrid control. 266 * By default, the DataGrid control uses the 267 * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods 268 * to draw the separators. 269 * 270 * @default undefined 271 */ 272[Style(name="horizontalSeparatorSkin", type="Class", inherit="no")] 273 274/** 275 * The class to use as the skin that defines the appearance of the 276 * separator between the locked and unlocked rows in a DataGrid control. 277 * By default, the DataGrid control uses the 278 * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods 279 * to draw the separators. 280 * 281 * @default undefined 282 */ 283[Style(name="horizontalLockedSeparatorSkin", type="Class", inherit="no")] 284 285/** 286 * The class to use as the skin that defines the appearance of the 287 * separators between columns in a DataGrid control. 288 * By default, the DataGrid control uses the 289 * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods 290 * to draw the separators. 291 * 292 * @default undefined 293 */ 294[Style(name="verticalSeparatorSkin", type="Class", inherit="no")] 295 296/** 297 * The class to use as the skin that defines the appearance of the 298 * separator between the locked and unlocked columns in a DataGrid control. 299 * By default, the DataGrid control uses the 300 * <code>drawHorizontalLine()</code> and <code>drawVerticalLine()</code> methods 301 * to draw the separators. 302 * 303 * @default undefined 304 */ 305[Style(name="verticalLockedSeparatorSkin", type="Class", inherit="no")] 306 307/** 308 * The class to use as the skin for the arrow that indicates the column sort 309 * direction. 310 * @default mx.skins.halo.DataGridSortArrow 311 */ 312[Style(name="sortArrowSkin", type="Class", inherit="no")] 313 314/** 315 * The class to use as the skin for the cursor that indicates that a column 316 * can be resized. 317 * The default value is the "cursorStretch" symbol from the Assets.swf file. 318 */ 319[Style(name="stretchCursor", type="Class", inherit="no")] 320 321/** 322 * The class to use as the skin that indicates that 323 * a column can be dropped in the current location. 324 * 325 * @default mx.skins.halo.DataGridColumnDropIndicator 326 */ 327[Style(name="columnDropIndicatorSkin", type="Class", inherit="no")] 328 329/** 330 * The name of a CSS style declaration for controlling aspects of the 331 * appearance of column when the user is dragging it to another location. 332 * 333 * @default "headerDragProxyStyle" 334 */ 335[Style(name="headerDragProxyStyleName", type="String", inherit="no")] 336 337//-------------------------------------- 338// Excluded APIs 339//-------------------------------------- 340 341[Exclude(name="columnCount", kind="property")] 342[Exclude(name="iconField", kind="property")] 343[Exclude(name="iconFunction", kind="property")] 344[Exclude(name="labelField", kind="property")] 345[Exclude(name="offscreenExtraRowsOrColumns", kind="property")] 346[Exclude(name="offscreenExtraRows", kind="property")] 347[Exclude(name="offscreenExtraRowsTop", kind="property")] 348[Exclude(name="offscreenExtraRowsBottom", kind="property")] 349[Exclude(name="offscreenExtraColumns", kind="property")] 350[Exclude(name="offscreenExtraColumnsLeft", kind="property")] 351[Exclude(name="offscreenExtraColumnsRight", kind="property")] 352[Exclude(name="offscreenExtraRowsOrColumnsChanged", kind="property")] 353[Exclude(name="showDataTips", kind="property")] 354[Exclude(name="cornerRadius", kind="style")] 355 356//-------------------------------------- 357// Other metadata 358//-------------------------------------- 359 360[AccessibilityClass(implementation="mx.accessibility.DataGridAccImpl")] 361 362[DataBindingInfo("acceptedTypes", "{ dataProvider: "String" }")] 363 364[DefaultBindingProperty(source="selectedItem", destination="dataProvider")] 365 366[DefaultProperty("dataProvider")] 367 368[DefaultTriggerEvent("change")] 369 370[IconFile("DataGrid.png")] 371 372[RequiresDataBinding(true)] 373 374/** 375 * The <code>DataGrid</code> control is like a List except that it can 376 * show more than one column of data making it suited for showing 377 * objects with multiple properties. 378 * <p> 379 * The DataGrid control provides the following features: 380 * <ul> 381 * <li>Columns of different widths or identical fixed widths</li> 382 * <li>Columns that the user can resize at runtime </li> 383 * <li>Columns that the user can reorder at runtime </li> 384 * <li>Optional customizable column headers</li> 385 * <li>Ability to use a custom item renderer for any column to display 386 * data 387 * other than text</li> 388 * <li>Support for sorting the data by clicking on a column</li> 389 * </ul> 390 * </p> 391 * The DataGrid control is intended for viewing data, and not as a 392 * layout tool like an HTML table. 393 * The mx.containers package provides those layout tools. 394 * 395 * <p>The DataGrid control has the following default sizing 396 * characteristics:</p> 397 * <table class="innertable"> 398 * <tr> 399 * <th>Characteristic</th> 400 * <th>Description</th> 401 * </tr> 402 * <tr> 403 * <td>Default size</td> 404 * <td>If the columns are empty, the default width is 300 405 * pixels. If the columns contain information but define 406 * no explicit widths, the default width is 100 pixels 407 * per column. The DataGrid width is sized to fit the 408 * width of all columns, if possible. 409 * The default number of displayed rows, including the 410 * header is 7, and each row, by default, is 20 pixels 411 * high. 412 * </td> 413 * </tr> 414 * <tr> 415 * <td>Minimum size</td> 416 * <td>0 pixels.</td> 417 * </tr> 418 * <tr> 419 * <td>Maximum size</td> 420 * <td>5000 by 5000.</td> 421 * </tr> 422 * </table> 423 * 424 * @mxml 425 * <p> 426 * The <code><mx:DataGrid></code> tag inherits all of the tag 427 * attributes of its superclass, except for <code>labelField</code>, 428 * <code>iconField</code>, and <code>iconFunction</code>, and adds the 429 * following tag attributes: 430 * </p> 431 * <pre> 432 * <mx:DataGrid 433 * <b>Properties</b> 434 * columns="<i>From dataProvider</i>" 435 * draggableColumns="true|false" 436 * editable="false|true" 437 * editedItemPosition="<code>null</code>" 438 * horizontalScrollPosition="null" 439 * imeMode="null" 440 * itemEditorInstance="null" 441 * minColumnWidth="<code>NaN</code>" 442 * resizableColumns="true|false" 443 * sortableColumns="true|false" 444 * 445 * <b>Styles</b> 446 * backgroundDisabledColor="0xEFEEEF" 447 * columnDropIndicatorSkin="DataGridColumnDropIndicator" 448 * columnResizeSkin="DataGridColumnResizeSkin" 449 * disabledIconColor="0x999999" 450 * headerColors="[#FFFFFF, #E6E6E6]" 451 * headerDragProxyStyleName="headerDragProxyStyle" 452 * headerSeparatorSkin="DataGridHeaderSeparator" 453 * headerStyleName="dataGridStyles" 454 * horizontalGridLineColor="<i>No default</i>" 455 * horizontalGridLines="false|true" 456 * horizontalLockedSeparatorSkin="undefined" 457 * horizontalSeparatorSkin="undefined" 458 * iconColor="0x111111" 459 * rollOverColor="#E3FFD6" 460 * selectionColor="#CDFFC1" 461 * sortArrowSkin="DataGridSortArrow" 462 * stretchCursor="<i>"cursorStretch" symbol from the Assets.swf file</i>" 463 * verticalGridLineColor="#666666" 464 * verticalGridLines="false|true" 465 * verticalLockedSeparatorSkin="undefined" 466 * verticalSeparatorSkin="undefined" 467 * 468 * <b>Events</b> 469 * columnStretch="<i>No default</i>" 470 * headerRelease="<i>No default</i>" 471 * headerShift="<i>No default</i>" 472 * itemEditBegin="<i>No default</i>" 473 * itemEditBeginning="<i>No default</i>" 474 * itemEditEnd="<i>No default</i>" 475 * itemFocusIn="<i>No default</i>" 476 * itemFocusOut="<i>No default</i>" 477 * /> 478 * 479 * <b>The following DataGrid code sample specifies the column order:</b> 480 * <mx:DataGrid> 481 * <mx:dataProvider> 482 * <mx:Object Artist="Pavement" Price="11.99" 483 * Album="Slanted and Enchanted"/> 484 * <mx:Object Artist="Pavement" 485 * Album="Brighten the Corners" Price="11.99"/> 486 * </mx:dataProvider> 487 * <mx:columns> 488 * <mx:DataGridColumn dataField="Album"/> 489 * <mx:DataGridColumn dataField="Price"/> 490 * </mx:columns> 491 * </mx:DataGrid> 492 * </pre> 493 * </p> 494 * 495 * @see mx.controls.dataGridClasses.DataGridItemRenderer 496 * @see mx.controls.dataGridClasses.DataGridColumn 497 * @see mx.controls.dataGridClasses.DataGridDragProxy 498 * @see mx.events.DataGridEvent 499 * 500 * @includeExample examples/SimpleDataGrid.mxml 501 */ 502public class DataGrid extends DataGridBase implements IIMESupport 503{ 504 include "../core/Version.as"; 505 506 //-------------------------------------------------------------------------- 507 // 508 // Class mixins 509 // 510 //-------------------------------------------------------------------------- 511 512 /** 513 * @private 514 * Placeholder for mixin by DataGridAccImpl. 515 */ 516 mx_internal static var createAccessibilityImplementation:Function; 517 518 //-------------------------------------------------------------------------- 519 // 520 // Constructor 521 // 522 //-------------------------------------------------------------------------- 523 524 /** 525 * Constructor. 526 */ 527 public function DataGrid() 528 { 529 super(); 530 531 _columns = []; 532 533 // pick a default row height 534 setRowHeight(20); 535 536 // Register default handlers for item editing and sorting events. 537 538 addEventListener(DataGridEvent.ITEM_EDIT_BEGINNING, 539 itemEditorItemEditBeginningHandler, 540 false, EventPriority.DEFAULT_HANDLER); 541 542 addEventListener(DataGridEvent.ITEM_EDIT_BEGIN, 543 itemEditorItemEditBeginHandler, 544 false, EventPriority.DEFAULT_HANDLER); 545 546 addEventListener(DataGridEvent.ITEM_EDIT_END, 547 itemEditorItemEditEndHandler, 548 false, EventPriority.DEFAULT_HANDLER); 549 550 addEventListener(DataGridEvent.HEADER_RELEASE, 551 headerReleaseHandler, 552 false, EventPriority.DEFAULT_HANDLER); 553 554 addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler); 555 } 556 557 //-------------------------------------------------------------------------- 558 // 559 // Variables 560 // 561 //-------------------------------------------------------------------------- 562 563 [Inspectable(environment="none")] 564 565 /** 566 * A reference to the currently active instance of the item editor, 567 * if it exists. 568 * 569 * <p>To access the item editor instance and the new item value when an 570 * item is being edited, you use the <code>itemEditorInstance</code> 571 * property. The <code>itemEditorInstance</code> property 572 * is not valid until after the event listener for 573 * the <code>itemEditBegin</code> event executes. Therefore, you typically 574 * only access the <code>itemEditorInstance</code> property from within 575 * the event listener for the <code>itemEditEnd</code> event.</p> 576 * 577 * <p>The <code>DataGridColumn.itemEditor</code> property defines the 578 * class of the item editor 579 * and, therefore, the data type of the item editor instance.</p> 580 * 581 * <p>You do not set this property in MXML.</p> 582 */ 583 public var itemEditorInstance:IListItemRenderer; 584 585 /** 586 * A reference to the item renderer 587 * in the DataGrid control whose item is currently being edited. 588 * 589 * <p>From within an event listener for the <code>itemEditBegin</code> 590 * and <code>itemEditEnd</code> events, 591 * you can access the current value of the item being edited 592 * using the <code>editedItemRenderer.data</code> property.</p> 593 */ 594 public function get editedItemRenderer():IListItemRenderer 595 { 596 if (!itemEditorInstance) return null; 597 598 return actualContentHolder.listItems[actualRowIndex][actualColIndex]; 599 } 600 601 /** 602 * @private 603 * true if we want to skip updating the headers during adjustListContent 604 */ 605 private var skipHeaderUpdate:Boolean = false; 606 607 /** 608 * @private 609 * true if we want to block editing on mouseUp 610 */ 611 private var dontEdit:Boolean = false; 612 613 /** 614 * @private 615 * true if we want to block editing on mouseUp 616 */ 617 private var losingFocus:Boolean = false; 618 619 /** 620 * @private 621 * true if we're in the endEdit call. Used to handle 622 * some timing issues with collection updates 623 */ 624 private var inEndEdit:Boolean = false; 625 626 /** 627 * @private 628 * true if we've disabled updates in the collection 629 */ 630 private var collectionUpdatesDisabled:Boolean = false; 631 632 /** 633 * @private 634 * The index of the column being sorted. 635 */ 636 mx_internal var sortIndex:int = -1; 637 638 /** 639 * @private 640 * The column being sorted. 641 */ 642 private var sortColumn:DataGridColumn; 643 644 /** 645 * @private 646 * The direction of the sort 647 */ 648 mx_internal var sortDirection:String; 649 650 /** 651 * @private 652 * The index of the last column being sorted on. 653 */ 654 mx_internal var lastSortIndex:int = -1; 655 656 /** 657 * @private 658 */ 659 private var lastItemDown:IListItemRenderer; 660 661 /** 662 * @private 663 */ 664 private var lastItemFocused:DisplayObject; 665 666 /** 667 * @private 668 */ 669 private var displayWidth:Number; 670 671 /** 672 * @private 673 */ 674 private var lockedColumnWidth:Number = 0; 675 676 /** 677 * @private 678 * The column that is being moved. 679 */ 680 mx_internal var movingColumn:DataGridColumn; 681 682 /** 683 * @private 684 * The column that is being resized. 685 */ 686 mx_internal var resizingColumn:DataGridColumn; 687 688 /** 689 * @private 690 * Columns with visible="true" 691 */ 692 private var displayableColumns:Array; 693 694 /** 695 * @private 696 * Whether we have auto-generated the set of columns 697 * Defaults to true so we'll run the auto-generation at init time if needed 698 */ 699 private var generatedColumns:Boolean = true; 700 701 // last known position of item editor instance 702 private var actualRowIndex:int; 703 private var actualColIndex:int; 704 private var actualContentHolder:ListBaseContentHolder; 705 706 /** 707 * @private 708 * Flag to indicate whether sorting is manual or programmatic. If it's 709 * not manual, we try to draw the sort arrow on the right column header. 710 */ 711 private var manualSort:Boolean; 712 713 714 //-------------------------------------------------------------------------- 715 // 716 // Overridden properties 717 // 718 //-------------------------------------------------------------------------- 719 720 //---------------------------------- 721 // baselinePosition 722 //---------------------------------- 723 724 /** 725 * @private 726 * The baselinePosition of a DataGrid is calculated 727 * for its first column header. 728 * If the headers aren't shown, it is calculated as for List. 729 */ 730 override public function get baselinePosition():Number 731 { 732 if (FlexVersion.compatibilityVersion < FlexVersion.VERSION_3_0) 733 { 734 var top:Number = 0; 735 736 if (border && border is IRectangularBorder) 737 top = IRectangularBorder(border).borderMetrics.top; 738 739 return top + measureText(" ").ascent; 740 } 741 742 if (!validateBaselinePosition()) 743 return NaN; 744 745 if (!showHeaders) 746 return super.baselinePosition; 747 748 var header0:IUIComponent = DataGridHeader(header).rendererArray[0]; 749 if (!header0) 750 return super.baselinePosition; 751 752 return header.y + header0.y + header0.baselinePosition; 753 } 754 755 /** 756 * @private 757 * Number of columns that can be displayed. 758 * Some may be offscreen depending on horizontalScrollPolicy 759 * and the width of the DataGrid. 760 */ 761 override public function get columnCount():int 762 { 763 if (_columns) 764 return _columns.length; 765 else 766 return 0; 767 } 768 769 //---------------------------------- 770 // enabled 771 //---------------------------------- 772 773 [Inspectable(category="General", enumeration="true,false", defaultValue="true")] 774 775 /** 776 * @private 777 */ 778 override public function set enabled(value:Boolean):void 779 { 780 super.enabled = value; 781 782 if (header) 783 header.enabled = value; 784 785 if (itemEditorInstance) 786 endEdit(DataGridEventReason.OTHER); 787 788 invalidateDisplayList(); 789 } 790 791 //---------------------------------- 792 // headerHeight 793 //---------------------------------- 794 795 /** 796 * @private 797 */ 798 override public function set headerHeight(value:Number):void 799 { 800 super.headerHeight = value; 801 _originalHeaderHeight = isNaN(value) ? 22 : value; 802 _originalExplicitHeaderHeight = !isNaN(value); 803 } 804 805 //---------------------------------- 806 // horizontalScrollPosition 807 //---------------------------------- 808 809 /** 810 * The offset into the content from the left edge. 811 * This can be a pixel offset in some subclasses or some other metric 812 * like the number of columns in a DataGrid control. 813 * 814 * The DataGrid scrolls by columns so the value of the 815 * <code>horizontalScrollPosition</code> property is always 816 * in the range of 0 to the index of the columns 817 * that will make the last column visible. This is different from the 818 * List control that scrolls by pixels. The DataGrid control always aligns the left edge 819 * of a column with the left edge of the DataGrid control. 820 */ 821 override public function set horizontalScrollPosition(value:Number):void 822 { 823 // if not init or no data; 824 if (!initialized || listItems.length == 0) 825 { 826 super.horizontalScrollPosition = value; 827 return; 828 } 829 830 var oldValue:int = super.horizontalScrollPosition; 831 super.horizontalScrollPosition = value; 832 833 // columns have variable width so we need to recalc scroll parms 834 scrollAreaChanged = true; 835 836 columnsInvalid = true; 837 calculateColumnSizes(); 838 839 // we are going to get a full repaint so don't repaint now 840 if (itemsSizeChanged) 841 return; 842 843 if (oldValue != value) 844 { 845 removeClipMask(); 846 847 var bookmark:CursorBookmark; 848 849 if (iterator) 850 bookmark = iterator.bookmark; 851 852 clearIndicators(); 853 clearVisibleData(); 854 //if we scrolled more than the number of scrollable columns 855 makeRowsAndColumns(0, 0, listContent.width, listContent.height, 0, 0); 856 if (lockedRowCount) 857 { 858 var cursorPos:CursorBookmark; 859 cursorPos = lockedRowContent.iterator.bookmark; 860 makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount); 861 if (iteratorValid) 862 lockedRowContent.iterator.seek(cursorPos, 0); 863 } 864 865 if (headerVisible && header) 866 { 867 header.visibleColumns = visibleColumns; 868 header.headerItemsChanged = true; 869 header.invalidateSize(); 870 header.validateNow(); 871 } 872 873 if (iterator && bookmark) 874 iterator.seek(bookmark, 0); 875 876 invalidateDisplayList(); 877 878 addClipMask(false); 879 } 880 } 881 882 //---------------------------------- 883 // horizontalScrollPolicy 884 //---------------------------------- 885 886 /** 887 * @private 888 * Accomodates ScrollPolicy.AUTO. 889 * Makes sure column widths stay in synch. 890 * 891 * @param policy on, off, or auto 892 */ 893 override public function set horizontalScrollPolicy(value:String):void 894 { 895 super.horizontalScrollPolicy = value; 896 columnsInvalid = true; 897 itemsSizeChanged = true; 898 invalidateDisplayList(); 899 } 900 901 /** 902 * @private 903 */ 904 override public function set verticalScrollPosition(value:Number):void 905 { 906 skipHeaderUpdate = true; 907 908 var oldValue:Number = super.verticalScrollPosition; 909 super.verticalScrollPosition = value; 910 if (oldValue != value) 911 { 912 if (lockedColumnContent) 913 drawRowGraphics(lockedColumnContent) 914 } 915 skipHeaderUpdate = false; 916 } 917 918 /** 919 * @private 920 * 921 */ 922 override protected function createChildren():void 923 { 924 super.createChildren(); 925 926 if (!header) 927 { 928 header = new headerClass(); 929 header.styleName = this; 930 addChild(header); 931 } 932 } 933 934 //---------------------------------- 935 // imeMode 936 //---------------------------------- 937 938 /** 939 * @private 940 */ 941 private var _imeMode:String = null; 942 943 [Inspectable(environment="none")] 944 945 /** 946 * Specifies the IME (input method editor) mode. 947 * The IME enables users to enter text in Chinese, Japanese, and Korean. 948 * Flex sets the specified IME mode when the control gets the focus, 949 * and sets it back to the previous value when the control loses the focus. 950 * 951 * <p>The flash.system.IMEConversionMode class defines constants for the 952 * valid values for this property. 953 * You can also specify <code>null</code> to specify no IME.</p> 954 * 955 * @see flash.system.IMEConversionMode 956 * 957 * @default null 958 */ 959 public function get imeMode():String 960 { 961 return _imeMode; 962 } 963 964 /** 965 * @private 966 */ 967 public function set imeMode(value:String):void 968 { 969 _imeMode = value; 970 } 971 972 //---------------------------------- 973 // itemRenderer 974 //---------------------------------- 975 976 /** 977 * @private 978 * 979 * Defer creations of the class factory 980 * to give a chance for the moduleFactory to be set. 981 */ 982 override public function get itemRenderer():IFactory 983 { 984 if (super.itemRenderer == null) 985 { 986 var fontName:String = 987 StringUtil.trimArrayElements(getStyle("fontFamily"), ","); 988 var fontWeight:String = getStyle("fontWeight"); 989 var fontStyle:String = getStyle("fontStyle"); 990 var bold:Boolean = (fontWeight == "bold"); 991 var italic:Boolean = (fontStyle == "italic"); 992 993 var flexModuleFactory:IFlexModuleFactory = 994 getFontContext(fontName, bold, italic); 995 996 super.itemRenderer = new ContextualClassFactory( 997 DataGridItemRenderer, flexModuleFactory); 998 } 999 1000 return super.itemRenderer; 1001 } 1002 1003 //---------------------------------- 1004 // minColumnWidth 1005 //---------------------------------- 1006 1007 /** 1008 * @private 1009 */ 1010 private var _minColumnWidth:Number; 1011 1012 /** 1013 * @private 1014 */ 1015 private var minColumnWidthInvalid:Boolean = false; 1016 1017 [Inspectable(defaultValue="NaN")] 1018 1019 /** 1020 * The minimum width of the columns, in pixels. If not NaN, 1021 * the DataGrid control applies this value as the minimum width for 1022 * all columns. Otherwise, individual columns can have 1023 * their own minimum widths. 1024 * 1025 * @default NaN 1026 */ 1027 public function get minColumnWidth():Number 1028 { 1029 return _minColumnWidth; 1030 } 1031 1032 /** 1033 * @private 1034 */ 1035 public function set minColumnWidth(value:Number):void 1036 { 1037 _minColumnWidth = value; 1038 minColumnWidthInvalid = true; 1039 itemsSizeChanged = true; 1040 columnsInvalid = true; 1041 invalidateDisplayList(); 1042 } 1043 1044 //-------------------------------------------------------------------------- 1045 // 1046 // Properties 1047 // 1048 //-------------------------------------------------------------------------- 1049 1050 //---------------------------------- 1051 // columns 1052 //---------------------------------- 1053 1054 /** 1055 * @private 1056 */ 1057 private var _columns:Array; // the array of our DataGridColumns 1058 1059 [Bindable("columnsChanged")] 1060 [Inspectable(category="General", arrayType="mx.controls.dataGridClasses.DataGridColumn")] 1061 1062 /** 1063 * An array of DataGridColumn objects, one for each column that 1064 * can be displayed. If not explicitly set, the DataGrid control 1065 * attempts to examine the first data provider item to determine the 1066 * set of properties and display those properties in alphabetic 1067 * order. 1068 * 1069 * <p>If you want to change the set of columns, you must get this array, 1070 * make modifications to the columns and order of columns in the array, 1071 * and then assign the new array to the columns property. This is because 1072 * the DataGrid control returned a new copy of the array of columns and therefore 1073 * did not notice the changes.</p> 1074 */ 1075 override public function get columns():Array 1076 { 1077 return _columns.slice(0); 1078 } 1079 1080 /** 1081 * @private 1082 */ 1083 override public function set columns(value:Array):void 1084 { 1085 var n:int; 1086 var i:int; 1087 1088 n = _columns.length; 1089 for (i = 0; i < n; i++) 1090 { 1091 columnRendererChanged(_columns[i]); 1092 } 1093 1094 freeItemRenderersTable = new Dictionary(false); 1095 columnMap = {}; 1096 1097 _columns = value.slice(0); 1098 columnsInvalid = true; 1099 generatedColumns = false; 1100 1101 n = value.length; 1102 for (i = 0; i < n; i++) 1103 { 1104 var column:DataGridColumn = _columns[i]; 1105 column.owner = this; 1106 column.colNum = i; 1107 if (column.cachedHeaderRenderer) 1108 { 1109 var item:DisplayObject = column.cachedHeaderRenderer as DisplayObject 1110 if (item.parent) 1111 item.parent.removeChild(item); 1112 column.cachedHeaderRenderer = null; 1113 } 1114 } 1115 1116 updateSortIndexAndDirection(); 1117 1118 itemsSizeChanged = true; 1119 invalidateDisplayList(); 1120 dispatchEvent(new Event("columnsChanged")); 1121 } 1122 1123 //---------------------------------- 1124 // draggableColumns 1125 //---------------------------------- 1126 1127 /** 1128 * @private 1129 * Storage for the draggableColumns property. 1130 */ 1131 private var _draggableColumns:Boolean = true; 1132 1133 [Inspectable(defaultValue="true")] 1134 1135 /** 1136 * A flag that indicates whether the user is allowed to reorder columns. 1137 * If <code>true</code>, the user can reorder the columns 1138 * of the DataGrid control by dragging the header cells. 1139 * 1140 * @default true 1141 */ 1142 public function get draggableColumns():Boolean 1143 { 1144 return _draggableColumns; 1145 } 1146 1147 /** 1148 * @private 1149 */ 1150 public function set draggableColumns(value:Boolean):void 1151 { 1152 _draggableColumns = value; 1153 } 1154 1155 //---------------------------------- 1156 // editable 1157 //---------------------------------- 1158 1159 [Inspectable(category="General")] 1160 1161 /** 1162 * A flag that indicates whether or not the user can edit 1163 * items in the data provider. 1164 * If <code>true</code>, the item renderers in the control are editable. 1165 * The user can click on an item renderer to open an editor. 1166 * 1167 * <p>You can turn off editing for individual columns of the 1168 * DataGrid control using the <code>DataGridColumn.editable</code> property, 1169 * or by handling the <code>itemEditBeginning</code> and 1170 * <code>itemEditBegin</code> events</p> 1171 * 1172 * @default false 1173 */ 1174 public var editable:Boolean = false; 1175 1176 //---------------------------------- 1177 // editedItemPosition 1178 //---------------------------------- 1179 1180 /** 1181 * @private 1182 */ 1183 private var bEditedItemPositionChanged:Boolean = false; 1184 1185 /** 1186 * @private 1187 * undefined means we've processed it 1188 * null means don't put up an editor 1189 * {} is the coordinates for the editor 1190 */ 1191 private var _proposedEditedItemPosition:*; 1192 1193 /** 1194 * @private 1195 * the last editedItemPosition and the last 1196 * position where editing was attempted if editing 1197 * was cancelled. We restore editing 1198 * to this point if we get focus from the TAB key 1199 */ 1200 private var lastEditedItemPosition:*; 1201 1202 /** 1203 * @private 1204 */ 1205 private var _editedItemPosition:Object; 1206 1207 /** 1208 * @private 1209 */ 1210 private var itemEditorPositionChanged:Boolean = false; 1211 1212 1213 [Bindable("itemFocusIn")] 1214 1215 /** 1216 * The column and row index of the item renderer for the 1217 * data provider item being edited, if any. 1218 * 1219 * <p>This Object has two fields, <code>columnIndex</code> and 1220 * <code>rowIndex</code>, 1221 * the zero-based column and row indexes of the item. 1222 * For example: {columnIndex:2, rowIndex:3}</p> 1223 * 1224 * <p>Setting this property scrolls the item into view and 1225 * dispatches the <code>itemEditBegin</code> event to 1226 * open an item editor on the specified item renderer.</p> 1227 * 1228 * @default null 1229 */ 1230 public function get editedItemPosition():Object 1231 { 1232 if (_editedItemPosition) 1233 return {rowIndex: _editedItemPosition.rowIndex, 1234 columnIndex: _editedItemPosition.columnIndex}; 1235 else 1236 return _editedItemPosition; 1237 } 1238 1239 /** 1240 * @private 1241 */ 1242 public function set editedItemPosition(value:Object):void 1243 { 1244 if (!value) 1245 { 1246 setEditedItemPosition(null); 1247 return; 1248 } 1249 1250 var newValue:Object = {rowIndex: value.rowIndex, 1251 columnIndex: value.columnIndex}; 1252 1253 setEditedItemPosition(newValue); 1254 } 1255 1256 1257 //---------------------------------- 1258 // resizableColumns 1259 //---------------------------------- 1260 1261 [Inspectable(category="General")] 1262 1263 /** 1264 * A flag that indicates whether the user can change the size of the 1265 * columns. 1266 * If <code>true</code>, the user can stretch or shrink the columns of 1267 * the DataGrid control by dragging the grid lines between the header cells. 1268 * If <code>true</code>, individual columns must also have their 1269 * <code>resizable</code> properties set to <code>false</code> to 1270 * prevent the user from resizing a particular column. 1271 * 1272 * @default true 1273 */ 1274 public var resizableColumns:Boolean = true; 1275 1276 //---------------------------------- 1277 // sortableColumns 1278 //---------------------------------- 1279 1280 [Inspectable(category="General")] 1281 1282 /** 1283 * A flag that indicates whether the user can sort the data provider items 1284 * by clicking on a column header cell. 1285 * If <code>true</code>, the user can sort the data provider items by 1286 * clicking on a column header cell. 1287 * The <code>DataGridColumn.dataField</code> property of the column 1288 * or the <code>DataGridColumn.sortCompareFunction</code> property 1289 * of the column is used as the sort field. 1290 * If a column is clicked more than once 1291 * the sort alternates between ascending and descending order. 1292 * If <code>true</code>, individual columns can be made to not respond 1293 * to a click on a header by setting the column's <code>sortable</code> 1294 * property to <code>false</code>. 1295 * 1296 * <p>When a user releases the mouse button over a header cell, the DataGrid 1297 * control dispatches a <code>headerRelease</code> event if both 1298 * this property and the column's sortable property are <code>true</code>. 1299 * If no handler calls the <code>preventDefault()</code> method on the event, the 1300 * DataGrid sorts using that column's <code>DataGridColumn.dataField</code> or 1301 * <code>DataGridColumn.sortCompareFunction</code> properties.</p> 1302 * 1303 * @default true 1304 * 1305 * @see mx.controls.dataGridClasses.DataGridColumn#dataField 1306 * @see mx.controls.dataGridClasses.DataGridColumn#sortCompareFunction 1307 */ 1308 public var sortableColumns:Boolean = true; 1309 1310 //-------------------------------------------------------------------------- 1311 // 1312 // Overridden methods 1313 // 1314 //-------------------------------------------------------------------------- 1315 1316 /** 1317 * @private 1318 */ 1319 override public function invalidateDisplayList():void 1320 { 1321 super.invalidateDisplayList(); 1322 if (header) 1323 { 1324 header.headerItemsChanged = true; 1325 header.invalidateSize(); 1326 header.invalidateDisplayList(); 1327 } 1328 if (lockedColumnHeader) 1329 { 1330 lockedColumnHeader.headerItemsChanged = true; 1331 lockedColumnHeader.invalidateSize(); 1332 lockedColumnHeader.invalidateDisplayList(); 1333 } 1334 } 1335 1336 [Inspectable(category="Data", defaultValue="undefined")] 1337 1338 /** 1339 * @private 1340 */ 1341 override public function set dataProvider(value:Object):void 1342 { 1343 if (itemEditorInstance) 1344 endEdit(DataGridEventReason.OTHER); 1345 1346 lastEditedItemPosition = null; 1347 1348 super.dataProvider = value; 1349 } 1350 1351 /** 1352 * @private 1353 */ 1354 override protected function initializeAccessibility():void 1355 { 1356 if (DataGrid.createAccessibilityImplementation != null) 1357 DataGrid.createAccessibilityImplementation(this); 1358 } 1359 1360 /** 1361 * @private 1362 * Measures the DataGrid based on its contents, 1363 * summing the total of the visible column widths. 1364 */ 1365 override protected function measure():void 1366 { 1367 super.measure(); 1368 1369 if (explicitRowCount != -1) 1370 { 1371 measuredHeight += headerHeight; 1372 measuredMinHeight += headerHeight; 1373 } 1374 1375 var o:EdgeMetrics = viewMetrics; 1376 1377 var n:int = columns.length; 1378 if (n == 0) 1379 { 1380 measuredWidth = DEFAULT_MEASURED_WIDTH; 1381 measuredMinWidth = DEFAULT_MEASURED_MIN_WIDTH; 1382 return; 1383 } 1384 1385 var columnWidths:Number = 0; 1386 var columnMinWidths:Number = 0; 1387 for (var i:int = 0; i < n; i++) 1388 { 1389 if (columns[i].visible) 1390 { 1391 columnWidths += columns[i].preferredWidth; 1392 if (isNaN(_minColumnWidth)) 1393 columnMinWidths += columns[i].minWidth; 1394 } 1395 } 1396 1397 if (!isNaN(_minColumnWidth)) 1398 columnMinWidths = n * _minColumnWidth; 1399 1400 measuredWidth = columnWidths + o.left + o.right; 1401 measuredMinWidth = columnMinWidths + o.left + o.right; 1402 1403 // factor out scrollbars if policy == AUTO. See Container.viewMetrics 1404 if (verticalScrollPolicy == ScrollPolicy.AUTO && 1405 verticalScrollBar && verticalScrollBar.visible) 1406 { 1407 measuredWidth -= verticalScrollBar.minWidth; 1408 measuredMinWidth -= verticalScrollBar.minWidth; 1409 } 1410 if (horizontalScrollPolicy == ScrollPolicy.AUTO && 1411 horizontalScrollBar && horizontalScrollBar.visible) 1412 { 1413 measuredHeight -= horizontalScrollBar.minHeight; 1414 measuredMinHeight -= horizontalScrollBar.minHeight; 1415 } 1416 1417 } 1418 1419 /** 1420 * @private 1421 * Sizes and positions the column headers, columns, and items based on the 1422 * size of the DataGrid. 1423 */ 1424 override protected function updateDisplayList(unscaledWidth:Number, 1425 unscaledHeight:Number):void 1426 { 1427 // Note: We can't immediately call super.updateDisplayList() 1428 // because the visibleColumns array must be populated first. 1429 1430 // trace(">>updateDisplayList"); 1431 if (displayWidth != unscaledWidth - viewMetrics.right - viewMetrics.left) 1432 { 1433 displayWidth = unscaledWidth - viewMetrics.right - viewMetrics.left; 1434 columnsInvalid = true; 1435 } 1436 1437 calculateColumnSizes(); 1438 1439 if (itemEditorPositionChanged) 1440 { 1441 itemEditorPositionChanged = false; 1442 // don't do this if mouse is down on an item 1443 // on mouse up, we'll let the edit session logic 1444 // request a new position 1445 if (!lastItemDown) 1446 scrollToEditedItem(editedItemPosition.rowIndex, editedItemPosition.colIndex); 1447 } 1448 1449 super.updateDisplayList(unscaledWidth, unscaledHeight); 1450 1451 if (collection && collection.length) 1452 { 1453 setRowCount(listItems.length); 1454 1455 if (listItems.length) 1456 setColumnCount(listItems[0].length); 1457 else 1458 setColumnCount(0); 1459 } 1460 1461 // If we have a vScroll only, we want the scrollbar to be below 1462 // the header. 1463 if (verticalScrollBar != null && verticalScrollBar.visible && 1464 (horizontalScrollBar == null || !horizontalScrollBar.visible) && 1465 headerVisible) 1466 { 1467 var hh:Number = header.height; 1468 var bm:EdgeMetrics = borderMetrics; 1469 1470 if (roomForScrollBar(verticalScrollBar, 1471 unscaledWidth-bm.left-bm.right, 1472 unscaledHeight-hh-bm.top-bm.bottom)) 1473 { 1474 verticalScrollBar.move(verticalScrollBar.x, viewMetrics.top + hh); 1475 verticalScrollBar.setActualSize( 1476 verticalScrollBar.width, 1477 unscaledHeight - viewMetrics.top - viewMetrics.bottom - hh); 1478 verticalScrollBar.visible = true; 1479 headerMask.width += verticalScrollBar.getExplicitOrMeasuredWidth(); 1480 1481 if (!DataGridHeader(header).needRightSeparator) 1482 { 1483 header.invalidateDisplayList(); 1484 DataGridHeader(header).needRightSeparator = true; 1485 } 1486 } 1487 else 1488 { 1489 if (DataGridHeader(header).needRightSeparator) 1490 { 1491 header.invalidateDisplayList(); 1492 DataGridHeader(header).needRightSeparator = false; 1493 } 1494 } 1495 } 1496 else 1497 { 1498 if (DataGridHeader(header).needRightSeparator) 1499 { 1500 header.invalidateDisplayList(); 1501 DataGridHeader(header).needRightSeparator = false; 1502 } 1503 } 1504 1505 if (bEditedItemPositionChanged) 1506 { 1507 bEditedItemPositionChanged = false; 1508 // don't do this if mouse is down on an item 1509 // on mouse up, we'll let the edit session logic 1510 // request a new position 1511 if (!lastItemDown) 1512 commitEditedItemPosition(_proposedEditedItemPosition); 1513 _proposedEditedItemPosition = undefined; 1514 itemsSizeChanged = false; 1515 } 1516 1517 drawRowBackgrounds(); 1518 drawLinesAndColumnBackgrounds(); 1519 1520 if (lockedRowCount && lockedRowContent) 1521 { 1522 drawRowGraphics(lockedRowContent); 1523 drawLinesAndColumnGraphics(lockedRowContent, visibleColumns, { bottom: 1}); 1524 if (lockedColumnCount) 1525 { 1526 drawRowGraphics(lockedColumnAndRowContent); 1527 drawLinesAndColumnGraphics(lockedColumnAndRowContent, visibleLockedColumns, { right: 1, bottom: 1}); 1528 } 1529 } 1530 if (lockedColumnCount) 1531 { 1532 drawRowGraphics(lockedColumnContent) 1533 drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1}) 1534 } 1535 1536 // trace("<<updateDisplayList"); 1537 } 1538 1539 /** 1540 * @private 1541 */ 1542 override protected function makeRowsAndColumns(left:Number, top:Number, 1543 right:Number, bottom:Number, 1544 firstCol:int, firstRow:int, 1545 byCount:Boolean = false, rowsNeeded:uint = 0):Point 1546 { 1547 allowItemSizeChangeNotification = false; 1548 1549 var pt:Point = super.makeRowsAndColumns(left, top, right, bottom, 1550 firstCol, firstRow, byCount, rowsNeeded); 1551 if (itemEditorInstance) 1552 { 1553 actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance), 1554 actualContentHolder.numChildren - 1); 1555 var col:DataGridColumn; 1556 if (lockedColumnCount && editedItemPosition.columnIndex && visibleLockedColumns[lockedColumnCount - 1].colNum) 1557 col = visibleLockedColumns[actualColIndex]; 1558 else 1559 col = visibleColumns[actualColIndex]; 1560 1561 var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex]; 1562 var rowData:ListRowInfo = actualContentHolder.rowInfo[actualRowIndex]; 1563 if (item && !col.rendererIsEditor) 1564 { 1565 var dx:Number = col.editorXOffset; 1566 var dy:Number = col.editorYOffset; 1567 var dw:Number = col.editorWidthOffset; 1568 var dh:Number = col.editorHeightOffset; 1569 itemEditorInstance.move(item.x + dx, rowData.y + dy); 1570 itemEditorInstance.setActualSize(Math.min(col.width + dw, actualContentHolder.width - itemEditorInstance.x), 1571 Math.min(rowData.height + dh, actualContentHolder.height - itemEditorInstance.y)); 1572 item.visible = false; 1573 1574 } 1575 1576 var lines:Sprite = Sprite(actualContentHolder.getChildByName("lines")); 1577 if (lines) 1578 actualContentHolder.setChildIndex(lines, actualContentHolder.numChildren - 1); 1579 } 1580 1581 allowItemSizeChangeNotification = variableRowHeight; 1582 return pt; 1583 } 1584 1585 /** 1586 * @private 1587 */ 1588 override protected function commitProperties():void 1589 { 1590 super.commitProperties(); 1591 1592 if (itemsNeedMeasurement) 1593 { 1594 itemsNeedMeasurement = false; 1595 if (isNaN(explicitRowHeight)) 1596 { 1597 if (iterator && columns.length > 0) 1598 { 1599 //set DataGridBase.visibleColumns to the set of 1600 //all columns 1601 visibleColumns = columns; 1602 columnsInvalid = true; 1603 1604 var paddingTop:Number = getStyle("paddingTop"); 1605 var paddingBottom:Number = getStyle("paddingBottom"); 1606 1607 var data:Object = iterator.current; 1608 var item:IListItemRenderer; 1609 var c:DataGridColumn; 1610 var ch:Number = 0; 1611 var n:int = columns.length; 1612 for (var j:int = 0; j < n; j++) 1613 { 1614 c = columns[j]; 1615 1616 if (!c.visible) 1617 continue; 1618 1619 item = c.getMeasuringRenderer(false, data); 1620 if (DisplayObject(item).parent == null) 1621 listContent.addChild(DisplayObject(item)); 1622 setupRendererFromData(c, item, data); 1623 ch = Math.max(ch, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); 1624 } 1625 1626 // unless specified otherwise, rowheight defaults to 20 1627 setRowHeight(Math.max(ch, 20)); 1628 } 1629 else 1630 setRowHeight(20); 1631 } 1632 } 1633 } 1634 1635 /** 1636 * @private 1637 * Instead of measuring the items, we measure the visible columns instead. 1638 */ 1639 override public function measureWidthOfItems(index:int = -1, count:int = 0):Number 1640 { 1641 var w:Number = 0; 1642 1643 var n:int = columns ? columns.length : 0; 1644 for (var i:int = 0; i < n; i++) 1645 { 1646 if (columns[i].visible) 1647 w += columns[i].width; 1648 } 1649 1650 return w; 1651 } 1652 1653 /** 1654 * @private 1655 */ 1656 1657 mx_internal function setupRendererFromData(c:DataGridColumn, item:IListItemRenderer, data:Object):void 1658 { 1659 var rowData:DataGridListData = DataGridListData(makeListData(data, itemToUID(data), 0, c.colNum, c)); 1660 if (item is IDropInListItemRenderer) 1661 IDropInListItemRenderer(item).listData = data ? rowData : null; 1662 item.data = data; 1663 item.explicitWidth = getWidthOfItem(item, c); 1664 UIComponentGlobals.layoutManager.validateClient(item, true); 1665 } 1666 1667 /** 1668 * @private 1669 */ 1670 override public function measureHeightOfItems(index:int = -1, count:int = 0):Number 1671 { 1672 return measureHeightOfItemsUptoMaxHeight(index, count); 1673 } 1674 1675 /** 1676 * @private 1677 */ 1678 mx_internal function measureHeightOfItemsUptoMaxHeight(index:int = -1, count:int = 0, maxHeight:Number = -1):Number 1679 { 1680 if (!columns.length) 1681 return rowHeight * count; 1682 1683 var h:Number = 0; 1684 1685 var item:IListItemRenderer; 1686 var c:DataGridColumn; 1687 var ch:Number = 0; 1688 var n:int; 1689 var j:int; 1690 1691 var paddingTop:Number = getStyle("paddingTop"); 1692 var paddingBottom:Number = getStyle("paddingBottom"); 1693 1694 var lockedCount:int = lockedRowCount; 1695 1696 if (headerVisible && count > 0 && index == -1) 1697 { 1698 h = calculateHeaderHeight(); 1699 1700 if (maxHeight != -1 && h > maxHeight) 1701 { 1702 setRowCount(0); 1703 return 0; 1704 } 1705 1706 // trace(this + " header preferredHeight = " + h); 1707 count --; 1708 index = 0; 1709 } 1710 1711 var bookmark:CursorBookmark = (iterator) ? iterator.bookmark : null; 1712 1713 var bMore:Boolean = iterator != null; 1714 if (index != -1 && iterator) 1715 { 1716 try 1717 { 1718 iterator.seek(CursorBookmark.FIRST, index); 1719 } 1720 catch(e:ItemPendingError) 1721 { 1722 bMore = false; 1723 } 1724 } 1725 1726 if (lockedCount > 0) 1727 { 1728 try 1729 { 1730 collectionIterator.seek(CursorBookmark.FIRST,0); 1731 } 1732 catch(e:ItemPendingError) 1733 { 1734 bMore = false; 1735 } 1736 } 1737 1738 for (var i:int = 0; i < count; i++) 1739 { 1740 var data:Object; 1741 if (bMore) 1742 { 1743 data = (lockedCount > 0) ? collectionIterator.current : iterator.current; 1744 ch = 0; 1745 n = columns.length; 1746 for (j = 0; j < n; j++) 1747 { 1748 c = columns[j]; 1749 1750 if (!c.visible) 1751 continue; 1752 1753 item = c.getMeasuringRenderer(false, data); 1754 if (DisplayObject(item).parent == null) 1755 listContent.addChild(DisplayObject(item)); 1756 setupRendererFromData(c, item, data); 1757 ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); 1758 } 1759 } 1760 1761 if (maxHeight != -1 && (h + ch > maxHeight || !bMore)) 1762 { 1763 try 1764 { 1765 if (iterator) 1766 iterator.seek(bookmark, 0); 1767 } 1768 catch(e:ItemPendingError) 1769 { 1770 // we don't recover here since we'd only get here if the first seek failed. 1771 } 1772 count = (headerVisible) ? i + 1 : i; 1773 setRowCount(count); 1774 return h; 1775 } 1776 1777 h += ch; 1778 if (iterator) 1779 { 1780 try 1781 { 1782 bMore = iterator.moveNext(); 1783 if (lockedCount > 0) 1784 { 1785 collectionIterator.moveNext(); 1786 lockedCount--; 1787 } 1788 } 1789 catch(e:ItemPendingError) 1790 { 1791 // if we run out of data, assume all remaining rows are the size of the previous row 1792 bMore = false; 1793 } 1794 } 1795 } 1796 1797 if (iterator) 1798 { 1799 try 1800 { 1801 iterator.seek(bookmark, 0); 1802 } 1803 catch(e:ItemPendingError) 1804 { 1805 // we don't recover here since we'd only get here if the first seek failed. 1806 } 1807 } 1808 1809 // trace("calcheight = " + h); 1810 return h; 1811 } 1812 1813 /** 1814 * @private 1815 */ 1816 mx_internal function calculateHeaderHeight():Number 1817 { 1818 if (!columns.length) 1819 return rowHeight; 1820 1821 // block bad behavior from PDG 1822 if (!listContent) 1823 return rowHeight; 1824 1825 var item:IListItemRenderer; 1826 var c:DataGridColumn; 1827 var rowData:DataGridListData; 1828 var ch:Number = 0; 1829 var n:int; 1830 var j:int; 1831 1832 var paddingTop:Number = getStyle("paddingTop"); 1833 var paddingBottom:Number = getStyle("paddingBottom"); 1834 1835 if (showHeaders) 1836 { 1837 ch = 0; 1838 n = columns.length; 1839 1840 if (_headerWordWrapPresent) 1841 { 1842 _headerHeight = _originalHeaderHeight; 1843 _explicitHeaderHeight = _originalExplicitHeaderHeight; 1844 } 1845 1846 for (j = 0; j < n; j++) 1847 { 1848 c = columns[j]; 1849 1850 if (!c.visible) 1851 continue; 1852 1853 item = c.cachedHeaderRenderer; 1854 if (!item) 1855 { 1856 item = createColumnItemRenderer(c, true, c); 1857 item.styleName = c; 1858 c.cachedHeaderRenderer = item; 1859 } 1860 if (DisplayObject(item).parent == null) 1861 { 1862 listContent.addChild(DisplayObject(item)); 1863 item.visible = false; 1864 } 1865 rowData = DataGridListData(makeListData(c, uid, 0, c.colNum, c)); 1866 rowMap[item.name] = rowData; 1867 if (item is IDropInListItemRenderer) 1868 IDropInListItemRenderer(item).listData = rowData; 1869 item.data = c; 1870 item.explicitWidth = c.width; 1871 UIComponentGlobals.layoutManager.validateClient(item, true); 1872 ch = Math.max(ch, _explicitHeaderHeight ? headerHeight : item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); 1873 1874 if (columnHeaderWordWrap(c)) 1875 _headerWordWrapPresent = true; 1876 } 1877 1878 if (_headerWordWrapPresent) 1879 { 1880 // take backups 1881 _originalHeaderHeight = _headerHeight; 1882 _originalExplicitHeaderHeight = _explicitHeaderHeight; 1883 1884 _headerHeight = ch; 1885 _explicitHeaderHeight = true; 1886 } 1887 } 1888 return ch; 1889 } 1890 1891 private var _headerWordWrapPresent:Boolean = false; 1892 private var _originalExplicitHeaderHeight:Boolean = false; 1893 private var _originalHeaderHeight:Number = 0; 1894 1895 /** 1896 * @private 1897 */ 1898 override protected function calculateRowHeight(data:Object, hh:Number, skipVisible:Boolean = false):Number 1899 { 1900 var item:IListItemRenderer; 1901 var c:DataGridColumn; 1902 1903 var n:int = columns.length; 1904 var j:int; 1905 var k:int = 0; 1906 var l:int = visibleLockedColumns.length; 1907 1908 if (skipVisible && visibleColumns.length == _columns.length) 1909 return hh; 1910 1911 var paddingTop:Number = getStyle("paddingTop"); 1912 var paddingBottom:Number = getStyle("paddingBottom"); 1913 1914 for (j = 0; j < n; j++) 1915 { 1916 // skip any columns that are visible 1917 if (skipVisible && k < l && visibleLockedColumns[k].colNum == columns[j].colNum) 1918 { 1919 k++; 1920 continue; 1921 } 1922 if (skipVisible && k - l < visibleColumns.length && visibleColumns[k - l].colNum == columns[j].colNum) 1923 { 1924 k++; 1925 continue; 1926 } 1927 c = columns[j]; 1928 1929 if (!c.visible) 1930 continue; 1931 1932 item = c.getMeasuringRenderer(false, data); 1933 if (DisplayObject(item).parent == null) 1934 listContent.addChild(DisplayObject(item)); 1935 setupRendererFromData(c, item, data); 1936 hh = Math.max(hh, item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop); 1937 } 1938 return hh; 1939 } 1940 1941 /** 1942 * @private 1943 */ 1944 override protected function scrollHandler(event:Event):void 1945 { 1946 if (event.target == verticalScrollBar || 1947 event.target == horizontalScrollBar) 1948 { 1949 // TextField.scroll bubbles so you might see it here 1950 if (event is ScrollEvent) 1951 { 1952 if (!liveScrolling && 1953 ScrollEvent(event).detail == ScrollEventDetail.THUMB_TRACK) 1954 { 1955 return; 1956 } 1957 1958 if (itemEditorInstance) 1959 endEdit(DataGridEventReason.OTHER); 1960 1961 var scrollBar:ScrollBar = ScrollBar(event.target); 1962 var pos:Number = scrollBar.scrollPosition; 1963 1964 if (scrollBar == verticalScrollBar) 1965 verticalScrollPosition = pos; 1966 else if (scrollBar == horizontalScrollBar) 1967 horizontalScrollPosition = pos; 1968 1969 super.scrollHandler(event); 1970 } 1971 } 1972 } 1973 1974 private function displayingPartialRow():Boolean 1975 { 1976 var index:int = listItems.length - 1 - offscreenExtraRowsBottom; 1977 if (rowInfo[index].y + rowInfo[index].height > listContent.heightExcludingOffsets - listContent.topOffset) 1978 return true; 1979 return false; 1980 } 1981 1982 /** 1983 * @private 1984 */ 1985 override protected function configureScrollBars():void 1986 { 1987 if (columnsInvalid) 1988 return; 1989 1990 if (!displayableColumns) 1991 return; 1992 1993 // for purposes of computing rows, we need to accomodate 1994 // the case where all the visible columns are locked columns 1995 var countableContentListItems:Array = this.listItems; 1996 if (visibleColumns && !visibleColumns.length && visibleLockedColumns && visibleLockedColumns.length) 1997 countableContentListItems = lockedColumnContent.listItems; 1998 1999 var oldHorizontalScrollBar:Object = horizontalScrollBar; 2000 var oldVerticalScrollBar:Object = verticalScrollBar; 2001 2002 var rowCount:int = countableContentListItems.length; 2003 if (rowCount == 0) 2004 { 2005 // Get rid of any existing scrollbars. 2006 if (oldHorizontalScrollBar || oldVerticalScrollBar) 2007 // protect against situation where the scrollbars 2008 // cause re-layout and the listContent is sized 2009 // to zero because of number of lockedRowCount 2010 if (listContent.height) 2011 setScrollBarProperties(0, 0, 0, 0); 2012 2013 return; 2014 } 2015 2016 // partial last rows don't count 2017 if (rowCount > 1 && displayingPartialRow()) 2018 rowCount--; 2019 2020 // offset, when added to rowCount, is the index of the dataProvider 2021 // item for that row. IOW, row 10 in listItems is showing dataProvider 2022 // item 10 + verticalScrollPosition - lockedRowCount; 2023 var offset:int = verticalScrollPosition; 2024 // don't count filler rows at the bottom either. 2025 var fillerRows:int = 0; 2026 while (rowCount && countableContentListItems[rowCount - 1].length == 0) 2027 { 2028 // as long as we're past the end of the collection, add up 2029 // fillerRows 2030 if (collection && rowCount + offset >= collection.length - lockedRowCount) 2031 { 2032 rowCount--; 2033 ++fillerRows; 2034 } 2035 else 2036 break; 2037 } 2038 2039 // we have to scroll up. We can't have filler rows unless the scrollPosition is 0 2040 if (verticalScrollPosition > 0 && fillerRows > 0) 2041 { 2042 if (adjustVerticalScrollPositionDownward(Math.max(rowCount, 1))) 2043 return; 2044 } 2045 2046 rowCount -= (offscreenExtraRowsTop + offscreenExtraRowsBottom); 2047 2048 var collectionHasRows:Boolean = collection && collection.length > 0; 2049 2050 var colCount:int = (collectionHasRows && rowCount > 0) ? listItems[0].length : visibleColumns.length; 2051 2052 // if the last column is visible and partially offscreen (but it isn't the only 2053 // column) then adjust the column count so we can scroll to see it 2054 if (collectionHasRows && rowCount > 0 && colCount > 1 && 2055 listItems[0][colCount - 1].x + 2056 visibleColumns[colCount - 1].width > (displayWidth - listContent.x + viewMetrics.left)) 2057 colCount--; 2058 else if (colCount > 1 && !collectionHasRows) 2059 { 2060 // the slower computation requires adding up the previous columns 2061 var colX:int = 0; 2062 for (var i:int = 0; i < visibleColumns.length; i++) 2063 { 2064 colX += visibleColumns[i].width; 2065 } 2066 if (colX > (displayWidth - listContent.x + viewMetrics.left)) 2067 colCount--; 2068 } 2069 2070 // trace("configureSB", verticalScrollPosition); 2071 2072 setScrollBarProperties(displayableColumns.length - lockedColumnCount, Math.max(colCount, 1), 2073 collection ? collection.length - lockedRowCount : 0, 2074 Math.max(rowCount, 1)); 2075 2076 if ((!verticalScrollBar || !verticalScrollBar.visible) && collection && 2077 collection.length - lockedRowCount > rowCount) 2078 maxVerticalScrollPosition = collection.length - lockedRowCount - rowCount; 2079 if ((!horizontalScrollBar || !horizontalScrollBar.visible) && 2080 displayableColumns.length - lockedColumnCount > colCount - lockedColumnCount) 2081 maxHorizontalScrollPosition = displayableColumns.length - lockedColumnCount - colCount; 2082 } 2083 2084 /** 2085 * @private 2086 * Makes verticalScrollPosition smaller until it is 0 or there 2087 * are no empty rows. This is needed if we're scrolled to the 2088 * bottom and something is deleted or the rows resize so more 2089 * rows can be shown. 2090 */ 2091 private function adjustVerticalScrollPositionDownward(rowCount:int):Boolean 2092 { 2093 var bookmark:CursorBookmark = iterator.bookmark; 2094 2095 // add up how much space we're currently taking with valid items 2096 var h:Number = 0; 2097 2098 var item:IListItemRenderer; 2099 var c:DataGridColumn; 2100 var ch:Number = 0; 2101 var n:int; 2102 var j:int; 2103 2104 var paddingTop:Number = getStyle("paddingTop"); 2105 var paddingBottom:Number = getStyle("paddingBottom"); 2106 2107 h = rowInfo[rowCount - 1].y + rowInfo[rowCount - 1].height; 2108 h = listContent.heightExcludingOffsets - listContent.topOffset - h; 2109 2110 // back up one 2111 var numRows:int = 0; 2112 try 2113 { 2114 if (iterator.afterLast) 2115 iterator.seek(CursorBookmark.LAST, 0) 2116 else 2117 var bMore:Boolean = iterator.movePrevious(); 2118 } 2119 catch(e:ItemPendingError) 2120 { 2121 bMore = false; 2122 } 2123 if (!bMore) 2124 { 2125 // reset to 0; 2126 super.verticalScrollPosition = 0; 2127 try 2128 { 2129 iterator.seek(CursorBookmark.FIRST, 0); 2130 // sometimes, if the iterator is invalid we'll get lucky and succeed 2131 // here, then we have to make the iterator valid again 2132 if (!iteratorValid) 2133 { 2134 iteratorValid = true; 2135 lastSeekPending = null; 2136 } 2137 } 2138 catch(e:ItemPendingError) 2139 { 2140 lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); 2141 e.addResponder(new ItemResponder(seekPendingResultHandler, seekPendingFailureHandler, 2142 lastSeekPending)); 2143 iteratorValid = false; 2144 invalidateList(); 2145 return true; 2146 } 2147 updateList(); 2148 return true; 2149 } 2150 2151 // now work backwards to see how many more rows we need to create 2152 while (h > 0 && bMore) 2153 { 2154 var data:Object; 2155 if (bMore) 2156 { 2157 data = iterator.current; 2158 ch = 0; 2159 n = columns.length; 2160 for (j = 0; j < n; j++) 2161 { 2162 c = columns[j]; 2163 2164 if (!c.visible) 2165 continue; 2166 2167 if (variableRowHeight) 2168 { 2169 item = c.getMeasuringRenderer(false, data); 2170 if (DisplayObject(item).parent == null) 2171 listContent.addChild(DisplayObject(item)); 2172 setupRendererFromData(c, item, data); 2173 } 2174 ch = Math.max(ch, variableRowHeight ? item.getExplicitOrMeasuredHeight() + paddingBottom + paddingTop : rowHeight); 2175 } 2176 } 2177 h -= ch; 2178 try 2179 { 2180 bMore = iterator.movePrevious(); 2181 numRows++; 2182 } 2183 catch(e:ItemPendingError) 2184 { 2185 // if we run out of data, assume all remaining rows are the size of the previous row 2186 bMore = false; 2187 } 2188 } 2189 // if we overrun, go back one. 2190 if (h < 0) 2191 { 2192 numRows--; 2193 } 2194 2195 iterator.seek(bookmark, 0); 2196 verticalScrollPosition = Math.max(0, verticalScrollPosition - numRows); 2197 2198 // make sure we get through configureScrollBars w/o coming in here. 2199 if (numRows > 0 && !variableRowHeight) 2200 configureScrollBars(); 2201 2202 return (numRows > 0); 2203 } 2204 2205 /** 2206 * @private 2207 */ 2208 override public function calculateDropIndex(event:DragEvent = null):int 2209 { 2210 if (event) 2211 { 2212 var item:IListItemRenderer; 2213 var lastItem:IListItemRenderer; 2214 var pt:Point = new Point(event.localX, event.localY); 2215 pt = DisplayObject(event.target).localToGlobal(pt); 2216 pt = listContent.globalToLocal(pt); 2217 2218 var n:int = listItems.length; 2219 for (var i:int = 0; i < n; i++) 2220 { 2221 if (listItems[i][0]) 2222 lastItem = listItems[i][0]; 2223 2224 if (rowInfo[i].y <= pt.y && pt.y < rowInfo[i].y + rowInfo[i].height) 2225 { 2226 item = listItems[i][0]; 2227 break; 2228 } 2229 } 2230 if (!item && lockedRowContent) 2231 { 2232 pt = listContent.localToGlobal(pt); 2233 pt = lockedRowContent.globalToLocal(pt); 2234 n = lockedRowContent.listItems.length; 2235 for (i = 0; i < n; i++) 2236 { 2237 if (lockedRowContent.rowInfo[i].y <= pt.y && pt.y < lockedRowContent.rowInfo[i].y + lockedRowContent.rowInfo[i].height) 2238 { 2239 item = lockedRowContent.listItems[i][0]; 2240 break; 2241 } 2242 } 2243 } 2244 2245 2246 if (item) 2247 lastDropIndex = itemRendererToIndex(item); 2248 else 2249 { 2250 if (lastItem) 2251 lastDropIndex = itemRendererToIndex(lastItem) + 1; 2252 else 2253 lastDropIndex = collection ? collection.length : 0; 2254 } 2255 } 2256 2257 return lastDropIndex; 2258 } 2259 2260 /** 2261 * @private 2262 */ 2263 override protected function drawRowBackgrounds():void 2264 { 2265 drawRowGraphics(listContent); 2266 } 2267 2268 /** 2269 * @private 2270 */ 2271 protected function drawRowGraphics(contentHolder:ListBaseContentHolder):void 2272 { 2273 var rowBGs:Sprite = Sprite(contentHolder.getChildByName("rowBGs")); 2274 if (!rowBGs) 2275 { 2276 rowBGs = new FlexSprite(); 2277 rowBGs.mouseEnabled = false; 2278 rowBGs.name = "rowBGs"; 2279 contentHolder.addChildAt(rowBGs, 0); 2280 } 2281 2282 var colors:Array; 2283 2284 colors = getStyle("alternatingItemColors"); 2285 2286 if (!colors || colors.length == 0) 2287 { 2288 while (rowBGs.numChildren > n) 2289 { 2290 rowBGs.removeChildAt(rowBGs.numChildren - 1); 2291 } 2292 return; 2293 } 2294 2295 StyleManager.getColorNames(colors); 2296 2297 var curRow:int = 0; 2298 2299 var i:int = 0; 2300 var actualRow:int = verticalScrollPosition; 2301 var n:int = contentHolder.listItems.length; 2302 2303 while (curRow < n) 2304 { 2305 drawRowBackground(rowBGs, i++, contentHolder.rowInfo[curRow].y, contentHolder.rowInfo[curRow].height, 2306 colors[actualRow % colors.length], actualRow); 2307 curRow++; 2308 actualRow++; 2309 } 2310 2311 while (rowBGs.numChildren > i) 2312 { 2313 rowBGs.removeChildAt(rowBGs.numChildren - 1); 2314 } 2315 } 2316 2317 /** 2318 * @private 2319 */ 2320 override protected function mouseEventToItemRenderer(event:MouseEvent):IListItemRenderer 2321 { 2322 var r:IListItemRenderer; 2323 2324 r = super.mouseEventToItemRenderer(event); 2325 2326 return r == itemEditorInstance ? null : r; 2327 } 2328 2329 /** 2330 * @private 2331 */ 2332 override protected function get dragImage():IUIComponent 2333 { 2334 var image:DataGridDragProxy = new DataGridDragProxy(); 2335 image.owner = this; 2336 image.moduleFactory = moduleFactory; 2337 return image; 2338 } 2339 2340 //-------------------------------------------------------------------------- 2341 // 2342 // Methods 2343 // 2344 //-------------------------------------------------------------------------- 2345 2346 2347 /** 2348 * @private 2349 * Move a column to a new position in the columns array, shifting all 2350 * other columns left or right and updating the sortIndex and 2351 * lastSortIndex variables accordingly. 2352 */ 2353 mx_internal function shiftColumns(oldIndex:int, newIndex:int, 2354 trigger:Event = null):void 2355 { 2356 if (newIndex >= 0 && oldIndex != newIndex) 2357 { 2358 var incr:int = oldIndex < newIndex ? 1 : -1; 2359 for (var i:int = oldIndex; i != newIndex; i += incr) 2360 { 2361 var j:int = i + incr; 2362 var c:DataGridColumn = _columns[i]; 2363 _columns[i] = _columns[j]; 2364 _columns[j] = c; 2365 _columns[i].colNum = i; 2366 _columns[j].colNum = j; 2367 } 2368 2369 if (sortIndex == oldIndex) 2370 sortIndex += newIndex - oldIndex; 2371 else if ((oldIndex < sortIndex && sortIndex <= newIndex) 2372 || (newIndex <= sortIndex && sortIndex < oldIndex)) 2373 sortIndex -= incr; 2374 2375 if (lastSortIndex == oldIndex) 2376 lastSortIndex += newIndex - oldIndex; 2377 else if ((oldIndex < lastSortIndex 2378 && lastSortIndex <= newIndex) 2379 || (newIndex <= lastSortIndex 2380 && lastSortIndex < oldIndex)) 2381 lastSortIndex -= incr; 2382 2383 columnsInvalid = true; 2384 itemsSizeChanged = true; 2385 invalidateDisplayList(); 2386 if (lockedColumnHeader) 2387 lockedColumnHeader.invalidateDisplayList(); 2388 2389 var icEvent:IndexChangedEvent = 2390 new IndexChangedEvent(IndexChangedEvent.HEADER_SHIFT); 2391 icEvent.oldIndex = oldIndex; 2392 icEvent.newIndex = newIndex; 2393 icEvent.triggerEvent = trigger; 2394 dispatchEvent(icEvent); 2395 } 2396 } 2397 2398 /** 2399 * @private 2400 * Searches the iterator to determine columns. 2401 */ 2402 private function generateCols():void 2403 { 2404 if (collection.length > 0) 2405 { 2406 var col:DataGridColumn; 2407 var newCols:Array = []; 2408 var cols:Array; 2409 if (dataProvider) 2410 { 2411 try 2412 { 2413 iterator.seek(CursorBookmark.FIRST); 2414 if (!iteratorValid) 2415 { 2416 iteratorValid = true; 2417 lastSeekPending = null; 2418 } 2419 } 2420 catch(e:ItemPendingError) 2421 { 2422 lastSeekPending = new ListBaseSeekPending(CursorBookmark.FIRST, 0); 2423 e.addResponder(new ItemResponder(generateColumnsPendingResultHandler, seekPendingFailureHandler, 2424 lastSeekPending)); 2425 iteratorValid = false; 2426 return; 2427 } 2428 var info:Object = 2429 ObjectUtil.getClassInfo(iterator.current, 2430 ["uid", "mx_internal_uid"]); 2431 2432 if (info) 2433 cols = info.properties; 2434 } 2435 2436 if (!cols) 2437 { 2438 // introspect the first item and use its fields 2439 var itmObj:Object = iterator.current; 2440 for (var p:String in itmObj) 2441 { 2442 if (p != "uid") 2443 { 2444 col = new DataGridColumn(); 2445 col.dataField = p; 2446 newCols.push(col); 2447 } 2448 } 2449 } 2450 else 2451 { 2452 // this is an old recordset - use its columns 2453 var n:int = cols.length; 2454 var colName:Object; 2455 for (var i:int = 0; i < n; i++) 2456 { 2457 colName = cols[i]; 2458 if (colName is QName) 2459 colName = QName(colName).localName; 2460 col = new DataGridColumn(); 2461 col.dataField = String(colName); 2462 newCols.push(col); 2463 } 2464 } 2465 columns = newCols; 2466 generatedColumns = true; 2467 } 2468 } 2469 2470 /** 2471 * @private 2472 */ 2473 private function generateColumnsPendingResultHandler(data:Object, info:ListBaseSeekPending):void 2474 { 2475 // generate cols if we haven't successfully generated them 2476 if (columns.length == 0) 2477 generateCols(); 2478 seekPendingResultHandler(data, info); 2479 } 2480 2481 /** 2482 * @private 2483 */ 2484 private function calculateColumnSizes():void 2485 { 2486 var delta:Number; 2487 var n:int; 2488 var i:int; 2489 var totalWidth:Number = 0; 2490 var col:DataGridColumn; 2491 var cw:Number; 2492 2493 if (columns.length == 0) 2494 { 2495 visibleColumns = []; 2496 visibleLockedColumns = []; 2497 lockedColumnWidth = 0; 2498 columnsInvalid = false; 2499 return; 2500 } 2501 2502 // no columns are visible so figure out which ones 2503 // to make visible 2504 if (columnsInvalid) 2505 { 2506 columnsInvalid = false; 2507 visibleColumns = []; 2508 visibleLockedColumns = []; 2509 lockedColumnWidth = 0; 2510 2511 if (minColumnWidthInvalid) 2512 { 2513 n = columns.length; 2514 for (i = 0; i < n; i++) 2515 { 2516 columns[i].minWidth = minColumnWidth; 2517 } 2518 minColumnWidthInvalid = false; 2519 } 2520 2521 displayableColumns = null; 2522 n = _columns.length; 2523 for (i = 0; i < n; i++) 2524 { 2525 if (displayableColumns && _columns[i].visible) 2526 { 2527 displayableColumns.push(_columns[i]); 2528 } 2529 else if (!displayableColumns && !_columns[i].visible) 2530 { 2531 displayableColumns = new Array(i); 2532 for (var k:int = 0; k < i; k++) 2533 displayableColumns[k] = _columns[k]; 2534 } 2535 } 2536 2537 // If there are no hidden columns, displayableColumns points to 2538 // _columns (we don't need a duplicate copy of _columns). 2539 if (!displayableColumns) 2540 displayableColumns = _columns; 2541 2542 // if no hscroll, then pack columns in available space 2543 if (horizontalScrollPolicy == ScrollPolicy.OFF) 2544 { 2545 n = displayableColumns.length; 2546 for (i = 0; i < n; i++) 2547 { 2548 col = displayableColumns[i]; 2549 2550 if (i < lockedColumnCount) 2551 { 2552 visibleLockedColumns.push(col); 2553 } 2554 else 2555 visibleColumns.push(col); 2556 } 2557 } 2558 else 2559 { 2560 n = displayableColumns.length; 2561 for (i = 0; i < n; i++) 2562 { 2563 if (i >= lockedColumnCount && 2564 i < lockedColumnCount + horizontalScrollPosition) 2565 { 2566 continue; 2567 } 2568 2569 col = displayableColumns[i]; 2570 if (col.preferredWidth < col.minWidth) 2571 col.preferredWidth = col.minWidth; 2572 2573 if (totalWidth < displayWidth) 2574 { 2575 if (i < lockedColumnCount) 2576 { 2577 lockedColumnWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth); 2578 visibleLockedColumns.push(col); 2579 } 2580 else 2581 visibleColumns.push(col); 2582 totalWidth += Math.max(isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth, col.minWidth); 2583 if (col.width != col.preferredWidth) 2584 col.setWidth(col.preferredWidth); 2585 } 2586 else 2587 { 2588 if (visibleColumns.length == 0) 2589 visibleColumns.push(displayableColumns[0]); 2590 break; 2591 } 2592 } 2593 } 2594 } 2595 2596 var lastColumn:DataGridColumn; 2597 var newSize:Number; 2598 2599 // if no hscroll, then pack columns in available space 2600 if (horizontalScrollPolicy == ScrollPolicy.OFF) 2601 { 2602 var numResizable:int = 0; 2603 var fixedWidth:Number = 0; 2604 2605 // trace("resizing columns"); 2606 2607 // count how many resizable columns and how wide they are 2608 n = visibleColumns.length; 2609 for (i = 0; i < n; i++) 2610 { 2611 // trace("column " + i + " width = " + visibleColumns[i].width); 2612 if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible) 2613 { 2614 // trace(" resizable"); 2615 if (!isNaN(visibleColumns[i].explicitWidth)) 2616 { 2617 // trace(" explicit width " + visibleColumns[i].width); 2618 fixedWidth += visibleColumns[i].width; 2619 } 2620 else 2621 { 2622 // trace(" implicitly resizable"); 2623 numResizable++; 2624 fixedWidth += visibleColumns[i].minWidth; 2625 // trace(" minWidth " + visibleColumns[i].minWidth); 2626 } 2627 } 2628 else 2629 { 2630 // trace(" not resizable"); 2631 fixedWidth += visibleColumns[i].width; 2632 } 2633 2634 totalWidth += visibleColumns[i].width; 2635 } 2636 n = visibleLockedColumns.length; 2637 for (i = 0; i < n; i++) 2638 { 2639 // trace("column " + i + " width = " + visibleLockedColumns[i].width); 2640 if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible) 2641 { 2642 // trace(" resizable"); 2643 if (!isNaN(visibleLockedColumns[i].explicitWidth)) 2644 { 2645 // trace(" explicit width " + visibleLockedColumns[i].width); 2646 fixedWidth += visibleLockedColumns[i].width; 2647 } 2648 else 2649 { 2650 // trace(" implicitly resizable"); 2651 numResizable++; 2652 fixedWidth += visibleLockedColumns[i].minWidth; 2653 // trace(" minWidth " + visibleLockedColumns[i].minWidth); 2654 } 2655 } 2656 else 2657 { 2658 // trace(" not resizable"); 2659 fixedWidth += visibleLockedColumns[i].width; 2660 } 2661 2662 totalWidth += visibleLockedColumns[i].width; 2663 } 2664 // trace("totalWidth = " + totalWidth); 2665 // trace("displayWidth = " + displayWidth); 2666 2667 var ratio:Number; 2668 var newTotal:Number = displayWidth; 2669 var minWidth:Number; 2670 if (displayWidth > fixedWidth && numResizable) 2671 { 2672 // we have flexible columns and room to honor minwidths and non-resizable 2673 // trace("have enough room"); 2674 2675 // divide and distribute the excess among the resizable 2676 n = visibleLockedColumns.length; 2677 for (i = 0; i < n; i++) 2678 { 2679 if (visibleLockedColumns[i].resizable && !visibleLockedColumns[i].newlyVisible && isNaN(visibleLockedColumns[i].explicitWidth)) 2680 { 2681 lastColumn = visibleLockedColumns[i]; 2682 if (totalWidth > displayWidth) 2683 ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); 2684 else 2685 ratio = lastColumn.width / totalWidth; 2686 newSize = lastColumn.width - (totalWidth - displayWidth) * ratio; 2687 minWidth = visibleLockedColumns[i].minWidth; 2688 visibleLockedColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); 2689 // trace("column " + i + " set to " + visibleLockedColumns[i].width); 2690 } 2691 newTotal -= visibleLockedColumns[i].width; 2692 visibleLockedColumns[i].newlyVisible = false; 2693 } 2694 n = visibleColumns.length; 2695 for (i = 0; i < n; i++) 2696 { 2697 if (visibleColumns[i].resizable && !visibleColumns[i].newlyVisible && isNaN(visibleColumns[i].explicitWidth)) 2698 { 2699 lastColumn = visibleColumns[i]; 2700 if (totalWidth > displayWidth) 2701 ratio = (lastColumn.width - lastColumn.minWidth)/ (totalWidth - fixedWidth); 2702 else 2703 ratio = lastColumn.width / totalWidth; 2704 newSize = lastColumn.width - (totalWidth - displayWidth) * ratio; 2705 minWidth = visibleColumns[i].minWidth; 2706 visibleColumns[i].setWidth(newSize > minWidth ? newSize : minWidth); 2707 // trace("column " + i + " set to " + visibleColumns[i].width); 2708 } 2709 newTotal -= visibleColumns[i].width; 2710 visibleColumns[i].newlyVisible = false; 2711 } 2712 if (newTotal && lastColumn) 2713 { 2714 // trace("excess = " + newTotal); 2715 lastColumn.setWidth(lastColumn.width + newTotal); 2716 } 2717 } 2718 else // can't honor minwidth and non-resizables so just scale everybody 2719 { 2720 // trace("too small or too big"); 2721 n = visibleLockedColumns.length; 2722 for (i = 0; i < n; i++) 2723 { 2724 lastColumn = visibleLockedColumns[i]; 2725 ratio = lastColumn.width / totalWidth; 2726 //totalWidth -= visibleLockedColumns[i].width; 2727 newSize = displayWidth * ratio; 2728 lastColumn.setWidth(newSize); 2729 lastColumn.explicitWidth = NaN; 2730 // trace("column " + i + " set to " + visibleLockedColumns[i].width); 2731 newTotal -= newSize; 2732 } 2733 n = visibleColumns.length; 2734 for (i = 0; i < n; i++) 2735 { 2736 lastColumn = visibleColumns[i]; 2737 ratio = lastColumn.width / totalWidth; 2738 //totalWidth -= visibleColumns[i].width; 2739 newSize = displayWidth * ratio; 2740 lastColumn.setWidth(newSize); 2741 lastColumn.explicitWidth = NaN; 2742 // trace("column " + i + " set to " + visibleColumns[i].width); 2743 newTotal -= newSize; 2744 } 2745 if (newTotal && lastColumn) 2746 { 2747 // trace("excess = " + newTotal); 2748 lastColumn.setWidth(lastColumn.width + newTotal); 2749 } 2750 } 2751 } 2752 else // we have or can have an horizontalScrollBar 2753 { 2754 totalWidth = 0; 2755 // drop any that completely overflow 2756 n = visibleColumns.length; 2757 for (i = 0; i < n; i++) 2758 { 2759 if (totalWidth > displayWidth - lockedColumnWidth) 2760 { 2761 visibleColumns.splice(i); 2762 break; 2763 } 2764 totalWidth += isNaN(visibleColumns[i].explicitWidth) ? visibleColumns[i].preferredWidth : visibleColumns[i].explicitWidth; 2765 } 2766 2767 if (visibleColumns.length == 0) 2768 return; 2769 2770 i = visibleColumns[visibleColumns.length - 1].colNum + 1; 2771 // add more if we have room 2772 if (totalWidth < displayWidth - lockedColumnWidth && i < displayableColumns.length) 2773 { 2774 n = displayableColumns.length; 2775 for (; i < n && totalWidth < displayWidth - lockedColumnWidth; i++) 2776 { 2777 col = displayableColumns[i]; 2778 2779 visibleColumns.push(col); 2780 totalWidth += isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; 2781 } 2782 } 2783 else if (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0) 2784 { 2785 while (totalWidth < displayWidth - lockedColumnWidth && horizontalScrollPosition > 0) 2786 { 2787 col = displayableColumns[lockedColumnCount + horizontalScrollPosition - 1]; 2788 cw = isNaN(col.explicitWidth) ? col.preferredWidth : col.explicitWidth; 2789 if (cw < displayWidth - lockedColumnWidth - totalWidth) 2790 { 2791 visibleColumns.splice(0, 0, col); 2792 super.horizontalScrollPosition--; 2793 totalWidth += cw; 2794 } 2795 else 2796 break; 2797 } 2798 } 2799 2800 lastColumn = visibleColumns[visibleColumns.length - 1]; 2801 cw = isNaN(lastColumn.explicitWidth) ? lastColumn.preferredWidth : lastColumn.explicitWidth; 2802 newSize = cw + displayWidth - lockedColumnWidth - totalWidth; 2803 if (lastColumn == displayableColumns[displayableColumns.length - 1] 2804 && lastColumn.resizable 2805 && newSize >= lastColumn.minWidth 2806 && newSize > cw) 2807 { 2808 lastColumn.setWidth(newSize); 2809 maxHorizontalScrollPosition = 2810 displayableColumns.length - visibleColumns.length; 2811 } 2812 else if (visibleColumns.length == 1 && 2813 lastColumn == displayableColumns[displayableColumns.length - 1]) 2814 { 2815 maxHorizontalScrollPosition = 2816 displayableColumns.length - visibleColumns.length; 2817 } 2818 else 2819 { 2820 maxHorizontalScrollPosition = 2821 displayableColumns.length - visibleColumns.length + 1; 2822 } 2823 } 2824 lockedColumnWidth = 0; 2825 if (visibleLockedColumns.length) 2826 { 2827 n = visibleLockedColumns.length; 2828 for (i = 0; i < n; i++) 2829 { 2830 col = visibleLockedColumns[i]; 2831 lockedColumnWidth += col.width; 2832 } 2833 } 2834 } 2835 2836 /** 2837 * @private 2838 * If there is no horizontal scroll bar, changes the display width of other columns when 2839 * one column's width is changed. 2840 * @param col column whose width is changed 2841 * @param w width of column 2842 */ 2843 override mx_internal function resizeColumn(col:int, w:Number):void 2844 { 2845 // there's a window of time before we calccolumnsizes 2846 // that someone can set width in AS 2847 if ((!visibleColumns || visibleColumns.length == 0) && (!visibleLockedColumns || visibleLockedColumns.length == 0)) 2848 { 2849 _columns[col].setWidth(w); 2850 _columns[col].preferredWidth = w; 2851 return; 2852 } 2853 2854 if (w < _columns[col].minWidth) 2855 w = _columns[col].minWidth; 2856 2857 // hScrollBar is present 2858 if (_horizontalScrollPolicy == ScrollPolicy.ON || 2859 _horizontalScrollPolicy == ScrollPolicy.AUTO) 2860 { 2861 // adjust the column's width 2862 _columns[col].setWidth(w); 2863 _columns[col].explicitWidth = w; 2864 _columns[col].preferredWidth = w; 2865 columnsInvalid = true; 2866 } 2867 else 2868 { 2869 // find the columns in the set of visible columns; 2870 var n:int = _columns.length; 2871 var i:int; 2872 for (i = 0; i < n; i++) 2873 { 2874 if (col == _columns[i].colNum) 2875 break; 2876 } 2877 if (i >= _columns.length - 1) // no resize of right most column 2878 return; 2879 col = i; 2880 2881 // we want all cols's new widths to the right of this to be in proportion 2882 // to what they were before the stretch. 2883 2884 // get the original space to the right not taken up by the column 2885 var totalSpace:Number = 0; 2886 var lastColumn:DataGridColumn; 2887 var newWidth:Number; 2888 //non-resizable columns don't count though 2889 for (i = col + 1; i < n; i++) 2890 { 2891 if (_columns[i].visible) 2892 if (_columns[i].resizable) 2893 totalSpace += _columns[i].width; 2894 } 2895 2896 var newTotalSpace:Number = _columns[col].width - w + totalSpace; 2897 if (totalSpace) 2898 { 2899 _columns[col].setWidth(w); 2900 _columns[col].explicitWidth = w; 2901 } 2902 2903 var totX:Number = 0; 2904 // resize the columns to the right proportionally to what they were 2905 for (i = col + 1; i < n; i++) 2906 { 2907 if (_columns[i].visible) 2908 if (_columns[i].resizable) 2909 { 2910 newWidth = Math.floor(_columns[i].width 2911 * newTotalSpace / totalSpace); 2912 if (newWidth < _columns[i].minWidth) 2913 newWidth = _columns[i].minWidth; 2914 _columns[i].setWidth(newWidth); 2915 totX += _columns[i].width; 2916 lastColumn = _columns[i]; 2917 } 2918 } 2919 2920 if (totX > newTotalSpace) 2921 { 2922 // if excess then should be taken out only from changing column 2923 // cause others would have already gone to their minimum 2924 newWidth = _columns[col].width - totX + newTotalSpace; 2925 if (newWidth < _columns[col].minWidth) 2926 newWidth = _columns[col].minWidth; 2927 _columns[col].setWidth(newWidth); 2928 } 2929 else if (lastColumn) 2930 { 2931 // if less then should be added in last column 2932 // dont need to check for minWidth as we are adding 2933 lastColumn.setWidth(lastColumn.width - totX + newTotalSpace); 2934 } 2935 } 2936 itemsSizeChanged = true 2937 2938 invalidateDisplayList(); 2939 } 2940 2941 /** 2942 * Draws a row background 2943 * at the position and height specified using the 2944 * color specified. This implementation creates a Shape as a 2945 * child of the input Sprite and fills it with the appropriate color. 2946 * This method also uses the <code>backgroundAlpha</code> style property 2947 * setting to determine the transparency of the background color. 2948 * 2949 * @param s A Sprite that will contain a display object 2950 * that contains the graphics for that row. 2951 * 2952 * @param rowIndex The row's index in the set of displayed rows. The 2953 * header does not count, the top most visible row has a row index of 0. 2954 * This is used to keep track of the objects used for drawing 2955 * backgrounds so a particular row can re-use the same display object 2956 * even though the index of the item that row is rendering has changed. 2957 * 2958 * @param y The suggested y position for the background 2959 * 2960 * @param height The suggested height for the indicator 2961 * 2962 * @param color The suggested color for the indicator 2963 * 2964 * @param dataIndex The index of the item for that row in the 2965 * data provider. This can be used to color the 10th item differently 2966 * for example. 2967 */ 2968 protected function drawRowBackground(s:Sprite, rowIndex:int, 2969 y:Number, height:Number, color:uint, dataIndex:int):void 2970 { 2971 var contentHolder:ListBaseContentHolder = ListBaseContentHolder(s.parent); 2972 2973 var background:Shape; 2974 if (rowIndex < s.numChildren) 2975 { 2976 background = Shape(s.getChildAt(rowIndex)); 2977 } 2978 else 2979 { 2980 background = new FlexShape(); 2981 background.name = "background"; 2982 s.addChild(background); 2983 } 2984 2985 background.y = y; 2986 2987 // Height is usually as tall is the items in the row, but not if 2988 // it would extend below the bottom of listContent 2989 var height:Number = Math.min(height, 2990 contentHolder.height - 2991 y); 2992 2993 var g:Graphics = background.graphics; 2994 g.clear(); 2995 g.beginFill(color, getStyle("backgroundAlpha")); 2996 g.drawRect(0, 0, contentHolder.width, height); 2997 g.endFill(); 2998 } 2999 3000 /** 3001 * Draws a column background for a column with the suggested color. 3002 * This implementation creates a Shape as a 3003 * child of the input Sprite and fills it with the appropriate color. 3004 * 3005 * @param s A Sprite that will contain a display object 3006 * that contains the graphics for that column. 3007 * 3008 * @param columnIndex The column's index in the set of displayed columns. 3009 * The left most visible column has a column index of 0. 3010 * This is used to keep track of the objects used for drawing 3011 * backgrounds so a particular column can re-use the same display object 3012 * even though the index of the DataGridColumn for that column has changed. 3013 * 3014 * @param color The suggested color for the indicator 3015 * 3016 * @param column The column of the DataGrid control that you are drawing the background for. 3017 */ 3018 protected function drawColumnBackground(s:Sprite, columnIndex:int, 3019 color:uint, column:DataGridColumn):void 3020 { 3021 var background:Shape; 3022 background = Shape(s.getChildByName(columnIndex.toString())); 3023 if (!background) 3024 { 3025 background = new FlexShape(); 3026 s.addChild(background); 3027 background.name = columnIndex.toString(); 3028 } 3029 3030 var g:Graphics = background.graphics; 3031 g.clear(); 3032 g.beginFill(color); 3033 3034 var lastRow:Object = rowInfo[listItems.length - 1]; 3035 var columnHeader:DataGridHeader = (s.parent == lockedColumnContent) ? 3036 DataGridHeader(lockedColumnHeader) : 3037 DataGridHeader(header); 3038 3039 var xx:Number = columnHeader.rendererArray[columnIndex].x 3040 var yy:Number = rowInfo[0].y 3041 3042 // Height is usually as tall is the items in the row, but not if 3043 // it would extend below the bottom of listContent 3044 var height:Number = Math.min(lastRow.y + lastRow.height, 3045 listContent.height - yy); 3046 3047 g.drawRect(xx, yy, columnHeader.visibleColumns[columnIndex].width, 3048 listContent.height - yy); 3049 g.endFill(); 3050 } 3051 3052 /** 3053 * Creates and sizes the horizontalSeparator skins. If none have been specified, then draws the lines using 3054 * drawHorizontalLine(). 3055 */ 3056 private function drawHorizontalSeparator(s:Sprite, rowIndex:int, color:uint, y:Number, useLockedSeparator:Boolean = false):void 3057 { 3058 var hSepSkinName:String = "hSeparator" + rowIndex; 3059 var hLockedSepSkinName:String = "hLockedSeparator" + rowIndex; 3060 var createThisSkinName:String = useLockedSeparator ? hLockedSepSkinName : hSepSkinName; 3061 var createThisStyleName:String = useLockedSeparator ? "horizontalLockedSeparatorSkin" : "horizontalSeparatorSkin"; 3062 3063 var sepSkin:IFlexDisplayObject; 3064 var lockedSepSkin:IFlexDisplayObject; 3065 var deleteThisSkin:IFlexDisplayObject; 3066 var createThisSkin:IFlexDisplayObject; 3067 3068 // Look for separator by name 3069 sepSkin = IFlexDisplayObject(s.getChildByName(hSepSkinName)); 3070 lockedSepSkin = IFlexDisplayObject(s.getChildByName(hLockedSepSkinName)); 3071 3072 createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; 3073 deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; 3074 3075 if (deleteThisSkin) 3076 { 3077 s.removeChild(DisplayObject(deleteThisSkin)); 3078 //delete deleteThisSkin; 3079 } 3080 3081 if (!createThisSkin) 3082 { 3083 var sepSkinClass:Class = Class(getStyle(createThisStyleName)); 3084 3085 if (sepSkinClass) 3086 { 3087 createThisSkin = IFlexDisplayObject(new sepSkinClass()); 3088 createThisSkin.name = createThisSkinName; 3089 3090 var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; 3091 if (styleableSkin) 3092 styleableSkin.styleName = this; 3093 3094 s.addChild(DisplayObject(createThisSkin)); 3095 } 3096 } 3097 3098 if (createThisSkin) 3099 { 3100 var mHeight:Number = !isNaN(createThisSkin.measuredHeight) ? createThisSkin.measuredHeight : 1; 3101 createThisSkin.setActualSize(displayWidth - lockedColumnWidth, mHeight); 3102 createThisSkin.move(0, y); 3103 } 3104 else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead 3105 { 3106 drawHorizontalLine(s, rowIndex, color, y); 3107 } 3108 3109 } 3110 3111 /** 3112 * Draws a line between rows. This implementation draws a line 3113 * directly into the given Sprite. The Sprite has been cleared 3114 * before lines are drawn into it. 3115 * 3116 * @param s A Sprite that will contain a display object 3117 * that contains the graphics for that row. 3118 * 3119 * @param rowIndex The row's index in the set of displayed rows. The 3120 * header does not count, the top most visible row has a row index of 0. 3121 * This is used to keep track of the objects used for drawing 3122 * backgrounds so a particular row can re-use the same display object 3123 * even though the index of the item that row is rendering has changed. 3124 * 3125 * @param color The suggested color for the indicator 3126 * 3127 * @param y The suggested y position for the background 3128 */ 3129 protected function drawHorizontalLine(s:Sprite, rowIndex:int, color:uint, y:Number):void 3130 { 3131 var contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder; 3132 var g:Graphics = s.graphics; 3133 3134 g.lineStyle(1, color); 3135 g.moveTo(0, y); 3136 g.lineTo(contentHolder.width, y); 3137 } 3138 3139 /** 3140 * Creates and sizes the verticalSeparator skins. If none have been specified, then draws the lines using 3141 * drawVerticalLine(). 3142 */ 3143 private function drawVerticalSeparator(s:Sprite, colIndex:int, color:uint, x:Number, useLockedSeparator:Boolean = false):void 3144 { 3145 var vSepSkinName:String = "vSeparator" + colIndex; 3146 var vLockedSepSkinName:String = "vLockedSeparator" + colIndex; 3147 var createThisSkinName:String = useLockedSeparator ? vLockedSepSkinName : vSepSkinName; 3148 var createThisStyleName:String = useLockedSeparator ? "verticalLockedSeparatorSkin" : "verticalSeparatorSkin"; 3149 3150 var sepSkin:IFlexDisplayObject; 3151 var lockedSepSkin:IFlexDisplayObject; 3152 var deleteThisSkin:IFlexDisplayObject; 3153 var createThisSkin:IFlexDisplayObject; 3154 3155 // Look for separator by name 3156 sepSkin = IFlexDisplayObject(s.getChildByName(vSepSkinName)); 3157 lockedSepSkin = IFlexDisplayObject(s.getChildByName(vLockedSepSkinName)); 3158 3159 createThisSkin = useLockedSeparator ? lockedSepSkin : sepSkin; 3160 deleteThisSkin = useLockedSeparator ? sepSkin : lockedSepSkin; 3161 3162 if (deleteThisSkin) 3163 { 3164 s.removeChild(DisplayObject(deleteThisSkin)); 3165 //delete deleteThisSkin; 3166 } 3167 3168 if (!createThisSkin) 3169 { 3170 var sepSkinClass:Class = Class(getStyle(createThisStyleName)); 3171 3172 if (sepSkinClass) 3173 { 3174 createThisSkin = IFlexDisplayObject(new sepSkinClass()); 3175 createThisSkin.name = createThisSkinName; 3176 3177 var styleableSkin:ISimpleStyleClient = createThisSkin as ISimpleStyleClient; 3178 if (styleableSkin) 3179 styleableSkin.styleName = this; 3180 3181 s.addChild(DisplayObject(createThisSkin)); 3182 } 3183 } 3184 3185 if (createThisSkin) 3186 { 3187 var mWidth:Number = !isNaN(createThisSkin.measuredWidth) ? createThisSkin.measuredWidth : 1; 3188 createThisSkin.setActualSize(mWidth, s.parent.parent.height); 3189 createThisSkin.move(x - Math.round(mWidth / 2), 0); 3190 } 3191 else // If we still don't have a sepSkin, then we have no skin style defined. Use the default function instead 3192 { 3193 drawVerticalLine(s, colIndex, color, x); 3194 } 3195 3196 } 3197 3198 /** 3199 * Draw lines between columns. This implementation draws a line 3200 * directly into the given Sprite. The Sprite has been cleared 3201 * before lines are drawn into it. 3202 * 3203 * @param s A Sprite that will contain a display object 3204 * that contains the graphics for that row. 3205 * 3206 * @param columnIndex The column's index in the set of displayed columns. 3207 * The left most visible column has a column index of 0. 3208 * 3209 * @param color The suggested color for the indicator 3210 * 3211 * @param x The suggested x position for the background 3212 */ 3213 protected function drawVerticalLine(s:Sprite, colIndex:int, color:uint, x:Number):void 3214 { 3215 var contentHolder:ListBaseContentHolder = s.parent.parent as ListBaseContentHolder; 3216 //draw our vertical lines 3217 var g:Graphics = s.graphics; 3218 g.lineStyle(1, color, 100); 3219 g.moveTo(x, headerVisible ? 0 : 1); 3220 g.lineTo(x, contentHolder.height); 3221 } 3222 3223 /** 3224 * Draw lines between columns, and column backgrounds. 3225 * This implementation calls the <code>drawHorizontalLine()</code>, 3226 * <code>drawVerticalLine()</code>, 3227 * and <code>drawColumnBackground()</code> methods as needed. 3228 * It creates a 3229 * Sprite that contains all of these graphics and adds it as a 3230 * child of the listContent at the front of the z-order. 3231 */ 3232 protected function drawLinesAndColumnBackgrounds():void 3233 { 3234 drawLinesAndColumnGraphics(listContent, visibleColumns, {}); 3235 } 3236 3237 /** 3238 * Draw lines between columns, and column backgrounds. 3239 * This implementation calls the <code>drawHorizontalLine()</code>, 3240 * <code>drawVerticalLine()</code>, 3241 * and <code>drawColumnBackground()</code> methods as needed. 3242 * It creates a 3243 * Sprite that contains all of these graphics and adds it as a 3244 * child of the listContent at the front of the z-order. 3245 * 3246 * @param contentHolder A container of all of the DataGrid's item renderers and item editors. 3247 * @param visibleColumns An array of the visible columns in the DataGrid. 3248 * @param separators An object that defines the top, bottom, left, and right lines that separate the columns and rows. 3249 */ 3250 protected function drawLinesAndColumnGraphics(contentHolder:ListBaseContentHolder, visibleColumns:Array, separators:Object):void 3251 { 3252 var lines:Sprite = Sprite(contentHolder.getChildByName("lines")); 3253 if (!lines) 3254 { 3255 lines = new UIComponent(); 3256 lines.name = "lines"; 3257 lines.cacheAsBitmap = true; 3258 lines.mouseEnabled = false; 3259 contentHolder.addChild(lines); 3260 } 3261 contentHolder.setChildIndex(lines, contentHolder.numChildren - 1); 3262 var rowInfo:Array = contentHolder.rowInfo; 3263 3264 lines.graphics.clear(); 3265 3266 var linesBody:Sprite = Sprite(lines.getChildByName("body")); 3267 3268 if (!linesBody) 3269 { 3270 linesBody = new UIComponent(); 3271 linesBody.name = "body"; 3272 linesBody.mouseEnabled = false; 3273 lines.addChild(linesBody); 3274 } 3275 3276 linesBody.graphics.clear(); 3277 while (linesBody.numChildren) 3278 { 3279 linesBody.removeChildAt(0); 3280 } 3281 3282 3283 var tmpHeight:Number = unscaledHeight - 1; // FIXME: can remove? 3284 var lineCol:uint; 3285 3286 var i:int; 3287 3288 var len:uint = visibleColumns ? visibleColumns.length : 0; 3289 var rowlen:uint = contentHolder.listItems.length 3290 3291 // draw horizontalGridlines if needed. 3292 lineCol = getStyle("horizontalGridLineColor"); 3293 if (getStyle("horizontalGridLines")) 3294 { 3295 for (i = 0; i < rowlen; i++) 3296 { 3297 var yy:Number = rowInfo[i].y + rowInfo[i].height; 3298 if (yy < contentHolder.height) 3299 drawHorizontalSeparator(linesBody, i, lineCol, yy); 3300 } 3301 } 3302 if (separators.top) 3303 drawHorizontalSeparator(linesBody, i++, 0, rowInfo[0].y, true); 3304 if (separators.bottom && rowlen > 0) 3305 drawHorizontalSeparator(linesBody, i++, 0, rowInfo[rowlen - 1].y + rowInfo[rowlen - 1].height, true); 3306 3307 var vLines:Boolean = getStyle("verticalGridLines"); 3308 lineCol = getStyle("verticalGridLineColor"); 3309 3310 if (len) 3311 { 3312 var colBGs:Sprite = Sprite(contentHolder.getChildByName("colBGs")); 3313 // traverse the columns, set the sizes, draw the column backgrounds 3314 var lastChild:int = -1; 3315 var xx:Number = 0; 3316 for (i = 0; i < len; i++) 3317 { 3318 // only draw the vertical separator for the ones in the middle (not beginning and not end) 3319 if (vLines && i < (len - 1)) 3320 drawVerticalSeparator(linesBody, i, lineCol, xx + visibleColumns[i].width); 3321 3322 var col:DataGridColumn = visibleColumns[i]; 3323 var bgCol:Object; 3324 if (enabled) 3325 bgCol = col.getStyle("backgroundColor"); 3326 else 3327 bgCol = col.getStyle("backgroundDisabledColor"); 3328 3329 if (bgCol !== null && !isNaN(Number(bgCol))) 3330 { 3331 if (!colBGs) 3332 { 3333 colBGs = new FlexSprite(); 3334 colBGs.mouseEnabled = false; 3335 colBGs.name = "colBGs"; 3336 contentHolder.addChildAt(colBGs, contentHolder.getChildIndex(contentHolder.getChildByName("rowBGs")) + 1); 3337 } 3338 drawColumnBackground(colBGs, i, Number(bgCol), col); 3339 lastChild = i; 3340 } 3341 else if (colBGs) 3342 { 3343 var background:Shape = Shape(colBGs.getChildByName(i.toString())); 3344 if (background) 3345 { 3346 var g:Graphics = background.graphics; 3347 g.clear(); 3348 colBGs.removeChild(background); 3349 } 3350 } 3351 xx += visibleColumns[i].width; 3352 } 3353 if (colBGs && colBGs.numChildren) 3354 { 3355 while (colBGs.numChildren) 3356 { 3357 var bg:DisplayObject = colBGs.getChildAt(colBGs.numChildren - 1); 3358 if (parseInt(bg.name) > lastChild) 3359 colBGs.removeChild(bg); 3360 else 3361 break; 3362 } 3363 } 3364 3365 } 3366 3367 if (separators.right && visibleColumns && visibleColumns.length) 3368 { 3369 if (contentHolder.listItems.length && contentHolder.listItems[0].length) 3370 drawVerticalSeparator(linesBody, i++, 0, contentHolder.listItems[0][len - 1].x + visibleColumns[len - 1].width, true); 3371 else 3372 { 3373 xx = 0; 3374 for (i = 0; i < len; i++) 3375 { 3376 xx += visibleColumns[i].width; 3377 } 3378 drawVerticalSeparator(linesBody, i++, 0, xx, true); 3379 } 3380 } 3381 if (separators.left) 3382 drawVerticalSeparator(linesBody, i++, 0, 0, true); 3383 3384 } 3385 3386 mx_internal function _drawHeaderBackground(headerBG:UIComponent):void 3387 { 3388 drawHeaderBackground(headerBG); 3389 } 3390 3391 /** 3392 * Draws the background of the headers into the given 3393 * UIComponent. The graphics drawn may be scaled horizontally 3394 * if the component's width changes or this method will be 3395 * called again to redraw at a different width and/or height 3396 * 3397 * @param headerBG A UIComponent that will contain the header 3398 * background graphics. 3399 */ 3400 protected function drawHeaderBackground(headerBG:UIComponent):void 3401 { 3402 DataGridHeader(headerBG.parent)._drawHeaderBackground(headerBG); 3403 } 3404 3405 mx_internal function _clearSeparators():void 3406 { 3407 clearSeparators(); 3408 } 3409 3410 /** 3411 * Removes column header separators that the user normally uses 3412 * to resize columns. 3413 */ 3414 protected function clearSeparators():void 3415 { 3416 DataGridHeader(header)._clearSeparators(); 3417 if (lockedColumnHeader) 3418 DataGridHeader(lockedColumnHeader)._clearSeparators(); 3419 } 3420 3421 mx_internal function _drawSeparators():void 3422 { 3423 drawSeparators(); 3424 } 3425 3426 /** 3427 * Creates and displays the column header separators that the user 3428 * normally uses to resize columns. This implementation uses 3429 * the same Sprite as the lines and column backgrounds and adds 3430 * instances of the <code>headerSeparatorSkin</code> and attaches mouse 3431 * listeners to them in order to know when the user wants 3432 * to resize a column. 3433 */ 3434 protected function drawSeparators():void 3435 { 3436 DataGridHeader(header)._drawSeparators(); 3437 if (lockedColumnHeader) 3438 DataGridHeader(lockedColumnHeader)._drawSeparators(); 3439 } 3440 3441 /** 3442 * @private 3443 * Update sortIndex and sortDirection based on sort info availabled in 3444 * underlying data provider. 3445 */ 3446 private function updateSortIndexAndDirection():void 3447 { 3448 // Don't show sort indicator if sortableColumns is false or if the 3449 // column sorted on has sortable="false" 3450 3451 if (!sortableColumns) 3452 { 3453 lastSortIndex = sortIndex; 3454 sortIndex = -1; 3455 3456 if (lastSortIndex != sortIndex) 3457 invalidateDisplayList(); 3458 3459 return; 3460 } 3461 3462 if (!dataProvider) 3463 return; 3464 3465 var view:ICollectionView = ICollectionView(dataProvider); 3466 var sort:Sort = view.sort; 3467 if (!sort) 3468 { 3469 sortIndex = lastSortIndex = -1; 3470 return; 3471 } 3472 3473 var fields:Array = sort.fields; 3474 if (!fields) 3475 return; 3476 3477 if (fields.length != 1) 3478 { 3479 lastSortIndex = sortIndex; 3480 sortIndex = -1; 3481 3482 if (lastSortIndex != sortIndex) 3483 invalidateDisplayList(); 3484 3485 return; 3486 } 3487 3488 // fields.length == 1, so the collection is sorted on a single field. 3489 var sortField:SortField = fields[0]; 3490 var n:int = _columns.length; 3491 sortIndex = -1; 3492 for (var i:int = 0; i < n; i++) 3493 { 3494 if (_columns[i].dataField == sortField.name) 3495 { 3496 sortIndex = _columns[i].sortable ? i : -1; 3497 sortDirection = sortField.descending ? "DESC" : "ASC"; 3498 return; 3499 } 3500 } 3501 } 3502 3503 mx_internal function _placeSortArrow():void 3504 { 3505 placeSortArrow(); 3506 } 3507 3508 /** 3509 * Draws the sort arrow graphic on the column that is the current sort key. 3510 * This implementation creates or reuses an instance of the skin specified 3511 * by <code>sortArrowSkin</code> style property and places 3512 * it in the appropriate column header. It 3513 * also shrinks the size of the column header if the text in the header 3514 * would be obscured by the sort arrow. 3515 */ 3516 protected function placeSortArrow():void 3517 { 3518 DataGridHeader(header)._placeSortArrow(); 3519 if (lockedColumnHeader) 3520 DataGridHeader(lockedColumnHeader)._placeSortArrow(); 3521 } 3522 3523 /** 3524 * @private 3525 */ 3526 private function sortByColumn(index:int):void 3527 { 3528 var c:DataGridColumn = columns[index]; 3529 var desc:Boolean = c.sortDescending; 3530 3531 // do the sort if we're allowed to 3532 if (c.sortable) 3533 { 3534 var s:Sort = collection.sort; 3535 var f:SortField; 3536 if (s) 3537 { 3538 s.compareFunction = null; 3539 // analyze the current sort to see what we've been given 3540 var sf:Array = s.fields; 3541 if (sf) 3542 { 3543 for (var i:int = 0; i < sf.length; i++) 3544 { 3545 3546 if (sf[i].name == c.dataField) 3547 { 3548 // we're part of the current sort 3549 f = sf[i] 3550 // flip the logic so desc is new desired order 3551 desc = !f.descending; 3552 break; 3553 } 3554 } 3555 } 3556 } 3557 else 3558 s = new Sort; 3559 3560 if (!f) 3561 f = new SortField(c.dataField); 3562 3563 3564 c.sortDescending = desc; 3565 var dir:String = (desc) ? "DESC" : "ASC"; 3566 sortDirection = dir; 3567 3568 // set the grid's sortIndex 3569 lastSortIndex = sortIndex; 3570 sortIndex = index; 3571 sortColumn = c; 3572 3573 // if you have a labelFunction you must supply a sortCompareFunction 3574 f.name = c.dataField; 3575 if (c.sortCompareFunction != null) 3576 { 3577 f.compareFunction = c.sortCompareFunction; 3578 } 3579 else 3580 { 3581 f.compareFunction = null; 3582 } 3583 f.descending = desc; 3584 s.fields = [f]; 3585 } 3586 collection.sort = s; 3587 collection.refresh(); 3588 3589 } 3590 3591 /** 3592 * @private 3593 */ 3594 private function setEditedItemPosition(coord:Object):void 3595 { 3596 bEditedItemPositionChanged = true; 3597 _proposedEditedItemPosition = coord; 3598 invalidateDisplayList(); 3599 } 3600 3601 /** 3602 * @private 3603 * focus an item renderer in the grid - harder than it looks 3604 */ 3605 private function commitEditedItemPosition(coord:Object):void 3606 { 3607 if (!enabled || !editable) 3608 return; 3609 3610 if (!collection || collection.length == 0) 3611 return; 3612 3613 // just give focus back to the itemEditorInstance 3614 if (itemEditorInstance && coord && 3615 itemEditorInstance is IFocusManagerComponent && 3616 _editedItemPosition.rowIndex == coord.rowIndex && 3617 _editedItemPosition.columnIndex == coord.columnIndex) 3618 { 3619 IFocusManagerComponent(itemEditorInstance).setFocus(); 3620 return; 3621 } 3622 3623 // dispose of any existing editor, saving away its data first 3624 if (itemEditorInstance) 3625 { 3626 var reason:String; 3627 if (!coord) 3628 { 3629 reason = DataGridEventReason.OTHER; 3630 } 3631 else 3632 { 3633 reason = (!editedItemPosition || coord.rowIndex == editedItemPosition.rowIndex) ? 3634 DataGridEventReason.NEW_COLUMN : 3635 DataGridEventReason.NEW_ROW; 3636 } 3637 if (!endEdit(reason) && reason != DataGridEventReason.OTHER) 3638 return; 3639 } 3640 3641 // store the value 3642 _editedItemPosition = coord; 3643 3644 // allow setting of undefined to dispose item editor instance 3645 if (!coord) 3646 return; 3647 3648 if (dontEdit) 3649 { 3650 return; 3651 } 3652 3653 var rowIndex:int = coord.rowIndex; 3654 var colIndex:int = coord.columnIndex; 3655 if (displayableColumns.length != _columns.length) 3656 { 3657 for (var i:int = 0; i < displayableColumns.length; i++) 3658 { 3659 if (displayableColumns[i].colNum >= colIndex) 3660 { 3661 colIndex = i; 3662 break; 3663 } 3664 } 3665 if (i == displayableColumns.length) 3666 colIndex = 0; 3667 } 3668 3669 // trace("commitEditedItemPosition ", coord.rowIndex, selectedIndex); 3670 3671 var needChangeEvent:Boolean = false; 3672 if (selectedIndex != coord.rowIndex) 3673 { 3674 commitSelectedIndex(coord.rowIndex); 3675 needChangeEvent = true; 3676 } 3677 3678 scrollToEditedItem(rowIndex, colIndex); 3679 3680 // get the actual references for the column, row, and item 3681 var item:IListItemRenderer = actualContentHolder.listItems[actualRowIndex][actualColIndex]; 3682 if (!item) 3683 { 3684 // assume that editing was cancelled 3685 commitEditedItemPosition(null); 3686 return; 3687 } 3688 if (!isItemEditable(item.data)) 3689 { 3690 // assume that editing was cancelled 3691 commitEditedItemPosition(null); 3692 return; 3693 } 3694 3695 if (needChangeEvent) 3696 { 3697 var evt:ListEvent = new ListEvent(ListEvent.CHANGE); 3698 evt.columnIndex = coord.columnIndex; 3699 evt.rowIndex = coord.rowIndex;; 3700 evt.itemRenderer = item; 3701 dispatchEvent(evt); 3702 } 3703 3704 var event:DataGridEvent = 3705 new DataGridEvent(DataGridEvent.ITEM_EDIT_BEGIN, false, true); 3706 // ITEM_EDIT events are cancelable 3707 event.columnIndex = displayableColumns[colIndex].colNum; 3708 event.rowIndex = _editedItemPosition.rowIndex; 3709 event.itemRenderer = item; 3710 dispatchEvent(event); 3711 3712 lastEditedItemPosition = _editedItemPosition; 3713 3714 // user may be trying to change the focused item renderer 3715 if (bEditedItemPositionChanged) 3716 { 3717 bEditedItemPositionChanged = false; 3718 commitEditedItemPosition(_proposedEditedItemPosition); 3719 _proposedEditedItemPosition = undefined; 3720 3721 } 3722 3723 if (!itemEditorInstance) 3724 { 3725 // assume that editing was cancelled 3726 commitEditedItemPosition(null); 3727 } 3728 } 3729 3730 // computes actualRowIndex, actualColIndex and actualContentHolder by 3731 // taking inputs for rowIndex, colIndex and scrolling to the right 3732 // place 3733 private function scrollToEditedItem(rowIndex:int, colIndex:int):void 3734 { 3735 actualContentHolder = listContent; 3736 var listItems:Array = actualContentHolder.listItems; 3737 3738 var lastRowIndex:int = verticalScrollPosition + listItems.length - 1 + lockedRowCount; 3739 var partialRow:int = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; 3740 3741 // actual row/column is the offset into one of the containers 3742 if (rowIndex > lockedRowCount) 3743 { 3744 // not a locked editable row make sure it is on screen 3745 if (rowIndex < verticalScrollPosition + lockedRowCount) 3746 verticalScrollPosition = rowIndex - lockedRowCount; 3747 else 3748 { 3749 // variable row heights means that we can't know how far to scroll sometimes so we loop 3750 // until we get it right 3751 while (rowIndex > lastRowIndex || 3752 // we're the last row, and we're partially visible, but we're not 3753 // the top scrollable row already 3754 (rowIndex == lastRowIndex && rowIndex > verticalScrollPosition + lockedRowCount && 3755 partialRow)) 3756 { 3757 if (verticalScrollPosition == maxVerticalScrollPosition) 3758 break; 3759 verticalScrollPosition = Math.min(verticalScrollPosition + (rowIndex > lastRowIndex ? rowIndex - lastRowIndex : partialRow), maxVerticalScrollPosition); 3760 lastRowIndex = verticalScrollPosition + listItems.length - 1 + lockedRowCount; 3761 partialRow = (rowInfo[listItems.length - 1].y + rowInfo[listItems.length - 1].height > listContent.height) ? 1 : 0; 3762 } 3763 } 3764 3765 actualRowIndex = rowIndex - verticalScrollPosition - lockedRowCount; 3766 3767 } 3768 else 3769 { 3770 if (rowIndex == lockedRowCount) 3771 { 3772 verticalScrollPosition = 0; 3773 actualRowIndex = rowIndex - lockedRowCount; 3774 } 3775 else 3776 { 3777 if (lockedRowCount) 3778 actualContentHolder = lockedRowContent; 3779 3780 actualRowIndex = rowIndex; 3781 } 3782 } 3783 3784 // reset since actualContentHolder could have changed 3785 listItems = actualContentHolder.listItems; 3786 3787 var len:uint = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length; 3788 var lastColIndex:int = horizontalScrollPosition + len - 1 + lockedColumnCount; 3789 3790 if (colIndex > lockedColumnCount) 3791 { 3792 var partialCol:int = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0; 3793 if (colIndex < horizontalScrollPosition + lockedColumnCount) 3794 horizontalScrollPosition = colIndex - lockedColumnCount; 3795 else 3796 { 3797 while (colIndex > lastColIndex || 3798 (colIndex == lastColIndex && colIndex > horizontalScrollPosition + lockedColumnCount && 3799 partialCol)) 3800 { 3801 if (horizontalScrollPosition == maxHorizontalScrollPosition) 3802 break; 3803 horizontalScrollPosition = Math.min(horizontalScrollPosition + (colIndex > lastColIndex ? colIndex - lastColIndex : partialCol), maxHorizontalScrollPosition); 3804 len = (listItems && listItems[0]) ? listItems[0].length : visibleColumns.length; 3805 lastColIndex = horizontalScrollPosition + len - 1 + lockedColumnCount; 3806 partialCol = (listItems[0][len - 1].x + listItems[0][len - 1].width > listContent.width) ? 1 : 0; 3807 } 3808 } 3809 actualColIndex = colIndex - horizontalScrollPosition - lockedColumnCount; 3810 } 3811 else 3812 { 3813 if (colIndex == lockedColumnCount) 3814 { 3815 horizontalScrollPosition = 0; 3816 actualColIndex = colIndex - lockedColumnCount; 3817 } 3818 else 3819 { 3820 if (lockedColumnCount) 3821 { 3822 if (actualContentHolder == lockedRowContent) 3823 actualContentHolder = lockedColumnAndRowContent; 3824 else 3825 actualContentHolder = lockedColumnContent; 3826 } 3827 3828 actualColIndex = colIndex; 3829 } 3830 } 3831 } 3832 3833 /** 3834 * Creates the item editor for the item renderer at the 3835 * <code>editedItemPosition</code> using the editor 3836 * specified by the <code>itemEditor</code> property. 3837 * 3838 * <p>This method sets the editor instance as the 3839 * <code>itemEditorInstance</code> property.</p> 3840 * 3841 * <p>You may only call this method from within the event listener 3842 * for the <code>itemEditBegin</code> event. 3843 * To create an editor at other times, set the 3844 * <code>editedItemPosition</code> property to generate 3845 * the <code>itemEditBegin</code> event.</p> 3846 * 3847 * @param colIndex The column index in the data provider of the item to be edited. 3848 * 3849 * @param rowIndex The row index in the data provider of the item to be edited. 3850 */ 3851 public function createItemEditor(colIndex:int, rowIndex:int):void 3852 { 3853 if (displayableColumns.length != _columns.length) 3854 { 3855 for (var i:int = 0; i < displayableColumns.length; i++) 3856 { 3857 if (displayableColumns[i].colNum >= colIndex) 3858 { 3859 colIndex = i; 3860 break; 3861 } 3862 } 3863 if (i == displayableColumns.length) 3864 colIndex = 0; 3865 } 3866 3867 var col:DataGridColumn = displayableColumns[colIndex]; 3868 if (rowIndex >= lockedRowCount) 3869 rowIndex -= verticalScrollPosition + lockedRowCount; 3870 3871 if (colIndex >= lockedColumnCount) 3872 colIndex -= horizontalScrollPosition + lockedColumnCount; 3873 3874 var item:IListItemRenderer = actualContentHolder.listItems[rowIndex][colIndex]; 3875 var rowData:ListRowInfo = actualContentHolder.rowInfo[rowIndex]; 3876 3877 if (!col.rendererIsEditor) 3878 { 3879 var dx:Number = 0; 3880 var dy:Number = -2; 3881 var dw:Number = 0; 3882 var dh:Number = 4; 3883 // if this isn't implemented, use an input control as editor 3884 if (!itemEditorInstance) 3885 { 3886 var itemEditor:IFactory = col.itemEditor; 3887 dx = col.editorXOffset; 3888 dy = col.editorYOffset; 3889 dw = col.editorWidthOffset; 3890 dh = col.editorHeightOffset; 3891 itemEditorInstance = itemEditor.newInstance(); 3892 itemEditorInstance.owner = this; 3893 itemEditorInstance.styleName = col; 3894 actualContentHolder.addChild(DisplayObject(itemEditorInstance)); 3895 } 3896 actualContentHolder.setChildIndex(DisplayObject(itemEditorInstance), actualContentHolder.numChildren - 1); 3897 // give it the right size, look and placement 3898 itemEditorInstance.visible = true; 3899 itemEditorInstance.move(item.x + dx, rowData.y + dy); 3900 itemEditorInstance.setActualSize( 3901 Math.min(col.width + dw, 3902 actualContentHolder.width - 1 - itemEditorInstance.x), 3903 Math.min(rowData.height + dh, 3904 actualContentHolder.height - itemEditorInstance.y)); 3905 DisplayObject(itemEditorInstance).addEventListener(FocusEvent.FOCUS_OUT, itemEditorFocusOutHandler); 3906 item.visible = false; 3907 3908 } 3909 else 3910 { 3911 // if the item renderer is also the editor, we'll use it 3912 itemEditorInstance = item; 3913 } 3914 3915 // listen for keyStrokes on the itemEditorInstance (which lets the grid supervise for ESC/ENTER) 3916 DisplayObject(itemEditorInstance).addEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); 3917 // we disappear on any mouse down outside the editor 3918 systemManager.getSandboxRoot(). 3919 addEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true, 0, true); 3920 systemManager.getSandboxRoot(). 3921 addEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler, false, 0, true); 3922 // we disappear if stage is resized 3923 systemManager.addEventListener(Event.RESIZE, editorStageResizeHandler, true, 0, true); 3924 3925 // Dispatch our item editor created event 3926 var event:DataGridEvent = 3927 new DataGridEvent(DataGridEvent.ITEM_EDITOR_CREATE, false, true, colIndex, null, rowIndex); 3928 dispatchEvent(event); 3929 } 3930 3931 /** 3932 * @private 3933 * Determines the next item renderer to navigate to using the Tab key. 3934 * If the item renderer to be focused falls out of range (the end or beginning 3935 * of the grid) then move focus outside the grid. 3936 */ 3937 private function findNextItemRenderer(shiftKey:Boolean):Boolean 3938 { 3939 if (!lastEditedItemPosition) 3940 return false; 3941 3942 // some other thing like a collection change has changed the 3943 // position, so bail and wait for commit to reset the editor. 3944 if (_proposedEditedItemPosition !== undefined) 3945 return true; 3946 3947 _editedItemPosition = lastEditedItemPosition; 3948 3949 var index:int = _editedItemPosition.rowIndex; 3950 var colIndex:int = _editedItemPosition.columnIndex; 3951 3952 var found:Boolean = false; 3953 var incr:int = shiftKey ? -1 : 1; 3954 var maxIndex:int = collection.length - 1; 3955 3956 // cycle till we find something worth focusing, or the end of the grid 3957 while (!found) 3958 { 3959 // go to next column 3960 colIndex += incr; 3961 if (colIndex >= _columns.length || colIndex < 0) 3962 { 3963 // if we fall off the end of the columns, wrap around 3964 colIndex = (colIndex < 0) ? _columns.length - 1 : 0; 3965 // and increment/decrement the row index 3966 index += incr; 3967 if (index > maxIndex || index < 0) 3968 { 3969 if (endEdit(DataGridEventReason.NEW_ROW)) 3970 { 3971 // if we've fallen off the rows, we need to leave the grid. get rid of the editor 3972 setEditedItemPosition(null); 3973 // set focus back to the grid so default handler will move it to the next component 3974 losingFocus = true; 3975 setFocus(); 3976 return false; 3977 } 3978 return true; 3979 } 3980 } 3981 // if we find a visible and editable column, move to it 3982 if (_columns[colIndex].editable && _columns[colIndex].visible) 3983 { 3984 found = true; 3985 // kill the old edit session 3986 var reason:String; 3987 reason = index == _editedItemPosition.rowIndex ? 3988 DataGridEventReason.NEW_COLUMN : 3989 DataGridEventReason.NEW_ROW; 3990 if (!itemEditorInstance || endEdit(reason)) 3991 { 3992 // send event to create the new one 3993 beginningEdit(colIndex, index); 3994 } 3995 } 3996 } 3997 return found; 3998 } 3999 4000 /** 4001 * This method closes an item editor currently open on an item renderer. 4002 * You typically only call this method from within the event listener 4003 * for the <code>itemEditEnd</code> event, after 4004 * you have already called the <code>preventDefault()</code> method to 4005 * prevent the default event listener from executing. 4006 */ 4007 public function destroyItemEditor():void 4008 { 4009 // trace("destroyItemEditor"); 4010 if (itemEditorInstance) 4011 { 4012 DisplayObject(itemEditorInstance).removeEventListener(KeyboardEvent.KEY_DOWN, editorKeyDownHandler); 4013 systemManager.getSandboxRoot(). 4014 removeEventListener(MouseEvent.MOUSE_DOWN, editorMouseDownHandler, true); 4015 systemManager.getSandboxRoot(). 4016 removeEventListener(SandboxMouseEvent.MOUSE_DOWN_SOMEWHERE, editorMouseDownHandler); 4017 systemManager.removeEventListener(Event.RESIZE, editorStageResizeHandler, true); 4018 4019 var event:DataGridEvent = 4020 new DataGridEvent(DataGridEvent.ITEM_FOCUS_OUT); 4021 event.columnIndex = _editedItemPosition.columnIndex; 4022 event.rowIndex = _editedItemPosition.rowIndex; 4023 event.itemRenderer = itemEditorInstance; 4024 dispatchEvent(event); 4025 4026 if (! _columns[_editedItemPosition.columnIndex].rendererIsEditor) 4027 { 4028 // FocusManager.removeHandler() does not find 4029 // itemEditors in focusableObjects[] array 4030 // and hence does not remove the focusRectangle 4031 if (itemEditorInstance && itemEditorInstance is UIComponent) 4032 UIComponent(itemEditorInstance).drawFocus(false); 4033 4034 // must call removeChild() so FocusManager.lastFocus becomes null 4035 actualContentHolder.removeChild(DisplayObject(itemEditorInstance)); 4036 editedItemRenderer.visible = true; 4037 } 4038 itemEditorInstance = null; 4039 _editedItemPosition = null; 4040 } 4041 } 4042 4043 /** 4044 * @private 4045 * dispatch an ITEM_EDIT_BEGINNING event; 4046 */ 4047 private function beginningEdit(columnIndex:int, rowIndex:int, itemRenderer:IListItemRenderer = null):void 4048 { 4049 var dataGridEvent:DataGridEvent = 4050 new DataGridEvent(DataGridEvent.ITEM_EDIT_BEGINNING, false, true); 4051 // ITEM_EDIT events are cancelable 4052 dataGridEvent.columnIndex = columnIndex; 4053 dataGridEvent.dataField = _columns[columnIndex].dataField; 4054 dataGridEvent.rowIndex = rowIndex; 4055 dataGridEvent.itemRenderer = itemRenderer; 4056 if (!dispatchEvent(dataGridEvent)) 4057 lastEditedItemPosition = { columnIndex: columnIndex, rowIndex: rowIndex }; 4058 4059 } 4060 4061 /** 4062 * @private 4063 * When the user finished editing an item, this method is called. 4064 * It dispatches the DataGridEvent.ITEM_EDIT_END event to start the process 4065 * of copying the edited data from 4066 * the itemEditorInstance to the data provider and hiding the itemEditorInstance. 4067 * returns true if nobody called preventDefault. 4068 */ 4069 private function endEdit(reason:String):Boolean 4070 { 4071 // this happens if the renderer is removed asynchronously ususally with FDS 4072 if (!editedItemRenderer) 4073 return true; 4074 4075 inEndEdit = true; 4076 4077 var dataGridEvent:DataGridEvent = 4078 new DataGridEvent(DataGridEvent.ITEM_EDIT_END, false, true); 4079 // ITEM_EDIT events are cancelable 4080 dataGridEvent.columnIndex = editedItemPosition.columnIndex; 4081 dataGridEvent.dataField = _columns[editedItemPosition.columnIndex].dataField; 4082 dataGridEvent.rowIndex = editedItemPosition.rowIndex; 4083 dataGridEvent.itemRenderer = editedItemRenderer; 4084 dataGridEvent.reason = reason; 4085 dispatchEvent(dataGridEvent); 4086 // set a flag to not open another edit session if the item editor is still up 4087 // this means somebody wants the old edit session to stay. 4088 dontEdit = itemEditorInstance != null; 4089 // trace("dontEdit", dontEdit); 4090 4091 if (!dontEdit && reason == DataGridEventReason.CANCELLED) 4092 { 4093 losingFocus = true; 4094 setFocus(); 4095 } 4096 4097 inEndEdit = false; 4098 4099 return !(dataGridEvent.isDefaultPrevented()) 4100 } 4101 4102 /** 4103 * Determines whether to allow editing of a dataprovider item on a per-row basis. 4104 * The default implementation of this method only checks the <code>editable</code> property 4105 * of the DataGrid and returns <code>false</code> if <code>editable</code> is <code>false</code> 4106 * or if the dataprovider item is <code>null</code>. 4107 * 4108 * <p>This method can be overridden to allow fine-grained control of which dataprovider items 4109 * are editable. For example, if you want to disallow editing of grouping rows or summary rows 4110 * you would override this method with custom logic to this behavior.</p> 4111 * 4112 * @param data The data provider item. The default implementation of this method returns 4113 * <code>false</code> if the data object is <code>null</code>. 4114 * 4115 * @return The default behavior is to return <code>true</code> if the DataGrid's <code>editable</code> property is 4116 * <code>true</code> and the data object is not <code>null</code>. 4117 */ 4118 public function isItemEditable(data:Object):Boolean 4119 { 4120 if (!editable) 4121 return false; 4122 4123 if (!data) 4124 return false; 4125 4126 return true; 4127 } 4128 4129 /** 4130 * @private 4131 */ 4132 override mx_internal function columnRendererChanged(c:DataGridColumn):void 4133 { 4134 var item:DisplayObject; 4135 4136 var factories:Dictionary = c.measuringObjects; 4137 if (factories) 4138 { 4139 for (var p:* in factories) 4140 { 4141 var factory:IFactory = IFactory(p); 4142 item = c.measuringObjects[factory]; 4143 if (item) 4144 { 4145 item.parent.removeChild(item); 4146 c.measuringObjects[factory] = null; 4147 } 4148 4149 if (c.freeItemRenderersByFactory && c.freeItemRenderersByFactory[factory]) 4150 { 4151 var d:Dictionary = c.freeItemRenderersByFactory[factory]; 4152 for (var q:* in d) 4153 { 4154 item = DisplayObject(q); 4155 item.parent.removeChild(item); 4156 } 4157 c.freeItemRenderersByFactory[factory] = new Dictionary(true); 4158 } 4159 } 4160 var freeRenderers:Array = freeItemRenderersTable[c] as Array; 4161 if (freeRenderers) 4162 { 4163 while (freeRenderers.length) 4164 { 4165 item = freeRenderers.pop(); 4166 } 4167 } 4168 } 4169 4170 item = c.cachedHeaderRenderer as DisplayObject; 4171 if (item && item.parent) 4172 item.parent.removeChild(item); 4173 c.cachedHeaderRenderer = null; 4174 4175 rendererChanged = true; 4176 invalidateDisplayList(); 4177 } 4178 4179 //-------------------------------------------------------------------------- 4180 // 4181 // Overridden event handlers 4182 // 4183 //-------------------------------------------------------------------------- 4184 4185 /** 4186 * @private 4187 * Catches any events from the model. Optimized for editing one item. 4188 * Creates columns when there are none. Inherited from list. 4189 * @param eventObj 4190 */ 4191 override protected function collectionChangeHandler(event:Event):void 4192 { 4193 if (event is CollectionEvent) 4194 { 4195 var curEditedItemPosition:Object; 4196 var ceEvent:CollectionEvent = CollectionEvent(event) 4197 if (ceEvent.kind == CollectionEventKind.RESET) 4198 { 4199 if (itemEditorInstance) 4200 endEdit(DataGridEventReason.CANCELLED); 4201 setEditedItemPosition(null); // nothing left to edit 4202 if (generatedColumns) 4203 generateCols(); 4204 else 4205 columnsInvalid = true; 4206 updateSortIndexAndDirection(); 4207 if (lockedRowContent) 4208 lockedRowContent.iterator = collection.createCursor(); 4209 if (lockedColumnAndRowContent) 4210 lockedColumnAndRowContent.iterator = collection.createCursor(); 4211 } 4212 else if (ceEvent.kind == CollectionEventKind.REFRESH && !manualSort) 4213 updateSortIndexAndDirection(); 4214 else 4215 { 4216 // if we get a add while editing adjust the editPosition 4217 if (ceEvent.kind == CollectionEventKind.ADD) 4218 { 4219 if (editedItemPosition) 4220 { 4221 if (ceEvent.location <= editedItemPosition.rowIndex) 4222 { 4223 curEditedItemPosition = editedItemPosition; 4224 4225 if (inEndEdit) 4226 _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 4227 rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)}; 4228 else if (itemEditorInstance) 4229 { 4230 _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 4231 rowIndex : Math.max(0, editedItemPosition.rowIndex + ceEvent.items.length)}; 4232 itemEditorPositionChanged = true; 4233 lastEditedItemPosition = _editedItemPosition; 4234 } 4235 else 4236 setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex, 4237 rowIndex : Math.max(0, curEditedItemPosition.rowIndex + ceEvent.items.length)}); 4238 } 4239 } 4240 } 4241 // if we get a remove while editing adjust the editPosition 4242 else if (ceEvent.kind == CollectionEventKind.REMOVE) 4243 { 4244 if (editedItemPosition) 4245 { 4246 if (collection.length == 0) 4247 { 4248 if (itemEditorInstance) 4249 endEdit(DataGridEventReason.CANCELLED); 4250 setEditedItemPosition(null); // nothing left to edit 4251 } 4252 else if (ceEvent.location <= editedItemPosition.rowIndex) 4253 { 4254 curEditedItemPosition = editedItemPosition; 4255 4256 // if the editor is up on the item going away, cancel the session 4257 if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) 4258 endEdit(DataGridEventReason.CANCELLED); 4259 4260 if (inEndEdit) 4261 _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 4262 rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)}; 4263 else if (itemEditorInstance) 4264 { 4265 _editedItemPosition = { columnIndex : editedItemPosition.columnIndex, 4266 rowIndex : Math.max(0, editedItemPosition.rowIndex - ceEvent.items.length)}; 4267 itemEditorPositionChanged = true; 4268 lastEditedItemPosition = _editedItemPosition; 4269 } 4270 else 4271 setEditedItemPosition({ columnIndex : curEditedItemPosition.columnIndex, 4272 rowIndex : Math.max(0, curEditedItemPosition.rowIndex - ceEvent.items.length)}); 4273 } 4274 } 4275 } 4276 else if (ceEvent.kind == CollectionEventKind.REPLACE) 4277 { 4278 if (editedItemPosition) 4279 { 4280 // if the editor is up on the item going away, cancel the session 4281 if (ceEvent.location == editedItemPosition.rowIndex && itemEditorInstance) 4282 endEdit(DataGridEventReason.CANCELLED); 4283 } 4284 } 4285 } 4286 } 4287 4288 super.collectionChangeHandler(event); 4289 4290 if (event is CollectionEvent) 4291 { 4292 // trace("ListBase collectionEvent"); 4293 var ce:CollectionEvent = CollectionEvent(event); 4294 if (ce.kind == CollectionEventKind.ADD) 4295 { 4296 // added first item, generate columns for it if needed 4297 if (collection.length == 1) 4298 if (generatedColumns) 4299 generateCols(); 4300 } 4301 else if (ce.kind == CollectionEventKind.REFRESH) 4302 { 4303 // refresh locked row count iterator 4304 if (lockedRowCount && lockedRowContent) 4305 lockedRowContent.iterator.seek(CursorBookmark.FIRST, 0); 4306 } 4307 } 4308 4309 4310// if (event.eventName != "sort" && bRowsChanged) 4311// invInitHeaders = true; 4312 } 4313 4314 /** 4315 * @private 4316 */ 4317 override protected function mouseDownHandler(event:MouseEvent):void 4318 { 4319 // trace(">>mouseDownHandler"); 4320 var r:IListItemRenderer; 4321 var s:Sprite; 4322 4323 r = mouseEventToItemRenderer(event); 4324 4325 lastItemDown = null; 4326 4327 var isItemEditor:Boolean = itemRendererContains(itemEditorInstance, DisplayObject(event.target)); 4328 4329 // If it isn't an item renderer, or an item editor do default behavior 4330 if (!isItemEditor) 4331 { 4332 var pos:Point; 4333 if (r) 4334 { 4335 lastItemDown = r; 4336 4337 pos = itemRendererToIndices(r); 4338 4339 var bEndedEdit:Boolean = true; 4340 4341 if (itemEditorInstance) 4342 { 4343 if (displayableColumns[pos.x].editable == false) 4344 { 4345 bEndedEdit = endEdit(DataGridEventReason.OTHER); 4346 } 4347 else 4348 { 4349 //Possible to get here and not have an editedItemPosition 4350 if (editedItemPosition) 4351 { 4352 bEndedEdit = endEdit(editedItemPosition.rowIndex == pos.y ? 4353 DataGridEventReason.NEW_COLUMN : 4354 DataGridEventReason.NEW_ROW); 4355 } 4356 else 4357 { 4358 bEndedEdit = false; 4359 } 4360 } 4361 } 4362 4363 // if we didn't end edit session, don't do default behavior (call super) 4364 if (!bEndedEdit) 4365 return; 4366 } 4367 else 4368 { 4369 // trace("end edit?"); 4370 if (itemEditorInstance) 4371 endEdit(DataGridEventReason.OTHER); 4372 } 4373 4374 super.mouseDownHandler(event); 4375 4376 if (r) 4377 { 4378 if (displayableColumns[pos.x].rendererIsEditor) 4379 resetDragScrolling(); 4380 } 4381 } 4382 else 4383 resetDragScrolling(); 4384 // trace("<<mouseDownHandler"); 4385 } 4386 4387 /** 4388 * @private 4389 */ 4390 override protected function mouseUpHandler(event:MouseEvent):void 4391 { 4392 var dataGridEvent:DataGridEvent; 4393 var r:IListItemRenderer; 4394 var s:Sprite; 4395 var n:int; 4396 var i:int; 4397 var pos:Point; 4398 4399 r = mouseEventToItemRenderer(event); 4400 4401 super.mouseUpHandler(event); 4402 4403 if (r && r != itemEditorInstance && 4404 (lastItemDown == r || itemRendererContains(lastItemDown, lastItemFocused))) 4405 { 4406 // if lastItemDown != r, we clicked in one cell and dragged to another. 4407 // if lastItemFocused is in that cell, then we give lastItemDown the 4408 // edit session 4409 if (lastItemDown != r) 4410 r = lastItemDown; 4411 4412 lastItemFocused = null; 4413 pos = itemRendererToIndices(r); 4414 4415 if (pos && pos.y >= 0 && editable && !dontEdit) 4416 { 4417 if (displayableColumns[pos.x].editable) 4418 { 4419 beginningEdit(displayableColumns[pos.x].colNum, pos.y, r); 4420 } 4421 else 4422 // if the item is not editable, set lastPosition to it anyways 4423 // so future tabbing starts from there 4424 lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y }; 4425 } 4426 } 4427 else if (lastItemDown && lastItemDown != itemEditorInstance) 4428 { 4429 pos = itemRendererToIndices(lastItemDown); 4430 4431 if (pos && pos.y >= 0 && editable && !dontEdit) 4432 { 4433 if (displayableColumns[pos.x].editable) 4434 { 4435 beginningEdit(displayableColumns[pos.x].colNum, pos.y, lastItemDown); 4436 } 4437 else 4438 // if the item is not editable, set lastPosition to it anyways 4439 // so future tabbing starts from there 4440 lastEditedItemPosition = { columnIndex: displayableColumns[pos.x].colNum, rowIndex: pos.y }; 4441 } 4442 } 4443 4444 lastItemDown = null; 4445 } 4446 4447 /** 4448 * @private 4449 * when the grid gets focus, focus an item renderer 4450 */ 4451 override protected function focusInHandler(event:FocusEvent):void 4452 { 4453 // trace(">>DGFocusIn ", selectedIndex); 4454 var dataGridEvent:DataGridEvent; 4455 4456 if (losingFocus) 4457 { 4458 losingFocus = false; 4459 // trace("losing focus via tab"); 4460 // trace("<<DGFocusIn "); 4461 return; 4462 } 4463 4464 if (editable) 4465 { 4466 addEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler); 4467 addEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler); 4468 } 4469 4470 if (event.target != this) 4471 { 4472 if (itemEditorInstance && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) 4473 { 4474 // trace("item editor got focus ignoring"); 4475 // trace("<<DGFocusIn "); 4476 return; 4477 } 4478 lastItemFocused = DisplayObject(event.target); 4479 // trace("subcomponent got focus ignoring"); 4480 // trace("<<DGFocusIn "); 4481 return; 4482 } 4483 lastItemFocused = null; 4484 4485 super.focusInHandler(event); 4486 4487 // isPressed is not correct if we switch in from a different window 4488 // via mouseDown. The activation calls focusIn (if we had focus 4489 // when we lost activation) before mouseDown 4490 if (editable && !isPressed) // don't do this if we're mouse focused 4491 { 4492 _editedItemPosition = lastEditedItemPosition; 4493 4494 var foundOne:Boolean = false; 4495 4496 // start somewhere 4497 if (!_editedItemPosition) 4498 _editedItemPosition = { rowIndex: 0, columnIndex: 0 }; 4499 4500 4501 for (; 4502 _editedItemPosition.columnIndex != _columns.length; 4503 _editedItemPosition.columnIndex++) 4504 { 4505 // If the editedItemPosition is valid, focus it, 4506 // otherwise find one. 4507 if (_columns[_editedItemPosition.columnIndex].editable && 4508 _columns[_editedItemPosition.columnIndex].visible) 4509 { 4510 foundOne = true; 4511 break; 4512 } 4513 } 4514 4515 if (foundOne) 4516 { 4517 // trace("setting focus", _editedItemPosition.columnIndex, _editedItemPosition.rowIndex); 4518 beginningEdit(_editedItemPosition.columnIndex, _editedItemPosition.rowIndex); 4519 } 4520 4521 } 4522 4523 // trace("<<DGFocusIn "); 4524 } 4525 4526 /** 4527 * @private 4528 * when the grid loses focus, close the editor 4529 */ 4530 override protected function focusOutHandler(event:FocusEvent):void 4531 { 4532 // trace(">>DGFocusOut " + itemEditorInstance + " " + event.relatedObject, event.target); 4533 if (event.target == this) 4534 super.focusOutHandler(event); 4535 4536 // just leave if item editor is losing focus back to grid. Usually happens 4537 // when someone clicks out of the editor onto a new item renderer. 4538 if (event.relatedObject == this && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) 4539 return; 4540 4541 // just leave if the cell renderer is losing focus to nothing while its editor exists. 4542 // this happens when we make the cell renderer invisible as we put up the editor 4543 // if the renderer can have focus. 4544 if (event.relatedObject == null && itemRendererContains(editedItemRenderer, DisplayObject(event.target))) 4545 return; 4546 4547 // just leave if item editor is losing focus to nothing. Usually happens 4548 // when someone clicks out of the textfield 4549 if (event.relatedObject == null && itemRendererContains(itemEditorInstance, DisplayObject(event.target))) 4550 return; 4551 4552 // however, if we're losing focus to anything other than the editor or the grid 4553 // hide the editor; 4554 if (itemEditorInstance && (!event.relatedObject || !itemRendererContains(itemEditorInstance, event.relatedObject))) 4555 { 4556 // find renderer for target 4557 var target:DisplayObject = DisplayObject(event.relatedObject); 4558 while (target && target != this) 4559 { 4560 if (target is IListItemRenderer && target.parent.parent == this && target.parent is ListBaseContentHolder) 4561 { 4562 if (target.visible) 4563 { 4564 // losing focus to another renderer 4565 // let other logic end the session 4566 return; 4567 } 4568 } 4569 4570 if (target is IUIComponent) 4571 target = IUIComponent(target).owner; 4572 else 4573 target = target.parent; 4574 } 4575 4576 // trace("call endEdit from focus out"); 4577 endEdit(DataGridEventReason.OTHER); 4578 removeEventListener(FocusEvent.KEY_FOCUS_CHANGE, keyFocusChangeHandler); 4579 removeEventListener(MouseEvent.MOUSE_DOWN, mouseFocusChangeHandler); 4580 } 4581 // trace("<<DGFocusOut " + itemEditorInstance + " " + event.relatedObject); 4582 } 4583 4584 /** 4585 * @private 4586 */ 4587 private function deactivateHandler(event:Event):void 4588 { 4589 // if stage losing activation, set focus to DG so when we get it back 4590 // we popup an editor again 4591 if (itemEditorInstance) 4592 { 4593 endEdit(DataGridEventReason.OTHER); 4594 losingFocus = true; 4595 setFocus(); 4596 } 4597 } 4598 4599 /** 4600 * @private 4601 */ 4602 override protected function keyDownHandler(event:KeyboardEvent):void 4603 { 4604 if (itemEditorInstance || event.target != event.currentTarget) 4605 return; 4606 4607 if (event.keyCode != Keyboard.SPACE) 4608 super.keyDownHandler(event); 4609 else if (caretIndex != -1) 4610 { 4611 moveSelectionVertically(event.keyCode, event.shiftKey, event.ctrlKey); 4612 } 4613 } 4614 4615 /** 4616 * @private 4617 * used by ListBase.findString. Shouldn't be used elsewhere 4618 * because column's itemToLabel is preferred 4619 */ 4620 override public function itemToLabel(data:Object):String 4621 { 4622 return displayableColumns[sortIndex == -1 ? 0 : sortIndex].itemToLabel(data); 4623 } 4624 4625 //-------------------------------------------------------------------------- 4626 // 4627 // Event handlers 4628 // 4629 //-------------------------------------------------------------------------- 4630 4631 4632 /** 4633 * @private 4634 */ 4635 private function editorMouseDownHandler(event:Event):void 4636 { 4637 if (event is MouseEvent && owns(DisplayObject(event.target))) 4638 return; 4639 4640 endEdit(DataGridEventReason.OTHER); 4641 // set focus back to the grid so grid logic will deal if focus doesn't 4642 // end up somewhere else 4643 losingFocus = true; 4644 setFocus(); 4645 } 4646 4647 /** 4648 * @private 4649 */ 4650 private function editorKeyDownHandler(event:KeyboardEvent):void 4651 { 4652 // ESC just kills the editor, no new data 4653 if (event.keyCode == Keyboard.ESCAPE) 4654 { 4655 endEdit(DataGridEventReason.CANCELLED); 4656 } 4657 else if (event.ctrlKey && event.charCode == 46) 4658 { // Check for Ctrl-. 4659 endEdit(DataGridEventReason.CANCELLED); 4660 } 4661 else if (event.charCode == Keyboard.ENTER && event.keyCode != 229) 4662 { 4663 // multiline editors can take the enter key. 4664 if (!_editedItemPosition) 4665 return; 4666 4667 if (columns[_editedItemPosition.columnIndex].editorUsesEnterKey) 4668 return; 4669 4670 // Enter edits the item, moves down a row 4671 // The 229 keyCode is for IME compatability. When entering an IME expression, 4672 // the enter key is down, but the keyCode is 229 instead of the enter key code. 4673 // Thanks to Yukari for this little trick... 4674 if (endEdit(DataGridEventReason.NEW_ROW) && !dontEdit) 4675 findNextEnterItemRenderer(event); 4676 } 4677 } 4678 4679 /** 4680 * @private 4681 */ 4682 private function editorStageResizeHandler(event:Event):void 4683 { 4684 if (event.target is DisplayObjectContainer && 4685 DisplayObjectContainer(event.target).contains(this)) 4686 endEdit(DataGridEventReason.OTHER); 4687 } 4688 4689 /** 4690 * @private 4691 * find the next item renderer down from the currently edited item renderer, and focus it. 4692 */ 4693 private function findNextEnterItemRenderer(event:KeyboardEvent):void 4694 { 4695 // some other thing like a collection change has changed the 4696 // position, so bail and wait for commit to reset the editor. 4697 if (_proposedEditedItemPosition !== undefined) 4698 return; 4699 4700 _editedItemPosition = lastEditedItemPosition; 4701 4702 // though endEdit checks this, an ITEM_EDIT_END event handler can, in 4703 // the meantime, wipe out the item editor (e.g. by setting a new data 4704 // provider). so we add another check here. 4705 if (!_editedItemPosition) 4706 return; 4707 4708 var rowIndex:int = _editedItemPosition.rowIndex; 4709 var columnIndex:int = _editedItemPosition.columnIndex; 4710 // modify direction with SHIFT (up or down) 4711 var newIndex:int = _editedItemPosition.rowIndex + 4712 (event.shiftKey ? -1 : 1); 4713 // only move if we're within range 4714 if (newIndex < collection.length && newIndex >= 0) 4715 rowIndex = newIndex; 4716 4717 // send event to create the new one 4718 beginningEdit(columnIndex, rowIndex); 4719 } 4720 4721 /** 4722 * @private 4723 * This gets called when the tab key is hit. 4724 */ 4725 private function mouseFocusChangeHandler(event:MouseEvent):void 4726 { 4727 // trace("mouseFocus handled by " + this); 4728 4729 if (itemEditorInstance && 4730 !event.isDefaultPrevented() && 4731 itemRendererContains(itemEditorInstance, DisplayObject(event.target))) 4732 { 4733 event.preventDefault(); 4734 } 4735 } 4736 4737 /** 4738 * @private 4739 * This gets called when the tab key is hit. 4740 */ 4741 private function keyFocusChangeHandler(event:FocusEvent):void 4742 { 4743 // trace("tabHandled by " + this); 4744 4745 if (event.keyCode == Keyboard.TAB && 4746 ! event.isDefaultPrevented() && 4747 findNextItemRenderer(event.shiftKey)) 4748 { 4749 event.preventDefault(); 4750 } 4751 } 4752 4753 /** 4754 * @private 4755 * Hides the itemEditorInstance. 4756 */ 4757 private function itemEditorFocusOutHandler(event:FocusEvent):void 4758 { 4759 // trace("itemEditorFocusOut " + event.relatedObject); 4760 if (event.relatedObject && contains(event.relatedObject)) 4761 return; 4762 4763 // ignore textfields losing focus on mousedowns 4764 if (!event.relatedObject) 4765 return; 4766 4767 // trace("endEdit from itemEditorFocusOut"); 4768 if (itemEditorInstance) 4769 endEdit(DataGridEventReason.OTHER); 4770 } 4771 4772 /** 4773 * @private 4774 */ 4775 private function itemEditorItemEditBeginningHandler(event:DataGridEvent):void 4776 { 4777 // trace("itemEditorItemEditBeginningHandler"); 4778 if (!event.isDefaultPrevented()) 4779 setEditedItemPosition({columnIndex: event.columnIndex, rowIndex: event.rowIndex}); 4780 else if (!itemEditorInstance) 4781 { 4782 _editedItemPosition = null; 4783 // return focus to the grid w/o selecting an item 4784 editable = false; 4785 setFocus(); 4786 editable = true; 4787 } 4788 } 4789 4790 /** 4791 * @private 4792 * focus an item renderer in the grid - harder than it looks 4793 */ 4794 private function itemEditorItemEditBeginHandler(event:DataGridEvent):void 4795 { 4796 if (root) 4797 systemManager.addEventListener(Event.DEACTIVATE, deactivateHandler, false, 0, true); 4798 4799 // if not prevented and if data is not null (might be from dataservices) 4800 if (!event.isDefaultPrevented() && actualContentHolder.listItems[actualRowIndex][actualColIndex].data != null) 4801 { 4802 createItemEditor(event.columnIndex, event.rowIndex); 4803 4804 if (editedItemRenderer is IDropInListItemRenderer && itemEditorInstance is IDropInListItemRenderer) 4805 IDropInListItemRenderer(itemEditorInstance).listData = IDropInListItemRenderer(editedItemRenderer).listData; 4806 // if rendererIsEditor, don't apply the data as the data may have already changed in some way. 4807 // This can happen if clicking on a checkbox rendererIsEditor as the checkbox will try to change 4808 // its value as we try to stuff in an old value here. 4809 if (!columns[event.columnIndex].rendererIsEditor) 4810 itemEditorInstance.data = editedItemRenderer.data; 4811 4812 if (itemEditorInstance is IInvalidating) 4813 IInvalidating(itemEditorInstance).validateNow(); 4814 4815 if (itemEditorInstance is IIMESupport) 4816 IIMESupport(itemEditorInstance).imeMode = 4817 (columns[event.columnIndex].imeMode == null) ? _imeMode : columns[event.columnIndex].imeMode; 4818 4819 var fm:IFocusManager = focusManager; 4820 // trace("setting focus to item editor"); 4821 if (itemEditorInstance is IFocusManagerComponent) 4822 fm.setFocus(IFocusManagerComponent(itemEditorInstance)); 4823 fm.defaultButtonEnabled = false; 4824 4825 var event:DataGridEvent = 4826 new DataGridEvent(DataGridEvent.ITEM_FOCUS_IN); 4827 event.columnIndex = _editedItemPosition.columnIndex; 4828 event.rowIndex = _editedItemPosition.rowIndex; 4829 event.itemRenderer = itemEditorInstance; 4830 dispatchEvent(event); 4831 } 4832 } 4833 4834 /** 4835 * @private 4836 */ 4837 private function itemEditorItemEditEndHandler(event:DataGridEvent):void 4838 { 4839 if (!event.isDefaultPrevented()) 4840 { 4841 var bChanged:Boolean = false; 4842 4843 if (event.reason == DataGridEventReason.NEW_COLUMN) 4844 { 4845 if (!collectionUpdatesDisabled) 4846 { 4847 collection.disableAutoUpdate(); 4848 collectionUpdatesDisabled = true; 4849 } 4850 } 4851 else 4852 { 4853 if (collectionUpdatesDisabled) 4854 { 4855 collection.enableAutoUpdate(); 4856 collectionUpdatesDisabled = false; 4857 } 4858 } 4859 4860 if (itemEditorInstance && event.reason != DataGridEventReason.CANCELLED) 4861 { 4862 var newData:Object = itemEditorInstance[_columns[event.columnIndex].editorDataField]; 4863 var property:String = _columns[event.columnIndex].dataField; 4864 var data:Object = event.itemRenderer.data; 4865 var typeInfo:String = ""; 4866 for each(var variable:XML in describeType(data).variable) 4867 { 4868 if (property == variable.@name.toString()) 4869 { 4870 typeInfo = variable.@type.toString(); 4871 break; 4872 } 4873 } 4874 4875 if (typeInfo == "String") 4876 { 4877 if (!(newData is String)) 4878 newData = newData.toString(); 4879 } 4880 else if (typeInfo == "uint") 4881 { 4882 if (!(newData is uint)) 4883 newData = uint(newData); 4884 } 4885 else if (typeInfo == "int") 4886 { 4887 if (!(newData is int)) 4888 newData = int(newData); 4889 } 4890 else if (typeInfo == "Number") 4891 { 4892 if (!(newData is int)) 4893 newData = Number(newData); 4894 } 4895 /** Old code assumed that the property would be a simply name that could be dereferenced 4896 * through array notation. Using a method call here provides, minimally, an override 4897 * point where developers could extend this functionality in their own datagrid subclass **/ 4898 if (property != null && getCurrentDataValue( data, property ) !== newData) 4899 { 4900 bChanged = setNewValue( data, property, newData, event.columnIndex ); 4901 } 4902 if (bChanged && !(data is IPropertyChangeNotifier || data is XML)) 4903 { 4904 collection.itemUpdated(data, property); 4905 } 4906 if (event.itemRenderer is IDropInListItemRenderer) 4907 { 4908 var listData:DataGridListData = DataGridListData(IDropInListItemRenderer(event.itemRenderer).listData); 4909 listData.label = _columns[event.columnIndex].itemToLabel(data); 4910 IDropInListItemRenderer(event.itemRenderer).listData = listData; 4911 } 4912 event.itemRenderer.data = data; 4913 } 4914 } 4915 else 4916 { 4917 if (event.reason != DataGridEventReason.OTHER) 4918 { 4919 if (itemEditorInstance && _editedItemPosition) 4920 { 4921 // edit session is continued so restore focus and selection 4922 if (selectedIndex != _editedItemPosition.rowIndex) 4923 selectedIndex = _editedItemPosition.rowIndex; 4924 var fm:IFocusManager = focusManager; 4925 // trace("setting focus to itemEditorInstance", selectedIndex); 4926 if (itemEditorInstance is IFocusManagerComponent) 4927 fm.setFocus(IFocusManagerComponent(itemEditorInstance)); 4928 } 4929 } 4930 } 4931 4932 if (event.reason == DataGridEventReason.OTHER || !event.isDefaultPrevented()) 4933 { 4934 destroyItemEditor(); 4935 } 4936 } 4937 4938 protected function isComplexColumn( property:String ):Boolean 4939 { 4940 return ( property.indexOf( "." ) != -1 ); 4941 } 4942 4943 //Gets the reference to the parent object where a property will be updated 4944 protected function deriveComplexFieldReference( data:Object, complexFieldNameComponents:Array ):Object 4945 { 4946 var currentRef:Object = data; 4947 if ( complexFieldNameComponents ) 4948 { 4949 for ( var i:int=0; i<complexFieldNameComponents.length; i++ ) 4950 currentRef = currentRef[ complexFieldNameComponents[ i ] ]; 4951 } 4952 4953 return currentRef; 4954 } 4955 4956 //default implementations of these two methods, intended for subclassing 4957 //not checking if it really is a complex value here as the performance hit of doing this here is negligible 4958 //compared with every display 4959 protected function getCurrentDataValue( data:Object, property:String ):String 4960 { 4961 if ( !isComplexColumn( property ) ) 4962 return data[ property ]; 4963 4964 var complexFieldNameComponents:Array = property.split( "." ); 4965 var obj:Object = deriveComplexFieldReference( data, complexFieldNameComponents ); 4966 4967 return String( obj ); 4968 } 4969 4970 //Passing all of these parameters as it basically allows everything you would need to subclass for all sorts of fun implementations 4971 protected function setNewValue( data:Object, property:String, value:Object, columnIndex:int ):Boolean 4972 { 4973 if ( !isComplexColumn( property ) ) 4974 { 4975 data[ property ] = value; 4976 } 4977 else 4978 { 4979 var complexFieldNameComponents:Array = property.split( "." ); 4980 var lastProp:String = complexFieldNameComponents.pop(); 4981 var parent:Object = deriveComplexFieldReference( data, complexFieldNameComponents ); 4982 parent[ lastProp ] = value; 4983 } 4984 4985 //The value they typed in is always converted to a string, but is the value actually a string in the dataprovider? 4986 //unknown as it is cast by datagridcolumn before datagrid ever gets to know... 4987 //control if this really causes an update in subclass 4988 return true; 4989 } 4990 4991 /** 4992 * @private 4993 */ 4994 private function headerReleaseHandler(event:DataGridEvent):void 4995 { 4996 if (!event.isDefaultPrevented()) 4997 { 4998 manualSort = true; 4999 sortByColumn(event.columnIndex); 5000 manualSort = false; 5001 } 5002 } 5003 5004 /** 5005 * @private 5006 */ 5007 override protected function mouseWheelHandler(event:MouseEvent):void 5008 { 5009 if (itemEditorInstance) 5010 endEdit(DataGridEventReason.OTHER); 5011 5012 super.mouseWheelHandler(event); 5013 } 5014 5015 /** 5016 * @private 5017 * if some drags from the same row as an editor we can be left 5018 * with updates disabled 5019 */ 5020 override protected function dragStartHandler(event:DragEvent):void 5021 { 5022 if (collectionUpdatesDisabled) 5023 { 5024 collection.enableAutoUpdate(); 5025 collectionUpdatesDisabled = false; 5026 } 5027 super.dragStartHandler(event); 5028 } 5029 5030 mx_internal function get vScrollBar():ScrollBar 5031 { 5032 return verticalScrollBar; 5033 } 5034 5035 /** 5036 * diagnostics 5037 */ 5038 override mx_internal function get rendererArray():Array 5039 { 5040 var arr:Array = listItems.slice(); 5041 var arr2:Array = DataGridHeader(header).rendererArray; 5042 arr.unshift(arr2); 5043 return arr; 5044 } 5045 5046 /** 5047 * diagnostics 5048 */ 5049 mx_internal function get sortArrow():IFlexDisplayObject 5050 { 5051 return DataGridHeader(header).sortArrow; 5052 } 5053 5054 /** 5055 * Called from the <code>updateDisplayList()</code> method to adjust the size and position of 5056 * listContent. 5057 * 5058 * @param unscaledWidth The width of the listContent. This value ignores changes to the width from external components or settings. 5059 * @param unscaledHeight The height of the listContent. This value ignores changes to the height from external components or settings. 5060 */ 5061 override protected function adjustListContent(unscaledWidth:Number = -1, 5062 unscaledHeight:Number = -1):void 5063 { 5064 var ww:Number; 5065 var hh:Number = 0; 5066 var lcx:Number; 5067 var lcy:Number; 5068 var hcx:Number; 5069 5070 if (headerVisible) 5071 { 5072 if (lockedColumnCount > 0) 5073 { 5074 lockedColumnHeader.visible = true; 5075 hcx = viewMetrics.left + Math.min(DataGridHeader(lockedColumnHeader).leftOffset, 0); 5076 lockedColumnHeader.move(hcx, viewMetrics.top); 5077 hh = lockedColumnHeader.getExplicitOrMeasuredHeight(); 5078 lockedColumnHeader.setActualSize(lockedColumnWidth + 1, hh); 5079 DataGridHeader(lockedColumnHeader).needRightSeparator = true; 5080 DataGridHeader(lockedColumnHeader).needRightSeparatorEvents = true; 5081 } 5082 header.visible = true; 5083 hcx = viewMetrics.left + lockedColumnWidth + Math.min(DataGridHeader(header).leftOffset, 0); 5084 header.move(hcx, viewMetrics.top); 5085 // If we have a vScroll only, we want the scrollbar to be below 5086 // the header. 5087 if (verticalScrollBar != null && verticalScrollBar.visible && 5088 (horizontalScrollBar == null || !horizontalScrollBar.visible) && headerVisible && 5089 roomForScrollBar(verticalScrollBar, unscaledWidth, unscaledHeight-header.height)) 5090 ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - borderMetrics.right; 5091 else 5092 ww = Math.max(0, DataGridHeader(header).rightOffset) - hcx - viewMetrics.right; 5093 hh = header.getExplicitOrMeasuredHeight(); 5094 header.setActualSize(unscaledWidth + ww, hh); 5095 if (!skipHeaderUpdate) 5096 { 5097 header.headerItemsChanged = true; 5098 header.invalidateDisplayList(); // make sure it redraws, even if size didn't change 5099 // internal renderers could have changed 5100 } 5101 } 5102 else 5103 { 5104 header.visible = false; 5105 if (lockedColumnCount > 0) 5106 lockedColumnHeader.visible = false; 5107 } 5108 5109 if (lockedRowCount > 0 && lockedRowContent && lockedRowContent.iterator) 5110 { 5111 try 5112 { 5113 lockedRowContent.iterator.seek(CursorBookmark.FIRST); 5114 var pt:Point = makeRows(lockedRowContent, 0, 0, unscaledWidth, unscaledHeight, 0, 0, true, lockedRowCount, true); 5115 5116 if (lockedColumnCount > 0) 5117 { 5118 lcx = viewMetrics.left + Math.min(lockedColumnAndRowContent.leftOffset, 0); 5119 lcy = viewMetrics.top + Math.min(lockedColumnAndRowContent.topOffset, 0) + Math.ceil(hh); 5120 lockedColumnAndRowContent.move(lcx, lcy); 5121 lockedColumnAndRowContent.setActualSize(lockedColumnWidth, lockedColumnAndRowContent.getExplicitOrMeasuredHeight()); 5122 } 5123 lcx = viewMetrics.left + lockedColumnWidth + Math.min(lockedRowContent.leftOffset, 0); 5124 lcy = viewMetrics.top + Math.min(lockedRowContent.topOffset, 0) + Math.ceil(hh); 5125 lockedRowContent.move(lcx, lcy); 5126 ww = Math.max(0, lockedRowContent.rightOffset) - lcx - viewMetrics.right; 5127 lockedRowContent.setActualSize(unscaledWidth + ww, lockedRowContent.getExplicitOrMeasuredHeight()); 5128 hh += lockedRowContent.getExplicitOrMeasuredHeight(); 5129 } 5130 catch (e:ItemPendingError) 5131 { 5132 e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler, 5133 null)); 5134 5135 } 5136 } 5137 5138 if (lockedColumnCount > 0) 5139 { 5140 lcx = viewMetrics.left + Math.min(lockedColumnContent.leftOffset, 0); 5141 lcy = viewMetrics.top + Math.min(lockedColumnContent.topOffset, 0) + Math.ceil(hh); 5142 lockedColumnContent.move(lcx, lcy); 5143 ww = lockedColumnWidth + lockedColumnContent.rightOffset - lockedColumnContent.leftOffset; 5144 lockedColumnContent.setActualSize(ww, 5145 unscaledHeight + Math.max(0, lockedColumnContent.bottomOffset) - lcy - viewMetrics.bottom); 5146 } 5147 lcx = viewMetrics.left + lockedColumnWidth + Math.min(listContent.leftOffset, 0); 5148 lcy = viewMetrics.top + Math.min(listContent.topOffset, 0) + Math.ceil(hh); 5149 listContent.move(lcx, lcy); 5150 ww = Math.max(0, listContent.rightOffset) - lcx - viewMetrics.right; 5151 hh = Math.max(0, listContent.bottomOffset) - lcy - viewMetrics.bottom; 5152 listContent.setActualSize(Math.max(0, unscaledWidth + ww), Math.max(0, unscaledHeight + hh)); 5153 5154 } 5155 5156 private function lockedRowSeekPendingResultHandler(data:Object, 5157 info:ListBaseSeekPending):void 5158 { 5159 try 5160 { 5161 lockedRowContent.iterator.seek(CursorBookmark.FIRST); 5162 } 5163 catch(e:ItemPendingError) 5164 { 5165 e.addResponder(new ItemResponder(lockedRowSeekPendingResultHandler, seekPendingFailureHandler, 5166 null)); 5167 } 5168 itemsSizeChanged = true; 5169 invalidateDisplayList(); 5170 } 5171 5172 5173 /** 5174 * @inheritDoc 5175 */ 5176 override protected function scrollPositionToIndex(horizontalScrollPosition:int, 5177 verticalScrollPosition:int):int 5178 { 5179 return iterator ? verticalScrollPosition + lockedRowCount : -1; 5180 } 5181 5182 /** 5183 * @inheritDoc 5184 */ 5185 override protected function scrollVertically(pos:int, deltaPos:int, 5186 scrollUp:Boolean):void 5187 { 5188 super.scrollVertically(pos, deltaPos, scrollUp); 5189 if (getStyle("horizontalGridLines")) 5190 { 5191 drawLinesAndColumnGraphics(listContent, visibleColumns, {}); 5192 if (lockedColumnCount) 5193 { 5194 drawLinesAndColumnGraphics(lockedColumnContent, visibleLockedColumns, { right: 1}) 5195 } 5196 } 5197 } 5198 5199 private var _focusPane:Sprite; 5200 5201 /** 5202 * @private 5203 */ 5204 override public function set focusPane(value:Sprite):void 5205 { 5206 super.focusPane = value; 5207 if (!value && _focusPane) 5208 _focusPane.mask = null; 5209 _focusPane = value; 5210 } 5211 5212 mx_internal var lockedColumnDropIndicator:IFlexDisplayObject; 5213 5214 /** 5215 * @private 5216 */ 5217 override public function showDropFeedback(event:DragEvent):void 5218 { 5219 super.showDropFeedback(event); 5220 5221 if (lockedColumnCount > 0) 5222 { 5223 if (!lockedColumnDropIndicator) 5224 { 5225 var dropIndicatorClass:Class = getStyle("dropIndicatorSkin"); 5226 if (!dropIndicatorClass) 5227 dropIndicatorClass = ListDropIndicator; 5228 lockedColumnDropIndicator = IFlexDisplayObject(new dropIndicatorClass()); 5229 5230 lockedColumnDropIndicator.x = 2; 5231 lockedColumnDropIndicator.setActualSize(lockedColumnContent.width - 2, 4); 5232 lockedColumnDropIndicator.visible = true; 5233 } 5234 if (dropIndicator.parent == listContent) 5235 lockedColumnContent.addChild(DisplayObject(lockedColumnDropIndicator)); 5236 else 5237 lockedColumnAndRowContent.addChild(DisplayObject(lockedColumnDropIndicator)); 5238 5239 5240 lockedColumnDropIndicator.y = dropIndicator.y; 5241 } 5242 } 5243 5244 /** 5245 * @private 5246 */ 5247 override public function hideDropFeedback(event:DragEvent):void 5248 { 5249 super.hideDropFeedback(event); 5250 5251 if (lockedColumnDropIndicator) 5252 { 5253 DisplayObject(lockedColumnDropIndicator).parent.removeChild(DisplayObject(lockedColumnDropIndicator)); 5254 lockedColumnDropIndicator = null; 5255 } 5256 } 5257 5258} 5259 5260} 5261