         return false;
     }
     
+    /**
+     *  @private
+     */
     private function dataProviderCollectionAdd(event:CollectionEvent):Boolean
     {
         rowCount +=  event.items.length;
         return true;
     }
     
+    /**
+     *  @private
+     */
     private function dataProviderCollectionRemove(event:CollectionEvent):Boolean
     {
         rowCount -= event.items.length;

import mx.events.CollectionEvent;
import mx.events.CollectionEventKind;

import spark.primitives.Rect;

public class GridDimensions 
{
    include "../../core/Version.as";    

    public function GridDimensions()
    {
        super();        
    }
    
    public var rowCount:int = 0;
    public var columnCount:int = 0;
    
    public var rowGap:Number = 0;
    public var columnGap:Number = 0;
    
    public var defaultRowHeight:Number = 32; 
    public var defaultColumnWidth:Number = 150;
    
    public var fixedRowHeight:Number = NaN; // if specified, applies to all rows
    
    // Unless setRowHeight is called, return the max cell height for this row
    public function getRowHeight(row:int):Number
    {
        return defaultRowHeight;
    }
    
    public function setRowHeight(row:int, height:Number):void
    {
    }

    public function getColumnWidth(col:int):Number
    {
        return defaultColumnWidth;
    }
    
    public function setColumnWidth(col:int, width:Number):void
    {
    }

    public function getCellHeight(row:int, col:int):Number
    {
        return defaultRowHeight;
    }
    
    public function setCellHeight(row:int, col:int, height:Number):void
    {
    }
    
    // TBD: provide optional return value (Rectangle) parameter
    public function getCellBounds(row:int, col:int):Rectangle
    {
        // TBD: return null if row or col are out of bounds
        return new Rectangle(col * (defaultColumnWidth + columnGap), row * (defaultRowHeight + rowGap), defaultColumnWidth, defaultRowHeight);
    }

    // TBD: provide optional return value (Rectangle) parameter    
    public function getRowBounds(row:int):Rectangle
    {
        if ((row < 0) || (row >= rowCount))
            return null;  // TBD: return empty Rectangle instead
        

        return new Rectangle(firstCellR.x, firstCellR.y, colWidth, colHeight); 
    }
    
    /**
     *  Returns the index of the row at the specified coordinates. If
     *  the coordinates lie in a gap area, the index returned is the
     *  previous row.
     */
    public function getRowIndexAt(x:Number, y:Number):int
    {
        // TODO (klin): fixed height rows.
        if (!isNaN(fixedRowHeight))
            return y / (fixedRowHeight + rowGap);
        
        if (rowList.length == 0)
            return y / (defaultRowHeight + rowGap);
        
        var node:GridRowNode = rowList.first;
        var index:int = 0;
        var cur:Number = y;
        var n:int;
        
        while (node)
        {
            // add not included rows.
            if (node.rowIndex > index)
            {
                n = node.rowIndex - index;
                for (var i:int = 0; i < n; i++)
                {
                    cur -= defaultRowHeight + rowGap;
                    if (cur < 0)
                        return index;
                    index++;
                }
            }
            
            cur -= node.maxCellHeight + rowGap;
            if (cur < 0)
                return index;
            index++;
            
            node = node.next;
        }
        
        n = this.rowCount;
        for (; index < n; index++)
        {
            cur -= defaultRowHeight + rowGap;
            if (cur < 0)
                return index;
        }
        
        return -1;
    }
    
    /**
     *  Returns the index of the column at the specified coordinates. If
     *  the coordinates lie in a gap area, the index returned is the
     *  previous column. Returns -1 if the coordinates are out of bounds.
     */
    public function getColumnIndexAt(x:Number, y:Number):int
    {
        if (!_columnWidths)
            return x / (defaultColumnWidth + columnGap);
        
        var cur:Number = x;
        var i:int;
        
        for (i = 0; i < _columnCount; i++)
        {
            var temp:Number = _columnWidths[i];
            
            if (isNaN(temp))
                cur -= defaultColumnWidth + columnGap;
            else
                cur -= temp + columnGap;
            
            if (cur < 0)
                return i;
        }
        
        return -1;
    }
    
    /**
     *  Returns the total layout width of the content including gaps.
     */
    public function get contentWidth():Number
    {
        if (!_columnWidths)
            return (_columnCount * (defaultColumnWidth + columnGap)) - columnGap;
        
        var width:Number = 0;
        
        for (var i:int = 0; i < _columnCount; i++)
        {
            if (isNaN(_columnWidths[i]))
                width += defaultColumnWidth + columnGap;
            else
                width += _columnWidths[i] + columnGap;
        }
        
        return width - columnGap;
    }
    
    /**
     *  Returns the total layout height of the content including gaps.
     */
    public function get contentHeight():Number
    {
        if (!isNaN(fixedRowHeight))
            return (rowCount * (fixedRowHeight + rowGap)) - rowGap;
        
        if (rowList.length == 0)
            return (rowCount * (defaultRowHeight + rowGap)) - rowGap;
        
        var height:Number = 0;
        var node:GridRowNode = rowList.first;
        var numRows:int = 0;
        
        while (node)
        {
            height += node.maxCellHeight;
            numRows++;
            node = node.next;
        }
        
        return height + ((rowCount - numRows) * (defaultRowHeight) + (rowCount - 1) * rowGap);
    }

    /**
     *  Inserts count number of rows starting from startRow. This shifts
     *  any rows after startRow down by count and will increment 
     *  rowCount.
     */
    public function insertRows(startRow:int, count:int):void
    {
    }
    
    /**
     *  Inserts count number of columns starting from startColumn. This
     *  shifts any columns after startColumn down by count and will
     *  increment columnCount. 
     */
    public function insertColumns(startColumn:int, count:int):void
    {
    }
    
    /**
     *  Removes count number of rows starting from startRow. This
     *  shifts any rows after startRow up by count and will
     *  decrement rowCount.
     */
    public function removeRows(startRow:int, count:int):void
    {
    }
    
    /**
     *  Removes count number of columns starting from startColumn. This
     *  shifts any columns after startColumn up by count and will
     *  decrement columnCount.
     */
    public function removeColumns(startColumn:int, count:int):void
    {
    }
    
    /**
     *  Moves count number of rows from the fromRow index to the toRow
     *  index. This operation will not affect rowCount.
     */
    public function moveRows(fromRow:int, toRow:int, count:int):void
    {
    }
    
    /**
     *  Moves count number of columns from the fromCol index to the toCol
     *  index. This operation will not affect colCount.
     */
    public function moveColumns(fromCol:int, toCol:int, count:int):void
    {
    }
    
    /**
     *  Removes all cells and sets rowCount to 0.
     */
    public function clear():void
    {
        rowCount = 0;
        rowList.removeAll();
    }

    /**
     *  Handles changes in the dataProvider.
     */
    public function dataProviderCollectionChanged(event:CollectionEvent):Boolean 
    {
        switch (event.kind)
