Merge remote-tracking branch 'origin/develop' into develop
diff --git a/frameworks/projects/spark/src/spark/components/GridColumnHeaderGroup.as b/frameworks/projects/spark/src/spark/components/GridColumnHeaderGroup.as
index a9209a1..0b202b7 100644
--- a/frameworks/projects/spark/src/spark/components/GridColumnHeaderGroup.as
+++ b/frameworks/projects/spark/src/spark/components/GridColumnHeaderGroup.as
@@ -783,6 +783,15 @@
         return (_visibleSortIndicatorIndices.indexOf(columnIndex) != -1);
     }
 
+    /**
+     *  True if either of this GridColumnHeaderGroup's views contains the global
+     *  coordinates in the event, or if they fall over this component's padding.
+     */
+    public function containsMouseEvent(event:MouseEvent):Boolean
+    {
+        return containsGlobalCoordinates(new Point(event.stageX, event.stageY));
+    }
+
     public function containsGlobalCoordinates(coordinates:Point):Boolean
     {
         return areCoordinatesOverAHeaderView(coordinates) || areCoordinatesOverPadding(coordinates);
@@ -795,7 +804,7 @@
 
     public function areCoordinatesOverPadding(coordinates:Point):Boolean
     {
-        return areCoordinatesOverLeftPadding(coordinates) || areCoordinatesOverRightPadding(coordinates);
+        return areCoordinatesOverLeftPadding(coordinates) || areCoordinatesOverTopPadding(coordinates) || areCoordinatesOverBottomPadding(coordinates);
     }
 
     public function areCoordinatesOverLeftPadding(coordinates:Point):Boolean
@@ -807,13 +816,22 @@
                 getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x + paddingLeft, coordinates.y)) != null;
     }
     
-    public function areCoordinatesOverRightPadding(coordinates:Point):Boolean
+    public function areCoordinatesOverTopPadding(coordinates:Point):Boolean
     {
-        var paddingRightStyle:Number = getStyle("paddingRight");
-        var paddingRight:Number = isNaN(paddingRightStyle) ? 0 : paddingRightStyle;
+        var paddingTopStyle:Number = getStyle("paddingTop");
+        var paddingTop:Number = isNaN(paddingTopStyle) ? 0 : paddingTopStyle;
 
         return getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x, coordinates.y)) == null &&
-        getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x, coordinates.y - paddingRight)) != null;
+        getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x, coordinates.y + paddingTop)) != null;
+    }
+
+    public function areCoordinatesOverBottomPadding(coordinates:Point):Boolean
+    {
+        var paddingBottomStyle:Number = getStyle("paddingBottom");
+        var paddingBottom:Number = isNaN(paddingBottomStyle) ? 0 : paddingBottomStyle;
+
+        return getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x, coordinates.y)) == null &&
+        getHeaderViewUnderGlobalCoordinates(new Point(coordinates.x, coordinates.y - paddingBottom)) != null;
     }
 
     //--------------------------------------------------------------------------
@@ -1037,11 +1055,11 @@
 		const ghl:GridHeaderLayout = layout as GridHeaderLayout;
 
 		const centerGridColumnHeaderView:GridColumnHeaderView = GridColumnHeaderView(ghl.centerGridColumnHeaderView);
-		if (centerGridColumnHeaderView && centerGridColumnHeaderView.containsStageCoordinates(globalCoordinates))
+		if (centerGridColumnHeaderView && centerGridColumnHeaderView.containsGlobalPoint(globalCoordinates))
 			return centerGridColumnHeaderView;
 		
 		const leftGridColumnHeaderView:GridColumnHeaderView = GridColumnHeaderView(ghl.leftGridColumnHeaderView);
-		if (leftGridColumnHeaderView && leftGridColumnHeaderView.containsStageCoordinates(globalCoordinates))
+		if (leftGridColumnHeaderView && leftGridColumnHeaderView.containsGlobalPoint(globalCoordinates))
 			return leftGridColumnHeaderView;
 		
 		return null;
diff --git a/frameworks/projects/spark/src/spark/components/gridClasses/GridColumnHeaderView.as b/frameworks/projects/spark/src/spark/components/gridClasses/GridColumnHeaderView.as
index 2e6f449..a836462 100644
--- a/frameworks/projects/spark/src/spark/components/gridClasses/GridColumnHeaderView.as
+++ b/frameworks/projects/spark/src/spark/components/gridClasses/GridColumnHeaderView.as
@@ -106,13 +106,13 @@
      */
     public function containsMouseEvent(event:MouseEvent):Boolean
     {
-        return containsStageCoordinates(new Point(event.stageX, event.stageY));
+        return containsGlobalPoint(new Point(event.stageX, event.stageY));
     }
 
