You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/12/21 19:05:22 UTC

svn commit: r1425063 [5/13] - in /incubator/flex/sdk/branches/develop: frameworks/ frameworks/projects/spark/ frameworks/projects/spark/src/ frameworks/projects/spark/src/spark/collections/ frameworks/projects/spark/src/spark/components/ frameworks/pro...

Added: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as?rev=1425063&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as (added)
+++ incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as Fri Dec 21 18:05:20 2012
@@ -0,0 +1,1042 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+package spark.components.gridClasses
+{
+import flash.geom.Rectangle;
+import flash.utils.Dictionary;
+
+import mx.collections.IList;
+import mx.core.IFactory;
+import mx.core.IVisualElement;
+import mx.core.mx_internal;
+import mx.events.CollectionEvent;
+import mx.events.CollectionEventKind;
+
+import spark.components.DataGrid;
+import spark.components.Grid;
+import spark.components.GridColumnHeaderGroup;
+import spark.components.Group;
+import spark.components.supportClasses.GroupBase;
+import spark.layouts.supportClasses.LayoutBase;
+
+use namespace mx_internal;
+
+[ExcludeClass]
+
+/**
+ *  @private
+ *  Virtual horizontal layout for each column header view Group.  This is not a general 
+ *  purpose layout class, it's only intended for column header view Groups.
+ * 
+ *  This layout tracks the layout of the corresponding GridView. 
+ *  
+ */
+public class GridHeaderViewLayout extends LayoutBase
+{
+    /**
+     *  Constructor. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.5
+     *  @productversion Flex 4.5
+     */    
+    public function GridHeaderViewLayout()
+    {
+        super();
+    }
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Internal variables
+    //
+    //--------------------------------------------------------------------------
+
+    /**
+     *  @private
+     *  Layers for header renderers and separators.
+     */
+    private var rendererLayer:Group;
+    private var overlayLayer:Group;
+    
+    /**
+     *  @private
+     *  Cached header renderer heights maintained by the measure() method, 
+     *  and the current content height.
+     */
+    private const rendererHeights:Array = new Array();
+    private var maxRendererHeight:Number = 0;
+    
+    /**
+     *  @private
+     *  Bounds of all currently visible header renderers.
+     */
+    private const visibleRenderersBounds:Rectangle = new Rectangle();
+    
+    /**
+     *  @private
+     *  Currently visible header renderers.
+     */
+    private const visibleHeaderRenderers:Vector.<IGridItemRenderer> = new Vector.<IGridItemRenderer>();
+    
+    /**
+     *  @private
+     *  Currently visible header separators.
+     */
+    private const visibleHeaderSeparators:Vector.<IVisualElement> = new Vector.<IVisualElement>();
+    
+    /**
+     *  @private
+     *  The elements available for reuse aka the "free list".   Maps from an IFactory 
+     *  to a list of the elements that have been allocated by that factory and then freed.   
+     *  The list is represented by a Vector.<IVisualElement>.
+     */
+    private var freeElementMap:Dictionary = new Dictionary(); 
+    
+    /**
+     *  @private
+     *  Records the IFactory used to allocate a Element so that freeVisualElement()
+     *  can find it again.
+     */
+    private var elementToFactoryMap:Dictionary = new Dictionary();
+	
+	//---------------------------------------------------------------
+	//
+	//  Properties
+	//
+	//---------------------------------------------------------------
+
+	//----------------------------------
+	//  columns
+	//----------------------------------
+	
+	private var _columnsView:IList;
+	
+	/**
+	 *  @private
+	 *  Returns a cached reference to gridView.columns. A local reference is kept so
+	 *  that we can remove the colllection change handler if the columns list changes.
+	 */
+	private function get columnsView():IList
+	{
+		const gridView:GridView = this.gridView;
+		const newColumns:IList = (gridView) ? gridView.gridViewLayout.columnsView : null;
+		
+		if (newColumns != _columnsView)
+		{
+			if (_columnsView)
+				_columnsView.removeEventListener(CollectionEvent.COLLECTION_CHANGE, columns_collectionChangeHandler);
+			
+			_columnsView = newColumns;
+			
+			if (_columnsView)
+				_columnsView.addEventListener(CollectionEvent.COLLECTION_CHANGE, columns_collectionChangeHandler);
+		}
+		
+		return _columnsView;
+	}
+	
+	//----------------------------------
+	//  grid (private read-only)
+	//----------------------------------
+	
+	/**
+	 *  @private
+	 */
+	private function get grid():Grid
+	{
+		const view:GridView = this.gridView;
+		return (view) ? view.parent as Grid : null;
+	}
+	
+	//----------------------------------
+	//  gridColumnHeaderGroup
+	//----------------------------------
+	
+	private var _gridColumnHeaderGroup:GridColumnHeaderGroup = null;
+	
+	/**
+	 *  The GridColumnHeaderGroup whose columns this header view is associated with.
+	 * 
+	 *  This property is set by GridColumnHeaderGroup.
+	 */
+	public function get gridColumnHeaderGroup():GridColumnHeaderGroup
+	{
+		return _gridColumnHeaderGroup
+	}
+	
+	/**
+	 *  @private
+	 */
+	public function set gridColumnHeaderGroup(value:GridColumnHeaderGroup):void
+	{
+		if (value == _gridColumnHeaderGroup)
+			return;
+		
+		_gridColumnHeaderGroup = value;
+	}	
+	
+	//----------------------------------
+	//  gridView
+	//----------------------------------
+	
+	private var _gridView:GridView = null;
+	
+	/**
+	 *  The GridView whose columns this header view is associated with.
+	 * 
+	 *  This property is set by GridColumnHeaderGroup.
+	 */
+	public function get gridView():GridView
+	{
+		return _gridView
+	}
+	
+	/**
+	 *  @private
+	 */
+	public function set gridView(value:GridView):void
+	{
+		if (value == _gridView)
+			return;
+		
+		_gridView = value;
+	}
+	
+    //---------------------------------------------------------------
+    //
+    //  Overridden methods
+    //
+    //---------------------------------------------------------------
+    
+    /**
+     *  @private
+     */
+    override public function set target(value:GroupBase):void
+    {
+        super.target = value;
+        
+        const group:Group = value as Group;
+		if (!group)
+			return;
+		
+		rendererLayer = new Group();
+		rendererLayer.layout = new LayoutBase();
+		group.addElement(rendererLayer);
+		
+		overlayLayer = new Group();
+		overlayLayer.layout = new LayoutBase();
+		group.addElement(overlayLayer);
+    }
+    
+    /**
+     *  @private
+     */
+    override public function get useVirtualLayout():Boolean
+    {
+        return true;
+    }
+    
+    /**
+     *  @private
+     */
+    override public function set useVirtualLayout(value:Boolean):void
+    {
+    }
+    
+    /**
+     *  @private
+     *  Clear everything.
+     */
+    override public function clearVirtualLayoutCache():void
+    {		
+		freeRenderers(visibleHeaderRenderers);
+        visibleHeaderRenderers.length = 0;
+		
+		freeVisualElements(visibleHeaderSeparators);
+        visibleHeaderSeparators.length = 0;
+		
+        rendererHeights.length = 0;
+        visibleRenderersBounds.setEmpty();
+        elementToFactoryMap = new Dictionary();
+        freeElementMap = new Dictionary();
+
+		if (gridColumnHeaderGroup)
+			gridColumnHeaderGroup.visibleSortIndicatorIndices = null;
+    }     
+    
+    /**
+     *  @private
+     */
+    override protected function scrollPositionChanged():void
+    {
+		const target:GroupBase = this.target;
+        if (!target)
+            return;
+        
+        super.scrollPositionChanged();  // sets target's scrollRect
+        
+        // Only invalidate if we're clipping and scrollR extends outside visibleRenderersBounds
+        const scrollR:Rectangle = target.scrollRect;
+		if (scrollR && !visibleRenderersBounds.containsRect(scrollR))
+            target.invalidateDisplayList();
+    }    
+    
+    /**
+     *  @private
+     */
+    override public function measure():void
+    {
+		const target:GroupBase = this.target;
+		if (!target)
+			return;
+		
+		updateRendererHeights();
+		
+		const measuredWidth:Number = Math.max(0, target.minWidth);
+		const measuredHeight:Number = Math.max(maxRendererHeight, target.minHeight);
+		
+        target.measuredWidth = Math.ceil(measuredWidth);
+        target.measuredHeight = Math.ceil(measuredHeight);
+    }
+
+    /**
+     *  @private
+     */
+    override public function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
+    {
+		const target:GroupBase = this.target;
+        const gridColumnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+        const gridView:GridView = this.gridView;
+        
+        if (!target || !gridColumnHeaderGroup || !gridView)
+            return;
+
+		// TBD explain about Grid relative column indices...
+		
+		const visibleColumnIndices:Vector.<int> = gridView.gridViewLayout.getVisibleColumnIndices();  
+		const visibleColumnCount:int = visibleColumnIndices.length;
+		const firstVisibleColumnIndex:int = (visibleColumnCount > 0) ? visibleColumnIndices[0] : -1;
+		const lastVisibleColumnIndex:int = (visibleColumnCount > 0) ? visibleColumnIndices[visibleColumnCount - 1] : -1;
+		
+        const oldRenderers:Array = [];
+        const rendererLayer:Group = this.rendererLayer;
+        const overlayLayer:Group = this.overlayLayer;
+        const columnSeparatorFactory:IFactory = gridColumnHeaderGroup.columnSeparator;
+        
+        var renderer:IGridItemRenderer;
+        var separator:IVisualElement;
+        var column:GridColumn;
+        var columnIndex:int = -1;
+        
+        // Add all of the renderers whose column is still visible to oldRenderers and free the rest
+        
+        for each (renderer in visibleHeaderRenderers)
+        {
+            column = renderer.column;
+            columnIndex = (column) ? column.columnIndex : -1; 
+            
+            if ((visibleColumnIndices.indexOf(columnIndex) != -1) && (oldRenderers[columnIndex] == null))
+                oldRenderers[columnIndex] = renderer;
+            else
+                freeRenderer(renderer);
+        }
+        visibleHeaderRenderers.length = 0;
+        
+        // Add all of the separators to the free-list, since laying them out is cheap.
+        
+		freeVisualElements(visibleHeaderSeparators);
+		visibleHeaderSeparators.length = 0;
+        
+        // Layout the header renderers and update the CHB's content size
+        // The loop below is written in terms of Grid - not GridView - column indices,
+        // and terminates when we reach GridColumnCount.
+        
+        const gridColumns:IList = grid.columns;  // TBD what if grid.columns is null?
+        const gridViewLayout:GridViewLayout = gridView.layout as GridViewLayout;
+        const gridColumnCount:int = gridViewLayout.viewColumnIndex + gridViewLayout.columnsView.length; 
+        
+		const rendererY:Number = 0;
+		const rendererHeight:Number = unscaledHeight;
+        const maxRendererX:Number = target.horizontalScrollPosition + unscaledWidth;
+        
+        var visibleLeft:Number = 0;
+        var visibleRight:Number = 0;
+        
+        // This isn't quite as simple as: 
+        //     for each (var columnIndex:int in visibleColumnIndices)
+        // since the GridColumnHeaderGroup may be wider than the grid because it
+        // spans the vertical scrollbar.  If it does, we may need to display 
+        // additional column headers (usually one).  
+        
+        for (var index:int = 0; /* termination conditions below */; index++)
+        {
+            if (index < visibleColumnIndices.length)
+                columnIndex = visibleColumnIndices[index];
+            else
+                columnIndex = grid.getNextVisibleColumnIndex(columnIndex);
+           
+            if (columnIndex < 0 || columnIndex >= gridColumnCount)
+                break;
+
+            column = gridColumns.getItemAt(columnIndex) as GridColumn;
+
+            // reuse or create a new renderer
+            
+            renderer = oldRenderers[columnIndex];
+            delete oldRenderers[columnIndex];
+            if (!renderer)
+            {
+                var factory:IFactory = column.headerRenderer;
+                if (!factory)
+                    factory = gridColumnHeaderGroup.headerRenderer;
+                renderer = allocateVisualElement(factory) as IGridItemRenderer;
+            }
+            visibleHeaderRenderers.push(renderer);
+            
+            // initialize the renderer
+            
+            initializeItemRenderer(renderer, columnIndex, column, true);
+            if (renderer.parent != rendererLayer)
+                rendererLayer.addElement(renderer);
+            
+            // layout the renderer
+            
+            var isLastColumn:Boolean = columnIndex == lastVisibleColumnIndex;
+            var headerViewColumnIndex:int = columnIndex - gridViewLayout.viewColumnIndex;
+			var rendererX:Number = gridViewLayout.gridDimensionsView.getCellX(0, headerViewColumnIndex);
+            var rendererWidth:Number = grid.getColumnWidth(columnIndex);
+            
+			if (isLastColumn)
+                rendererWidth = horizontalScrollPosition + unscaledWidth - rendererX - 1; // TODO: this is a temporary hack
+			
+            renderer.setLayoutBoundsSize(rendererWidth, rendererHeight);
+            renderer.setLayoutBoundsPosition(rendererX, rendererY);
+            
+            if (index == 0)
+                visibleLeft = rendererX;
+            visibleRight = rendererX + rendererWidth;
+            
+            renderer.prepare(!createdVisualElement);
+            
+            if (isLastColumn || ((rendererX + rendererWidth) >= maxRendererX))
+                break;
+            
+            // allocate and layout a column separator
+            
+            if (columnSeparatorFactory && !isLastColumn)
+            {
+                separator = allocateVisualElement(columnSeparatorFactory);
+                visibleHeaderSeparators.push(separator);
+                separator.visible = true;
+                if (separator.parent != overlayLayer)
+                    overlayLayer.addElement(separator);
+                
+                var separatorWidth:Number = separator.getPreferredBoundsWidth();
+                var separatorX:Number = rendererX + rendererWidth;
+                separator.setLayoutBoundsSize(separatorWidth, rendererHeight);
+                separator.setLayoutBoundsPosition(separatorX, rendererY);
+            }
+        }
+
+        target.setContentSize(grid.contentWidth, rendererHeight);
+
+		visibleRenderersBounds.left = visibleLeft;
+		visibleRenderersBounds.right = visibleRight = 0;
+		visibleRenderersBounds.top = rendererY;
+        visibleRenderersBounds.height = rendererHeight;
+		
+        // We may have created new renderers or changed their visibility.  Force
+        // validation to avoid a display list flash.
+
+        target.validateNow();
+        
+        // Update the renderer heights cache.
+        // Invalidates the target's size if the maxRendererHeight has changed.
+        
+        updateRendererHeights(true);
+    }
+    
+    //---------------------------------------------------------------
+    //
+    //  Public methods
+    //
+    //--------------------------------------------------------------- 
+    
+    /**
+     *  Returns the column index corresponding to the specified coordinates,
+     *  or -1 if the coordinates are out of bounds. The coordinates are 
+     *  resolved with respect to the column header view layout target.
+     * 
+     *  <p>If all of the columns or rows for the grid have not yet been scrolled
+     *  into view, the returned index may only be an approximation, 
+     *  based on all of the columns' <code>typicalItem</code>s.</p>
+     *  
+     *  @param x The pixel's x coordinate relative to the target column header view
+     *  @param y The pixel's y coordinate relative to the target column header view
+     *  @return the index of the column or -1 if the coordinates are out of bounds. 
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.5
+     */
+    public function getHeaderIndexAt(x:Number, y:Number):int
+    {
+        return gridView.gridViewLayout.gridDimensionsView.getColumnIndexAt(x, y);
+        
+        // TODO: restore the special case handling below
+        /*
+        const gridColumnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+        const grid:Grid = this.grid;
+        const columnsView:IList = this.columnsView;
+        
+        if (!gridColumnHeaderGroup || !grid || !columnsView)
+            return -1; 
+        
+        const paddingLeft:Number = gridColumnHeaderGroup.getStyle("paddingLeft");
+        const paddedX:Number = x + paddingLeft;
+        var columnIndex:int = grid.getColumnIndexAt(paddedX, 0);
+        
+        // Special case for the stretched renderer above the vertical scrollbar
+        // TODO (klin): Rethink this case if we change how the last header looks.
+        if (columnIndex < 0)
+        {
+            const contentWidth:Number = gridColumnHeaderGroup.contentWidth;
+            const totalWidth:Number = horizontalScrollPosition + gridColumnHeaderGroup.width - gridColumnHeaderGroup.getStyle("paddingRight");
+            if (paddedX >= contentWidth && paddedX < totalWidth)
+                columnIndex = grid.getPreviousVisibleColumnIndex(columnsView.length)
+        }
+        
+        return columnIndex;
+        */
+    }
+    
+    /**
+     *  Returns the column separator index corresponding to the specified 
+     *  coordinates, or -1 if the coordinates don't overlap a separator. The 
+     *  coordinates are resolved with respect to the GridColumnHeaderGroup layout target.
+     * 
+     *  <p>A separator is considered to "overlap" the specified location if the
+     *  x coordinate is within <code>separatorMouseWidth</code> of separator's
+     *  horizontal midpoint.</p>
+     *  
+     *  <p>The separator index is the same as the index of the column on the left
+     *  (assuming that this component's layoutDirection is "rtl").  That means 
+     *  that all column headers are flanked by two separators, except for the first
+     *  visible column, which just has a separator on the right, and the last visible
+     *  column, which just has a separator on the left.</p>
+     * 
+     *  <p>If all of the columns or rows for the grid have not yet been scrolled
+     *  into view, the returned index may only be an approximation, 
+     *  based on all of the columns' <code>typicalItem</code>s.</p>
+     *  
+     *  @param x The pixel's x coordinate relative to the columnHeaderGroup
+     *  @param y The pixel's y coordinate relative to the columnHeaderGroup
+     *  @return the index of the column or -1 if the coordinates don't overlap a separator.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.5
+     */
+    public function getSeparatorIndexAt(x:Number, y:Number):int
+    {
+        const gdv:GridDimensionsView = gridView.gridViewLayout.gridDimensionsView;
+        const columnIndex:int = gdv.getColumnIndexAt(x, y); 
+        if (columnIndex == -1)
+            return -1;        
+        
+        const isFirstColumn:Boolean = columnIndex == gridView.getNextVisibleColumnIndex(-1);
+        const isLastColumn:Boolean = false; //columnIndex == gridView.getPreviousVisibleColumnIndex(gridView.viewColumnCount);
+        
+        const columnLeft:Number = gdv.getCellX(0, columnIndex);
+        const columnRight:Number = columnLeft + gdv.getColumnWidth(columnIndex);
+        const smw:Number = gridColumnHeaderGroup.getStyle("separatorAffordance");
+        
+        if (!isFirstColumn && (x > (columnLeft - smw)) && (x < (columnLeft + smw)))
+            return grid.getPreviousVisibleColumnIndex(columnIndex);
+        
+        if (!isLastColumn && (x > (columnRight - smw)) && (x < columnRight + smw))
+            return columnIndex;
+        
+        return -1;
+    }
+    
+    /**
+     *  Returns the current pixel bounds of the specified header (renderer), or null if 
+     *  no such column exists.  Header bounds are reported in GridColumnHeaderGroup coordinates.
+     * 
+     *  <p>If all of the visible columns preceeding the specified column have not 
+     *  yet been scrolled into view, the returned bounds may only be an approximation, 
+     *  based on all of the Grid's <code>typicalItem</code>s.</p>
+     * 
+     *  @param columnIndex The 0-based index of the column. 
+     *  @return A <code>Rectangle</code> that represents the column header's pixel bounds, or null.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.5
+     */ 
+    public function getHeaderBounds(columnIndex:int):Rectangle
+    {
+        const gridColumnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+        const grid:Grid = this.grid;
+        
+        if (!gridColumnHeaderGroup || !grid)
+            return null;
+        
+        const columns:IList = columns;
+        const columnsLength:int = (columns) ? columns.length : 0;
+        
+        if (columnIndex >= columnsLength)
+            return null;
+        
+        const column:GridColumn = columns.getItemAt(columnIndex) as GridColumn;
+        if (!column.visible)
+            return null;
+        
+        const paddingLeft:Number = gridColumnHeaderGroup.getStyle("paddingLeft");
+        const paddingRight:Number = gridColumnHeaderGroup.getStyle("paddingRight");
+        const paddingTop:Number = gridColumnHeaderGroup.getStyle("paddingTop");
+        const paddingBottom:Number = gridColumnHeaderGroup.getStyle("paddingBottom");
+        
+        var isLastColumn:Boolean = columnIndex == grid.getPreviousVisibleColumnIndex(columnsLength);
+        var rendererX:Number = grid.getCellX(0, columnIndex) + paddingLeft;
+        const rendererY:Number = paddingTop;
+        var rendererWidth:Number = grid.getColumnWidth(columnIndex); 
+        const rendererHeight:Number = gridColumnHeaderGroup.height - paddingTop - paddingBottom;        
+        
+        if (isLastColumn)
+            rendererWidth = horizontalScrollPosition + gridColumnHeaderGroup.width - rendererX - paddingRight;
+        
+        return new Rectangle(rendererX, rendererY, rendererWidth, rendererHeight);
+    }
+
+    /**
+     *  If the requested header renderer is visible, returns a reference to 
+     *  the header renderer currently displayed for the specified column. 
+     *  Note that once the returned header renderer is no longer visible it 
+     *  may be recycled and its properties reset.  
+     * 
+     *  <p>If the requested header renderer is not visible then, 
+     *  each time this method is called, a new header renderer is created.  The
+     *  new item renderer is not visible</p>
+     * 
+     *  <p>The width of the returned renderer is the same as for item renderers
+     *  returned by DataGrid/getItemRendererAt().</p>
+     *  
+     *  @param columnIndex The 0-based column index of the header renderer's column
+     *  @return The item renderer or null if the column index is invalid.
+     * 
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.0
+     *  @productversion Flex 4.5
+     */
+    public function getHeaderRendererAt(columnIndex:int):IGridItemRenderer
+    {
+        const gridColumnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+        const grid:Grid = this.grid;
+        
+        if (!gridColumnHeaderGroup || !grid || (columnIndex < 0))
+            return null;
+        
+        // If columnIndex refers to a visible header renderer, return it
+        
+        const rendererLayer:Group = rendererLayer;
+        const visibleColumnIndices:Vector.<int> = grid.getVisibleColumnIndices();
+        const eltIndex:int = visibleColumnIndices.indexOf(columnIndex);
+        if (eltIndex != -1)
+        {
+            const rendererLayerNumElements:int = rendererLayer.numElements;
+            for (var index:int = 0; index < rendererLayerNumElements; index++)
+            {
+                var elt:IGridItemRenderer = rendererLayer.getElementAt(index) as IGridItemRenderer;
+                if (elt && elt.visible && elt.column && (elt.column.columnIndex == columnIndex))
+                    return elt;
+            }
+            return null;
+        }
+            
+        // create a new renderer
+
+        const columns:IList = columns;
+        if (!columns || (columns.length <= columnIndex))
+            return null;
+        const column:GridColumn = columns.getItemAt(columnIndex) as GridColumn;
+        if (!column.visible)
+            return null;
+        
+        var factory:IFactory = column.headerRenderer;
+        if (!factory)
+            factory = gridColumnHeaderGroup.headerRenderer;
+        const renderer:IGridItemRenderer = allocateVisualElement(factory) as IGridItemRenderer;
+        
+        rendererLayer.addElement(renderer);
+
+        // initialize the renderer
+        
+        initializeItemRenderer(renderer, columnIndex, column, renderer.visible);
+        
+        // layout the renderer
+
+        const paddingLeft:Number = gridColumnHeaderGroup.getStyle("paddingLeft");
+        const paddingRight:Number = gridColumnHeaderGroup.getStyle("paddingRight");
+        const paddingTop:Number = gridColumnHeaderGroup.getStyle("paddingTop");
+        const paddingBottom:Number = gridColumnHeaderGroup.getStyle("paddingBottom");
+        
+        const isLastColumn:Boolean = columnIndex == grid.getPreviousVisibleColumnIndex(columns.length);
+        const rendererX:Number = grid.getCellX(0, columnIndex) + paddingLeft;
+        const rendererY:Number = paddingTop;
+        const rendererHeight:Number = gridColumnHeaderGroup.height - paddingTop - paddingBottom;
+        var rendererWidth:Number = grid.getColumnWidth(columnIndex); 
+        
+        if (isLastColumn)
+            rendererWidth = horizontalScrollPosition + gridColumnHeaderGroup.width - rendererX - paddingRight;
+        
+        renderer.setLayoutBoundsSize(rendererWidth, rendererHeight);
+        renderer.setLayoutBoundsPosition(rendererX, rendererY);
+        
+        rendererLayer.removeElement(renderer);
+        renderer.visible = false;
+        
+        return renderer;
+    }
+    
+	import flash.events.MouseEvent;
+	import flash.geom.Point;
+	import flash.geom.Rectangle;
+
+    //---------------------------------------------------------------
+    //
+    //  Internal methods, properties
+    //
+    //---------------------------------------------------------------    
+    
+    /**
+     *  @private
+     */
+    private function initializeItemRenderer(renderer:IGridItemRenderer,
+                                            columnIndex:int,
+                                            column:GridColumn,
+                                            visible:Boolean=true):void
+    {
+        renderer.visible = visible;
+        renderer.column = column;
+        renderer.label = column.headerText;
+        
+        const columnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+
+        const dataGrid:DataGrid = columnHeaderGroup.dataGrid;
+        if (dataGrid)
+            renderer.owner = dataGrid;
+        
+        renderer.hovered = columnIndex == columnHeaderGroup.hoverColumnIndex;
+        renderer.selected = columnIndex == columnHeaderGroup.selectedColumnIndex;
+        renderer.down = columnIndex == columnHeaderGroup.downColumnIndex;
+    }
+    
+    /**
+     *  @private
+     *  Let the allocateGridElement() caller know if the returned element was 
+     *  created or recycled.
+     */
+    private var createdVisualElement:Boolean = false;
+    
+    /**
+     *  @private
+     */
+    private function createVisualElement(factory:IFactory):IVisualElement
+    {
+        createdVisualElement = true;
+        const newElement:IVisualElement = factory.newInstance() as IVisualElement;
+        elementToFactoryMap[newElement] = factory;
+        return newElement;
+    }
+    
+    /**
+     *  @private
+     *  If the freeElementMap "free list" contains an instance of this factory, then 
+     *  remove if from the free list and return it, otherwise create a new instance
+     *  using createVisualElement().
+     */
+    private function allocateVisualElement(factory:IFactory):IVisualElement
+    {
+        createdVisualElement = false;
+        const freeElements:Vector.<IVisualElement> = freeElementMap[factory] as Vector.<IVisualElement>;
+        if (freeElements)
+        {
+            const freeElement:IVisualElement = freeElements.pop();
+            if (freeElements.length == 0)
+                delete freeElementMap[factory];
+            if (freeElement)
+                return freeElement;
+        }
+        
+        return createVisualElement(factory);
+    }
+    
+    /**
+     *  @private
+     *  Move the specified element to the free list after hiding it. Note that we 
+     *  do not actually remove the element from its parent.
+     */
+    private function freeVisualElement(element:IVisualElement):void
+    {
+        const factory:IFactory = elementToFactoryMap[element];
+
+        var freeElements:Vector.<IVisualElement> = freeElementMap[factory];
+        if (!freeElements)
+        {
+            freeElements = new Vector.<IVisualElement>();
+            freeElementMap[factory] = freeElements;
+        }
+        freeElements.push(element);
+        
+        element.visible = false;
+    }
+	
+	private function freeVisualElements(elements:Vector.<IVisualElement>):void
+	{
+		for each (var elt:IVisualElement in elements)
+		    freeVisualElement(elt);
+			
+		elements.length = 0;
+	}
+	
+	private function freeRenderer(renderer:IGridItemRenderer):void
+	{
+		freeVisualElement(renderer as IVisualElement);
+		renderer.discard(true);	
+	}
+	
+	private function freeRenderers(renderers:Vector.<IGridItemRenderer>):void
+	{
+		for each (var renderer:IGridItemRenderer in renderers)
+			freeRenderer(renderer);
+			
+		renderers.length = 0;
+	}		
+    
+    /**
+     *  @private
+     *  Updates the renderer heights cache and the current max renderer height.
+     *  Invalidates the target's size if the max renderer height has changed.
+     * 
+     *  <p>If the max live renderer height is the same as the max cached height, then
+     *  just update the cache.
+     *  If the max live renderer height is greater than the max cached height, then
+     *  update the cache, cache the new height, and invalidate the target's size if
+     *  necessary.
+     *  If the max live renderer height is less than the max cached height, then
+     *  update the cache, check to see if the cached max height has lowered, and
+     *  invalidate the target's size if necessary.
+     */
+    private function updateRendererHeights(inUpdateDisplayList:Boolean = false):void
+    {
+        const columns:IList = this.columnsView;
+        rendererHeights.length = (columns) ? columns.length : 0;
+        
+        var newHeight:Number = 0;
+        
+        // update cached renderer heights with live renderer heights.
+        for each (var renderer:IGridItemRenderer in visibleHeaderRenderers)
+        {
+            var preferredHeight:Number = renderer.getPreferredBoundsHeight();
+            rendererHeights[renderer.column.columnIndex] = preferredHeight;
+            if (preferredHeight > newHeight)
+                newHeight = preferredHeight;
+        }
+        
+        // Do nothing if the heights are the same.
+        if (newHeight == maxRendererHeight)
+            return;
+        
+        if (newHeight < maxRendererHeight)
+        {
+            // If the live renderers' max height is less than the current
+            // max height, check if this also lowers the maxRendererHeight.
+			for each (var rendererHeight:Number in rendererHeights)
+			{
+                if (!isNaN(rendererHeight) && rendererHeight > newHeight)
+                    newHeight = rendererHeight;
+            }
+        }
+        
+        maxRendererHeight = newHeight;
+        
+        if (inUpdateDisplayList) // TBD: should be target.invalidateSize()?
+            gridColumnHeaderGroup.invalidateSize();
+    }
+    
+
+    /**
+     *  @private
+     *  Handles changes to the columns IList that might affect the visible sort
+     *  indicators.
+     */
+    private function columns_collectionChangeHandler(event:CollectionEvent):void
+    {
+        // TODO (klin): The cache could be adjusted here too.
+        switch (event.kind)
+        {
+            case CollectionEventKind.ADD: 
+            {
+                columns_collectionChangeAdd(event);
+                break;
+            }
+            
+            case CollectionEventKind.REMOVE:
+            {
+                columns_collectionChangeRemove(event);
+                break;
+            }
+                
+            case CollectionEventKind.MOVE:
+            {
+                columns_collectionChangeMove(event);
+                break;
+            }
+                
+            case CollectionEventKind.REPLACE:
+            case CollectionEventKind.UPDATE:
+            {
+                // Do nothing.
+                break;
+            }
+                
+            case CollectionEventKind.REFRESH:
+            case CollectionEventKind.RESET:
+            {
+                clearVirtualLayoutCache();
+                break;
+            }                
+        }
+    }
+    
+    /**
+     *  @private
+     *  Adjusts the visibleSortIndicatorIndices to the correct columns
+     *  after columns are added.
+     */
+    private function columns_collectionChangeAdd(event:CollectionEvent):void
+    {   
+        const itemsLength:int = event.items.length;
+        if (itemsLength <= 0)
+            return;
+        
+        const chg:GridColumnHeaderGroup = gridColumnHeaderGroup;
+        const indices:Vector.<int> = chg.visibleSortIndicatorIndices;
+        const indicesLength:int = indices.length;
+        const startIndex:int = event.location;
+        
+        for (var i:int = 0; i < indicesLength; i++)
+        {
+            if (indices[i] >= startIndex)
+                indices[i] += itemsLength;
+        }
+        chg.visibleSortIndicatorIndices = indices;
+    }
+    
+    /**
+     *  @private
+     *  Adjusts the visibleSortIndicatorIndices to the correct columns
+     *  after columns are removed.
+	 * 
+	 *  TBD: Remove the rendererHeights cache entries for the corresponding columns.
+     */
+    private function columns_collectionChangeRemove(event:CollectionEvent):void
+    {
+        const itemsLength:int = event.items.length;
+        if (itemsLength <= 0)
+            return;
+        
+        const chg:GridColumnHeaderGroup = gridColumnHeaderGroup;
+        const indices:Vector.<int> = chg.visibleSortIndicatorIndices;
+        const indicesLength:int = indices.length;
+        const startIndex:int = event.location;
+        const lastIndex:int = startIndex + itemsLength;
+        const newIndices:Vector.<int> = new Vector.<int>();
+        var index:int;
+        
+        for each (index in indices)
+        {
+            if (index < startIndex)
+                newIndices.push(index);
+            else if (index >= lastIndex)
+                newIndices.push(index - lastIndex);
+        }
+        chg.visibleSortIndicatorIndices = newIndices;
+    }
+    
+    /**
+     *  @private
+     *  Adjusts the visibleSortIndicatorIndices to the correct columns
+     *  after columns are moved.
+     */
+    private function columns_collectionChangeMove(event:CollectionEvent):void
+    {
+        const itemsLength:int = event.items.length;
+        if (itemsLength <= 0)
+            return;
+        
+        const gridColumnHeaderGroup:GridColumnHeaderGroup = this.gridColumnHeaderGroup;
+        const indices:Vector.<int> = gridColumnHeaderGroup.visibleSortIndicatorIndices;
+        const indicesLength:int = indices.length;
+        const oldStart:int = event.oldLocation;
+        const oldEnd:int = event.oldLocation + itemsLength;
+        const newStart:int = event.location;
+        const newEnd:int = event.location + itemsLength;
+        var index:int;
+        
+        for (var i:int = 0; i < indicesLength; i++)
+        {
+            index = indices[i];
+            
+            if (index >= oldStart && index < oldEnd)
+            {
+                // Moved items move up to new position
+                indices[i] = newStart + (index - oldStart);
+                continue;
+            }
+            
+            // Two cases:
+            //      1) New position is greater than old position, so we
+            //         decrement their position by the number of moved items.
+            //      2) New position is less than old position, so we
+            //         increment their position by the number of moved items.
+            if (newStart > oldStart)
+            {
+                if (index >= oldEnd && index < newEnd)
+                    indices[i] -= itemsLength;
+            }
+            else if (newStart < oldStart)
+            {
+                if (index >= newStart && index < oldStart)
+                    indices[i] += itemsLength;
+            }
+        }
+		
+        gridColumnHeaderGroup.visibleSortIndicatorIndices = indices;
+    }
+}
+}
\ No newline at end of file