-    public function containsStageCoordinates(coordinates:Point):Boolean
+    public function containsGlobalPoint(globalCoordinates:Point):Boolean
     {
-        const eventStageX:Number = coordinates.x;
-        const eventStageY:Number = coordinates.y;
+        const stageX:Number = globalCoordinates.x;
+        const stageY:Number = globalCoordinates.y;
         const origin:Point = localToGlobal(zeroPoint);
 
         origin.x += horizontalScrollPosition;
@@ -121,8 +121,11 @@
 
         origin.y += verticalScrollPosition;
         
-        return (eventStageX >= origin.x) && (eventStageY >= origin.y) && 
-            (eventStageX < (origin.x + width)) && (eventStageY < (origin.y + height));
+
+        var headerViewContainsGlobalCoordinates:Boolean = (stageX >= origin.x) && (stageY >= origin.y) &&
+                (stageX < (origin.x + width)) && (stageY < (origin.y + height));
+
+        return headerViewContainsGlobalCoordinates;
     }   
 }
 }
\ No newline at end of file
diff --git a/frameworks/projects/spark/src/spark/components/gridClasses/GridDimensions.as b/frameworks/projects/spark/src/spark/components/gridClasses/GridDimensions.as
index 0c679db..9c975b2 100644
--- a/frameworks/projects/spark/src/spark/components/gridClasses/GridDimensions.as
+++ b/frameworks/projects/spark/src/spark/components/gridClasses/GridDimensions.as
@@ -1191,21 +1191,21 @@
 
         for (i = 0; i < _columnCount; i++)
         {
-            var temp:Number = _columnWidths[i];
+            var columnWidth:Number = _columnWidths[i];
             
             // fall back on typical widths if the actual width isn't set.
-            if (isNaN(temp))
+            if (isNaN(columnWidth))
             {
-                temp = typicalCellWidths[i];
-                if (temp == 0) // invisible column
+                columnWidth = typicalCellWidths[i];
+                if (columnWidth == 0) // invisible column
                     continue;
             }
             
             // fall back on defaultColumnWidth
-            if (isNaN(temp))
-                temp = defaultColumnWidth;
+            if (isNaN(columnWidth))
+                columnWidth = defaultColumnWidth;
             
-            current -= temp + columnGap;
+            current -= columnWidth + columnGap;
 
             if (current < 0)
                 return i;
diff --git a/frameworks/projects/spark/tests/spark/components/gridClasses/GridHeaderViewLayout_FLEX_35260_Tests.as b/frameworks/projects/spark/tests/spark/components/gridClasses/GridHeaderViewLayout_FLEX_35260_Tests.as
index 925ea0c..dad721d 100644
--- a/frameworks/projects/spark/tests/spark/components/gridClasses/GridHeaderViewLayout_FLEX_35260_Tests.as
+++ b/frameworks/projects/spark/tests/spark/components/gridClasses/GridHeaderViewLayout_FLEX_35260_Tests.as
@@ -84,11 +84,18 @@
         private static const ITSELF:Matrix = new Matrix(1, 0, 0, 1, 0, 0); //the point, unmodified
         private static const directions:Array = [ITSELF, N, NE, E, SE, S, SW, W, NW];
 
+        private static const COLUMN_HEADER_RECTANGLES:String = "columnHeaderRectangles";
+        private static const ENTIRE_HEADER_RECTANGLE:String = "headerRectangle";
+        private static const MAIN_HEADER_VIEW_RECTANGLE:String = "mainHeaderViewRectangle";
+        private static const FIXED_HEADER_VIEW_RECTANGLE:String = "fixedHeaderViewRectangle";
+
         private static var _dataGrid:DataGrid;
 
         private var _keyRectangles:Array;
         private var _keyPoints:Array;
 
+        //@TODO we probably have to account for paddingTop and paddingBottom as well
+        //@TODO add cases with horizontal scroll, with fixed columns, and RTL
         public static var dimensions:Array = [/*x, y, width, header padding left, header padding top, [column widths] */
             [[10, 0, 300, 5, 0, [25, 150]]],
             [[0, 0, 300, 5, 0, [25, 150]]]
@@ -124,20 +131,7 @@
             _keyPoints = null;
         }
 
-        /*[Ignore]
-        [Test]
-        public function test_global_coordinates_over_header_view(globalPoint:Array, expectedHeaderIndex:int):void
-        {
-            //given
-            var pointOverAHeaderView:Boolean = expectedHeaderIndex != -1;
-
-            //when
-            var doesHeaderContainThisPoint:Boolean = _sut.areCoordinatesOverAHeaderView(new Point(globalPoint[0], globalPoint[1]));
-
-            //then
-            assertEquals(pointOverAHeaderView, doesHeaderContainThisPoint);
-        }
-
+        /*
         [Ignore]
         [Test]
         public function test_column_index(globalPoint:Array, expectedColumnIndex:int):void
@@ -196,25 +190,43 @@
             {
                 for (var i:int = 0; i < directions.length; i++)
                 {
+                    //given
+                    var pointToTest:Point = getAdjacentPoint(_keyPoints[pointName], directions[i]);
+
                     //when
-                    var adjacentPoint:Point = getAdjacentPoint(_keyPoints[pointName], directions[i]);
-                    var expectedHeaderIndex:int = getHeaderIndexAssumption(adjacentPoint);
-                    var actualHeaderIndex:int = getHeaderIndexAtGlobalPoint(adjacentPoint);
+                    var expectedHeaderIndex:int = getHeaderIndexAssumption(pointToTest);
+                    var actualHeaderIndex:int = getHeaderIndexAtGlobalPoint(pointToTest);
+                    const errorMessageHeaderIndex:String = getHeaderIndexErrorMessage(pointName, directions[i], pointToTest, expectedHeaderIndex, actualHeaderIndex);
+
+                    var shouldBeContainedInMainHeaderView:Boolean = getMainHeaderViewContainsPointAssumption(pointToTest);
+                    var shouldBeContainedInFixedHeaderView:Boolean = getFixedHeaderViewContainsPointAssumption(pointToTest);
+                    const shouldBeContainedInAHeaderView:Boolean = shouldBeContainedInMainHeaderView || shouldBeContainedInFixedHeaderView;
+                    var actuallyContainedInAHeaderView:Boolean = _sut.areCoordinatesOverAHeaderView(pointToTest);
+                    const errorMessageHeaderViewContainsPoint:String = getHeaderContainsPointErrorMessage(pointName, directions[i], pointToTest, shouldBeContainedInAHeaderView, actuallyContainedInAHeaderView);
 
                     //then
-                    const errorMessage:String = getHeaderIndexErrorMessage(pointName, directions[i], adjacentPoint, expectedHeaderIndex, actualHeaderIndex);
-                    assertEquals(errorMessage, expectedHeaderIndex, actualHeaderIndex);
+                    assertEquals(errorMessageHeaderIndex, expectedHeaderIndex, actualHeaderIndex);
+                    assertEquals(errorMessageHeaderViewContainsPoint, shouldBeContainedInAHeaderView, actuallyContainedInAHeaderView);
                 }
             }
         }
 
-        private function getHeaderIndexErrorMessage(pointName:String, direction:Matrix, transformedPoint:Point, expectedHeaderIndex:int, actualHeaderIndex:int):String
+        private function getHeaderIndexErrorMessage(pointName:String, direction:Matrix, transformedPoint:Point, expectedColumnHeaderIndex:int, actualColumnHeaderIndex:int):String
         {
             return "The point " + pointName + " transformed with Matrix " + direction + " (resulting in " + transformedPoint + ") should be "
-                    + (expectedHeaderIndex == -1 ? "outside any header bounds" : "inside the header with index " + expectedHeaderIndex)
+                    + (expectedColumnHeaderIndex == -1 ? "outside any header bounds" : "inside the column header with index " + expectedColumnHeaderIndex)
                     + " but was mistakenly found to be "
-                    + (actualHeaderIndex == -1 ? "outside any header bounds" : "inside the header with index " + actualHeaderIndex
-                    + "\n DEBUG INFO: headerRectangles=" + headerRectangles);
+                    + (actualColumnHeaderIndex == -1 ? "outside any header bounds" : "inside the column header with index " + actualColumnHeaderIndex
+                    + "\n DEBUG INFO: headerRectangles=" + columnHeaderRectangles);
+        }
+
+        private function getHeaderContainsPointErrorMessage(pointName:String, direction:Matrix, transformedPoint:Point, shouldBeContainedInHeader:Boolean, isActuallyContainedInHeader:Boolean):String
+        {
+            return "The point " + pointName + " transformed with Matrix " + direction + " (resulting in " + transformedPoint + ") should be "
+                    + (shouldBeContainedInHeader ? "within " : "outside ") + "a header view"
+                    + " but was mistakenly found to be "
+                    + (isActuallyContainedInHeader ? "within" : "outside")
+                    + "\n DEBUG INFO: header views=" + fixedHeaderViewRectangle + "; " + mainHeaderViewRectangle;
         }
 
         private function getHeaderIndexAtGlobalPoint(globalPoint:Point):int
@@ -223,9 +235,24 @@
             return _sut.getHeaderIndexAt(localPoint.x, localPoint.y);
         }
 
+        private function getHeaderContainsPointAssumption(point:Point):Boolean
+        {
+            return rectangleContainsPoint(headerRectangle, point);
+        }
+
+        private function getFixedHeaderViewContainsPointAssumption(point:Point):Boolean
+        {
+            return rectangleContainsPoint(fixedHeaderViewRectangle, point);
+        }
+
+        private function getMainHeaderViewContainsPointAssumption(point:Point):Boolean
+        {
+            return rectangleContainsPoint(mainHeaderViewRectangle, point);
+        }
+
         private function getHeaderIndexAssumption(point:Point):int
         {
-            return getIndexOfHeaderRectangleWhichContainsPoint(point, headerRectangles);
+            return getIndexOfHeaderRectangleWhichContainsPoint(point, columnHeaderRectangles);
         }
 
         private function getAdjacentPoint(point:Point, direction:Matrix):Point
@@ -289,12 +316,35 @@
         {
             var keyRectangles:Array = [];
 
-            keyRectangles["headerRectangles"] = generateHeaderRectangles(keyPoints, dimensions);
+            keyRectangles[COLUMN_HEADER_RECTANGLES] = generateHeaderColumnRectangles(keyPoints, dimensions);
+            keyRectangles[ENTIRE_HEADER_RECTANGLE] = generateVisibleHeaderRectangle(keyPoints, dimensions);
+            keyRectangles[MAIN_HEADER_VIEW_RECTANGLE] = generateMainHeaderViewRectangle(keyPoints, dimensions);
+            keyRectangles[FIXED_HEADER_VIEW_RECTANGLE] = generateFixedHeaderViewRectangle(keyPoints, dimensions);
 
             return keyRectangles;
         }
 
-        private function generateHeaderRectangles(keyPoints:Array, dimensions:Array):Array
+        private function generateMainHeaderViewRectangle(keyPoints:Array, dimensions:Array):Rectangle
+        {
+            //this is the GridColumnHeaderGroup.centerGridColumnHeaderView, which is holds the non-fixed columns; padding excluded
+            const topLeftCorner:Point = keyPoints["c0"];
+            return new Rectangle(topLeftCorner.x, topLeftCorner.y, getHeaderWidthFromKeyPoints(keyPoints) - getHeaderPaddingLeft(dimensions), getHeaderHeightFromKeyPoints(keyPoints));
+        }
+
+        private function generateFixedHeaderViewRectangle(keyPoints:Array, dimensions:Array):Rectangle
+        {
+            //this is the GridColumnHeaderGroup.centerGridColumnHeaderView, which is holds the non-fixed columns; padding excluded
+            const topLeftCorner:Point = keyPoints["b"];
+            return new Rectangle(topLeftCorner.x, topLeftCorner.y, 0, 0);
+        }
+
+        private function generateVisibleHeaderRectangle(keyPoints:Array, dimensions:Array):Rectangle
+        {
+            const topLeftCorner:Point = keyPoints["b"];
+            return new Rectangle(topLeftCorner.x, topLeftCorner.y, getHeaderWidthFromKeyPoints(keyPoints), getHeaderHeightFromKeyPoints(keyPoints));
+        }
+
+        private function generateHeaderColumnRectangles(keyPoints:Array, dimensions:Array):Array
         {
             var headerRectangles:Array = [];
 
@@ -314,9 +364,24 @@
             return headerRectangles;
         }
 
-        private function get headerRectangles():Array
+        private function get columnHeaderRectangles():Array
         {
-            return _keyRectangles["headerRectangles"];
+            return _keyRectangles[COLUMN_HEADER_RECTANGLES];
+        }
+
+        private function get headerRectangle():Rectangle
+        {
+            return _keyRectangles[ENTIRE_HEADER_RECTANGLE] as Rectangle;
+        }
+
+        private function get mainHeaderViewRectangle():Rectangle
+        {
+            return _keyRectangles[MAIN_HEADER_VIEW_RECTANGLE] as Rectangle;
+        }
+
+        private function get fixedHeaderViewRectangle():Rectangle
+        {
+            return _keyRectangles[FIXED_HEADER_VIEW_RECTANGLE] as Rectangle;
         }
 
         private function getColumnWidthFromKeyPoints(keyPoints:Array, columnIndex:int):Number
@@ -361,6 +426,11 @@
             return Point(keyPoints["h"]).y - Point(keyPoints["e"]).y;
         }
 
+        private function getHeaderWidthFromKeyPoints(keyPoints:Array):Number
+        {
+            return Point(keyPoints["e"]).x - Point(keyPoints["b"]).x;
+        }
+
         private function getColumnWidths(dimensions:Array):Array
         {
             return dimensions[5];