Propchange: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridHeaderViewLayout.as
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditor.as
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditor.as?rev=1425063&r1=1425062&r2=1425063&view=diff
==============================================================================
--- incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditor.as (original)
+++ incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditor.as Fri Dec 21 18:05:20 2012
@@ -37,6 +37,7 @@ import spark.components.DataGrid;
 import spark.components.Group;
 import spark.components.gridClasses.GridColumn;
 
+
 use namespace mx_internal;
 
 /**
@@ -542,6 +543,19 @@ public class GridItemEditor extends Grou
     }
     
     /**
+     *  @inheritDoc 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 10
+     *  @playerversion AIR 2.5
+     *  @productversion Flex 4.5
+     */
+    public function cancel():Boolean
+    {
+        return true;
+    }
+    
+    /**
      *  Tests if the value in the editor is valid and may be saved.
      * 
      *  @return <code>true</code> if the value in the editor is valid. 

Added: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as?rev=1425063&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as (added)
+++ incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as Fri Dec 21 18:05:20 2012
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package spark.components.gridClasses
+{
+    /**
+     *  The EditorActivationMouseEvent class defines the possible values for the 
+     *  the kinds of mouse events that cause an editor to be opened on a Spark
+     *  DataGrid component.
+     *  
+     *  @see spark.components.DataGrid
+     *
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+public final class GridItemEditorActivationMouseEvent
+{
+    include "../../core/Version.as";
+    
+    //--------------------------------------------------------------------------
+    //
+    //  Class constants
+    //
+    //--------------------------------------------------------------------------
+    
+    /**
+     *  A single click mouse evnet on a previously selected cell.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    public static const SINGLE_CLICK_ON_SELECTED_CELL:String = "singleClickOnSelectedCell";
+
+    /**
+     *  A single click mouse event.
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    public static const SINGLE_CLICK:String = "singleClick";
+
+    /**
+     *  A double click mouse event. A DataGrid component must have its 
+     *  <code>doubleClickEnabled</code> property set to <code>true</code>
+     *  in order for the component to receive a double click mouse event. 
+     *  
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    public static const DOUBLE_CLICK:String = "doubleClick";
+
+    
+    /**
+     *  No mouse event will cause an editor to be opened. An editor may still
+     *  be opened using the keyboard or programatically.
+     *    
+     *  @langversion 3.0
+     *  @playerversion Flash 11
+     *  @playerversion AIR 3.0
+     *  @productversion Flex 5.0
+     */
+    public static const NONE:String = "none";
+    
+}
+}
\ No newline at end of file

Propchange: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridItemEditorActivationMouseEvent.as
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridLayer.as
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridLayer.as?rev=1425063&r1=1425062&r2=1425063&view=diff
==============================================================================
--- incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridLayer.as (original)
+++ incubator/flex/sdk/branches/develop/frameworks/projects/spark/src/spark/components/gridClasses/GridLayer.as Fri Dec 21 18:05:20 2012
@@ -62,7 +62,7 @@ public class GridLayer extends Group
     public function GridLayer()
     {
         super();
-        layout = new LayoutBase();        
+        layout = new LayoutBase();   // essentially "no layout"       
     }
     
     //--------------------------------------------------------------------------