changeset 788:fdc80b4d14e9

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.2/MASTER/rt
author leifs
date Mon, 09 Apr 2012 13:05:50 -0700
parents b66c767bcbcb cce17d461292
children c2f82cdfe9fd e9d45acca75f
files
diffstat 65 files changed, 3674 insertions(+), 729 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-charts/src/javafx/scene/chart/AreaChart.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/AreaChart.java	Mon Apr 09 13:05:50 2012 -0700
@@ -98,6 +98,30 @@
         return (number == null) ? nullDefault : number.doubleValue();
     }
 
+       /** @inheritDoc */
+    @Override protected void updateAxisRange() {
+        final Axis<X> xa = getXAxis();
+        final Axis<Y> ya = getYAxis();
+        List<X> xData = null;
+        List<Y> yData = null;
+        if(xa.isAutoRanging()) xData = new ArrayList<X>();
+        if(ya.isAutoRanging()) yData = new ArrayList<Y>();
+        if(xData != null || yData != null) {
+            for(Series<X,Y> series : getData()) {
+                for(Data<X,Y> data: series.getData()) {
+                    if(xData != null) xData.add(data.getXValue());
+                    if(yData != null) yData.add(data.getYValue());
+                }
+            }
+            if(xData != null && !(xData.size() == 1 && getXAxis().toNumericValue(xData.get(0)) == 0)) {
+                xa.invalidateRange(xData);
+            }
+            if(yData != null && !(xData.size() == 1 && getYAxis().toNumericValue(yData.get(0)) == 0)) {
+                ya.invalidateRange(yData);
+            }
+        }
+    }
+    
     @Override protected void dataItemAdded(Series<X,Y> series, int itemIndex, Data<X,Y> item) {
         final Node symbol = createSymbol(series, getData().indexOf(series), item, itemIndex);
         if (shouldAnimate()) {
@@ -161,7 +185,7 @@
         int itemIndex = series.getItemIndex(item);
         if (shouldAnimate()) {
             boolean animate = false;
-            if (itemIndex > 0 && itemIndex < series.getDataSize()) {
+            if (itemIndex > 0 && itemIndex < series.getDataSize()-1) {
                 animate = true;
                 int index=0; Data<X,Y> d;
                 for (d = series.begin; d != null && index != itemIndex - 1; d=d.next) index++;
@@ -365,7 +389,11 @@
                     symbol.resizeRelocate(x-(w/2), y-(h/2),w,h);
                 }
             }
-            fillPath.getElements().add(new LineTo(lastX, getYAxis().getZeroPosition()));
+            if (fillPath.getElements().size() >= 1) {
+                fillPath.getElements().add(new LineTo(lastX, getYAxis().getZeroPosition()));
+            } else {
+                fillPath.getElements().add(new MoveTo(lastX, getYAxis().getZeroPosition()));
+            }
             fillPath.getElements().add(new ClosePath());
         }
     }
--- a/javafx-ui-charts/src/javafx/scene/chart/PieChart.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/PieChart.java	Mon Apr 09 13:05:50 2012 -0700
@@ -31,7 +31,6 @@
 import javafx.animation.Interpolator;
 import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
-import javafx.beans.DefaultProperty;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.DoubleProperty;
 import javafx.beans.property.DoublePropertyBase;
@@ -81,7 +80,6 @@
  * pie slice labels or not.
  *
  */
-@DefaultProperty("data")
 public class PieChart extends Chart {
 
     // -------------- PRIVATE FIELDS -----------------------------------------------------------------------------------
--- a/javafx-ui-charts/src/javafx/scene/chart/ScatterChart.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/ScatterChart.java	Mon Apr 09 13:05:50 2012 -0700
@@ -168,9 +168,9 @@
                 double y = getYAxis().getDisplayPosition(item.getCurrentY());
                 Node symbol = item.getNode();
                 if (symbol != null) {
-                    symbol.setLayoutX(x);
-                    symbol.setLayoutY(y);
-                    symbol.resize(symbol.prefWidth(-1), symbol.prefHeight(-1));
+                    final double w = symbol.prefWidth(-1);
+                    final double h = symbol.prefHeight(-1);
+                    symbol.resizeRelocate(x-(w/2), y-(h/2),w,h); 
                 }
             }
         }
--- a/javafx-ui-charts/src/javafx/scene/chart/StackedAreaChart.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/StackedAreaChart.java	Mon Apr 09 13:05:50 2012 -0700
@@ -364,7 +364,7 @@
                     totalY += maxY;
 
                 }
-                yData.add(totalY);
+                if(totalY > 0) yData.add(totalY);
                 ya.invalidateRange(yData);
             }
         }
--- a/javafx-ui-charts/src/javafx/scene/chart/XYChart.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/XYChart.java	Mon Apr 09 13:05:50 2012 -0700
@@ -33,7 +33,6 @@
 import javafx.animation.Interpolator;
 import javafx.animation.KeyFrame;
 import javafx.animation.KeyValue;
-import javafx.beans.DefaultProperty;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ObjectPropertyBase;
@@ -1407,7 +1406,6 @@
     /**
      * A named series of data items
      */
-    @DefaultProperty("data")
     public static final class Series<X,Y> {
 
         // -------------- PRIVATE PROPERTIES ----------------------------------------
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1241,7 +1241,17 @@
     public ObservableList<String> getErrors() {
         return errors;
     }
-            
+
+    private static class StyleHelperCacheContainer {
+        private final StyleHelper styleHelper;
+        private final Reference<StyleHelper> styleHelperRef;
+        private StyleHelperCacheContainer(StyleHelper styleHelper, 
+                Reference<StyleHelper> styleHelperRef) {
+            this.styleHelper = styleHelper;
+            this.styleHelperRef = styleHelperRef;
+        }        
+    }
+    
     /**
      * Creates and caches StyleHelpers, reusing them as often as practical.
      */
@@ -1251,25 +1261,30 @@
         private final List<Rule> rules;
         private final long pseudoclassStateMask;
         private final boolean impactsChildren;
-        private final Map<Long, Reference<StyleHelper>> cache;
+        private final Map<Long, StyleHelperCacheContainer> cache;
 
         Cache(List<Rule> rules, long pseudoclassStateMask, boolean impactsChildren) {
             this.rules = rules;
             this.pseudoclassStateMask = pseudoclassStateMask;
             this.impactsChildren = impactsChildren;
-            cache = new HashMap<Long, Reference<StyleHelper>>();
+            cache = new HashMap<Long, StyleHelperCacheContainer>();
         }
 
         private void clear() {
 
-            for(Reference<StyleHelper> helperRef : cache.values()) {
-               StyleHelper helper = helperRef.get();
+            for(StyleHelperCacheContainer helperContainer : cache.values()) {
+                
+                final StyleHelper helper = (helperContainer != null)
+                        ? helperContainer.styleHelper
+                        : null;
+                
                 if (helper == null) {
                     continue;
                 }
                 helper.valueCache = null;
                 helper.clearStyleMap();
-                helperRef.clear();
+                helperContainer.styleHelperRef.clear();
+                
             }
 
             cache.clear();
@@ -1319,9 +1334,9 @@
             }
 
             if (cache.containsKey(key)) {
-                Reference helperRef = cache.get(key);
-                if (helperRef.get() != null) {
-                    return helperRef;
+                final StyleHelperCacheContainer helperContainer = cache.get(key);
+                if (helperContainer != null) {
+                    return helperContainer.styleHelperRef;
                 }
                 cache.remove(key);
             } 
@@ -1332,10 +1347,15 @@
             final StyleHelper helper =
                 StyleHelper.create(styles, pseudoclassStateMask,
                     ++(container.helperCount));
-            final Reference helperRef = new WeakReference(helper);
 
             helper.valueCache = container.valueCache;
-            cache.put(key, helperRef);
+            
+            final Reference<StyleHelper> helperRef =
+                new WeakReference<StyleHelper>(helper);            
+            final StyleHelperCacheContainer helperContainer = 
+                new StyleHelperCacheContainer(helper, helperRef);
+            cache.put(key, helperContainer);
+
             return helperRef;
         }
 
--- a/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Mon Apr 09 13:05:50 2012 -0700
@@ -707,6 +707,21 @@
             ParsedValue value = parseStrokeType(root);
             if (value == null) error(root, "Expected \'centered', \'inside\' or \'outside\'");
             return value;
+        } else if ("-fx-font-smoothing-type".equals(prop)) {
+            // TODO: Figure out a way that these properties don't need to be
+            // special cased.
+            String str = null;
+            int ttype = -1;
+            final Token token = root.token;
+            
+            if (root.token == null
+                    || ((ttype = root.token.getType()) != CSSLexer.STRING
+                         && ttype != CSSLexer.IDENT)
+                    || (str = root.token.getText()) == null
+                    || str.isEmpty()) {
+                error(root,  "Expected STRING or IDENT");
+            }
+            return new ParsedValue<String, String>(stripQuotes(str), null, false);            
         }
         return parse(root);
     }
--- a/javafx-ui-common/src/javafx/scene/Node.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/javafx/scene/Node.java	Mon Apr 09 13:05:50 2012 -0700
@@ -873,7 +873,18 @@
 
                 @Override
                 protected void invalidated() {
-                     impl_reapplyCSS();
+                    //
+                    // Here, we used to call reapplyCSS(). But there is 
+                    // no need to create a new StyleHelper if just the 
+                    // inline style has changed since the inline style is
+                    // not cached. 
+                    //
+                    if (getScene() != null &&
+                        cssFlag != CSSFlags.REAPPLY && 
+                        cssFlag != CSSFlags.UPDATE) {
+                        cssFlag = CSSFlags.UPDATE; 
+                        notifyParentsOfInvalidatedCSS();
+                    } 
                 }
 
                 @Override
@@ -6734,6 +6745,38 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
+    public void impl_cssResetInitialValues() {
+        
+        //
+        // RT-9784: reset all properties that have been set by CSS back
+        // to their default value. Called when a stylesheet is removed from
+        // a parent or from the scene. This has to be done before calling 
+        // impl_reapplyCSS.
+        //
+        final List<StyleableProperty> styleables = impl_getStyleableProperties();
+        final int nStyleables = styleables != null ? styleables.size() : 0;
+        for (int n=0; n<nStyleables; n++) {
+            final StyleableProperty styleable = styleables.get(n);
+            if (styleable.isSettable(this) == false) continue;
+            final WritableValue writable = styleable.getWritableValue(this);
+            if (writable != null) {
+                final Stylesheet.Origin origin = StyleableProperty.getOrigin(writable);
+                if (origin != null && origin != Stylesheet.Origin.USER) {
+                    // If a property is never set by the user or by CSS, then 
+                    // the Origin of the property is null. So, passing null 
+                    // here makes the property look (to CSS) like it was
+                    // initialized but never used.
+                    styleable.set(this, styleable.getInitialValue(this), null);
+                }
+            }
+        }        
+    }
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
     public final void impl_reapplyCSS() {
         // If there is no scene, then we cannot make it dirty, so we'll leave
         // the flag alone
@@ -6832,6 +6875,8 @@
             styleHelper = styleHelperRef.get();
         }
 
+        // set the key to null here so the next call to impl_getStyleCacheKey
+        // will cause a new key to be created
         styleCacheKeyRef = null;
         return styleHelper;
     }
--- a/javafx-ui-common/src/javafx/scene/Parent.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/javafx/scene/Parent.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1057,6 +1057,13 @@
         @Override
         protected void onChanged(Change<String> c) {
             StyleManager.getInstance().parentStylesheetsChanged(c);
+            // RT-9784 - if stylesheet is removed, reset styled properties to 
+            // their initial value.
+            while(c.next()) {
+                if (c.wasRemoved() == false) continue;
+                impl_cssResetInitialValues();
+                break; // no point in resetting more than once...
+            }
             impl_reapplyCSS();
         }
     };
@@ -1129,6 +1136,28 @@
             }
         }
     }
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override public void impl_cssResetInitialValues() {
+        
+        // RT-9784
+        
+        super.impl_cssResetInitialValues();
+
+        final List kids = this.getChildren();
+        final int max = kids.size();
+        for (int c=0; c<max; c++) {
+            Node kid = (Node)kids.get(c);
+            if (kid != null) {
+                kid.impl_cssResetInitialValues();
+            }
+        }
+    }
+    
 
     /***********************************************************************
      *                               Misc                                  *
--- a/javafx-ui-common/src/javafx/scene/Scene.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/javafx/scene/Scene.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1138,6 +1138,13 @@
         @Override
         protected void onChanged(Change<String> c) {
             StyleManager.getInstance().updateStylesheets(Scene.this);
+            // RT-9784 - if stylesheet is removed, reset styled properties to 
+            // their initial value.
+            while(c.next()) {
+                if (c.wasRemoved() == false) continue;
+                getRoot().impl_cssResetInitialValues();
+                break; // no point in resetting more than once...
+            }
             getRoot().impl_reapplyCSS();
         }
     };
--- a/javafx-ui-common/src/javafx/scene/layout/Region.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/src/javafx/scene/layout/Region.java	Mon Apr 09 13:05:50 2012 -0700
@@ -79,8 +79,8 @@
  * around its content. By default it's a rectangle with possible rounded corners,
  * depending on borders. It can be made into any shape by specifying the {@code shape}.
  * It is designed to support as much of the CSS3 specification for backgrounds
- * and borders as is relevant to JavaFX. The full specification is available at
- * <a href="http://www.w3.org/TR/css3-background/">css3-background</a>.
+ * and borders as is relevant to JavaFX. 
+ * <a href="http://www.w3.org/TR/css3-background/"> The full specification is available at css3-background</a>.
  * <p>
  * By default a Region inherits the layout behavior of its superclass, {@link Parent},
  * which means that it will resize any resizable child nodes to their preferred
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest.java	Mon Apr 09 13:05:50 2012 -0700
@@ -35,6 +35,7 @@
 import javafx.scene.paint.Color;
 import javafx.scene.shape.Rectangle;
 import javafx.scene.text.Font;
+import javafx.scene.text.FontSmoothingType;
 import javafx.scene.text.Text;
 import javafx.stage.Stage;
 import javafx.stage.Window;
@@ -261,7 +262,7 @@
         
     }
     
-    @Test
+    @Test @Ignore("RT-20210 breaks this, but RT-20513 is working")
     public void testInlineStyleInheritedFromParentApplies() {
         
         // must remove the id so we don't pick up the ua style
@@ -297,5 +298,33 @@
         assertEquals(14, text.getStrokeWidth(), 0.00001);
         
     }
+
+    
+    @Test public void test_RT_20686_UAStyleDoesNotOverrideSetFontSmoothingType() {
+        
+        text.setId("rt-20686-ua");
+        text.setFontSmoothingType(FontSmoothingType.LCD);
+        
+        Toolkit.getToolkit().firePulse();
+         
+        assertEquals(FontSmoothingType.LCD, text.getFontSmoothingType());
+        
+    }
+    
+    @Test public void test_RT_20686_AuthorStyleOverridesSetFontSmoothingType() {
+        
+        String url = getClass().getResource("HonorDeveloperSettingsTest_AUTHOR.css").toExternalForm();
+        scene.getStylesheets().add(url);
+
+        text.setId("rt-20686-author");
+        text.setFontSmoothingType(FontSmoothingType.GRAY);
+        
+        Toolkit.getToolkit().firePulse();
+                
+        assertEquals(FontSmoothingType.LCD, text.getFontSmoothingType());
+        
+    }
+
+    
     
 }
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest_AUTHOR.css	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest_AUTHOR.css	Mon Apr 09 13:05:50 2012 -0700
@@ -8,4 +8,4 @@
 #text { -fx-stroke: red; -fx-stroke-width: 1em; }
 #rectangle { -fx-stroke: red; -fx-stroke-width: 1em; }
 
-
+#rt-20686-author { -fx-font-smoothing-type: lcd; }
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest_UA.css	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/HonorDeveloperSettingsTest_UA.css	Mon Apr 09 13:05:50 2012 -0700
@@ -10,4 +10,6 @@
 
 #text {
     -fx-font: 32pt "Amble Cn";
-}
\ No newline at end of file
+}
+
+#rt-20686-ua { -fx-font-smoothing-type: gray; }
\ No newline at end of file
--- a/javafx-ui-common/test/unit/javafx/scene/text/Text_cssMethods_Test.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-common/test/unit/javafx/scene/text/Text_cssMethods_Test.java	Mon Apr 09 13:05:50 2012 -0700
@@ -52,7 +52,9 @@
                    "-fx-text-alignment", TextAlignment.CENTER),
             config(TEST_TEXT, "textOrigin", VPos.BASELINE,
                    "-fx-text-origin", VPos.BOTTOM),
-            config(TEST_TEXT, "translateX", 0.0, "-fx-translate-x", 10.0)
+            config(TEST_TEXT, "translateX", 0.0, "-fx-translate-x", 10.0),
+            config(TEST_TEXT, "fontSmoothingType", FontSmoothingType.LCD,
+                "-fx-font-smoothing-type", FontSmoothingType.GRAY)
         });
     }
 
--- a/javafx-ui-controls/build.xml	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/build.xml	Mon Apr 09 13:05:50 2012 -0700
@@ -4,4 +4,7 @@
 
     <import file="../build-defs.xml"/>
     <import file="build-common.xml"/>
+
+    <target name="-pre-init" depends="javafx-ui-controls-common.-pre-init"/>
+
 </project>
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPicker.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPicker.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1,6 +1,26 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
  */
 package com.sun.javafx.scene.control;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPickerAddColorPane.java	Mon Apr 09 13:05:50 2012 -0700
@@ -0,0 +1,613 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.scene.control;
+
+import javafx.beans.binding.DoubleBinding;
+import javafx.beans.binding.ObjectBinding;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.HPos;
+import javafx.geometry.VPos;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.*;
+import javafx.scene.paint.*;
+import javafx.scene.shape.Circle;
+import javafx.scene.shape.CircleBuilder;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Modality;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.stage.Window;
+
+/**
+ *
+ * @author paru
+ */
+public class ColorPickerAddColorPane extends StackPane {
+    
+    private static final int CONTENT_PADDING = 10;
+    private static final int RECT_SIZE = 200;
+    private static final int CONTROLS_WIDTH = 252;
+    
+    final Stage dialog = new Stage();
+    ColorRectPane colorRectPane;
+    ControlsPane controlsPane;
+    
+    Circle colorRectIndicator;
+    Rectangle colorRect;
+    Rectangle colorRectOverlayOne;
+    Rectangle colorRectOverlayTwo;
+    Rectangle colorBar;
+    Rectangle colorBarIndicator;
+    
+    public ColorPickerAddColorPane(Window owner) {
+        getStyleClass().add("add-color-pane");
+//        if (owner != null) dialog.initOwner(owner);
+        dialog.initModality(Modality.WINDOW_MODAL);
+        dialog.initStyle(StageStyle.UTILITY);
+        colorRectPane = new ColorRectPane();
+        controlsPane = new ControlsPane();
+        
+        Scene scene = new Scene(new Group());
+        Group dialogRoot = (Group) scene.getRoot();
+        getChildren().addAll(colorRectPane, controlsPane);
+        dialogRoot.getChildren().addAll(this);
+        
+        dialog.setScene(scene);
+        
+//        Button dialogButton = new Button("Dismiss");
+//        dialogButton.setLayoutX(CONTENT_PADDING+colorRectGroup.prefWidth(-1)+30);
+//        dialogButton.setLayoutY(CONTENT_PADDING+10);
+//        dialogButton.setOnAction(new EventHandler<ActionEvent>() {
+//            @Override public void handle(ActionEvent e) {
+//                dialog.hide();
+//            }
+//        });
+//        dialogRoot.getChildren().addAll(contentGroup, dialogButton);
+    }
+    
+    public void show() {
+        dialog.show();
+    }
+    
+    @Override public void layoutChildren() {
+         controlsPane.relocate(colorRectPane.prefWidth(-1), 0);
+    }
+    
+    @Override public double computePrefWidth(double height) {
+        return getInsets().getLeft() + colorRectPane.prefWidth(height) +
+                controlsPane.prefWidth(height) + getInsets().getRight();
+    }
+    
+    @Override public double computePrefHeight(double width) {
+        return getInsets().getTop() + Math.max(colorRectPane.prefHeight(width),
+                controlsPane.prefHeight(width) + getInsets().getBottom());
+    }
+    
+    static double computeXOffset(double width, double contentWidth, HPos hpos) {
+        switch(hpos) {
+            case LEFT:
+               return 0;
+            case CENTER:
+               return (width - contentWidth) / 2;
+            case RIGHT:
+               return width - contentWidth;
+        }
+        return 0;
+    }
+
+    static double computeYOffset(double height, double contentHeight, VPos vpos) {
+       switch(vpos) {
+            case TOP:
+               return 0;
+            case CENTER:
+               return (height - contentHeight) / 2;
+            case BOTTOM:
+               return height - contentHeight;
+        }
+       return 0;
+    }
+    
+    /* ------------------------------------------------------------------------*/
+    
+    class ColorRectPane extends StackPane {
+        
+        private boolean changeIsLocal = false;
+        private DoubleProperty hue = new SimpleDoubleProperty() {
+            @Override protected void invalidated() {
+                if (!changeIsLocal) {
+                    changeIsLocal = true;
+                    color.set(Color.hsb(hue.get(), clamp(sat.get() / 100), clamp(bright.get() / 100)));
+                    changeIsLocal = false;
+                }
+            }
+        };
+        private DoubleProperty sat = new SimpleDoubleProperty() {
+            @Override protected void invalidated() {
+                if (!changeIsLocal) {
+                    changeIsLocal = true;
+                    color.set(Color.hsb(hue.get(), clamp(sat.get() / 100), clamp(bright.get() / 100)));
+                    changeIsLocal = false;
+                }
+            }
+        };
+        private DoubleProperty bright = new SimpleDoubleProperty() {
+            @Override protected void invalidated() {
+                if (!changeIsLocal) {
+                    changeIsLocal = true;
+                    color.set(Color.hsb(hue.get(), clamp(sat.get() / 100), clamp(bright.get() / 100)));
+                    changeIsLocal = false;
+                }
+            }
+        };
+        private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.RED) {
+            @Override protected void invalidated() {
+                if (!changeIsLocal) {
+                    changeIsLocal = true;
+                    final Color c = get();
+                    hue.set(c.getHue());
+                    sat.set(c.getSaturation() * 100);
+                    bright.set(c.getBrightness() * 100);
+                    changeIsLocal = false;
+                }
+            }
+        };
+        public ObjectProperty<Color> colorProperty() { return color; }
+        public Color getColor() { return color.get(); }
+        public void setColor(Color newColor) { color.set(newColor); }
+
+        public ColorRectPane() {
+            
+            getStyleClass().add("color-rect-pane");
+            colorRectIndicator = 
+                    CircleBuilder.create().centerX(60).centerY(60).radius(5).stroke(Color.WHITE).
+                    fill(null).effect(new DropShadow(2, 0, 1, Color.BLACK)).build();
+            colorRectIndicator.centerXProperty().bind(new DoubleBinding() {
+                { bind(sat); }
+                @Override protected double computeValue() {
+                    return (CONTENT_PADDING + 10) + (RECT_SIZE * (sat.get() / 100));
+                }
+            });
+        
+            colorRectIndicator.centerYProperty().bind(new DoubleBinding() {
+                { bind(bright); }
+                @Override protected double computeValue() {
+                    return (CONTENT_PADDING + 10) + (RECT_SIZE * (1 - (bright.get() / 100)));
+                }
+            });
+        
+            colorRect = new Rectangle(RECT_SIZE, RECT_SIZE);
+            colorRect.fillProperty().bind(new ObjectBinding<Paint>() {
+                { bind(color); }
+                @Override protected Paint computeValue() {
+                    return Color.hsb(hue.getValue(), 1, 1);
+                }
+            });
+        
+            colorRectOverlayOne = new Rectangle(RECT_SIZE, RECT_SIZE);
+            colorRectOverlayOne.setFill(new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE, 
+                    new Stop(0, Color.rgb(255, 255, 255, 1)), 
+                    new Stop(1, Color.rgb(255, 255, 255, 0))));
+            colorRectOverlayOne.setStroke(Color.BLACK);
+        
+            EventHandler<MouseEvent> rectMouseHandler = new EventHandler<MouseEvent>() {
+                @Override public void handle(MouseEvent event) {
+                    final double x = event.getX() - colorRect.getX();
+                    final double y = event.getY() - colorRect.getY();
+                    sat.set(clamp(x / RECT_SIZE) * 100);
+                    bright.set(100 - (clamp(y / RECT_SIZE) * 100));
+                }
+            };
+        
+            colorRectOverlayTwo = new Rectangle(RECT_SIZE, RECT_SIZE);
+            colorRectOverlayTwo.setFill(new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, 
+                        new Stop(0, Color.rgb(0, 0, 0, 0)), new Stop(1, Color.rgb(0, 0, 0, 1))));
+            colorRectOverlayTwo.setOnMouseDragged(rectMouseHandler);
+            colorRectOverlayTwo.setOnMouseClicked(rectMouseHandler);
+            
+            colorBar = new Rectangle(20, RECT_SIZE);
+            colorBar.setFill(createHueGradient());
+
+            colorBarIndicator = new Rectangle(24, 10, null);
+            colorBarIndicator.setArcWidth(4);
+            colorBarIndicator.setArcHeight(4);
+            colorBarIndicator.setStroke(Color.WHITE);
+            colorBarIndicator.setEffect(new DropShadow(2, 0, 1, Color.BLACK));
+        
+            colorBarIndicator.yProperty().bind(new DoubleBinding() {
+                { bind(hue); }
+                @Override protected double computeValue() {
+                    return (CONTENT_PADDING + 5) + (RECT_SIZE * (hue.get() / 360));
+                }
+            });
+            EventHandler<MouseEvent> barMouseHandler = new EventHandler<MouseEvent>() {
+                @Override public void handle(MouseEvent event) {
+                    final double y = event.getY() - colorBar.getY();
+                    hue.set(clamp(y / RECT_SIZE) * 360);
+                }
+            };
+            colorBar.setOnMouseDragged(barMouseHandler);
+            colorBar.setOnMouseClicked(barMouseHandler);
+            
+            getChildren().addAll(colorRect, colorRectOverlayOne, colorRectOverlayTwo, 
+                    colorBar, colorRectIndicator, colorBarIndicator);
+        }
+        
+        @Override public void layoutChildren() {
+            double x = getInsets().getLeft();
+            double y = getInsets().getTop();
+//            double w = getWidth() - (getInsets().getLeft() + getInsets().getRight());
+//            double h = getHeight() - (getInsets().getTop() + getInsets().getBottom());
+            colorRect.relocate(x, y);
+            colorRectOverlayOne.relocate(x, y);
+            colorRectOverlayTwo.relocate(x, y);
+            
+            colorBar.relocate(x+colorRect.getWidth()+10, y);
+            colorBarIndicator.relocate(x+colorRect.getWidth()+8, y+5);
+        }
+    }
+    
+    /* ------------------------------------------------------------------------*/
+    
+    enum ColorSettingsMode {
+        HSB,
+        RGB,
+        WEB
+    }
+    
+    class ControlsPane extends StackPane {
+        
+        Label currentColorLabel;
+        Label newColorLabel;
+        Rectangle currentColorRect;
+        Rectangle newColorRect;
+        GridPane currentAndNewColor;
+        GridPane pillButtons;
+        Rectangle currentNewColorBorder;
+        ToggleButton hsbButton;
+        ToggleButton rgbButton;
+        ToggleButton webButton;
+        HBox hBox;
+        GridPane hsbSettings;
+        GridPane rgbSettings;
+        GridPane webSettings;
+        
+        GridPane alphaSettings;
+        HBox buttonBox;
+        StackPane whiteBox;
+        ColorSettingsMode colorSettingsMode = ColorSettingsMode.HSB;
+        
+        StackPane settingsPane = new StackPane();
+        
+        public ControlsPane() {
+            getStyleClass().add("controls-pane");
+            
+            currentNewColorBorder = new Rectangle(CONTROLS_WIDTH, 18, null);
+            currentNewColorBorder.setStroke(Color.BLACK);
+            
+            currentColorRect = new Rectangle(CONTROLS_WIDTH/2, 18, Color.YELLOW);
+            newColorRect = new Rectangle(CONTROLS_WIDTH/2, 18, Color.LIGHTGREEN);
+            currentColorLabel = new Label("Current Color");
+            newColorLabel = new Label("New Color");
+            Rectangle spacer = new Rectangle(0, 18);
+            
+            whiteBox = new StackPane();
+            whiteBox.getStyleClass().add("addcolor-controls-background");
+            
+            hsbButton = new ToggleButton("HSB");
+            hsbButton.setId("pill-left");
+            rgbButton = new ToggleButton("RGB");
+            rgbButton.setId("pill-center");
+            webButton = new ToggleButton("Web");
+            webButton.setId("pill-right");
+            ToggleGroup group = new ToggleGroup();
+            hsbButton.setToggleGroup(group);
+            rgbButton.setToggleGroup(group);
+            webButton.setToggleGroup(group);
+            group.selectToggle(hsbButton);
+            
+            showHSBSettings(); // Color settings Grid Pane
+            
+            hsbButton.setOnAction(new EventHandler<ActionEvent>() {
+                @Override public void handle(ActionEvent t) {
+                    if (colorSettingsMode != ColorSettingsMode.HSB) {
+                        colorSettingsMode = ColorSettingsMode.HSB;
+                        showHSBSettings();
+                        requestLayout();
+                    }
+                }
+            });
+            rgbButton.setOnAction(new EventHandler<ActionEvent>() {
+                @Override public void handle(ActionEvent t) {
+                    if (colorSettingsMode != ColorSettingsMode.RGB) {
+                        colorSettingsMode = ColorSettingsMode.RGB;
+                        showRGBSettings();
+                        requestLayout();
+                    }
+                }
+            });
+            webButton.setOnAction(new EventHandler<ActionEvent>() {
+                @Override public void handle(ActionEvent t) {
+                    if (colorSettingsMode != ColorSettingsMode.WEB) {
+                        colorSettingsMode = ColorSettingsMode.WEB;
+                        showWebSettings();
+                        requestLayout();
+                    }
+                }
+            });
+            
+            hBox = new HBox();
+            hBox.getChildren().addAll(hsbButton, rgbButton, webButton);
+            
+            currentAndNewColor = new GridPane();
+            currentAndNewColor.add(currentColorLabel, 0, 0, 2, 1);
+            currentAndNewColor.add(newColorLabel, 2, 0, 2, 1);
+            currentAndNewColor.add(currentColorRect, 0, 1, 2, 1);
+            currentAndNewColor.add(newColorRect, 2, 1, 2, 1);
+            currentAndNewColor.add(spacer, 0, 2, 4, 1);
+            
+            // Color settings Grid Pane
+            alphaSettings = new GridPane();
+            alphaSettings.setHgap(5);
+            alphaSettings.setVgap(0);
+            alphaSettings.setManaged(false);
+            alphaSettings.getStyleClass().add("alpha-settings");
+//            alphaSettings.setGridLinesVisible(true);
+            
+            Rectangle spacer4 = new Rectangle(0, 4);
+            alphaSettings.add(spacer4, 0, 0, 3, 1);
+            
+            Label alphaLabel = new Label("Alpha:       ");
+            alphaLabel.setMinWidth(Control.USE_PREF_SIZE);
+            alphaSettings.add(alphaLabel, 0, 1);
+            
+            Slider alphaSlider = new Slider(0, 100, 50);
+//            alphaSlider.valueProperty().bind(colorRectPane.bright);
+            alphaSettings.add(alphaSlider, 1, 1);
+            
+            TextField alphaField = new TextField("50");
+            alphaField.setPrefColumnCount(6);
+            alphaSettings.add(alphaField, 2, 1);
+            
+            Rectangle spacer5 = new Rectangle(0, 12);
+            alphaSettings.add(spacer5, 0, 2, 3, 1);
+            
+            buttonBox = new HBox(4);
+            Button addButton = new Button("Add");
+            Button cancelButton = new Button("Cancel");
+            buttonBox.getChildren().addAll(addButton, cancelButton);
+            
+            getChildren().addAll(currentAndNewColor, currentNewColorBorder, whiteBox, 
+                                            hBox, settingsPane, alphaSettings, buttonBox);
+        }
+        
+        private void showHSBSettings() {
+            if (hsbSettings == null) {
+                hsbSettings = new GridPane();
+                hsbSettings.setHgap(5);
+                hsbSettings.setVgap(4);
+                hsbSettings.setManaged(false);
+    //            colorSettings.setGridLinesVisible(true);
+                Rectangle spacer2 = new Rectangle(0, 3);
+                hsbSettings.add(spacer2, 0, 0, 3, 1);
+
+                Label hueLabel = new Label("Hue:");
+                hueLabel.setMinWidth(Control.USE_PREF_SIZE);
+                hsbSettings.add(hueLabel, 0, 1);
+
+                Slider hueSlider = new Slider(0, 100, 50);
+    //            hueSlider.valueProperty().bind(colorRectPane.bright);
+                hsbSettings.add(hueSlider, 1, 1);
+
+                TextField hueField = new TextField("50");
+                hueField.setPrefColumnCount(6);
+                hsbSettings.add(hueField, 2, 1);
+
+                Label saturationLabel = new Label("Saturation:");
+                saturationLabel.setMinWidth(Control.USE_PREF_SIZE);
+                hsbSettings.add(saturationLabel, 0, 2);
+
+                Slider saturationSlider = new Slider(0, 100, 50);
+    //            saturationSlider.valueProperty().bind(colorRectPane.sat);
+                hsbSettings.add(saturationSlider, 1, 2);
+
+                TextField saturationField = new TextField("50");
+                saturationField.setPrefColumnCount(6);
+                hsbSettings.add(saturationField, 2, 2);
+
+                Label brightnessLabel = new Label("Brightness:");
+                brightnessLabel.setMinWidth(Control.USE_PREF_SIZE);
+                hsbSettings.add(brightnessLabel, 0, 3);
+
+                Slider brightnessSlider = new Slider(0, 100, 50);
+    //            brightnessSlider.valueProperty().bind(colorRectPane.bright);
+                hsbSettings.add(brightnessSlider, 1, 3);
+
+                TextField brightnessField = new TextField("50");
+                brightnessField.setPrefColumnCount(6);
+                hsbSettings.add(brightnessField, 2, 3);
+
+                Rectangle spacer3 = new Rectangle(0, 4);
+                hsbSettings.add(spacer3, 0, 4, 3, 1);
+            }
+            settingsPane.getChildren().setAll(hsbSettings);
+        }
+        
+        
+        private void showRGBSettings() {
+            if (rgbSettings == null) {
+                rgbSettings = new GridPane();
+                rgbSettings.setHgap(5);
+                rgbSettings.setVgap(4);
+                rgbSettings.setManaged(false);
+    //            colorSettings.setGridLinesVisible(true);
+                Rectangle spacer2 = new Rectangle(0, 3);
+                rgbSettings.add(spacer2, 0, 0, 3, 1);
+
+                Label redLabel = new Label("Red:     ");
+                redLabel.setMinWidth(Control.USE_PREF_SIZE);
+                rgbSettings.add(redLabel, 0, 1);
+
+                Slider redSlider = new Slider(0, 100, 50);
+    //            hueSlider.valueProperty().bind(colorRectPane.bright);
+                rgbSettings.add(redSlider, 1, 1);
+
+                TextField redField = new TextField("50");
+                redField.setPrefColumnCount(6);
+                rgbSettings.add(redField, 2, 1);
+
+                Label greenLabel = new Label("Green:     ");
+                greenLabel.setMinWidth(Control.USE_PREF_SIZE);
+                rgbSettings.add(greenLabel, 0, 2);
+
+                Slider greenSlider = new Slider(0, 100, 50);
+    //            saturationSlider.valueProperty().bind(colorRectPane.sat);
+                rgbSettings.add(greenSlider, 1, 2);
+
+                TextField greenField = new TextField("50");
+                greenField.setPrefColumnCount(6);
+                rgbSettings.add(greenField, 2, 2);
+
+                Label blueLabel = new Label("Blue:      ");
+                blueLabel.setMinWidth(Control.USE_PREF_SIZE);
+                rgbSettings.add(blueLabel, 0, 3);
+
+                Slider blueSlider = new Slider(0, 100, 50);
+    //            brightnessSlider.valueProperty().bind(colorRectPane.bright);
+                rgbSettings.add(blueSlider, 1, 3);
+
+                TextField blueField = new TextField("50");
+                blueField.setPrefColumnCount(6);
+                rgbSettings.add(blueField, 2, 3);
+
+                Rectangle spacer3 = new Rectangle(0, 4);
+                rgbSettings.add(spacer3, 0, 4, 3, 1);
+            }
+            settingsPane.getChildren().setAll(rgbSettings);
+        }
+        
+        private void showWebSettings() {
+            if (webSettings == null) {
+                webSettings = new GridPane();
+                webSettings.setHgap(5);
+                webSettings.setVgap(4);
+                webSettings.setManaged(false);
+    //            colorSettings.setGridLinesVisible(true);
+                Rectangle spacer2 = new Rectangle(0, 3);
+                webSettings.add(spacer2, 0, 0, 3, 1);
+
+                Label webLabel = new Label("Web:        ");
+                webLabel.setMinWidth(Control.USE_PREF_SIZE);
+                webSettings.add(webLabel, 0, 1);
+
+                TextField webField = new TextField("50");
+                webField.setPrefColumnCount(6);
+                webSettings.add(webField, 1, 1);
+
+                webSettings.add(new Rectangle(0,18), 1, 2);
+
+                Rectangle spacer3 = new Rectangle(0, 18);
+                webSettings.add(spacer3, 0, 2, 3, 1);
+
+                Rectangle spacer4 = new Rectangle(0, 18);
+                webSettings.add(spacer4, 0, 3, 3, 1);
+
+                Rectangle spacer5 = new Rectangle(0, 4);
+                webSettings.add(spacer5, 0, 4, 3, 1);
+            } 
+            settingsPane.getChildren().setAll(webSettings);
+        }
+        
+        public Label getCurrentColorLabel() {
+            return currentColorLabel;
+        }
+        
+        @Override public void layoutChildren() {
+            double x = getInsets().getLeft();
+            double y = getInsets().getTop();
+//            double w = getWidth() - (getInsets().getLeft() + getInsets().getRight());
+//            double h = getHeight() - (getInsets().getTop() + getInsets().getBottom());
+            currentAndNewColor.resizeRelocate(x+10,
+                    y, CONTROLS_WIDTH, 18);
+            currentNewColorBorder.relocate(x+10, 
+                    y+controlsPane.currentColorLabel.prefHeight(-1));
+            double hBoxX = computeXOffset(currentAndNewColor.prefWidth(-1), hBox.prefWidth(-1), HPos.CENTER);
+            
+            GridPane settingsGrid = (GridPane)settingsPane.getChildren().get(0);
+            settingsGrid.resize(CONTROLS_WIDTH-28, settingsGrid.prefHeight(-1));
+            
+            double settingsHeight = settingsPane.getChildren().get(0).prefHeight(-1);
+            
+            whiteBox.resizeRelocate(x+10, y+currentAndNewColor.prefHeight(-1)+hBox.prefHeight(-1)/2, 
+                    CONTROLS_WIDTH, settingsHeight+hBox.prefHeight(-1)/2+6);
+            
+            hBox.resizeRelocate(x+10+hBoxX, y+currentAndNewColor.prefHeight(-1), 
+                    hBox.prefWidth(-1), hBox.prefHeight(-1));
+            
+            settingsPane.resizeRelocate(x+20, y+currentAndNewColor.prefHeight(-1)+hBox.prefHeight(-1)+5,
+                    CONTROLS_WIDTH-28, settingsHeight);
+            
+            alphaSettings.resizeRelocate(x+20, 
+                    y+currentAndNewColor.prefHeight(-1)+hBox.prefHeight(-1)+5+settingsHeight,
+                    CONTROLS_WIDTH-28, alphaSettings.prefHeight(-1));
+             
+            double buttonBoxX = computeXOffset(currentAndNewColor.prefWidth(-1), buttonBox.prefWidth(-1), HPos.RIGHT);
+            buttonBox.resizeRelocate(x+10+buttonBoxX, y+currentAndNewColor.prefHeight(-1)+hBox.prefHeight(-1)+5+
+                    settingsHeight+alphaSettings.prefHeight(-1), buttonBox.prefWidth(-1), buttonBox.prefHeight(-1));
+        }
+        
+        @Override public double computePrefHeight(double width) {
+            double settingsHeight = settingsPane.getChildren().get(0).prefHeight(-1);
+            return getInsets().getTop() + currentAndNewColor.prefHeight(width) +
+                    currentNewColorBorder.prefHeight(width) + hBox.prefHeight(width) +
+                    settingsHeight + alphaSettings.prefHeight(width) + 
+                    buttonBox.prefHeight(width) + getInsets().getBottom();
+        }
+    }
+    
+    private static double clamp(double value) {
+        return value < 0 ? 0 : value > 1 ? 1 : value;
+    }
+    
+    private static LinearGradient createHueGradient() {
+        double offset;
+        Stop[] stops = new Stop[255];
+        for (int y = 0; y < 255; y++) {
+            offset = (double)(1 - (1.0 / 255) * y);
+            int h = (int)((y / 255.0) * 360);
+            stops[y] = new Stop(offset, Color.hsb(h, 1.0, 1.0));
+        }
+        return new LinearGradient(0f, 1f, 1f, 0f, true, CycleMethod.NO_CYCLE, stops);
+    }
+    
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPickerPanel.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPickerPanel.java	Mon Apr 09 13:05:50 2012 -0700
@@ -4,22 +4,20 @@
  */
 package com.sun.javafx.scene.control;
 
+import java.security.acl.Owner;
 import java.util.List;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.collections.FXCollections;
+import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
-import javafx.scene.Group;
 import javafx.scene.control.Button;
+import javafx.scene.control.Separator;
 import javafx.scene.effect.DropShadow;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.GridPane;
 import javafx.scene.layout.Region;
-import javafx.scene.layout.VBox;
 import javafx.scene.paint.Color;
-import javafx.scene.paint.CycleMethod;
-import javafx.scene.paint.LinearGradient;
-import javafx.scene.paint.Stop;
 import javafx.scene.shape.ArcTo;
 import javafx.scene.shape.ClosePath;
 import javafx.scene.shape.LineTo;
@@ -27,6 +25,7 @@
 import javafx.scene.shape.Path;
 import javafx.scene.shape.Rectangle;
 import javafx.scene.shape.StrokeType;
+import javafx.stage.Window;
 
 
 public class ColorPickerPanel extends Region {
@@ -36,6 +35,7 @@
     
     ColorPickerGrid cpg;
     Path path;
+    ColorPicker colorPicker;
     
     private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.WHITE);
     public ObjectProperty<Color> colorProperty() { return color; }
@@ -48,20 +48,23 @@
         colorProperty().bindBidirectional(cpg.colorProperty());
         // create popup path for main shape
         path = new Path();
-        path.setFill(new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, new Stop(0, Color.web("#313131")), new Stop(0.5, Color.web("#5f5f5f")), new Stop(1, Color.web("#313131"))));
+//        path.setFill(new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, new Stop(0, Color.web("#313131")), new Stop(0.5, Color.web("#5f5f5f")), new Stop(1, Color.web("#313131"))));
+        path.setFill(Color.LIGHTGRAY);
         path.setStroke(null);
         path.setEffect(new DropShadow(15, 0, 1, Color.gray(0, 0.6)));
         path.setCache(true);
         getChildren().addAll(path, cpg);
     }
+    public void setOwner(ColorPicker colorPicker) {
+        this.colorPicker = colorPicker;
+        cpg.owner = colorPicker.getScene().getWindow();
+    }
     
     @Override protected void layoutChildren() {
         double paddingX = getInsets().getLeft();
         double paddingY = getInsets().getTop();
         double popupWidth = cpg.prefWidth(-1) + paddingX+getInsets().getRight();
         double popupHeight = cpg.prefHeight(-1) + getInsets().getTop() + getInsets().getBottom();
-        System.out.println("cpg width = "+cpg.prefWidth(-1)+" paddingX = "+paddingX+" paddingY = "+
-                paddingY);
         double arrowX = paddingX+RADIUS;
         path.getElements().addAll(
                 new MoveTo(paddingX, getInsets().getTop() + ARROW_SIZE + RADIUS), 
@@ -86,8 +89,10 @@
     
     private Color currentColor = null;
     private final List<ColorSquare> squares;
+    Window owner;
     
     public ColorPickerGrid(Color initPaint) {
+        getStyleClass().add("color-picker-grid");
         setId("ColorCustomizerColorGrid");
         setGridLinesVisible(true);
         int columnIndex = 0, rowIndex = 0;
@@ -111,8 +116,25 @@
                 rowIndex++;
             }
         }
+        System.out.println("getWidth = "+getWidth());
+        add(new Rectangle(getWidth(), SQUARE_SIZE), 0, ++rowIndex, 12, 1);
+        add(new Separator(), 0, ++rowIndex, 12, 1);
+        add(new Rectangle(getWidth(), SQUARE_SIZE), 0, ++rowIndex, 12, 1);
+        Button addColorButton = new Button("Add Color...");
+        addColorButton.setPrefWidth(SQUARE_SIZE*NUM_OF_COLUMNS);
+        add(addColorButton, 0, ++rowIndex, 12, 1);
+        add(new Rectangle(getWidth(), SQUARE_SIZE), 0, ++rowIndex, 12, 1);
         
-        setColor(initPaint);
+    setColor(initPaint);
+        
+        addColorButton.setOnAction(new EventHandler<ActionEvent>() {
+            @Override
+            public void handle(ActionEvent t) {
+                ColorPickerAddColorPane addColorDialog = new ColorPickerAddColorPane(owner);
+                addColorDialog.show();
+            }
+        });
+        
     }
     
     private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.RED) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/Pagination.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/Pagination.java	Mon Apr 09 13:05:50 2012 -0700
@@ -112,6 +112,9 @@
 
     private IntegerProperty numberOfVisiblePages;
     public final void setNumberOfVisiblePages(int value) {
+        if (value < 1) {
+            value = 1;
+        }
         numberOfVisiblePagesProperty().set(value);
     }
 
@@ -123,7 +126,12 @@
      * The max number of items per page
      */
     public final IntegerProperty itemsPerPage = new SimpleIntegerProperty(this, "itemsPerPage", 10);
-    public final void setItemsPerPage(int value) { itemsPerPage.set(value); }
+    public final void setItemsPerPage(int value) { 
+        if (value < 1) {
+            value = 1;
+        }        
+        itemsPerPage.set(value); 
+    }
     public final int getItemsPerPage() { return itemsPerPage.get(); }
     public final IntegerProperty itemsPerPageProperty() { return itemsPerPage; }
 
@@ -131,7 +139,7 @@
      * The total number of items
      */
     public final IntegerProperty numberOfItems = new SimpleIntegerProperty(this, "numberOfItems", 1);
-    private final void setNumberOfItems(int value) { numberOfItems.set(value); }
+    public final void setNumberOfItems(int value) { numberOfItems.set(value); }
     public final int getNumberOfItems() { return numberOfItems.get(); }
     public final IntegerProperty numberOfItemsProperty() { return numberOfItems; }
 
@@ -139,7 +147,12 @@
      * The current page index
      */
     public final IntegerProperty pageIndex = new SimpleIntegerProperty(this, "pageIndex", 0);
-    public final void setPageIndex(int value) { pageIndex.set(value); }
+    public final void setPageIndex(int value) { 
+        if (value < 0) {
+            value = 0;
+        }                
+        pageIndex.set(value); 
+    }
     public final int getPageIndex() { return pageIndex.get(); }
     public final IntegerProperty pageIndexProperty() { return pageIndex; }
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/PaginationCell.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/PaginationCell.java	Mon Apr 09 13:05:50 2012 -0700
@@ -49,6 +49,7 @@
 public class PaginationCell<T> extends ListCell<T> {
 
     public PaginationCell() {
-        getStyleClass().addAll("pagination-cell");
+        // We use setAll because we dont want any of ListCell's styles.
+        getStyleClass().setAll("pagination-cell");
     }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -134,9 +134,23 @@
         if (PlatformUtil.isMac()) {
             LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(UP, "FocusPreviousRow").vertical().meta());
             LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(DOWN, "FocusNextRow").vertical().meta());
+            
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(UP, "DiscontinuousSelectPreviousRow").vertical().meta().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(DOWN, "DiscontinuousSelectNextRow").vertical().meta().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").vertical().meta().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").vertical().meta().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").vertical().meta().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(END, "DiscontinuousSelectAllToLastRow").vertical().meta().shift());
         } else {
             LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(UP, "FocusPreviousRow").vertical().ctrl());
             LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(DOWN, "FocusNextRow").vertical().ctrl());
+            
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(UP, "DiscontinuousSelectPreviousRow").vertical().ctrl().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(DOWN, "DiscontinuousSelectNextRow").vertical().ctrl().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").vertical().ctrl().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").vertical().ctrl().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").vertical().ctrl().shift());
+            LIST_VIEW_BINDINGS.add(new ListViewKeyBinding(END, "DiscontinuousSelectAllToLastRow").vertical().ctrl().shift());
         }
         // --- end of vertical
 
@@ -200,6 +214,12 @@
         else if ("FocusLastRow".equals(name)) focusLastRow();
         else if ("toggleFocusOwnerSelection".equals(name)) toggleFocusOwnerSelection();
         else if ("SelectAllToFocus".equals(name)) selectAllToFocus();
+        else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow();
+        else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow();
+        else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp();
+        else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown();
+        else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow();
+        else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow();
         else super.callAction(name);
     }
 
@@ -224,8 +244,8 @@
     private boolean isShiftDown = false;
     private boolean isCtrlDown = false;
     
-    private Callback<Void, Integer> onScrollPageUp;
-    private Callback<Void, Integer> onScrollPageDown;
+    private Callback<Integer, Integer> onScrollPageUp;
+    private Callback<Integer, Integer> onScrollPageDown;
     private Runnable onFocusPreviousRow;
     private Runnable onFocusNextRow;
     private Runnable onSelectPreviousRow;
@@ -233,8 +253,8 @@
     private Runnable onMoveToFirstCell;
     private Runnable onMoveToLastCell;
 
-    public void setOnScrollPageUp(Callback<Void, Integer> c) { onScrollPageUp = c; }
-    public void setOnScrollPageDown(Callback<Void, Integer> c) { onScrollPageDown = c; }
+    public void setOnScrollPageUp(Callback<Integer, Integer> c) { onScrollPageUp = c; }
+    public void setOnScrollPageDown(Callback<Integer, Integer> c) { onScrollPageDown = c; }
     public void setOnFocusPreviousRow(Runnable r) { onFocusPreviousRow = r; }
     public void setOnFocusNextRow(Runnable r) { onFocusNextRow = r; }
     public void setOnSelectPreviousRow(Runnable r) { onSelectPreviousRow = r; }
@@ -359,7 +379,7 @@
     private void scrollPageUp() {
         int newSelectedIndex = -1;
         if (onScrollPageUp != null) {
-            newSelectedIndex = onScrollPageUp.call(null);
+            newSelectedIndex = onScrollPageUp.call(getAnchor());
         }
         if (newSelectedIndex == -1) return;
         
@@ -371,7 +391,7 @@
     private void scrollPageDown() {
         int newSelectedIndex = -1;
         if (onScrollPageDown != null) {
-            newSelectedIndex = onScrollPageDown.call(null);
+            newSelectedIndex = onScrollPageDown.call(getAnchor());
         }
         if (newSelectedIndex == -1) return;
         
@@ -429,7 +449,7 @@
     }
     
     private void focusPageUp() {
-        int newFocusIndex = onScrollPageUp.call(null);
+        int newFocusIndex = onScrollPageUp.call(getAnchor());
         
         FocusModel fm = getControl().getFocusModel();
         if (fm == null) return;
@@ -437,7 +457,7 @@
     }
     
     private void focusPageDown() {
-        int newFocusIndex = onScrollPageDown.call(null);
+        int newFocusIndex = onScrollPageDown.call(getAnchor());
         
         FocusModel fm = getControl().getFocusModel();
         if (fm == null) return;
@@ -574,7 +594,7 @@
             setAnchor(leadIndex);
         }
         
-        int leadSelectedIndex = onScrollPageUp.call(null);
+        int leadSelectedIndex = onScrollPageUp.call(getAnchor());
         
         MultipleSelectionModel sm = getControl().getSelectionModel();
         if (sm == null) return;
@@ -595,7 +615,7 @@
             setAnchor(leadIndex);
         }
         
-        int leadSelectedIndex = onScrollPageDown.call(null);
+        int leadSelectedIndex = onScrollPageDown.call(getAnchor());
         
         MultipleSelectionModel sm = getControl().getSelectionModel();
         if (sm == null) return;
@@ -702,6 +722,83 @@
         
         setAnchor(focusedIndex);
     }
+    
+    /**************************************************************************
+     * Discontinuous Selection                                                *
+     *************************************************************************/
+    
+    private void discontinuousSelectPreviousRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int index = fm.getFocusedIndex() - 1;
+        if (index < 0) return;
+        sm.select(index);
+    }
+    
+    private void discontinuousSelectNextRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        sm.select(index);
+    }
+    
+    private void discontinuousSelectPageUp() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageUp.call(getAnchor());
+        sm.selectRange(leadSelectedIndex, leadIndex + 1);
+    }
+    
+    private void discontinuousSelectPageDown() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageDown.call(getAnchor());
+        sm.selectRange(leadIndex, leadSelectedIndex + 1);
+    }
+    
+    private void discontinuousSelectAllToFirstRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex();
+        sm.selectRange(0, index);
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+    
+    private void discontinuousSelectAllToLastRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        sm.selectRange(index, getRowCount());
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }
 
     private static class ListViewKeyBinding extends OrientedKeyBinding {
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PaginationBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PaginationBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -45,8 +45,10 @@
 package com.sun.javafx.scene.control.behavior;
 
 import com.sun.javafx.scene.control.Pagination;
+import com.sun.javafx.scene.control.skin.PaginationSkin;
 import java.util.ArrayList;
 import java.util.List;
+import javafx.scene.input.KeyCode;
 import javafx.scene.input.MouseEvent;
 
 public class PaginationBehavior<T> extends BehaviorBase<Pagination<T>> {
@@ -54,9 +56,13 @@
     /**************************************************************************
      *                          Setup KeyBindings                             *
      *************************************************************************/
+    private static final String LEFT = "Left";
+    private static final String RIGHT = "Right";
 
     protected static final List<KeyBinding> PAGINATION_BINDINGS = new ArrayList<KeyBinding>();
     static {
+        PAGINATION_BINDINGS.add(new KeyBinding(KeyCode.LEFT, LEFT));
+        PAGINATION_BINDINGS.add(new KeyBinding(KeyCode.RIGHT, RIGHT));
         PAGINATION_BINDINGS.addAll(TRAVERSAL_BINDINGS);
     }
 
@@ -64,6 +70,18 @@
         return PAGINATION_BINDINGS;
     }
 
+    @Override protected void callAction(String name) {
+        if (LEFT.equals(name)) {
+            PaginationSkin ps = (PaginationSkin)getControl().getSkin();
+            ps.getSelectionModel().selectPrevious();
+        } else if (RIGHT.equals(name)) {
+            PaginationSkin ps = (PaginationSkin)getControl().getSkin();
+            ps.getSelectionModel().selectNext();
+        } else {
+            super.callAction(name);
+        }
+    }
+
     /***************************************************************************
      *                                                                         *
      * Mouse event handling                                                    *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PasswordFieldBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.scene.control.behavior;
+
+import javafx.scene.control.PasswordField;
+
+import com.sun.javafx.scene.text.HitInfo;
+
+
+/**
+ * Password field behavior.
+ */
+public class PasswordFieldBehavior extends TextFieldBehavior {
+
+    public PasswordFieldBehavior(PasswordField passwordField) {
+        super(passwordField);
+    }
+
+    // RT-18711 & RT-18854: Stub out word based navigation and editing
+    // for security reasons.
+    protected void deletePreviousWord() { }
+    protected void deleteNextWord() { }
+    protected void selectPreviousWord() { }
+    protected void selectNextWord() { }
+    protected void previousWord() { }
+    protected void nextWord() { }
+    protected void mouseDoubleClick(HitInfo hit) {
+        getControl().selectAll();
+    }
+
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -60,6 +60,7 @@
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.ListChangeListener;
+import javafx.scene.control.*;
 import javafx.util.Callback;
 
 public class TableViewBehavior<T> extends BehaviorBase<TableView<T>> {
@@ -131,6 +132,15 @@
             TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl().meta());
             TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").meta());
             TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").meta());
+            
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").meta().shift());
         } else {
             TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").ctrl());
             TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").ctrl());
@@ -144,6 +154,15 @@
             TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl());
             TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").ctrl());
             TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").ctrl());
+            
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").ctrl().shift());
         }
 
         TABLE_VIEW_BINDINGS.add(new KeyBinding(ENTER, "Activate"));
@@ -191,6 +210,14 @@
         else if ("SelectAllToFocus".equals(name)) selectAllToFocus();
         else if ("FocusPageUp".equals(name)) focusPageUp();
         else if ("FocusPageDown".equals(name)) focusPageDown();
+        else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow();
+        else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow();
+        else if ("DiscontinuousSelectNextColumn".equals(name)) discontinuousSelectNextColumn();
+        else if ("DiscontinuousSelectPreviousColumn".equals(name)) discontinuousSelectPreviousColumn();
+        else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp();
+        else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown();
+        else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow();
+        else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow();
         else super.callAction(name);
     }
 
@@ -998,4 +1025,134 @@
         sm.clearAndSelect(focusedCell.getRow(), endColumn);
     }
      */
+    
+    
+    /**************************************************************************
+     * Discontinuous Selection                                                *
+     *************************************************************************/
+    
+    private void discontinuousSelectPreviousRow() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int index = fm.getFocusedIndex() - 1;
+        if (index < 0) return;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.select(index);
+        } else {
+            sm.select(index, fm.getFocusedCell().getTableColumn());
+        }
+    }
+    
+    private void discontinuousSelectNextRow() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.select(index);
+        } else {
+            sm.select(index, fm.getFocusedCell().getTableColumn());
+        }
+    }
+    
+    private void discontinuousSelectPreviousColumn() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        TableColumn tc = getColumn(fm.getFocusedCell().getTableColumn(), -1);
+        sm.select(fm.getFocusedIndex(), tc);
+    }
+    
+    private void discontinuousSelectNextColumn() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        TableColumn tc = getColumn(fm.getFocusedCell().getTableColumn(), 1);
+        sm.select(fm.getFocusedIndex(), tc);
+    }
+    
+    private void discontinuousSelectPageUp() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageUp.call(null);
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(leadSelectedIndex, leadIndex + 1);
+        }
+    }
+    
+    private void discontinuousSelectPageDown() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageDown.call(null);
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(leadIndex, leadSelectedIndex + 1);
+        }
+    }
+    
+    private void discontinuousSelectAllToFirstRow() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex();
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(0, index);
+        } else {
+            for (int i = 0; i < index; i++) {
+                sm.select(i, fm.getFocusedCell().getTableColumn());
+            }
+        }
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+    
+    private void discontinuousSelectAllToLastRow() {
+        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        TableViewFocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(index, getItemCount());
+        } else {
+            for (int i = index; i < getItemCount(); i++) {
+                sm.select(i, fm.getFocusedCell().getTableColumn());
+            }
+        }
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }   
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextAreaBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -39,6 +39,8 @@
 import com.sun.javafx.scene.text.HitInfo;
 import static javafx.scene.input.KeyCode.*;
 import static javafx.scene.input.KeyEvent.*;
+import static com.sun.javafx.PlatformUtil.*;
+
 
 /**
  * Text area behavior.
@@ -71,7 +73,7 @@
         TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_UP, KEY_PRESSED, "SelectPreviousPage").shift()); // new
         TEXT_AREA_BINDINGS.add(new KeyBinding(PAGE_DOWN, KEY_PRESSED, "SelectNextPage").shift()); // new
         // Platform specific settings
-        if (PlatformUtil.isMac()) {
+        if (isMac()) {
             TEXT_AREA_BINDINGS.add(new KeyBinding(LEFT, KEY_PRESSED, "LineStart").meta()); // changed
             TEXT_AREA_BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED, "LineStart").meta()); // changed
             TEXT_AREA_BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED, "LineEnd").meta()); // changed
@@ -283,7 +285,7 @@
                     // selection, and set the mark to be the other side and
                     // the dot to be the new position.
                     // everywhere else we just move the dot.
-                    if(macOS) {
+                    if (isMac()) {
                         textArea.extendSelection(i);
                     } else {
                         skin.positionCaret(hit, true, false);
@@ -332,7 +334,11 @@
     protected void mouseDoubleClick(HitInfo hit) {
         final TextArea textArea = getControl();
         textArea.previousWord();
-        textArea.selectNextWord();
+        if (isWindows()) {
+            textArea.selectNextWord();
+        } else {
+            textArea.selectEndOfNextWord();
+        }
     }
 
     protected void mouseTripleClick(HitInfo hit) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextFieldBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -42,6 +42,8 @@
 import com.sun.javafx.scene.control.skin.TextFieldSkin;
 import com.sun.javafx.scene.text.HitInfo;
 
+import static com.sun.javafx.PlatformUtil.*;
+
 /**
  * Text field behavior.
  */
@@ -216,7 +218,7 @@
                     // selection, and set the mark to be the other side and
                     // the dot to be the new position.
                     // everywhere else we just move the dot.
-                    if(macOS) {
+                    if (isMac()) {
                         textField.extendSelection(i);
                     } else {
                         skin.positionCaret(hit, true);
@@ -289,7 +291,11 @@
     protected void mouseDoubleClick(HitInfo hit) {
         final TextField textField = getControl();
         textField.previousWord();
-        textField.selectNextWord();
+        if (isWindows()) {
+            textField.selectNextWord();
+        } else {
+            textField.selectEndOfNextWord();
+        }
     }
 
     protected void mouseTripleClick(HitInfo hit) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextInputControlBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextInputControlBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -25,17 +25,20 @@
 
 package com.sun.javafx.scene.control.behavior;
 
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
 import javafx.scene.control.IndexRange;
 import javafx.scene.control.TextInputControl;
 import javafx.scene.input.KeyEvent;
 import java.util.ArrayList;
 import java.util.List;
 
-import com.sun.javafx.PlatformUtil;
 import com.sun.javafx.scene.control.skin.SkinBase;
 
 import static javafx.scene.input.KeyEvent.KEY_PRESSED;
 
+import static com.sun.javafx.PlatformUtil.*;
+
 /**
  * Abstract base class for text input behaviors.
  */
@@ -61,10 +64,7 @@
      */
     private KeyEvent lastEvent;
 
-    /**
-     * Simple flag to know if we are on mac OS
-     */
-    protected boolean macOS = PlatformUtil.isMac();
+    private UndoManager undoManager = new UndoManager();
 
     /**************************************************************************
      * Constructors                                                           *
@@ -76,6 +76,15 @@
      */
     public TextInputControlBehavior(T textInputControl) {
         super(textInputControl);
+
+        textInputControl.textProperty().addListener(new InvalidationListener() {
+            @Override public void invalidated(Observable observable) {
+                if (!isEditing()) {
+                    // Text changed, but not by user action
+                    undoManager.reset();
+                }
+            }
+        });
     }
 
     /**************************************************************************
@@ -112,21 +121,28 @@
         TextInputControl textInputControl = getControl();
         if (textInputControl.isEditable()) {
             setCaretAnimating(false);
+            setEditing(true);
+            final IndexRange selection = textInputControl.getSelection();
+            final int start = selection.getStart();
+            final int end = selection.getEnd();
+
             if ("InputCharacter".equals(name)) defaultKeyTyped(lastEvent);
-            else if ("Cut".equals(name)) textInputControl.cut();
+            else if ("Cut".equals(name)) cut();
             else if ("Copy".equals(name)) textInputControl.copy();
-            else if ("Paste".equals(name)) textInputControl.paste();
+            else if ("Paste".equals(name)) paste();
             else if ("SelectBackward".equals(name)) textInputControl.selectBackward();
             else if ("SelectForward".equals(name)) textInputControl.selectForward();
-            else if ("PreviousWord".equals(name)) textInputControl.previousWord();
+            else if ("PreviousWord".equals(name)) previousWord();
             else if ("NextWord".equals(name)) nextWord();
-            else if ("SelectPreviousWord".equals(name)) textInputControl.selectPreviousWord();
+            else if ("SelectPreviousWord".equals(name)) selectPreviousWord();
             else if ("SelectNextWord".equals(name)) selectNextWord();
             else if ("SelectAll".equals(name)) textInputControl.selectAll();
             else if ("Home".equals(name)) textInputControl.home();
             else if ("End".equals(name)) textInputControl.end();
             else if ("DeletePreviousChar".equals(name)) deletePreviousChar();
             else if ("DeleteNextChar".equals(name)) deleteNextChar();
+            else if ("DeletePreviousWord".equals(name)) deletePreviousWord();
+            else if ("DeleteNextWord".equals(name)) deleteNextWord();
             else if ("DeleteSelection".equals(name)) deleteSelection();
             else if ("Forward".equals(name)) textInputControl.forward();
             else if ("Backward".equals(name)) textInputControl.backward();
@@ -136,8 +152,14 @@
             else if ("SelectEnd".equals(name)) selectEnd();
             else if ("SelectHomeExtend".equals(name)) selectHomeExtend();
             else if ("SelectEndExtend".equals(name)) selectEndExtend();
+            else if ("Undo".equals(name)) undoManager.undo();
+            else if ("Redo".equals(name)) undoManager.redo();
             /*DEBUG*/else if ("UseVK".equals(name)) ((com.sun.javafx.scene.control.skin.TextInputControlSkin)textInputControl.getSkin()).toggleUseVK();
-            else super.callAction(name);
+            else {
+                setEditing(false);
+                super.callAction(name);
+            }
+            setEditing(false);
             setCaretAnimating(true);
         } else if ("Copy".equals(name)) {
             // If the key event is for the "copy" action then we go ahead
@@ -170,8 +192,8 @@
         if (character.length() == 0) return;
 
         // Filter out control keys except control+Alt on PC or Alt on Mac
-        if (event.isControlDown() || event.isAltDown() || (PlatformUtil.isMac() && event.isMetaDown())) {
-            if (!((event.isControlDown() || PlatformUtil.isMac()) && event.isAltDown())) return;
+        if (event.isControlDown() || event.isAltDown() || (isMac() && event.isMetaDown())) {
+            if (!((event.isControlDown() || isMac()) && event.isAltDown())) return;
         }
 
         // Ignore characters in the control range and the ASCII delete
@@ -187,6 +209,7 @@
 //                + character.length() > textInput.getMaximumLength()) {
 //                // TODO Beep?
 //            } else {
+                undoManager.addChange(start, textInput.getText().substring(start, end), character, true);
                 replaceText(start, end, character);
 //            }
 
@@ -195,31 +218,117 @@
     }
 
     private void deletePreviousChar() {
+        TextInputControl textInputControl = getControl();
+        IndexRange selection = textInputControl.getSelection();
+        int start = selection.getStart();
+        int end = selection.getEnd();
+
+        if (start > 0 || end > start) {
+            if (selection.getLength() == 0) {
+                end = start;
+                start--;
+            }
+            undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
+        }
         deleteChar(true);
     }
 
     private void deleteNextChar() {
+        TextInputControl textInputControl = getControl();
+        IndexRange selection = textInputControl.getSelection();
+        int start = selection.getStart();
+        int end = selection.getEnd();
+
+        if (start < textInputControl.getLength() || end > start) {
+            if (selection.getLength() == 0) {
+                end = start + 1;
+            }
+            undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
+        }
         deleteChar(false);
     }
 
+    protected void deletePreviousWord() {
+        TextInputControl textInputControl = getControl();
+        int end = textInputControl.getCaretPosition();
+
+        if (end > 0) {
+            textInputControl.previousWord();
+            int start = textInputControl.getCaretPosition();
+            undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
+            replaceText(start, end, "");
+        }
+    }
+
+    protected void deleteNextWord() {
+        TextInputControl textInputControl = getControl();
+        int start = textInputControl.getCaretPosition();
+
+        if (start < textInputControl.getLength()) {
+            nextWord();
+            int end = textInputControl.getCaretPosition();
+            undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
+            replaceText(start, end, "");
+        }
+    }
+
     private void deleteSelection() {
-        if (getControl().getSelection().getLength() > 0) {
+        TextInputControl textInputControl = getControl();
+        IndexRange selection = textInputControl.getSelection();
+
+        if (selection.getLength() > 0) {
+            int start = selection.getStart();
+            int end = selection.getEnd();
+            undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
             deleteChar(false);
         }
     }
 
-    private void selectNextWord() {
+    private void cut() {
         TextInputControl textInputControl = getControl();
-        if (macOS) {
+        IndexRange selection = textInputControl.getSelection();
+        int start = selection.getStart();
+        int end = selection.getEnd();
+
+        undoManager.addChange(start, textInputControl.getText().substring(start, end), null);
+        textInputControl.cut();
+    }
+
+    private void paste() {
+        TextInputControl textInputControl = getControl();
+        IndexRange selection = textInputControl.getSelection();
+        int start = selection.getStart();
+        int end = selection.getEnd();
+        String text = textInputControl.getText();
+        String deleted = text.substring(start, end);
+        int tail = text.length() - end;
+
+        textInputControl.paste();
+
+        text = textInputControl.getText();
+        undoManager.addChange(start, deleted, text.substring(start, text.length() - tail));
+    }
+
+    protected void selectPreviousWord() {
+        getControl().selectPreviousWord();
+    }
+
+    protected void selectNextWord() {
+        TextInputControl textInputControl = getControl();
+        if (isMac() || isLinux()) {
             textInputControl.selectEndOfNextWord();
         } else {
             textInputControl.selectNextWord();
         }
     }
 
-    private void nextWord() {
+    protected void previousWord() {
+        getControl().previousWord();
+    }
+
+    protected void nextWord() {
         TextInputControl textInputControl = getControl();
-        if (macOS) {
+        if (isMac() || isLinux()) {
             textInputControl.endOfNextWord();
         } else {
             textInputControl.nextWord();
@@ -244,4 +353,112 @@
         TextInputControl textInputControl = getControl();
         textInputControl.extendSelection(textInputControl.getLength());
     }
+
+    private boolean editing = false;
+    protected void setEditing(boolean b) {
+        editing = b;
+    }
+    public boolean isEditing() {
+        return editing;
+    }
+
+    public boolean canUndo() {
+        return undoManager.canUndo();
+    }
+
+    public boolean canRedo() {
+        return undoManager.canRedo();
+    }
+
+    static class Change {
+        int start;
+        String oldText;
+        String newText;
+        boolean appendable;
+
+        Change(int start, String oldText, String newText) {
+            this(start, oldText, newText, false);
+        }
+
+        Change(int start, String oldText, String newText, boolean appendable) {
+            this.start = start;
+            this.oldText = oldText;
+            this.newText = newText;
+            this.appendable = appendable;
+        }
+    }
+
+    class UndoManager {
+        private ArrayList<Change> chain = new ArrayList<Change>();
+        private int currentIndex = 0;
+
+        public void addChange(int start, String oldText, String newText) {
+            addChange(start, oldText, newText, false);
+        }
+
+        public void addChange(int start, String oldText, String newText, boolean appendable) {
+            truncate();
+            if (appendable && currentIndex > 0 && (oldText == null || oldText.length() == 0)) {
+                Change change = chain.get(currentIndex - 1);
+                if (change.appendable && start == change.start + change.newText.length()) {
+                    // Append text to previous Change
+                    change.newText += newText;
+                    return;
+                }
+            }
+            chain.add(new Change(start, oldText, newText, appendable));
+            currentIndex++;
+        }
+
+        public void undo() {
+            if (currentIndex > 0) {
+                // Apply reverse change here
+                Change change = chain.get(currentIndex - 1);
+                replaceText(change.start,
+                            change.start + ((change.newText != null) ? change.newText.length() : 0),
+                            (change.oldText != null) ? change.oldText : "");
+                currentIndex--;
+                if (currentIndex > 0) {
+                    chain.get(currentIndex - 1).appendable = false;
+                }
+            }
+            // else beep ?
+        }
+
+        public void redo() {
+            if (currentIndex < chain.size()) {
+                // Apply change here
+                Change change = chain.get(currentIndex);
+                replaceText(change.start,
+                            change.start + ((change.oldText != null) ? change.oldText.length() : 0),
+                            (change.newText != null) ? change.newText : "");
+                change.appendable = false;
+                currentIndex++;
+            }
+            // else beep ?
+        }
+
+        public boolean canUndo() {
+            return (currentIndex > 0);
+        }
+
+        public boolean canRedo() {
+            return (currentIndex < chain.size());
+        }
+
+        public void reset() {
+            chain.clear();
+            currentIndex = 0;
+        }
+
+        private void truncate() {
+            if (currentIndex > 0 && chain.size() > currentIndex) {
+                chain.get(currentIndex - 1).appendable = false;
+            }
+
+            while (chain.size() > currentIndex) {
+                chain.remove(chain.size() - 1);
+            }
+        }
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextInputControlBindings.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TextInputControlBindings.java	Mon Apr 09 13:05:50 2012 -0700
@@ -86,6 +86,8 @@
             BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED,    "PreviousWord").alt());
             BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED,      "NextWord").alt());
             BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED,   "NextWord").alt());
+            BINDINGS.add(new KeyBinding(DELETE, KEY_PRESSED,     "DeleteNextWord").meta());
+            BINDINGS.add(new KeyBinding(BACK_SPACE, KEY_PRESSED, "DeletePreviousWord").meta());
             BINDINGS.add(new KeyBinding(X, KEY_PRESSED,          "Cut").meta());
             BINDINGS.add(new KeyBinding(C, KEY_PRESSED,          "Copy").meta());
             BINDINGS.add(new KeyBinding(INSERT, KEY_PRESSED,     "Copy").meta());
@@ -101,6 +103,8 @@
             BINDINGS.add(new KeyBinding(KP_LEFT, KEY_PRESSED,    "SelectPreviousWord").shift().alt());
             BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED,      "SelectNextWord").shift().alt());
             BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED,   "SelectNextWord").shift().alt());
+            BINDINGS.add(new KeyBinding(Z, KEY_PRESSED,          "Undo").meta());
+            BINDINGS.add(new KeyBinding(Z, KEY_PRESSED,          "Redo").shift().meta());
         } else {
             BINDINGS.add(new KeyBinding(HOME, KEY_PRESSED,       "SelectHome").shift());
             BINDINGS.add(new KeyBinding(END, KEY_PRESSED,        "SelectEnd").shift());
@@ -112,8 +116,8 @@
             BINDINGS.add(new KeyBinding(RIGHT, KEY_PRESSED,      "NextWord").ctrl());
             BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED,   "NextWord").ctrl());
             BINDINGS.add(new KeyBinding(H, KEY_PRESSED,          "DeletePreviousChar").ctrl());
-            BINDINGS.add(new KeyBinding(DELETE, KEY_PRESSED,     "DeleteNextChar").ctrl());
-            BINDINGS.add(new KeyBinding(BACK_SPACE, KEY_PRESSED, "DeleteNextChar").ctrl());
+            BINDINGS.add(new KeyBinding(DELETE, KEY_PRESSED,     "DeleteNextWord").ctrl());
+            BINDINGS.add(new KeyBinding(BACK_SPACE, KEY_PRESSED, "DeletePreviousWord").ctrl());
             BINDINGS.add(new KeyBinding(X, KEY_PRESSED,          "Cut").ctrl());
             BINDINGS.add(new KeyBinding(C, KEY_PRESSED,          "Copy").ctrl());
             BINDINGS.add(new KeyBinding(INSERT, KEY_PRESSED,     "Copy").ctrl());
@@ -126,6 +130,13 @@
             BINDINGS.add(new KeyBinding(KP_RIGHT, KEY_PRESSED,   "SelectNextWord").ctrl().shift());
             BINDINGS.add(new KeyBinding(A, KEY_PRESSED,          "SelectAll").ctrl());
             BINDINGS.add(new KeyBinding(BACK_SLASH, KEY_PRESSED, "Unselect").ctrl());
+            if (PlatformUtil.isLinux()) {
+                BINDINGS.add(new KeyBinding(Z, KEY_PRESSED,          "Undo").ctrl());
+                BINDINGS.add(new KeyBinding(Z, KEY_PRESSED,          "Redo").ctrl().shift());
+            } else {  // Windows
+                BINDINGS.add(new KeyBinding(Z, KEY_PRESSED,          "Undo").ctrl());
+                BINDINGS.add(new KeyBinding(Y, KEY_PRESSED,          "Redo").ctrl());
+            }
         }
         // Any other key press first goes to normal text input
         // Note this is KEY_TYPED because otherwise the character is not available in the event.
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Mon Apr 09 13:05:50 2012 -0700
@@ -79,6 +79,13 @@
             TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").meta());
             TREE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").meta());
             TREE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").meta());
+            
+            TREE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").meta().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").meta().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").meta().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").meta().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").meta().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").meta().shift());
         } else {
             TREE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").ctrl());
             TREE_VIEW_BINDINGS.add(new KeyBinding(BACK_SLASH, "ClearSelection").ctrl());
@@ -88,6 +95,13 @@
             TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").ctrl());
             TREE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").ctrl());
             TREE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").ctrl());
+            
+            TREE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").ctrl().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").ctrl().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").ctrl().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").ctrl().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").ctrl().shift());
+            TREE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").ctrl().shift());
         }
 
         TREE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "CollapseRow"));
@@ -143,6 +157,12 @@
         else if ("FocusPageDown".equals(name)) focusPageDown();
         else if ("FocusPreviousRow".equals(name)) focusPreviousRow();
         else if ("FocusNextRow".equals(name)) focusNextRow();
+        else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow();
+        else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow();
+        else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp();
+        else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown();
+        else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow();
+        else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow();
         else super.callAction(name);
     }
 
@@ -171,11 +191,11 @@
     // set when focus is moved by keyboard to allow for proper selection positions
 //    private int selectPos = -1;
     
-    private Callback<Void, Integer> onScrollPageUp;
-    public void setOnScrollPageUp(Callback<Void, Integer> c) { onScrollPageUp = c; }
+    private Callback<Integer, Integer> onScrollPageUp;
+    public void setOnScrollPageUp(Callback<Integer, Integer> c) { onScrollPageUp = c; }
 
-    private Callback<Void, Integer> onScrollPageDown;
-    public void setOnScrollPageDown(Callback<Void, Integer> c) { onScrollPageDown = c; }
+    private Callback<Integer, Integer> onScrollPageDown;
+    public void setOnScrollPageDown(Callback<Integer, Integer> c) { onScrollPageDown = c; }
 
     private Runnable onSelectPreviousRow;
     public void setOnSelectPreviousRow(Runnable r) { onSelectPreviousRow = r; }
@@ -279,7 +299,7 @@
     private void scrollUp() {
         int newSelectedIndex = -1;
         if (onScrollPageUp != null) {
-            newSelectedIndex = onScrollPageUp.call(null);
+            newSelectedIndex = onScrollPageUp.call(getAnchor());
         }
         if (newSelectedIndex == -1) return;
         
@@ -291,7 +311,7 @@
     private void scrollDown() {
         int newSelectedIndex = -1;
         if (onScrollPageDown != null) {
-            newSelectedIndex = onScrollPageDown.call(null);
+            newSelectedIndex = onScrollPageDown.call(getAnchor());
         }
         if (newSelectedIndex == -1) return;
         
@@ -349,7 +369,7 @@
     }
     
     private void focusPageUp() {
-        int newFocusIndex = onScrollPageUp.call(null);
+        int newFocusIndex = onScrollPageUp.call(getAnchor());
         
         FocusModel fm = getControl().getFocusModel();
         if (fm == null) return;
@@ -357,7 +377,7 @@
     }
     
     private void focusPageDown() {
-        int newFocusIndex = onScrollPageDown.call(null);
+        int newFocusIndex = onScrollPageDown.call(getAnchor());
         
         FocusModel fm = getControl().getFocusModel();
         if (fm == null) return;
@@ -537,7 +557,7 @@
             setAnchor(leadIndex);
         }
         
-        int leadSelectedIndex = onScrollPageUp.call(null);
+        int leadSelectedIndex = onScrollPageUp.call(getAnchor());
         
         MultipleSelectionModel sm = getControl().getSelectionModel();
         if (sm == null) return;
@@ -558,7 +578,7 @@
             setAnchor(leadIndex);
         }
         
-        int leadSelectedIndex = onScrollPageDown.call(null);
+        int leadSelectedIndex = onScrollPageDown.call(getAnchor());
         
         MultipleSelectionModel sm = getControl().getSelectionModel();
         if (sm == null) return;
@@ -684,4 +704,81 @@
         
         setAnchor(focusedIndex);
     }
+    
+    /**************************************************************************
+     * Discontinuous Selection                                                *
+     *************************************************************************/
+    
+    private void discontinuousSelectPreviousRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int index = fm.getFocusedIndex() - 1;
+        if (index < 0) return;
+        sm.select(index);
+    }
+    
+    private void discontinuousSelectNextRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        sm.select(index);
+    }
+    
+    private void discontinuousSelectPageUp() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageUp.call(getAnchor());
+        sm.selectRange(leadSelectedIndex, leadIndex + 1);
+    }
+    
+    private void discontinuousSelectPageDown() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+        
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageDown.call(getAnchor());
+        sm.selectRange(leadIndex, leadSelectedIndex + 1);
+    }
+    
+    private void discontinuousSelectAllToFirstRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex();
+        sm.selectRange(0, index);
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+    
+    private void discontinuousSelectAllToLastRow() {
+        MultipleSelectionModel sm = getControl().getSelectionModel();
+        if (sm == null) return;
+        
+        FocusModel fm = getControl().getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        sm.selectRange(index, getControl().impl_getTreeItemCount());
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ButtonSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ButtonSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -106,14 +106,6 @@
         }
     }
 
-    @Override protected double computePrefHeight(double width) {
-        double w = -1;
-        if (width != -1) {
-            w = Math.max(0, width - getInsets().getLeft() - getInsets().getRight());
-        }
-        return super.computePrefHeight(w);
-    }
-
     Runnable defaultButtonRunnable = new Runnable() {
             public void run() {
                 getSkinnable().fire();
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1,6 +1,26 @@
 /*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
  */
 package com.sun.javafx.scene.control.skin;
 
@@ -49,6 +69,7 @@
     
     public ColorPickerSkin(final ColorPicker<T> colorPicker) {
         super(colorPicker, new ColorPickerBehavior<T>(colorPicker));
+        popup.setOwner(colorPicker);
         initialize();
         if (arrowButton.getOnMouseReleased() == null) {
             colorPicker.setOnMouseReleased(new EventHandler<MouseEvent>() {
@@ -58,15 +79,16 @@
                 }
             });
         }
+        getPopup().setAutoHide(false);
     }
     
     private void initialize() {
         updateComboBoxMode();
-        popup.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
-            @Override public void handle(MouseEvent t) {
-                ((ColorPicker)getSkinnable()).hide();
-            }
-        });
+//        popup.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+//            @Override public void handle(MouseEvent t) {
+//                ((ColorPicker)getSkinnable()).hide();
+//            }
+//        });
         popup.colorProperty().addListener(new ChangeListener<Color>() {
             @Override public void changed(ObservableValue<? extends Color> ov, Color t, Color t1) {
                 setColor(t1);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxBaseSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxBaseSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -151,7 +151,7 @@
                 snapSpace(arrowButtonPadding.getRight()));
         
         if (displayNode != null) {
-            displayNode.resizeRelocate(x, y, w-arrowButtonWidth, h);
+            displayNode.resizeRelocate(x, y, w, h);
         }
         
         if (isButton()) return;
@@ -178,6 +178,13 @@
     @Override protected double computePrefHeight(double width) {
         if (displayNode == null) {
             updateDisplayArea();
+            
+            // RT-20575: The display node is being brought into the scenegraph
+            // early so we get the correct prefHeight, but at this point it
+            // may not have had a layout pass run over it itself, so the 
+            // displayNode will return a prefHeight of 0. Here we are forcing
+            // a one-off run of the layout over the displayNode.
+            displayNode.impl_processCSS(true);
         }
 
         double ph;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -214,7 +214,8 @@
         // externally for people listening to the focus property.
         textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
             @Override public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
-                comboBox.requestFocus();
+	        // RT-20657 focus ring gets stuck in a editable combobox
+                // comboBox.requestFocus();
             }
         });
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVK.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVK.java	Mon Apr 09 13:05:50 2012 -0700
@@ -27,6 +27,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 
 import javafx.application.Platform;
@@ -77,12 +78,24 @@
 
 
     private static FXVK vk;
+    static int vkType;
+    private static HashMap<Integer, FXVK> vkMap = new HashMap<Integer, FXVK>();
 
     public static void attach(final TextInputControl textInput) {
+        int type = textInput.getImpl_virtualKeyboardType();
+
+        if (vk != null && vkType != type) {
+            detach();
+        }
+
+        vkType = type;
+        vk = vkMap.get(vkType);
+
         if (vk == null) {
             vk = new FXVK();
 //             vk.impl_processCSS(true);
             vk.setSkin(new FXVKSkin(vk));
+            vkMap.put(vkType, vk);
         }
 
         vk.setAttachedNode(textInput);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVKCharEntities.java	Mon Apr 09 13:05:50 2012 -0700
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.scene.control.skin;
+
+import java.util.HashMap;
+
+/**
+ * Provides a mapping from HTML ISO-8859-1 character entity names to Unicode.
+ */
+class FXVKCharEntities  {
+    private static final HashMap<String, Character> map = new HashMap<String, Character>();
+
+    public static String get(String name) {
+        Character c = map.get(name);
+        if (c == null) {
+            if (name.length() != 1) {
+                //System.err.println("FXVKCharEntities: Unknown HTML entity name <"+name+">");
+            }
+            return name;
+        } else {
+            return ""+c;
+        }
+    }
+
+    private static void put(String name, int decimalChar) {
+        map.put(name, (char)decimalChar);
+    }
+
+    static {
+        put("space",     32); // Non-standard
+        put("quot",      34);
+        put("apos",      39);
+        put("amp",       38);
+        put("lt",        60);
+        put("gt",        62);
+        put("nbsp",     160);
+        put("iexcl",    161);
+        put("cent",     162);
+        put("pound",    163);
+        put("curren",   164);
+        put("yen",      165);
+        put("brvbar",   166);
+        put("sect",     167);
+        put("uml",      168);
+        put("copy",     169);
+        put("ordf",     170);
+        put("laquo",    171);
+        put("not",      172);
+        put("shy",      173);
+        put("reg",      174);
+        put("macr",     175);
+        put("deg",      176);
+        put("plusmn",   177);
+        put("sup2",     178);
+        put("sup3",     179);
+        put("acute",    180);
+        put("micro",    181);
+        put("para",     182);
+        put("middot",   183);
+        put("cedil",    184);
+        put("sup1",     185);
+        put("ordm",     186);
+        put("raquo",    187);
+        put("frac14",   188);
+        put("frac12",   189);
+        put("frac34",   190);
+        put("iquest",   191);
+        put("times",    215);
+        put("divide",   247);
+
+        put("Agrave",   192);
+        put("Aacute",   193);
+        put("Acirc",    194);
+        put("Atilde",   195);
+        put("Auml",     196);
+        put("Aring",    197);
+        put("AElig",    198);
+        put("Ccedil",   199);
+        put("Egrave",   200);
+        put("Eacute",   201);
+        put("Ecirc",    202);
+        put("Euml",     203);
+        put("Igrave",   204);
+        put("Iacute",   205);
+        put("Icirc",    206);
+        put("Iuml",     207);
+        put("ETH",      208);
+        put("Ntilde",   209);
+        put("Ograve",   210);
+        put("Oacute",   211);
+        put("Ocirc",    212);
+        put("Otilde",   213);
+        put("Ouml",     214);
+        put("Oslash",   216);
+        put("Ugrave",   217);
+        put("Uacute",   218);
+        put("Ucirc",    219);
+        put("Uuml",     220);
+        put("Yacute",   221);
+        put("THORN",    222);
+        put("szlig",    223);
+        put("agrave",   224);
+        put("aacute",   225);
+        put("acirc",    226);
+        put("atilde",   227);
+        put("auml",     228);
+        put("aring",    229);
+        put("aelig",    230);
+        put("ccedil",   231);
+        put("egrave",   232);
+        put("eacute",   233);
+        put("ecirc",    234);
+        put("euml",     235);
+        put("igrave",   236);
+        put("iacute",   237);
+        put("icirc",    238);
+        put("iuml",     239);
+        put("eth",      240);
+        put("ntilde",   241);
+        put("ograve",   242);
+        put("oacute",   243);
+        put("ocirc",    244);
+        put("otilde",   245);
+        put("ouml",     246);
+        put("oslash",   248);
+        put("ugrave",   249);
+        put("uacute",   250);
+        put("ucirc",    251);
+        put("uuml",     252);
+        put("yacute",   253);
+        put("thorn",    254);
+        put("yuml",     255);
+
+        put("scedil", 0x015f); // Non-standard
+        put("scaron", 0x0161); // Non-standard
+        put("ycirc",  0x0177); // Non-standard
+        put("ymacron",0x0233); // Non-standard
+        put("pi",     0x03c0); // Non-standard
+        put("sigma",  0x03c3); // Non-standard
+        put("ygrave", 0x1ef3); // Non-standard
+        put("yhook",  0x1ef7); // Non-standard
+        put("permil", 0x2030); // Non-standard
+        put("euro",   0x20ac); // Non-standard
+        put("tm",     0x2122); // Non-standard
+        put("neq",    0x2260); // Non-standard
+    }
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVKSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVKSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -35,15 +35,15 @@
 import javafx.application.Platform;
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
-import javafx.geometry.Insets;
-import javafx.geometry.Pos;
 import javafx.event.ActionEvent;
 import javafx.event.Event;
 import javafx.event.EventHandler;
 import javafx.event.EventTarget;
 import javafx.event.EventType;
 import javafx.geometry.HPos;
+import javafx.geometry.Insets;
 import javafx.geometry.Point2D;
+import javafx.geometry.Pos;
 import javafx.geometry.VPos;
 import javafx.scene.Node;
 import javafx.scene.Parent;
@@ -64,17 +64,20 @@
 import static javafx.scene.input.KeyCode.*;
 import static javafx.scene.input.MouseEvent.*;
 
+import static com.sun.javafx.scene.control.skin.resources.ControlResources.*;
+
 public class FXVKSkin extends SkinBase<FXVK, BehaviorBase<FXVK>> {
 
     private static Region oldRoot;
-    private static Pane newRoot;
+    private static NewRootPane newRoot;
     private static Popup secondaryPopup;
     private static FXVK primaryVK;
+
+    private Timeline slideInTimeline;
+    private Timeline slideOutTimeline;
+    private static Timeline slideRootTimeline;
+
     private static FXVK secondaryVK;
-    private static Popup vkPopup;
-    private static Timeline slideInTimeline;
-    private static Timeline slideOutTimeline;
-    private static Timeline slideRootTimeline;
     private static Timeline secondaryVKDelay;
     private static CharKey secondaryVKKey;
 
@@ -85,9 +88,8 @@
 
     enum State { NORMAL, SHIFTED, SHIFT_LOCK, NUMERIC; };
 
-    static State state = State.NORMAL;
+    private State state = State.NORMAL;
 
-    static final boolean USE_POPUP = false;
     static final double VK_WIDTH = 800;
     static final double VK_HEIGHT = 230;
     static final double VK_SLIDE_MILLIS = 250;
@@ -108,109 +110,67 @@
 
         fxvk.setFocusTraversable(false);
 
-        createKeys();
+        slideInTimeline = new Timeline();
+        slideOutTimeline = new Timeline();
 
         fxvk.attachedNodeProperty().addListener(new InvalidationListener() {
             @Override public void invalidated(Observable valueModel) {
                 Node oldNode = attachedNode;
                 attachedNode = fxvk.getAttachedNode();
 
-if (USE_POPUP) {
-                if (fxvk != secondaryVK && oldNode != null) {
-                    translatePane(oldNode.getScene().getRoot(), 0);
-                }
-} else {
                 if (fxvk != secondaryVK && oldRoot != null) {
                     translatePane(oldRoot, 0);
                 }
-}
 
                 if (attachedNode != null) {
+                    if (keyRows == null) {
+                        createKeys();
+                    }
+
                     final Scene scene = attachedNode.getScene();
+                    fxvk.setVisible(true);
 
-                    if (secondaryVKDelay == null) {
-                        secondaryVKDelay = new Timeline();
+                    if (fxvk != secondaryVK) {
+                        if (secondaryVKDelay == null) {
+                            secondaryVKDelay = new Timeline();
+                        }
                         KeyFrame kf = new KeyFrame(Duration.millis(500), new EventHandler<ActionEvent>() {
                             @Override public void handle(ActionEvent event) {
                                 if (secondaryVKKey != null) {
-                                    showSecondaryVK(secondaryVKKey, fxvk, state);
+                                    showSecondaryVK(secondaryVKKey);
                                 }
                             }
                         });
-                        secondaryVKDelay.getKeyFrames().add(kf);
-                    }
+                        secondaryVKDelay.getKeyFrames().setAll(kf);
 
-if (USE_POPUP) {
-                    if (vkPopup == null) {
-                        vkPopup = new Popup();
-                        vkPopup.getContent().add(fxvk);
-                    }
 
-                    if (vkPopup.isShowing()) {
-                        Point2D nodePoint =
-                            com.sun.javafx.Utils.pointRelativeTo(attachedNode,
-                                                                 vkPopup.getWidth(), vkPopup.getHeight(),
-                                                                 HPos.CENTER, VPos.BOTTOM, 0, 2, true);
-                        Point2D point =
-                            com.sun.javafx.Utils.pointRelativeTo(scene.getRoot(),
-                                                                 vkPopup.getWidth(), vkPopup.getHeight(),
-                                                                 HPos.CENTER, VPos.BOTTOM, 0, 0, true);
-                        double y = point.getY() - fxvk.prefHeight(-1);
-                        double nodeBottom = nodePoint.getY();
-                        if (y < nodeBottom) {
-                            translatePane(scene.getRoot(), y - nodeBottom);
+                        if (newRoot == null) {
+                            oldRoot = (Region)scene.getRoot();
+                            newRoot = new NewRootPane(oldRoot);
+                            scene.setRoot(newRoot);
                         }
-                    } else {
-                        Platform.runLater(new Runnable() {
-                            public void run() {
-                                Point2D nodePoint =
-                                    com.sun.javafx.Utils.pointRelativeTo(attachedNode,
-                                                                         vkPopup.getWidth(), vkPopup.getHeight(),
-                                                                         HPos.CENTER, VPos.BOTTOM, 0, 2, true);
-                                Point2D point =
-                                    com.sun.javafx.Utils.pointRelativeTo(scene.getRoot(),
-                                                                         vkPopup.getWidth(), vkPopup.getHeight(),
-                                                                         HPos.CENTER, VPos.BOTTOM, 0, 0, true);
-                                double y = point.getY() - fxvk.prefHeight(-1);
-                                vkPopup.show(attachedNode, point.getX(), y);
 
+                        if (!newRoot.getChildren().contains(fxvk)){
+                            newRoot.getChildren().add(fxvk);
+                        }
 
-                                double nodeBottom = nodePoint.getY();
-                                if (y < nodeBottom) {
-                                    translatePane(scene.getRoot(), y - nodeBottom);
-                                }
-                            }
-                        });
-                    }
+                        newRoot.slideInTimeline = slideInTimeline;
+                        newRoot.slideOutTimeline = slideOutTimeline;
+                        newRoot.updateTimelines(fxvk);
 
-                    if (oldNode == null || oldNode.getScene() != attachedNode.getScene()) {
-                        fxvk.setPrefWidth(scene.getWidth());
-                        fxvk.setMaxWidth(USE_PREF_SIZE);
-                        fxvk.setPrefHeight(200);
-                    }
-} else {
-                    if (newRoot == null) {
-                        oldRoot = (Region)scene.getRoot();
-                        newRoot = new NewRootPane(oldRoot);
-                        scene.setRoot(newRoot);
-                        newRoot.getChildren().add(fxvk);
-                        slideInTimeline = new Timeline();
-                    }
+                        if (fxvk.getHeight() > 0 &&
+                            (fxvk.getLayoutY() == 0 || fxvk.getLayoutY() > scene.getHeight() - fxvk.getHeight())) {
 
-                    fxvk.setVisible(true);
-                    if (fxvk != secondaryVK && fxvk.getHeight() > 0 &&
-                        (fxvk.getLayoutY() == 0 || fxvk.getLayoutY() > scene.getHeight() - fxvk.getHeight())) {
+                            slideOutTimeline.stop();
+                            slideInTimeline.playFromStart();
+                        }
 
-                        slideOutTimeline.stop();
-                        slideInTimeline.playFromStart();
-                    }
 
-                    if (fxvk != secondaryVK) {
                         Platform.runLater(new Runnable() {
                             public void run() {
                                 double nodeBottom =
                                     attachedNode.localToScene(attachedNode.getBoundsInLocal()).getMaxY() + 2;
-                                if (nodeBottom > fxvk.getLayoutY()) {
+                                if (fxvk.getLayoutY() > 0 && nodeBottom > fxvk.getLayoutY()) {
                                     translatePane(oldRoot, fxvk.getLayoutY() - nodeBottom);
                                 }
                             }
@@ -223,18 +183,12 @@
                             fxvk.setMinHeight(USE_PREF_SIZE);
                         }
                     }
-}
                 } else {
-if (USE_POPUP) {
-                    if (vkPopup != null) {
-                        vkPopup.hide();
-                    }
-} else {
                     if (fxvk != secondaryVK) {
                         slideInTimeline.stop();
                         slideOutTimeline.playFromStart();
                     }
-}
+
                     if (secondaryVK != null) {
                         secondaryVK.setAttachedNode(null);
                         secondaryPopup.hide();
@@ -245,7 +199,7 @@
         });
     }
 
-    private void translatePane(Parent pane, double y) {
+    private static void translatePane(Parent pane, double y) {
         if (slideRootTimeline == null) {
             slideRootTimeline = new Timeline();
         } else {
@@ -264,51 +218,52 @@
         if (fxvk.chars != null) {
             // Secondary popup
             int nKeys = fxvk.chars.length;
-            int nRows = (int)Math.floor(Math.sqrt(Math.max(1, nKeys - 2)));
-            int nKeysPerRow = (int)Math.ceil(nKeys / (double)nRows);
-            keyRows = new Control[nRows][];
-            for (int i = 0; i < nRows; i++) {
-                keyRows[i] =
-                    makeKeyRow((Object[])Arrays.copyOfRange(fxvk.chars, i * nKeysPerRow,
-                                                            Math.min((i + 1) * nKeysPerRow, fxvk.chars.length)));
+            if (nKeys > 1) {
+                int nRows = (int)Math.floor(Math.sqrt(Math.max(1, nKeys - 2)));
+                int nKeysPerRow = (int)Math.ceil(nKeys / (double)nRows);
+                keyRows = new Control[nRows][];
+                for (int i = 0; i < nRows; i++) {
+                    keyRows[i] =
+                        makeKeyRow((String[])Arrays.copyOfRange(fxvk.chars, i * nKeysPerRow,
+                                                                Math.min((i + 1) * nKeysPerRow, fxvk.chars.length)));
+                }
+            } else {
+                keyRows = new Control[0][];
             }
         } else {
-            // TODO: Move this to a resource bundle.
-            keyRows = new Control[][] {
-                makeKeyRow("q 1 [",
-                           "w 2 ]",
-                           "e 3 { \u00e8 \u00e9 \u00ea \u00eb", // e 3 { egrave eacute ecircumflex ediaeresis
-                           "r 4 } \u00ae", // r 4 } registered
-                           "t 5 \\ \u2122", // t 5 \ TM
-                           "y 6 | \u1ef3 \u00fd \u0177 \u0233 \u00ff \u1ef7", // y 6 | ygrave yacute ycircumflex ymacron ydiaeresis yhook
-                           "u 7 \" \u00f9 \u00fa \u00fb \u00fc", // u 7 \" ugrave uacute ucircumflex udiaeresis
-                           "i 8 < \u00ec \u00ed \u00ee \u00ef", // i 8 < igrave iacute icircumflex idiaeresis
-                           "o 9 > \u00f2 \u00f3 \u00f4 \u00f5 \u00f6 \u00f8 \u00b0", // o 9 > ograve oacute ocircumflex otilde odiaeresis oslash degree
-                           "p 0 _ \u00a7 \u00b6 \u03c0"),       // p 0 _ paragraph pilcrow pi
-                makeKeyRow("a @ ~ \u00e0 \u00e1 \u00e2 \u00e3 \u00e4 \u00e5", // a @ ~ agrave aacute acircumflex atilde adiaeresis aring
-                           "s # ` \u015f \u0161 \u00df \u03c3", // s # ` scedilla scaron sharps sigma
-                           "d $ \u20ac \u00f0",                 // d $ euro eth
-                           "f % \u00a3",                        // f % sterling
-                           "g ^ \u00a5",                        // g ^ yen
-                           "h & \u00a7",                        // h & paragraph (TODO: use only once)
-                           "j * \u00b7",                        // j * middledot
-                           "k ( \u00b0",                        // k ( degree (TODO: use only once)
-                           "l ) \u2260"),                       // l ) notequalto
-                makeKeyRow(shiftKey = new ShiftKey(keyWidth * 1.5),
-                           "z - \u00a1",                        // z - invertedexclamationmark
-                           "x = \u00bf",                        // x = invertedquestionmark
-                           "c + \u2030 \u00e7 \u00a9 \u00a2",   // c + permille ccedilla copyright cent
-                           "v ; \u00ae",                        // v ; registered (TODO: use only once)
-                           "b : \u2122",                        // b : TM  (TODO: use only once)
-                           "n / \u00ab \u00f1",                 // n / doubleleftangle ntilde
-                           "m ' \u00bb",                        // m ' doublerightangle (add micro)
-                           new CommandKey("\u232b", BACK_SPACE, keyWidth * 1.5)),
-                makeKeyRow(symbolKey = new SymbolKey("!#123 ABC", keyWidth * 2.5 + (9-4) * hGap / 2),
-                           ", !",                               // , !
-                           " ",                                 // space
-                           ". ?",                               // . ?
-                           new CommandKey("\u21b5", ENTER, keyWidth * 2.5 + (9-4) * hGap / 2))
-            };
+            // Read keyboard layout from resource bundle
+            ArrayList<Control[]> rows = new ArrayList<Control[]>();
+            ArrayList<String> row = new ArrayList<String>();
+            ArrayList<Double> keyWidths = new ArrayList<Double>();
+            String typeString = new String[] { "Text", "Numeric", "URL", "Email" }[fxvk.vkType];
+            int r = 0;
+            try {
+                String format = "FXVK."+typeString+".row%d.key%02d";
+                while (getBundle().containsKey(String.format(format, ++r, 1))) {
+                    int c = 0;
+                    String keyChars;
+                    while (getBundle().containsKey(String.format(format, r, ++c))) {
+                        row.add(getString(String.format(format, r, c)));
+                        Double w = -1.0;
+                        String widthLookup = String.format(format+".width", r, c);
+                        if (getBundle().containsKey(widthLookup)) {
+                            try {
+                                w = new Double(getString(widthLookup));
+                            } catch (NumberFormatException ex) {
+                                System.err.println(widthLookup+"="+getString(widthLookup));
+                                System.err.println(ex);
+                            }
+                        }
+                        keyWidths.add(w);
+                    }
+                    rows.add(makeKeyRow(row, keyWidths));
+                    row.clear();
+                    keyWidths.clear();
+                }
+            } catch (Exception ex) {
+                ex.printStackTrace();
+            }
+            keyRows = rows.toArray(new Control[rows.size()][]);
         }
 
         VBox vbox = new VBox(vGap);
@@ -331,7 +286,14 @@
                     if (fxvk.chars != null) {
                         key.getStyleClass().add("secondary-key");
                     }
-                    key.setStyle("-fx-font-size: "+primaryFontSize+"px;");
+
+                    int textLen = key.getText().length();
+                    if (textLen == 1 || !key.getClass().getSimpleName().equals("CharKey")) {
+                        key.setStyle("-fx-font-size: "+primaryFontSize+"px;");
+                    } else {
+                        key.setStyle("-fx-font-size: "+(primaryFontSize* Math.min(1.0, 3.0/textLen))+"px;");
+                        key.setGraphicTextGap(key.getGraphicTextGap() + 2*textLen);
+                    }
                     if (key.getGraphic() instanceof Label) {
                         ((Label)key.getGraphic()).setStyle("-fx-font-size: "+secondaryFontSize+"px;");
                     }
@@ -341,18 +303,28 @@
     }
 
 
-    private Control[] makeKeyRow(Object... obj) {
-        List<Object> keyList = Arrays.asList((Object[])obj);
-        return makeKeyRow(keyList);
+    private Control[] makeKeyRow(String... obj) {
+        return makeKeyRow(Arrays.asList(obj), null);
     }
 
-    private Control[] makeKeyRow(List<Object> keyList) {
+    private Control[] makeKeyRow(List<String> keyList, List<Double> widths) {
         Control[] keyRow = new Control[keyList.size()];
         for (int i = 0; i < keyRow.length; i++) {
-            if (keyList.get(i) instanceof String) {
-                keyRow[i] = new CharKey((String)keyList.get(i));
+            String str = keyList.get(i);
+            Double w = 1.0;
+            if (widths != null && widths.get(i) > 0) {
+                w = widths.get(i);
+            }
+            if ("BACKSPACE".equals(str)) {
+                keyRow[i] = new CommandKey("\u232b", BACK_SPACE, keyWidth * w);
+            } else if ("ENTER".equals(str)) {
+                keyRow[i] = new CommandKey("\u21b5", ENTER, keyWidth * w + (9-4) * hGap / 2);
+            } else if ("SHIFT".equals(str)) {
+                keyRow[i] = shiftKey = new ShiftKey(keyWidth * w);
+            } else if ("SYM".equals(str)) {
+                keyRow[i] = symbolKey = new SymbolKey("!#123 ABC", keyWidth * w + (9-4) * hGap / 2);
             } else {
-                keyRow[i] = (Control)keyList.get(i);
+                keyRow[i] = new CharKey((String)keyList.get(i), keyWidth * w);
             }
         }
         return keyRow;
@@ -406,7 +378,9 @@
                 }
             }
         }
-        symbolKey.setText(symbolKey.chars[(state == State.NUMERIC) ? 1 : 0]);
+        if (symbolKey != null) {
+            symbolKey.setText(symbolKey.chars[(state == State.NUMERIC) ? 1 : 0]);
+        }
     }
 
     private void fireKeyEvent(Node target, EventType<? extends KeyEvent> eventType,
@@ -476,25 +450,21 @@
                 }
 
                 if (fxvk == secondaryVK) {
-                    showSecondaryVK(null, fxvk, state);
+                    showSecondaryVK(null);
                 }
             }
         };
 
-        CharKey(String str) {
+        CharKey(String str, double width) {
             this.str = str;
             setOnAction(actionHandler);
 
             if (fxvk != secondaryVK) {
                 setOnMousePressed(new EventHandler<MouseEvent>() {
                     @Override public void handle(MouseEvent event) {
-                        showSecondaryVK(null, fxvk, state);
-                        if (state != State.NUMERIC || chars.length > 2) {
-                            secondaryVKKey = CharKey.this;
-                            secondaryVKDelay.playFromStart();
-                        } else {
-                            secondaryVKKey = null;
-                        }
+                        showSecondaryVK(null);
+                        secondaryVKKey = CharKey.this;
+                        secondaryVKDelay.playFromStart();
                     }
                 });
 
@@ -509,18 +479,21 @@
                 chars = new String[] { str };
             } else {
                 chars = str.split(" ");
+                for (int i = 0; i < chars.length; i++) {
+                    chars[i] = FXVKCharEntities.get(chars[i]);
+                }
             }
-            setContentDisplay(ContentDisplay.RIGHT);
+            setContentDisplay(ContentDisplay.TOP);
+            setGraphicTextGap(-8);
             setText(chars[0]);
-            if (chars.length > 1) {
-                graphic = new Label((chars.length > 1) ? chars[1] : " ");
-                graphic.setPrefWidth(keyWidth / 2 - 10);
-                graphic.setMinWidth(USE_PREF_SIZE);
-                graphic.setPrefHeight(keyHeight - 6);
-                setGraphic(graphic);
-            }
 
-            setPrefWidth((str == " ") ? keyWidth * 3 : keyWidth);
+            graphic = new Label((chars.length > 1) ? chars[1] : " ");
+            graphic.setPrefWidth(keyWidth - 12);
+            graphic.setMinWidth(USE_PREF_SIZE);
+            graphic.setPrefHeight(keyHeight / 2 - 8);
+            setGraphic(graphic);
+
+            setPrefWidth(width);
         }
     }
 
@@ -529,7 +502,7 @@
 
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
             @Override public void handle(ActionEvent e) {
-                showSecondaryVK(null, null, null);
+                showSecondaryVK(null);
                 Node target = fxvk.getAttachedNode();
                 if (target instanceof EventTarget) {
                     String txt = getText();
@@ -555,7 +528,7 @@
     private class ShiftKey extends Key {
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
             @Override public void handle(ActionEvent e) {
-                showSecondaryVK(null, null, null);
+                showSecondaryVK(null);
                 toggleShift();
             }
         };
@@ -576,8 +549,10 @@
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
             @Override public void handle(ActionEvent e) {
                 state = (state == State.NUMERIC) ? State.NORMAL : State.NUMERIC;
-                shiftKey.setDisable(state == State.NUMERIC);
-                showSecondaryVK(null, null, null);
+                if (shiftKey != null) {
+                    shiftKey.setDisable(state == State.NUMERIC);
+                }
+                showSecondaryVK(null);
                 updateLabels();
             }
         };
@@ -619,9 +594,9 @@
         super.layoutChildren();
     }
 
-    private static void showSecondaryVK(final CharKey key, FXVK primVK, State state) {
+    private void showSecondaryVK(final CharKey key) {
         if (key != null) {
-            primaryVK = primVK;
+            primaryVK = fxvk;
             final Node textInput = primaryVK.getAttachedNode();
 
             if (secondaryPopup == null) {
@@ -649,36 +624,38 @@
                 secondaryVK.chars = key.chars;
             }
 
-            if (secondaryVK.getSkin() != null) {
-                ((FXVKSkin)secondaryVK.getSkin()).createKeys();
+            if (secondaryVK.chars.length > 1) {
+                if (secondaryVK.getSkin() != null) {
+                    ((FXVKSkin)secondaryVK.getSkin()).createKeys();
+                }
+
+                secondaryVK.setAttachedNode(textInput);
+                FXVKSkin primarySkin = (FXVKSkin)primaryVK.getSkin();
+                Insets insets = primarySkin.getInsets();
+                int nKeys = secondaryVK.chars.length;
+                int nRows = (int)Math.floor(Math.sqrt(Math.max(1, nKeys - 2)));
+                int nKeysPerRow = (int)Math.ceil(nKeys / (double)nRows);
+                final double w = insets.getLeft() + insets.getRight() +
+                                 nKeysPerRow * primarySkin.keyWidth + (nKeys - 1) * hGap;
+                final double h = nRows * primarySkin.keyHeight + (nRows-1) * vGap + 5;
+                secondaryVK.setPrefWidth(w);
+                secondaryVK.setMinWidth(USE_PREF_SIZE);
+                secondaryVK.setPrefHeight(h);
+                secondaryVK.setMinHeight(USE_PREF_SIZE);
+                Platform.runLater(new Runnable() {
+                    public void run() {
+                        // Position popup on screen
+                        Point2D nodePoint =
+                            com.sun.javafx.Utils.pointRelativeTo(key, w, h, HPos.CENTER, VPos.TOP,
+                                                                 5, -3, true);
+                        double x = nodePoint.getX();
+                        double y = nodePoint.getY();
+                        Scene scene = key.getScene();
+                        x = Math.min(x, scene.getWindow().getX() + scene.getWidth() - w);
+                        secondaryPopup.show(key.getScene().getWindow(), x, y);
+                    }
+                });
             }
-
-            secondaryVK.setAttachedNode(textInput);
-            FXVKSkin primarySkin = (FXVKSkin)primaryVK.getSkin();
-            Insets insets = primarySkin.getInsets();
-            int nKeys = secondaryVK.chars.length;
-            int nRows = (int)Math.floor(Math.sqrt(Math.max(1, nKeys - 2)));
-            int nKeysPerRow = (int)Math.ceil(nKeys / (double)nRows);
-            final double w = insets.getLeft() + insets.getRight() +
-                             nKeysPerRow * primarySkin.keyWidth + (nKeys - 1) * hGap;
-            final double h = nRows * primarySkin.keyHeight + (nRows-1) * vGap + 5;
-            secondaryVK.setPrefWidth(w);
-            secondaryVK.setMinWidth(USE_PREF_SIZE);
-            secondaryVK.setPrefHeight(h);
-            secondaryVK.setMinHeight(USE_PREF_SIZE);
-            Platform.runLater(new Runnable() {
-                public void run() {
-                    // Position popup on screen
-                    Point2D nodePoint =
-                        com.sun.javafx.Utils.pointRelativeTo(key, w, h, HPos.CENTER, VPos.TOP,
-                                                             5, -3, true);
-                    double x = nodePoint.getX();
-                    double y = nodePoint.getY();
-                    Scene scene = key.getScene();
-                    x = Math.min(x, scene.getWindow().getX() + scene.getWidth() - w);
-                    secondaryPopup.show(key.getScene().getWindow(), x, y);
-                }
-            });
         } else {
             if (secondaryVK != null) {
                 secondaryVK.setAttachedNode(null);
@@ -687,7 +664,9 @@
         }
     }
 
-    class NewRootPane extends Pane {
+    static class NewRootPane extends Pane {
+        Timeline slideInTimeline;
+        Timeline slideOutTimeline;
         double dragStartY;
 
         NewRootPane(final Region oldRoot) {
@@ -705,11 +684,17 @@
 
             addEventHandler(MOUSE_DRAGGED, new EventHandler<MouseEvent>() {
                 @Override public void handle(MouseEvent e) {
-                    if (fxvk.isVisible()) {
-                        double y =
-                            Math.min(0, Math.max(e.getY() - dragStartY,
-                                                 fxvk.getLayoutY() - oldRoot.getHeight()));
-                        oldRoot.setTranslateY(y);
+                    for (Node child : getChildren()) {
+                        if (child instanceof FXVK) {
+                            FXVK fxvk = (FXVK)child;
+                            if (fxvk.isVisible()) {
+                                double y =
+                                    Math.min(0, Math.max(e.getY() - dragStartY,
+                                                         fxvk.getLayoutY() - oldRoot.getHeight()));
+                                oldRoot.setTranslateY(y);
+                                break;
+                            }
+                        }
                     }
                     e.consume();
                 }
@@ -724,22 +709,12 @@
             return oldRoot.prefHeight(width);
         }
 
-        @Override public void layoutChildren() {
+        private void updateTimelines(FXVK fxvk) {
             double scale = getWidth() / fxvk.prefWidth(-1);
             double rootHeight = getHeight();
             double vkHeight = fxvk.prefHeight(-1) * scale;
 
-            boolean resized = false;
-            if (fxvk.getWidth() != getWidth() || fxvk.getHeight() != vkHeight) {
-                fxvk.resize(getWidth(), vkHeight);
-                resized = true;
-            }
-
-            if (fxvk.getLayoutY() == 0) {
-                fxvk.setLayoutY(rootHeight);
-            }
-
-            slideInTimeline.getKeyFrames().setAll(
+            ((FXVKSkin)fxvk.getSkin()).slideInTimeline.getKeyFrames().setAll(
                 new KeyFrame(Duration.ZERO,
                              new KeyValue(fxvk.visibleProperty(), true),
                              new KeyValue(fxvk.layoutYProperty(), rootHeight)),
@@ -749,8 +724,7 @@
                                           Math.floor(rootHeight - vkHeight),
                                           Interpolator.EASE_BOTH)));
 
-            slideOutTimeline = new Timeline();
-            slideOutTimeline.getKeyFrames().setAll(
+            ((FXVKSkin)fxvk.getSkin()).slideOutTimeline.getKeyFrames().setAll(
                 new KeyFrame(Duration.ZERO,
                              new KeyValue(fxvk.layoutYProperty(),
                                           Math.floor(rootHeight - vkHeight))),
@@ -759,14 +733,51 @@
                                           rootHeight,
                                           Interpolator.EASE_BOTH),
                              new KeyValue(fxvk.visibleProperty(), false)));
+        }
 
-            if (fxvk.isVisible()) {
-                if (fxvk.getLayoutY() >= rootHeight) {
-                    slideOutTimeline.stop();
-                    slideInTimeline.playFromStart();
-                } else if (resized && slideInTimeline.getStatus() == Status.STOPPED
-                                   && slideOutTimeline.getStatus() == Status.STOPPED) {
-                    fxvk.setLayoutY(rootHeight - vkHeight);
+        @Override public void layoutChildren() {
+            for (Node child : getChildren()) {
+                if (child instanceof FXVK) {
+                    final FXVK fxvk = (FXVK)child;
+                    double scale = getWidth() / fxvk.prefWidth(-1);
+                    final double rootHeight = getHeight();
+                    final double vkHeight = fxvk.prefHeight(-1) * scale;
+
+                    boolean resized = false;
+                    if (fxvk.getWidth() != getWidth() || fxvk.getHeight() != vkHeight) {
+                        fxvk.resize(getWidth(), vkHeight);
+                        resized = true;
+                    }
+
+                    if (fxvk.getLayoutY() == 0) {
+                        fxvk.setLayoutY(rootHeight);
+                    }
+
+                    updateTimelines(fxvk);
+
+
+                    if (fxvk.isVisible()) {
+                        if (fxvk.getLayoutY() >= rootHeight) {
+                            slideOutTimeline.stop();
+                            slideInTimeline.playFromStart();
+                        } else if (resized && slideInTimeline.getStatus() == Status.STOPPED
+                                           && slideOutTimeline.getStatus() == Status.STOPPED) {
+                            fxvk.setLayoutY(rootHeight - vkHeight);
+                        }
+                        Platform.runLater(new Runnable() {
+                            public void run() {
+                                Node attachedNode = fxvk.getAttachedNode();
+                                if (attachedNode != null) {
+                                    double oldRootY = oldRoot.getTranslateY();
+                                    double nodeBottom =
+                                        attachedNode.localToScene(attachedNode.getBoundsInLocal()).getMaxY() + 2;
+                                    if (nodeBottom > rootHeight - vkHeight) {
+                                        translatePane(oldRoot, rootHeight - vkHeight - nodeBottom + oldRootY);
+                                    }
+                                }
+                            }
+                        });
+                    }
                 }
             }
         }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledSkinBase.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledSkinBase.java	Mon Apr 09 13:05:50 2012 -0700
@@ -218,6 +218,39 @@
         }
     }
 
+    protected double topPadding() {
+        return snapSpace(getInsets().getTop());
+    }
+
+    protected double topLabelPadding() {
+        return snapSpace(getSkinnable().getLabelPadding().getTop());
+    }
+
+    protected double bottomPadding() {
+        return snapSpace(getInsets().getBottom());
+    }
+
+    protected double bottomLabelPadding() {
+        return snapSpace(getSkinnable().getLabelPadding().getBottom());
+    }
+
+    protected double leftPadding() {
+        return snapSpace(getInsets().getLeft());
+    }
+
+    protected double leftLabelPadding() {
+        return snapSpace(getSkinnable().getLabelPadding().getLeft());
+    }
+
+    protected double rightPadding() {
+        return snapSpace(getInsets().getRight());
+    }
+
+    protected double rightLabelPadding() {
+        return snapSpace(getSkinnable().getLabelPadding().getRight());
+    }
+
+
     /**
      * Called whenever some state has changed that affects the text metrics.
      * Changes here will involve invalidating the display text so the next
@@ -339,10 +372,11 @@
                     mnemonic_underscore.setEndY(0.0f);
                     mnemonic_underscore.getStyleClass().clear();
                     mnemonic_underscore.getStyleClass().setAll("mnemonic-underline");
+                }
+                if (!getChildren().contains(mnemonic_underscore)) {
                     getChildren().add(mnemonic_underscore);
                 }
-            }
-            else {
+            } else {
                 /*
                 ** we don't need a mnemonic....
                 */
@@ -377,11 +411,8 @@
                     (labeled.getContentDisplay() == ContentDisplay.LEFT ||
                     labeled.getContentDisplay() == ContentDisplay.RIGHT);
 
-            Insets padding = getInsets();
-            Insets labelPadding = labeled.getLabelPadding();
-
-            double availableWidth = labeled.getWidth() - padding.getLeft() - padding.getRight() -
-                                    labelPadding.getLeft() - labelPadding.getRight();
+            double availableWidth = labeled.getWidth() - leftPadding() - leftLabelPadding() -
+                                    rightPadding() - rightLabelPadding();
             availableWidth = Math.max(availableWidth, 0);
 
             if (w == -1) {
@@ -399,8 +430,8 @@
                     (labeled.getContentDisplay() == ContentDisplay.TOP ||
                     labeled.getContentDisplay() == ContentDisplay.BOTTOM);
 
-            double availableHeight = labeled.getHeight() - padding.getTop() - padding.getBottom() -
-                                     labelPadding.getTop() - labelPadding.getBottom();
+            double availableHeight = labeled.getHeight() - topPadding() - topLabelPadding() -
+                                     bottomPadding() - bottomLabelPadding();
             availableHeight = Math.max(availableHeight, 0);
 
             if (h == -1) {
@@ -481,7 +512,7 @@
             // Note that the wrapping width needs to be set to zero before
             // getting the text's real preferred width.
             double w = Math.min(text.prefWidth(-1), textClip.getWidth());
-            text.setWrappingWidth((int)snapSize(w));
+            text.setWrappingWidth(w);
         }
     }
 
@@ -577,10 +608,8 @@
         final boolean emptyText = string == null || string.isEmpty();
         final ContentDisplay contentDisplay = labeled.getContentDisplay();
         final double gap = labeled.getGraphicTextGap();
-        final Insets padding = getInsets();
-        final Insets labelPadding = labeled.getLabelPadding();
-        final double widthPadding = padding.getLeft() + padding.getRight() +
-                                    labelPadding.getLeft() + labelPadding.getRight();
+        final double widthPadding = leftPadding() + leftLabelPadding() +
+                                    rightPadding() + rightLabelPadding();
 
         double minTextWidth = 0;
         if (!emptyText) {
@@ -647,10 +676,8 @@
                 h = Math.max(textHeight, graphic.minHeight(-1));
             }
         }
-        Insets padding = getInsets();
-        Insets labelPadding = labeled.getLabelPadding();
 
-        return padding.getTop() + h + padding.getBottom() + labelPadding.getTop() - labelPadding.getBottom();
+        return topPadding() + h + bottomPadding() + topLabelPadding() - bottomLabelPadding();
     }
 
     @Override protected double computePrefWidth(double height) {
@@ -659,9 +686,11 @@
         final Font font = text.getFont();
         final String string = labeled.getText();
         boolean emptyText = string == null || string.isEmpty();
-        final Insets padding = getInsets();
-        Insets labelPadding = labeled.getLabelPadding();
-        double widthPadding = padding.getLeft() + padding.getRight() + labelPadding.getLeft() + labelPadding.getRight();
+        double widthPadding = leftPadding() + leftLabelPadding() +
+                              rightPadding() + rightLabelPadding();
+        double heightPadding = topPadding() + topLabelPadding() +
+                               bottomPadding() + bottomLabelPadding();
+
         double textWidth = emptyText ? 0 : Utils.computeTextWidth(font, string, 0);
 
         // Now add on the graphic, gap, and padding as appropriate
@@ -683,9 +712,8 @@
         final Font font = text.getFont();
         final ContentDisplay contentDisplay = labeled.getContentDisplay();
         final double gap = labeled.getGraphicTextGap();
-        final Insets padding = getInsets();
-        final Insets labelPadding = labeled.getLabelPadding();
-        final double widthPadding = padding.getLeft() + padding.getRight() + labelPadding.getLeft() + labelPadding.getRight();
+        double widthPadding = leftPadding() + leftLabelPadding() +
+                              rightPadding() + rightLabelPadding();
 
         String str = labeled.getText();
         if (str != null && str.endsWith("\n")) {
@@ -715,7 +743,7 @@
             }
         }
 
-        return padding.getTop() + h + padding.getBottom() + labelPadding.getTop() + labelPadding.getBottom();
+        return topPadding() + h + bottomPadding() + topLabelPadding() + bottomLabelPadding();
     }
 
     @Override protected double computeMaxWidth(double height) {
@@ -742,9 +770,7 @@
             }
         }
                 
-        Insets padding = getInsets();
-        Insets labelPadding = labeled.getLabelPadding();
-        return padding.getTop() + labelPadding.getTop() + h;
+        return topPadding() + topLabelPadding() + h;
     }
 
     public TextBinding bindings;
@@ -768,12 +794,10 @@
      *  - position the graphic and text
      */
     @Override protected void layoutChildren() {
-        final Insets padding = getInsets();
-
-        final double x = padding.getLeft();
-        final double y = padding.getTop();
-        final double w = getWidth() - (padding.getLeft() + padding.getRight());
-        final double h = getHeight() - (padding.getTop() + padding.getBottom());
+        final double x = leftPadding();
+        final double y = topPadding();
+        final double w = getWidth() - (leftPadding() + rightPadding());
+        final double h = getHeight() - (topPadding() + bottomPadding());
 
         layoutLabelInArea(x, y, w, h);
     }
@@ -825,11 +849,10 @@
         final boolean ignoreGraphic = isIgnoreGraphic();
         final boolean ignoreText = isIgnoreText();
 
-        final Insets labelPadding = labeled.getLabelPadding();
-        x += labelPadding.getLeft();
-        y += labelPadding.getTop();
-        w -= labelPadding.getLeft() + labelPadding.getRight();
-        h -= labelPadding.getTop() + labelPadding.getBottom();
+        x += leftLabelPadding();
+        y += topLabelPadding();
+        w -= leftLabelPadding() + rightLabelPadding();
+        h -= topLabelPadding() + bottomLabelPadding();
 
         // Compute some standard useful numbers for the graphic, text, and gap
         double graphicWidth;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledText.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledText.java	Mon Apr 09 13:05:50 2012 -0700
@@ -11,6 +11,7 @@
 import javafx.beans.property.Property;
 import javafx.beans.value.WritableValue;
 import javafx.scene.control.Labeled;
+import javafx.scene.paint.Color;
 import javafx.scene.paint.Paint;
 import javafx.scene.text.Font;
 import javafx.scene.text.Text;
@@ -121,7 +122,7 @@
 
        private static final StyleableProperty<LabeledText,Paint> FILL =
            new StyleableProperty<LabeledText,Paint>("-fx-fill", 
-               PaintConverter.getInstance(), null) {
+               PaintConverter.getInstance(), Color.BLACK) {
 
             @Override
             public void set(LabeledText node, Paint value, Stylesheet.Origin origin) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListViewSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListViewSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -100,11 +100,11 @@
         getBehavior().setOnMoveToLastCell(new Runnable() {
             @Override public void run() { onMoveToLastCell(); }
         });
-        getBehavior().setOnScrollPageDown(new Callback<Void, Integer>() {
-            @Override public Integer call(Void param) { return onScrollPageDown(); }
+        getBehavior().setOnScrollPageDown(new Callback<Integer, Integer>() {
+            @Override public Integer call(Integer anchor) { return onScrollPageDown(anchor); }
         });
-        getBehavior().setOnScrollPageUp(new Callback<Void, Integer>() {
-            @Override public Integer call(Void param) { return onScrollPageUp(); }
+        getBehavior().setOnScrollPageUp(new Callback<Integer, Integer>() {
+            @Override public Integer call(Integer anchor) { return onScrollPageUp(anchor); }
         });
         getBehavior().setOnSelectPreviousRow(new Runnable() {
             @Override public void run() { onSelectPreviousCell(); }
@@ -326,12 +326,13 @@
      * Function used to scroll the container down by one 'page', although
      * if this is a horizontal container, then the scrolling will be to the right.
      */
-    private int onScrollPageDown() {
+    private int onScrollPageDown(int anchor) {
         IndexedCell lastVisibleCell = flow.getLastVisibleCellWithinViewPort();
         if (lastVisibleCell == null) return -1;
 
         int newSelectionIndex = -1;
-        if (! (lastVisibleCell.isSelected() || lastVisibleCell.isFocused())) {
+        int lastVisibleCellIndex = lastVisibleCell.getIndex();
+        if (! (lastVisibleCell.isSelected() || lastVisibleCell.isFocused()) || (lastVisibleCellIndex != anchor)) {
             // if the selection is not on the 'bottom' most cell, we firstly move
             // the selection down to that, without scrolling the contents
             newSelectionIndex = lastVisibleCell.getIndex();
@@ -353,12 +354,13 @@
      * Function used to scroll the container up by one 'page', although
      * if this is a horizontal container, then the scrolling will be to the left.
      */
-    private int onScrollPageUp() {
+    private int onScrollPageUp(int anchor) {
         IndexedCell firstVisibleCell = flow.getFirstVisibleCellWithinViewPort();
         if (firstVisibleCell == null) return -1;
 
         int newSelectionIndex = -1;
-        if (! (firstVisibleCell.isSelected() || firstVisibleCell.isFocused())) {
+        int firstVisibleCellIndex = firstVisibleCell.getIndex();
+        if (! (firstVisibleCell.isSelected() || firstVisibleCell.isFocused()) || (firstVisibleCellIndex != anchor)) {
             // if the selection is not on the 'top' most cell, we firstly move
             // the selection up to that, without scrolling the contents
             newSelectionIndex = firstVisibleCell.getIndex();
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationCellSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationCellSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -46,22 +46,40 @@
 
 import javafx.scene.control.ListView;
 import com.sun.javafx.scene.control.PaginationCell;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.geometry.HPos;
+import javafx.geometry.VPos;
+import javafx.scene.Node;
 
 public class PaginationCellSkin extends ListCellSkin {
 
     public PaginationCellSkin(PaginationCell control) {
         super(control);
+
+        getSkinnable().getListView().layoutBoundsProperty().addListener(new InvalidationListener() {
+            @Override
+            public void invalidated(Observable o) {
+                requestLayout();
+            }
+        });
     }
 
     @Override protected double computePrefWidth(double height) {
         ListView listView = getSkinnable().getListView();
-        double d = listView == null ? 0 : listView.getLayoutBounds().getWidth();
-        return d;
+        double nodeWidth = listView == null ? 0 : listView.getLayoutBounds().getWidth();
+        for (Node n: getChildren()) {
+            nodeWidth = Math.max(nodeWidth, n.prefWidth(height));
+        }
+        return nodeWidth;
     }
 
     @Override protected double computePrefHeight(double width) {
         ListView listView = getSkinnable().getListView();
-        double d = listView == null ? 0 : listView.getLayoutBounds().getHeight();
-        return d;
-    }
+        double nodeHeight = listView == null ? 0 : listView.getLayoutBounds().getHeight();
+        for (Node n: getChildren()) {
+            nodeHeight = Math.max(nodeHeight, n.prefHeight(width));
+        }
+        return nodeHeight;
+    }    
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -62,11 +62,10 @@
 import javafx.event.EventHandler;
 import javafx.geometry.HPos;
 import javafx.geometry.Orientation;
-import javafx.geometry.Pos;
 import javafx.geometry.VPos;
-import javafx.scene.Node;
 import javafx.scene.control.*;
 import javafx.scene.input.MouseEvent;
+import javafx.scene.input.SwipeEvent;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.StackPane;
 import javafx.scene.shape.Rectangle;
@@ -75,7 +74,7 @@
 public class PaginationSkin<T> extends SkinBase<Pagination<T>, PaginationBehavior<T>>  {
 
     private Pagination<T> pagination;
-    private PaginationListView<T> paginationListView;    
+    private PaginationListView<T> paginationListView;
     private ObservableList dummyListViewItems;
 
     private Rectangle clipRect;
@@ -85,7 +84,6 @@
     private int previousIndex;
     private int currentIndex;
     private int toIndex;
-    private int totalNumberOfPages;
     private int numberOfPages;
     private int numberOfVisiblePages;
 
@@ -94,60 +92,82 @@
 
         setManaged(false);
         clipRect = new Rectangle();
-        setClip(clipRect);
-        
+        //setClip(clipRect);
+
         this.pagination = pagination;
-        this.paginationListView = new PaginationListView<T>();                
+        this.paginationListView = new PaginationListView<T>();
         updateListViewItems();
         updateCellFactory();
-        resetIndexes();
-                
-        this.navigation = new NavigationControl();        
-        
+        resetIndexes(true);
+
+        this.navigation = new NavigationControl();
+
         getChildren().addAll(paginationListView, navigation);
 
         pagination.numberOfVisiblePagesProperty().addListener(new InvalidationListener() {
             @Override
             public void invalidated(Observable o) {
-                resetIndexes();
-                getChildren().remove(navigation);
-                navigation = new NavigationControl();
-                getChildren().add(navigation);
+                resetIndexes(false);
+                navigation.initializePageIndicators();
+                navigation.updatePageIndicators();
             }
         });
-        
+
         registerChangeListener(pagination.itemsPerPageProperty(), "ITEMS_PER_PAGE");
         registerChangeListener(pagination.numberOfItemsProperty(), "NUMBER_OF_ITEMS");
         registerChangeListener(pagination.pageIndexProperty(), "PAGE_INDEX");
         registerChangeListener(pagination.pageFactoryProperty(), "PAGE_FACTORY");
+
+        setOnSwipeLeft(new EventHandler<SwipeEvent>() {
+            @Override
+            public void handle(SwipeEvent t) {
+                paginationListView.getSelectionModel().selectNext();
+            }
+        });
+
+        setOnSwipeRight(new EventHandler<SwipeEvent>() {
+            @Override
+            public void handle(SwipeEvent t) {
+                paginationListView.getSelectionModel().selectPrevious();
+            }
+        });
     }
 
-    private void resetIndexes() {
+    public MultipleSelectionModel getSelectionModel() {
+        return paginationListView.getSelectionModel();
+    }
+
+    private void resetIndexes(boolean usePageIndex) {
         numberOfVisiblePages = getSkinnable().getNumberOfVisiblePages();
-        totalNumberOfPages = totalNumberOfPages();
-        numberOfPages = totalNumberOfPages;
-        if (totalNumberOfPages > numberOfVisiblePages) {
+        numberOfPages = totalNumberOfPages();
+        if (totalNumberOfPages() > numberOfVisiblePages) {
             numberOfPages = numberOfVisiblePages;
+        } else {
+            // If the number of pages is less than the visible number of pages
+            // we want to set it to the new value.
+            getSkinnable().setNumberOfVisiblePages(numberOfPages);
         }
 
-        fromIndex = 0;        
+        fromIndex = 0;
         previousIndex = 0;
-        currentIndex = getSkinnable().getPageIndex();
-        toIndex = numberOfPages - 1;
+        currentIndex = usePageIndex ? getSkinnable().getPageIndex() : 0;
+        toIndex = fromIndex + (numberOfPages - 1);
+
+        paginationListView.reset();
         paginationListView.getSelectionModel().select(currentIndex);
     }
 
-    private void updateListViewItems() {        
+    private void updateListViewItems() {
         if (dummyListViewItems == null) {
             dummyListViewItems = FXCollections.observableArrayList();
         } else {
+            dummyListViewItems.clear();
             dummyListViewItems.removeListener(weakListViewItemsListener);
         }
 
         for (int i = 0; i < totalNumberOfPages(); i++) {
             dummyListViewItems.add(i);
         }
-        paginationListView.setItems(null);
         paginationListView.setItems(dummyListViewItems);
 
         if (dummyListViewItems != null) {
@@ -165,8 +185,8 @@
     private final WeakListChangeListener weakListViewItemsListener =
             new WeakListChangeListener(listViewItemsListener);
 
-    private void updateCellFactory() {        
-        Callback<ListView<T>, ListCell<T>> cell = createCellFactory();        
+    private void updateCellFactory() {
+        Callback<ListView<T>, ListCell<T>> cell = createCellFactory();
         paginationListView.setCellFactory(cell);
     }
 
@@ -176,7 +196,7 @@
                 return new PaginationCell<T>() {
                     @Override public void updateItem(T item, boolean empty) {
                         super.updateItem(item, empty);
-
+                        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                         if (empty || item == null) {
                             setText(null);
                             setGraphic(null);
@@ -192,17 +212,34 @@
     }
 
     private int totalNumberOfPages() {
-        return getSkinnable().getNumberOfItems()/getSkinnable().getItemsPerPage();
+        int totalNumberOfPages = getSkinnable().getNumberOfItems()/getSkinnable().getItemsPerPage();
+        if (getSkinnable().getNumberOfItems()%getSkinnable().getItemsPerPage() != 0) {
+            // Add the remaining to the next page.
+            totalNumberOfPages += 1;
+        }
+        return totalNumberOfPages;
     }
-                    
+
     @Override protected void handleControlPropertyChanged(String p) {
         super.handleControlPropertyChanged(p);
-        // TODO need to implement
-        if (p == "ITEMS_PER_PAGE") {            
-        } else if (p == "NUMBER_OF_ITEMS") {            
+        if (p == "ITEMS_PER_PAGE") {
+            updateListViewItems();
+            resetIndexes(false);
+            navigation.initializePageIndicators();
+            navigation.updatePageIndicators();
+        } else if (p == "NUMBER_OF_ITEMS") {
+            updateListViewItems();
+            updateCellFactory();
+            resetIndexes(false);
+            navigation.initializePageIndicators();
+            navigation.updatePageIndicators();
         } else if (p == "PAGE_INDEX") {
+            paginationListView.getSelectionModel().select(getSkinnable().getPageIndex());
         } else if (p == "PAGE_FACTORY") {
+            updateCellFactory();
+            resetIndexes(false);
         }
+        requestLayout();
     }
 
     @Override protected void setWidth(double value) {
@@ -259,41 +296,38 @@
             StackPane leftArrow = new StackPane();
             leftArrow.getStyleClass().add("left-arrow");
             leftArrowButton = new StackPane();
-            leftArrowButton.getStyleClass().add("arrow-button");
+            leftArrowButton.getStyleClass().add("page-navigation");
             leftArrowButton.getChildren().setAll(leftArrow);
 
             StackPane rightArrow = new StackPane();
             rightArrow.getStyleClass().add("right-arrow");
             rightArrowButton = new StackPane();
-            rightArrowButton.getStyleClass().add("arrow-button");
+            rightArrowButton.getStyleClass().add("page-navigation");
             rightArrowButton.getChildren().setAll(rightArrow);
 
             indicatorButton = new ArrayList<IndicatorButton>();
 
             getChildren().addAll(leftArrowButton, rightArrowButton);
-            setupPageIndicators();
-            setupEventHandlers();
-            indicatorButton.get(0).setSelected(true);
-            leftArrowButton.setVisible(false);
+            initializeNavigationHandlers();
+            initializePageIndicators();
+            updatePageIndex();
         }
 
-        private void setupEventHandlers() {
+        private void initializeNavigationHandlers() {
             leftArrowButton.setOnMousePressed(new EventHandler<MouseEvent>() {
                 @Override
                 public void handle(MouseEvent arg0) {
                     paginationListView.getSelectionModel().selectPrevious();
                     //System.out.println("LEFT BUTTON " + paginationListView.getSelectionModel().getSelectedIndex());
-                    paginationListView.show(paginationListView.getSelectionModel().getSelectedIndex());
                     requestLayout();
                 }
             });
 
             rightArrowButton.setOnMousePressed(new EventHandler<MouseEvent>() {
                 @Override
-                public void handle(MouseEvent arg0) {                   
+                public void handle(MouseEvent arg0) {
                     paginationListView.getSelectionModel().selectNext();
                     //System.out.println("RIGHT BUTTON " + paginationListView.getSelectionModel().getSelectedIndex() + " TNP " + (totalNumberOfPages - 1));
-                    paginationListView.show(paginationListView.getSelectionModel().getSelectedIndex());
                     requestLayout();
                 }
             });
@@ -303,39 +337,12 @@
                 public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
                     previousIndex = arg1.intValue();
                     currentIndex = arg2.intValue();
-
-                    //System.out.println("\nSELECT PROPERTY FROM " + fromIndex + " TO " + toIndex + " PREVIOUS " + previousIndex + " CURRENT "+ currentIndex);
-                    if (currentIndex == 0) {
-                        // Grey out the left arrow we are at the beginning.
-                        leftArrowButton.setVisible(false);
-                    } else if (currentIndex == (totalNumberOfPages - 1)) {
-                        // Grey out the right arrow we reached the end.
-                        rightArrowButton.setVisible(false);
-                    } else {
-                        leftArrowButton.setVisible(true);
-                        rightArrowButton.setVisible(true);
-                        if (numberOfPages == numberOfVisiblePages) {
-                            scroll();
-                        }
-                    }
-                    // Update the current page index
-                    pagination.setPageIndex(currentIndex);
-                    
-                    // Update the indictor buttons
-                    for (int i = 0; i < indicatorButton.size(); i++) {
-                        if (indicatorButton.get(i).getPageNumber() == previousIndex) {
-                            indicatorButton.get(i).setSelected(false);
-                        }
-                        if (indicatorButton.get(i).getPageNumber() == currentIndex) {
-                            indicatorButton.get(i).setSelected(true);
-                        }
-                    }
-                    requestLayout();
+                    updatePageIndex();
                 }
             });
         }
 
-        private void setupPageIndicators() {
+        private void initializePageIndicators() {
             if (!indicatorButton.isEmpty()) {
                 getChildren().removeAll(indicatorButton);
                 indicatorButton.clear();
@@ -347,7 +354,35 @@
             getChildren().addAll(indicatorButton);
         }
 
-        private void scroll() {
+        private void updatePageIndicators() {
+            for (int i = 0; i < indicatorButton.size(); i++) {
+                if (indicatorButton.get(i).getPageNumber() == previousIndex) {
+                    indicatorButton.get(i).setSelected(false);
+                }
+                if (indicatorButton.get(i).getPageNumber() == currentIndex) {
+                    indicatorButton.get(i).setSelected(true);
+                }
+            }
+        }
+
+        private void updatePageIndex() {
+            //System.out.println("SELECT PROPERTY FROM " + fromIndex + " TO " + toIndex + " PREVIOUS " + previousIndex + " CURRENT "+ currentIndex + " NOP " + numberOfPages + " NOVP " + numberOfVisiblePages);
+            if (currentIndex >= 0 && currentIndex <= (totalNumberOfPages() - 1)) {
+                if (numberOfPages == numberOfVisiblePages) {
+                    if (scroll()) {
+                        initializePageIndicators();
+                    }
+                }
+                // Update the current page index
+                pagination.setPageIndex(currentIndex);
+                updatePageIndicators();
+                requestLayout();
+            }
+        }
+
+        // Only scroll to the next set when the current index is at the start or the end of the set.
+        // Return true only if we have scrolled to the next/previous set.
+        private boolean scroll() {
             if (previousIndex < currentIndex && currentIndex % numberOfVisiblePages == 0) {
                 // Scroll to the right
                 fromIndex = currentIndex;
@@ -357,12 +392,18 @@
                 toIndex = currentIndex;
                 fromIndex = toIndex - (numberOfVisiblePages - 1);
             } else {
-                return;
+                // We need to scroll if the currentIndex is out of range.
+                if (currentIndex < fromIndex || currentIndex > toIndex) {
+                    fromIndex = currentIndex - (currentIndex % numberOfVisiblePages);
+                    toIndex = fromIndex + (numberOfVisiblePages - 1);
+                } else {
+                    return false;
+                }
             }
 
             // We have gone past the total number of pages
-            if (toIndex > totalNumberOfPages - 1) {
-                toIndex = totalNumberOfPages - 1;
+            if (toIndex > totalNumberOfPages() - 1) {
+                toIndex = totalNumberOfPages() - 1;
             }
 
             // We have gone past the starting page
@@ -370,8 +411,7 @@
                 fromIndex = 0;
                 toIndex = fromIndex + (numberOfVisiblePages - 1);
             }
-            //System.out.println("SCROLL from " + fromIndex + " to " + toIndex + " previous " + previousIndex + " current " + currentIndex);
-            setupPageIndicators();
+            return true;
         }
 
         @Override protected double computeMinWidth(double height) {
@@ -428,6 +468,24 @@
             double arrowButtonY = top + Utils.computeYOffset(height, leftArrowHeight, VPos.CENTER);
             double indicatorButtonY = top + Utils.computeYOffset(height, indicatorHeight, VPos.CENTER);
 
+            leftArrowButton.setVisible(true);
+            rightArrowButton.setVisible(true);
+
+            if (currentIndex == 0) {
+                // Grey out the left arrow if we are at the beginning.
+                leftArrowButton.setVisible(false);
+            }
+            if (currentIndex == (totalNumberOfPages() - 1)) {
+                // Grey out the right arrow if we have reached the end.
+                rightArrowButton.setVisible(false);
+            }
+
+            if (previousIndex < 0) {
+                paginationListView.showOffset(0, paginationListView.getSelectionModel().getSelectedIndex());
+            } else {
+                paginationListView.showOffset(currentIndex - previousIndex, paginationListView.getSelectionModel().getSelectedIndex());
+            }
+
             leftArrowButton.resize(leftArrowWidth, leftArrowHeight);
 
             positionInArea(leftArrowButton, left, arrowButtonY, leftArrowWidth, leftArrowHeight, 0, HPos.CENTER, VPos.CENTER);
@@ -451,7 +509,7 @@
         private boolean selected;
 
         public IndicatorButton(int pageNumber) {
-            getStyleClass().add("arrow-button");
+            getStyleClass().add("page-navigation");
             this.selected = false;
             this.pageNumber = pageNumber;
             pageIndicator = new Label(Integer.toString(this.pageNumber + 1));
@@ -478,19 +536,18 @@
                     // We do not need to update the selection if it has not changed.
                     if (selected != IndicatorButton.this.pageNumber) {
                         paginationListView.getSelectionModel().select(IndicatorButton.this.pageNumber);
-                        paginationListView.show(paginationListView.getSelectionModel().getSelectedIndex());
                         requestLayout();
                     }
                 }
             });
         }
 
-        private final void setIndicatorType() {
+        private void setIndicatorType() {
             if (getSkinnable().getStyleClass().contains(Pagination.STYLE_CLASS_BULLET)) {
-                indicator.getStyleClass().setAll("bullet-indicator");
+                indicator.getStyleClass().setAll("bullet");
                 indicator.getChildren().remove(pageIndicator);
             } else {
-                indicator.getStyleClass().setAll("number-indicator");
+                indicator.getStyleClass().setAll("number");
                 indicator.getChildren().setAll(pageIndicator);
             }
         }
@@ -532,15 +589,31 @@
             StyleManager.getInstance().getPseudoclassMask("selected");
 
     class PaginationListView<T> extends ListView<T> {
+        private int previousIndex;
         public PaginationListView() {
             super();
-            setId("list-view");
             setOrientation(Orientation.HORIZONTAL);
-            getSelectionModel().setSelectionMode(SelectionMode.SINGLE);            
+            getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
+            previousIndex = -1;
         }
 
         public void show(int index) {
-            getProperties().put(VirtualContainerBase.SCROLL_TO_INDEX_TOP, index);
+            if (previousIndex != index) {
+                getProperties().put(VirtualContainerBase.SCROLL_TO_INDEX_TOP, index);
+                previousIndex = index;
+            }
+        }
+
+        public void showOffset(int offset, int index) {
+            if (previousIndex != index) {
+                getProperties().put(VirtualContainerBase.SCROLL_TO_OFFSET, offset);
+                previousIndex = index;
+            }
+        }
+
+        public void reset() {
+            getProperties().put(VirtualContainerBase.SCROLL_TO_INDEX_TOP, 0);
+            previousIndex = -1;
         }
     }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PasswordFieldSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PasswordFieldSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -28,6 +28,8 @@
 import javafx.scene.control.PasswordField;
 import javafx.scene.control.TextField;
 
+import com.sun.javafx.scene.control.behavior.PasswordFieldBehavior;
+
 /**
  * Password field skin.
  */
@@ -35,7 +37,7 @@
     public static final char BULLET = '\u2022';
 
     public PasswordFieldSkin(PasswordField passwordField) {
-        super(passwordField);
+        super(passwordField, new PasswordFieldBehavior(passwordField));
     }
 
     @Override protected String maskText(String txt) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollBarSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollBarSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -350,8 +350,10 @@
         // compute x,y,w,h of content area
         double x = getInsets().getLeft();
         double y = getInsets().getTop();
-        double w = getWidth() - (getInsets().getLeft() + getInsets().getRight());
-        double h = getHeight() - (getInsets().getTop() + getInsets().getBottom());
+        double wTotal = snapSize(getWidth());
+        double hTotal = snapSize(getHeight());
+        double wNoInsets = snapSize(wTotal - (getInsets().getLeft() + getInsets().getRight()));
+        double hNoInsets = snapSize(hTotal - (getInsets().getTop() + getInsets().getBottom()));
 
         /**
          * Compute the percentage length of thumb as (visibleAmount/range)
@@ -367,34 +369,45 @@
         
 
         if (getSkinnable().getOrientation() == Orientation.VERTICAL) {
-            double decHeight = decButton.prefHeight(-1);
-            double incHeight = incButton.prefHeight(-1);
 
-            trackLength = h - (decHeight + incHeight);
-            thumbLength = Utils.clamp(minThumbLength(), (trackLength * visiblePortion), trackLength);
+            double decHeight = snapSize(decButton.prefHeight(-1));
+            double incHeight = snapSize(incButton.prefHeight(-1));
 
-            decButton.resizeRelocate(x, y, w, decHeight+3);
-            incButton.resizeRelocate(x, y + h - (incHeight+4), w, incHeight+4);
-            track.resizeRelocate(x, y + decHeight, w, trackLength);
-            thumb.resize(x >= 0 ? w : w + x, thumbLength); // Account for negative padding (see also RT-10719)
+            decButton.resize(wTotal, decHeight);
+            incButton.resize(wTotal, incHeight);
+
+
+            trackLength = snapSize(hNoInsets - (decHeight + incHeight));
+            thumbLength = snapSize(Utils.clamp(minThumbLength(), (trackLength * visiblePortion), trackLength));
+
+            decButton.relocate(snapPosition(x), snapPosition(y));
+            incButton.relocate(snapPosition(x), snapPosition(y + hNoInsets - incHeight));
+            track.resizeRelocate(snapPosition(x), snapPosition(y + decHeight), wTotal, trackLength);
+            thumb.resize(snapSize(x >= 0 ? wTotal : wTotal + x), thumbLength); // Account for negative padding (see also RT-10719)
             positionThumb();
         } else {
-            double decWidth = decButton.prefWidth(-1);
-            double incWidth = incButton.prefWidth(-1);
 
-            trackLength = w - (decWidth + incWidth);
-            thumbLength = Utils.clamp(minThumbLength(), (trackLength * visiblePortion), trackLength);
+            double decWidth = snapSize(decButton.prefWidth(-1));
+            double incWidth = snapSize(incButton.prefWidth(-1));
 
-            decButton.resizeRelocate(x, y, decWidth+3, h);
-            incButton.resizeRelocate(x + w - incWidth-4, y, incWidth+4, h);
-            track.resizeRelocate(x + decWidth, y, trackLength, h);
-            thumb.resize(thumbLength, y >= 0 ? h : h + y); // Account for negative padding (see also RT-10719)
+            decButton.resize(decWidth, hTotal);
+            incButton.resize(incWidth, hTotal);
+
+            trackLength = snapSize(wNoInsets - (decWidth + incWidth));
+            thumbLength = snapSize(Utils.clamp(minThumbLength(), (trackLength * visiblePortion), trackLength));
+
+            decButton.relocate(snapPosition(x), snapPosition(y));
+            incButton.relocate(snapPosition(x + wNoInsets - incWidth), snapPosition(y));
+            track.resizeRelocate(snapPosition(x + decWidth), snapPosition(y), trackLength, hTotal);
+            thumb.resize(thumbLength, snapSize(y >= 0 ? hTotal : hTotal + y)); // Account for negative padding (see also RT-10719)
             positionThumb();
         }
 
+        resize(snapSize(getWidth()), snapSize(getHeight()));
+
         // things should be invisible only when well below minimum length
-        if (getSkinnable().getOrientation() == Orientation.VERTICAL && h >= (computeMinHeight(-1) - (getInsets().getTop()+getInsets().getBottom())) ||
-            getSkinnable().getOrientation() == Orientation.HORIZONTAL && w >= (computeMinWidth(-1) - (getInsets().getLeft()+getInsets().getRight()))) {
+        if (getSkinnable().getOrientation() == Orientation.VERTICAL && hNoInsets >= (computeMinHeight(-1) - (getInsets().getTop()+getInsets().getBottom())) ||
+            getSkinnable().getOrientation() == Orientation.HORIZONTAL && wNoInsets >= (computeMinWidth(-1) - (getInsets().getLeft()+getInsets().getRight()))) {
             track.setVisible(true);
             thumb.setVisible(true);
             incButton.setVisible(true);
@@ -408,13 +421,13 @@
             ** once the space is big enough for one button we 
             ** can look at drawing
             */
-            if (h >= decButton.computeMinWidth(-1)) {
+            if (hNoInsets >= decButton.computeMinWidth(-1)) {
                 decButton.setVisible(true);
             }
             else {
                 decButton.setVisible(false);
             }
-            if (h >= incButton.computeMinWidth(-1)) {
+            if (hNoInsets >= incButton.computeMinWidth(-1)) {
                 incButton.setVisible(true);
             }
             else {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollPaneSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollPaneSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -221,7 +221,7 @@
                     computeScrollNodeSize(getWidth(),getHeight());
                 }
                 if (scrollNode != null && scrollNode.isResizable()) {
-                    scrollNode.resize(nodeWidth,nodeHeight);
+                    scrollNode.resize(snapSize(nodeWidth), snapSize(nodeHeight));
                     if (vsbvis != determineVerticalSBVisible() || hsbvis != determineHorizontalSBVisible()) {
                         ScrollPaneSkin.this.requestLayout();
                     }
@@ -445,8 +445,8 @@
                 }
                 scrollNode = getSkinnable().getContent();
                 if (scrollNode != null) {
-                    nodeWidth = Math.floor(scrollNode.getLayoutBounds().getWidth());
-                    nodeHeight = Math.floor(scrollNode.getLayoutBounds().getHeight());
+                    nodeWidth = snapSize(scrollNode.getLayoutBounds().getWidth());
+                    nodeHeight = snapSize(scrollNode.getLayoutBounds().getHeight());
                     viewRect.getChildren().setAll(scrollNode);
                     scrollNode.layoutBoundsProperty().addListener(nodeListener);
                     scrollNode.layoutBoundsProperty().addListener(boundsChangeListener);
@@ -610,8 +610,14 @@
         hsb.setMin(control.getHmin());
         hsb.setMax(control.getHmax());
 
-        contentWidth = control.getWidth() - getInsets().getLeft() - getInsets().getRight();
-        contentHeight = control.getHeight() - getInsets().getTop() - getInsets().getBottom();
+        contentWidth = control.getWidth() - (getInsets().getLeft() + getInsets().getRight());
+        contentHeight = control.getHeight() - (getInsets().getTop() + getInsets().getBottom());
+
+        /*
+        ** we want the scrollbars to go right to the border
+        */
+        double hsbWidth = contentWidth + getPadding().getLeft() + getPadding().getRight();
+        double vsbHeight = contentHeight + getPadding().getTop() + getPadding().getBottom();
 
         computeScrollNodeSize(contentWidth, contentHeight);
         computeScrollBarSize();
@@ -620,9 +626,11 @@
 
         if (vsbvis) {
             contentWidth -= vsbWidth;
+            hsbWidth -= vsbWidth;
         }
         if (hsbvis) {
             contentHeight -= hsbHeight;
+            vsbHeight -= hsbHeight;
         }
         if (scrollNode != null && scrollNode.isResizable()) {
             // maybe adjust size now that scrollbars may take up space
@@ -650,15 +658,15 @@
         }
 
         // figure out the content area that is to be filled
-        double cx = getInsets().getLeft();
-        double cy = getInsets().getTop();
+        double cx = getInsets().getLeft()-getPadding().getLeft();
+        double cy = getInsets().getTop()-getPadding().getTop();
 
         vsb.setVisible(vsbvis);
         if (vsbvis) {
             /*
             ** round up position of ScrollBar, round down it's size.
             */
-            vsb.resizeRelocate(Math.ceil(control.getWidth() - vsbWidth - getInsets().getRight()), Math.ceil(cy), Math.floor(vsbWidth), Math.ceil(contentHeight));
+            vsb.resizeRelocate(snapPosition(control.getWidth() - (vsbWidth + (getInsets().getRight()-getPadding().getRight()))), snapPosition(cy), snapSize(vsbWidth), snapSize(vsbHeight));
         }
         updateVerticalSB();
 
@@ -667,18 +675,18 @@
             /*
             ** round up position of ScrollBar, round down it's size.
             */
-            hsb.resizeRelocate(Math.ceil(cx), Math.ceil(control.getHeight() - (hsbHeight + getInsets().getBottom())), Math.ceil(contentWidth), Math.floor(hsbHeight));
+            hsb.resizeRelocate(snapPosition(cx), snapPosition(control.getHeight() - (hsbHeight + (getInsets().getBottom()-getPadding().getBottom()))), snapSize(hsbWidth), snapSize(hsbHeight));
         }
         updateHorizontalSB();
 
-        viewRect.resize(contentWidth, contentHeight);
-        clipRect.setWidth(contentWidth);
-        clipRect.setHeight(contentHeight);
-        clipRect.relocate(getInsets().getLeft() - viewRect.getLayoutX(), getInsets().getTop() - viewRect.getLayoutY());
+        viewRect.resize(snapSize(contentWidth), snapSize(contentHeight));
+        clipRect.setWidth(snapSize(contentWidth));
+        clipRect.setHeight(snapSize(contentHeight));
+        clipRect.relocate(snapPosition(getInsets().getLeft() - viewRect.getLayoutX()), snapPosition(getInsets().getTop() - viewRect.getLayoutY()));
 
         if (vsbvis && hsbvis) {
             corner.setVisible(true);
-            corner.resizeRelocate(vsb.getLayoutX(), hsb.getLayoutY(), vsbWidth, hsbHeight);
+            corner.resizeRelocate(snapPosition(vsb.getLayoutX()), snapPosition(hsb.getLayoutY()), snapSize(vsbWidth), snapSize(hsbHeight));
         } else {
             corner.setVisible(false);
         }
@@ -734,12 +742,12 @@
     }
 
     private void computeScrollBarSize() {
-        vsbWidth = vsb.prefWidth(-1);
+        vsbWidth = snapSize(vsb.prefWidth(-1));
         if (vsbWidth == 0) {
             //            println("*** WARNING ScrollPaneSkin: can't get scroll bar width, using {DEFAULT_SB_BREADTH}");
             vsbWidth = DEFAULT_SB_BREADTH;
         }
-        hsbHeight = hsb.prefHeight(-1);
+        hsbHeight = snapSize(hsb.prefHeight(-1));
         if (hsbHeight == 0) {
             //            println("*** WARNING ScrollPaneSkin: can't get scroll bar height, using {DEFAULT_SB_BREADTH}");
             hsbHeight = DEFAULT_SB_BREADTH;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -158,8 +158,12 @@
      * @param textField not null
      */
     public TextFieldSkin(final TextField textField) {
-        super(textField, new TextFieldBehavior(textField));
-        getBehavior().setTextFieldSkin(this);
+        this(textField, new TextFieldBehavior(textField));
+    }
+
+    public TextFieldSkin(final TextField textField, final TextFieldBehavior behavior) {
+        super(textField, behavior);
+        behavior.setTextFieldSkin(this);
 
 
         caretPosition = new IntegerBinding() {
@@ -332,6 +336,15 @@
             }
         };
 
+        textField.textProperty().addListener(new InvalidationListener() {
+            @Override public void invalidated(Observable observable) {
+                if (!getBehavior().isEditing()) {
+                    // Text changed, but not by user action
+                    updateTextPos();
+                }
+            }
+        });
+
         if (usePromptText.get()) {
             createPromptNode();
         }
@@ -409,6 +422,10 @@
         return lineHeight + (padding.getTop() + padding.getBottom());
     }
 
+    @Override protected double computeMaxHeight(double width) {
+        return getSkinnable().prefHeight(width);
+    }
+
     @Override
     public double getBaselineOffset() {
         FontMetrics fontMetrics = super.fontMetrics.get();       
@@ -530,7 +547,8 @@
 
           case LEFT:
           default:
-            if (textBounds.getMinX() < clipBounds.getMinX() + caretWidth / 2) {
+            if (textBounds.getMinX() < clipBounds.getMinX() + caretWidth / 2 &&
+                textBounds.getMaxX() <= clipBounds.getMaxX()) {
                 double delta = caretMaxXOld - caretBounds.getMaxX() - textTranslateX.get();
                 if (textBounds.getMaxX() + delta < clipBounds.getMaxX()) {
                     if (textMaxXOld <= clipBounds.getMaxX()) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextInputControlSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextInputControlSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -229,12 +229,17 @@
     protected final Path caretPath = new Path();
 
     static boolean useVK = false;
+    static int vkType = -1;
     public void toggleUseVK() {
-        useVK = !useVK;
-        if (useVK) {
+        vkType = (vkType + 1) % 5;
+        if (vkType < 4) {
+            useVK = true;
+            getSkinnable().setImpl_virtualKeyboardType(vkType);
             FXVK.attach(getSkinnable());
         } else {
             FXVK.detach();
+            vkType = -1;
+            useVK = false;
         }
     }
 
@@ -304,13 +309,15 @@
                 }
             }
 
+            final MenuItem undoMI   = new ContextMenuItem("Undo");
+            final MenuItem redoMI   = new ContextMenuItem("Redo");
             final MenuItem cutMI    = new ContextMenuItem("Cut");
             final MenuItem copyMI   = new ContextMenuItem("Copy");
             final MenuItem pasteMI  = new ContextMenuItem("Paste");
             final MenuItem deleteMI = new ContextMenuItem("DeleteSelection");
             final MenuItem selectMI = new ContextMenuItem("SelectAll");
 
-            final ContextMenu cm = new ContextMenu(cutMI, copyMI, pasteMI, deleteMI,
+            final ContextMenu cm = new ContextMenu(undoMI, redoMI, cutMI, copyMI, pasteMI, deleteMI,
                                                    new SeparatorMenuItem(), selectMI);
 
             cm.setOnShowing(new EventHandler<WindowEvent>() {
@@ -318,6 +325,8 @@
                     boolean hasSelection = (textInput.getSelection().getLength() > 0);
                     boolean maskText = (maskText("A") != "A");
 
+                    undoMI.setDisable(!getBehavior().canUndo());
+                    redoMI.setDisable(!getBehavior().canRedo());
                     cutMI.setDisable(maskText || !hasSelection);
                     copyMI.setDisable(maskText || !hasSelection);
                     pasteMI.setDisable(!Clipboard.getSystemClipboard().hasString());
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TreeViewSkin.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TreeViewSkin.java	Mon Apr 09 13:05:50 2012 -0700
@@ -92,11 +92,11 @@
         getBehavior().setOnMoveToLastCell(new Runnable() {
             @Override public void run() { onMoveToLastCell(); }
         });
-        getBehavior().setOnScrollPageDown(new Callback<Void, Integer>() {
-            @Override public Integer call(Void param) { return onScrollPageDown(); }
+        getBehavior().setOnScrollPageDown(new Callback<Integer, Integer>() {
+            @Override public Integer call(Integer anchor) { return onScrollPageDown(anchor); }
         });
-        getBehavior().setOnScrollPageUp(new Callback<Void, Integer>() {
-            @Override public Integer call(Void param) { return onScrollPageUp(); }
+        getBehavior().setOnScrollPageUp(new Callback<Integer, Integer>() {
+            @Override public Integer call(Integer anchor) { return onScrollPageUp(anchor); }
         });
         getBehavior().setOnSelectPreviousRow(new Runnable() {
             @Override public void run() { onSelectPreviousCell(); }
@@ -353,12 +353,13 @@
      * Function used to scroll the container down by one 'page', although
      * if this is a horizontal container, then the scrolling will be to the right.
      */
-    public int onScrollPageDown() {
+    public int onScrollPageDown(int anchor) {
         IndexedCell lastVisibleCell = flow.getLastVisibleCellWithinViewPort();
         if (lastVisibleCell == null) return -1;
 
         int newSelectionIndex = -1;
-        if (! (lastVisibleCell.isSelected() || lastVisibleCell.isFocused())) {
+        int lastVisibleCellIndex = lastVisibleCell.getIndex();
+        if (! (lastVisibleCell.isSelected() || lastVisibleCell.isFocused()) || (lastVisibleCellIndex != anchor)) {
             // if the selection is not on the 'bottom' most cell, we firstly move
             // the selection down to that, without scrolling the contents
             newSelectionIndex = lastVisibleCell.getIndex();
@@ -380,12 +381,13 @@
      * Function used to scroll the container up by one 'page', although
      * if this is a horizontal container, then the scrolling will be to the left.
      */
-    public int onScrollPageUp() {
+    public int onScrollPageUp(int anchor) {
         IndexedCell firstVisibleCell = flow.getFirstVisibleCellWithinViewPort();
         if (firstVisibleCell == null) return -1;
 
         int newSelectionIndex = -1;
-        if (! (firstVisibleCell.isSelected() || firstVisibleCell.isFocused())) {
+        int firstVisibleCellIndex = firstVisibleCell.getIndex();
+        if (! (firstVisibleCell.isSelected() || firstVisibleCell.isFocused()) || (firstVisibleCellIndex != anchor)) {
             // if the selection is not on the 'top' most cell, we firstly move
             // the selection up to that, without scrolling the contents
             newSelectionIndex = firstVisibleCell.getIndex();
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualContainerBase.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualContainerBase.java	Mon Apr 09 13:05:50 2012 -0700
@@ -30,6 +30,7 @@
 import javafx.scene.control.IndexedCell;
 
 import com.sun.javafx.scene.control.behavior.BehaviorBase;
+import java.util.Map;
 
 /**
  * Parent class to control skins whose contents are virtualized and scrollable.
@@ -43,31 +44,19 @@
 
     public static final String SCROLL_TO_INDEX_CENTERED = "VirtualContainerBase.scrollToIndexCentered";
     public static final String SCROLL_TO_INDEX_TOP = "VirtualContainerBase.scrollToIndexTop";
+    public static final String SCROLL_TO_OFFSET = "VirtualContainerBase.scrollToOffset";    
 
-    public VirtualContainerBase(C control, B behavior) {
+    public VirtualContainerBase(final C control, B behavior) {
         super(control, behavior);
         
         flow = new VirtualFlow();
+        handleControlProperties(control);
 
         control.getProperties().addListener(new MapChangeListener<Object, Object>() {
             @Override
             public void onChanged(Change<? extends Object, ? extends Object> c) {
-                if (c.wasAdded() && SCROLL_TO_INDEX_CENTERED.equals(c.getKey())) {
-                    Object row = c.getValueAdded();
-                    if (row instanceof Integer) {
-                        // we want the index to be centered
-                        flow.scrollTo((Integer)row, true);
-                    }
-
-                    c.getMap().remove(SCROLL_TO_INDEX_CENTERED);
-                } else if (c.wasAdded() && SCROLL_TO_INDEX_TOP.equals(c.getKey())) {
-                    Object index = c.getValueAdded();
-                    if (index instanceof Integer) {
-                        // we don't want the index to be centered
-                        flow.scrollTo((Integer)index, false);
-                    }
-
-                    c.getMap().remove(SCROLL_TO_INDEX_TOP);
+                if (c.wasAdded()) {
+                    handleControlProperties(control);
                 }
             }
         });
@@ -108,4 +97,32 @@
         
         return height + getInsets().getTop() + getInsets().getBottom();
     }
+    
+    private void handleControlProperties(C control) {
+        Map<Object, Object>properties = control.getProperties();
+        if (properties.containsKey(SCROLL_TO_INDEX_CENTERED)) {
+            Object row = properties.get(SCROLL_TO_INDEX_CENTERED);
+            if (row instanceof Integer) {
+                // we want the index to be centered
+                flow.scrollTo((Integer)row, true);
+            }
+
+            properties.remove(SCROLL_TO_INDEX_CENTERED);
+        } else if (properties.containsKey(SCROLL_TO_INDEX_TOP)) {
+            Object index = properties.get(SCROLL_TO_INDEX_TOP);
+            if (index instanceof Integer) {
+                // we don't want the index to be centered
+                flow.scrollTo((Integer)index, false);
+            }
+
+            properties.remove(SCROLL_TO_INDEX_TOP);
+        } else if (properties.containsKey(SCROLL_TO_OFFSET)) {
+            Object offset = properties.get(SCROLL_TO_OFFSET);
+            if (offset instanceof Integer) {
+                flow.scrollToOffset((Integer)offset);
+            }
+
+            properties.remove(SCROLL_TO_OFFSET);
+        }        
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Mon Apr 09 13:05:50 2012 -0700
@@ -1286,13 +1286,28 @@
 
         // Now position and update the scroll bars
         if (breadthBar.isVisible()) {
+            double topPadding;
+            double bottomPadding;
+            double leftPadding;
+            double rightPadding;
+
+            Parent parent = getParent();
+            if (parent instanceof Region) {
+                topPadding = ((Region)parent).getInsets().getTop();
+                bottomPadding = ((Region)parent).getInsets().getBottom();
+                leftPadding = ((Region)parent).getInsets().getLeft();
+                rightPadding = ((Region)parent).getInsets().getRight();
+            }
+            else {
+                topPadding = bottomPadding = leftPadding = rightPadding = 0.0;
+            }
+
             if (isVertical()) {
-                hbar.resizeRelocate(0, viewportLength,
-                        viewportBreadth, hbar.prefHeight(viewportBreadth));
+                hbar.resizeRelocate(0-leftPadding, viewportLength+topPadding,
+                                    viewportBreadth+(leftPadding+rightPadding), hbar.prefHeight(viewportBreadth));
             } else {
-                vbar.resizeRelocate(viewportLength, 0,
-                        vbar.prefWidth(viewportBreadth), viewportBreadth);
-
+                vbar.resizeRelocate(viewportLength+leftPadding, 0-topPadding,
+                                    vbar.prefWidth(viewportBreadth), viewportBreadth+(topPadding+bottomPadding));
             }
 
             // There was a weird bug where the newMax would sometimes go < 0
@@ -1334,11 +1349,26 @@
 //                lengthBar.setValue(0.99);
 //            }
 
+            double topPadding;
+            double bottomPadding;
+            double leftPadding;
+            double rightPadding;
+
+            Parent parent = getParent();
+            if (parent instanceof Region) {
+                topPadding = ((Region)parent).getInsets().getTop();
+                bottomPadding = ((Region)parent).getInsets().getBottom();
+                leftPadding = ((Region)parent).getInsets().getLeft();
+                rightPadding = ((Region)parent).getInsets().getRight();
+            }
+            else {
+                topPadding = bottomPadding = leftPadding = rightPadding = 0.0;
+            }
+
             if (isVertical()) {
-                double h = viewportLength;
-                vbar.resizeRelocate(viewportBreadth, 0, vbar.prefWidth(h), h);
+                vbar.resizeRelocate(viewportBreadth+leftPadding, 0-topPadding, vbar.prefWidth(viewportLength), viewportLength+(topPadding+bottomPadding));
             } else {
-                hbar.resizeRelocate(0, viewportBreadth, viewportLength, hbar.prefHeight(-1));
+                hbar.resizeRelocate(0-leftPadding, viewportBreadth+topPadding, viewportLength+(leftPadding+rightPadding), hbar.prefHeight(-1));
             }
         }
 
@@ -1793,6 +1823,12 @@
         requestLayout();        
     }
     
+    //TODO We assume all the cell have the same length.  We will need to support
+    // cells of different lengths.
+    public void scrollToOffset(int offset) {
+        adjustPixels(offset * getCellLength(0));
+    }    
+    
     /**
      * Given a delta value representing a number of pixels, this method attempts
      * to move the VirtualFlow in the given direction (positive is down/right,
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Mon Apr 09 13:05:50 2012 -0700
@@ -1308,12 +1308,12 @@
 
 .scroll-pane > * > .scroll-bar:horizontal {
     -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0.0 0.0 -1.0 -1.0;
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .scroll-pane > * > .scroll-bar:vertical {
     -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -1.0 -1.0 -1.0 0.0;
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .scroll-pane .corner {
@@ -1491,12 +1491,12 @@
 
 .list-view .scroll-bar:vertical{
     -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -0.083333em -0.083333em -0.083333em 0.0em; /* -1 -1 -1 0 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .list-view .scroll-bar:horizontal{
     -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0.0em 0.0em -0.083333em -0.083333em; /* 0 0 -1 -1 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .list-view:disabled {
@@ -1604,12 +1604,12 @@
 
 .tree-view .scroll-bar:vertical{
     -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -0.083333em -0.083333em -0.083333em 0.0em; /* -1 -1 -1 0 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .tree-view .scroll-bar:horizontal{
     -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0.0em 0.0em -0.083333em -0.083333em; /* 0 0 -1 -1 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .tree-view:disabled {
@@ -2098,12 +2098,12 @@
 
 .table-view .scroll-bar:vertical{
     -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -0.083333em -0.083333em -0.083333em 0.0em; /* -1 -1 -1 0 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .table-view .scroll-bar:horizontal{
     -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0.0em 0.0em -0.083333em -0.083333em; /* 0 0 -1 -1 */
+    -fx-padding: 0.0 0.0 0.0 0.0;
 }
 
 .table-view .corner {
@@ -3115,41 +3115,6 @@
     -fx-skin: "com.sun.javafx.scene.control.skin.ComboBoxListViewSkin";
 }
 
-.color-picker {
-    -fx-skin: "com.sun.javafx.scene.control.skin.ColorPickerSkin";
-}
-
-.color-picker .color-picker-label .text {
-    -fx-padding: 0.0em 0.833333em 0.0em 0.0em;  /* 0 10 0 0 */
-}
-
-.color-picker.split-button .arrow-button {
-    -fx-background-color: -fx-outer-border, -fx-inner-border, -fx-body-color;
-    -fx-background-insets: 0, 1, 2;
-    -fx-background-radius: 0 5 5 0, 0 4 4 0, 0 3 3 0;
-    -fx-padding: 0.5em 0.416667em 0.5em 0.416667em; /* 6 5 6 5 */
-}
-
-.color-picker:hover {
-    -fx-color: -fx-base;
-}
-
-.color-picker.split-button .arrow-button:hover {
-    -fx-color: -fx-hover-base;
-}
-
-.color-picker.split-button .color-picker-label:hover {
-     -fx-color: -fx-hover-base;
-}
-
-.color-picker .picker-color {
-    -fx-padding: 4;
-    -fx-background-color: null;
-}
-
-.color-panel {
-    -fx-padding: 15;
-}
 /* Customie the ListCell that appears in the ComboBox button itself */
 .combo-box .list-cell {
     -fx-background: transparent;
@@ -3183,62 +3148,203 @@
     -fx-text-fill: -fx-text-inner-color;
 }
 
+/* -------------------------- STYLES FOR THE COLOR-PICKER CONTROL ----- */
+.color-picker {
+    -fx-skin: "com.sun.javafx.scene.control.skin.ColorPickerSkin";
+}
+
+.color-picker .color-picker-label .text {
+    -fx-padding: 0.0em 0.833333em 0.0em 0.0em;  /* 0 10 0 0 */
+}
+
+.color-picker.split-button .arrow-button {
+    -fx-background-color: -fx-outer-border, -fx-inner-border, -fx-body-color;
+    -fx-background-insets: 0, 1, 2;
+    -fx-background-radius: 0 5 5 0, 0 4 4 0, 0 3 3 0;
+    -fx-padding: 0.5em 0.416667em 0.5em 0.416667em; /* 6 5 6 5 */
+}
+
+.color-picker:hover {
+    -fx-color: -fx-base;
+}
+
+.color-picker.split-button .arrow-button:hover {
+    -fx-color: -fx-hover-base;
+}
+
+.color-picker.split-button .color-picker-label:hover {
+     -fx-color: -fx-hover-base;
+}
+
+.color-picker .picker-color {
+    -fx-padding: 4;
+    -fx-background-color: null;
+}
+
+.color-panel {
+    -fx-padding: 15;
+}
+
+.color-picker-grid .separator .line {
+    -fx-background-color: -fx-pressed-base;
+    -fx-padding: 6 0 0 0;
+    -fx-background-insets: 4 -10 0 -10,5 -10 0 -10;
+    -fx-border-style: none;
+    -fx-border-color: null;
+}
+.add-color-pane .color-rect-pane {
+    -fx-padding: 15;
+    -fx-background-color: -fx-hover-base; 
+}
+
+.add-color-pane .controls-pane {
+    -fx-padding: 15 15 5 15;
+    -fx-background-color: -fx-hover-base;   
+}
+.add-color-pane  {
+    -fx-padding: 0 0 0 10;
+    -fx-background-color: -fx-hover-base;
+}
+
+.controls-pane #pill-left {
+    -fx-padding: 0 8 0 8;
+    -fx-border-image-source: url("left-btn.png");
+    -fx-border-image-slice: 4 4 4 4 fill;
+    -fx-border-image-width: 4 4 4 4;
+    -fx-border-image-insets: 0;
+    -fx-border-image-repeat: stretch;
+    -fx-background-color: null !important; 
+}
+.controls-pane #pill-left:selected { -fx-border-image-source: url("left-btn-selected.png"); }
+.controls-pane #pill-left .label {
+    -fx-text-fill: #d3d3d3; 
+    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.75) , 0, 0.0 , 0 , -1 );
+}
+
+.controls-pane #pill-left:selected .label {
+   -fx-text-fill: white;
+    -fx-effect: dropshadow( one-pass-box , white , 0, 0.0 , 0 , 1 );
+}
+.controls-pane #pill-center {
+    -fx-padding: 0 8 0 8;
+    -fx-border-image-source: url("center-btn.png");
+    -fx-border-image-slice: 4 4 4 4 fill;
+    -fx-border-image-width: 4 4 4 4;
+    -fx-border-image-insets: 0;
+    -fx-border-image-repeat: stretch;
+    -fx-background-color: null !important;
+}
+.controls-pane #pill-center:selected { -fx-border-image-source: url("center-btn-selected.png"); }
+.controls-pane #pill-center .label {
+    -fx-text-fill: #d3d3d3;
+    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.75) , 0, 0.0 , 0 , -1 );
+}
+.controls-pane #pill-center:selected .label {
+    -fx-text-fill: black;
+    -fx-effect: dropshadow( one-pass-box , white , 0, 0.0 , 0 , 1 );
+}
+.controls-pane #pill-right {
+    -fx-padding: 0 8 0 8;
+    -fx-border-image-source: url("right-btn.png");
+    -fx-border-image-slice: 4 4 4 4 fill;
+    -fx-border-image-width: 4 4 4 4;
+    -fx-border-image-insets: 0;
+    -fx-border-image-repeat: stretch;
+    -fx-background-color: null !important;
+}
+.controls-pane #pill-right:selected { -fx-border-image-source: url("right-btn-selected.png"); }
+.controls-pane #pill-right .label {
+    -fx-text-fill: #d3d3d3;
+    -fx-effect: dropshadow( one-pass-box , rgba(0,0,0,0.75) , 0, 0.0 , 0 , -1 );
+}
+.controls-pane #pill-right:selected .label {
+    -fx-text-fill: black;
+    -fx-effect: dropshadow( one-pass-box , white , 0, 0.0 , 0 , 1 );
+}
+
+
+.controls-pane .addcolor-controls-background {
+    -fx-background-color: -fx-text-box-border, -fx-text-box-border, -fx-control-inner-background;
+    -fx-background-insets: 0, 1, 2;
+    -fx-background-radius: 3, 2, 2;
+    -fx-padding: 3 5 3 5;
+}
+
+.controls-pane .alpha-settings .text-field {
+/*    -fx-background-color: -fx-shadow-highlight-color, -fx-text-box-border, -fx-control-inner-background;*/
+    -fx-background-color: -fx-text-box-border, -fx-control-inner-background;
+    -fx-background-insets: 0, 1;
+    -fx-background-radius: 3, 2;
+    -fx-padding: 3 5 3 5;
+    -fx-text-fill: -fx-text-inner-color;
+    -fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
+    -fx-cursor: text;
+}
+
+/* -------------------------- STYLES FOR PAGINATION CONTROL ----- */
 .pagination-cell {
-    -fx-skin: "com.sun.javafx.scene.control.skin.PaginationCellSkin";
-    -fx-backgound-color: yellow;
-}
-
-.pagination-cell:odd {
-    -fx-backgound-color: green;
+    -fx-skin: "com.sun.javafx.scene.control.skin.PaginationCellSkin";    
+    -fx-padding: 0;
 }
 
 .pagination {
     -fx-skin: "com.sun.javafx.scene.control.skin.PaginationSkin";
+    -fx-padding: 0;
 }
 
 .pagination .pagination-control {
     -fx-alignment: BOTTOM_CENTER;
-    -fx-background-color: transparent;
-}
-
-.pagination .pagination-control > .arrow-button {    
+    -fx-background-color: transparent;    
+}
+
+.pagination .pagination-control > .page-navigation {    
     -fx-padding: 0.833333em 0.416667em 0.833333em 0.416667em; /* 10 5 10 5 */    
 }
 
-.pagination .pagination-control > .arrow-button .left-arrow {
+.pagination .pagination-control > .page-navigation .left-arrow {
     -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
     -fx-background-insets: 1 0 -1 0, 0;
     -fx-padding: 0.25em 0.3125em 0.25em 0.3125em; /* 3 3.75 3 3.75 */
     -fx-shape: "M 0 0 L -4 3.5 L 0 7 z";
 }
 
-.pagination .pagination-control > .arrow-button .right-arrow {
+.pagination .pagination-control > .page-navigation .right-arrow {
     -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
     -fx-background-insets: 1 0 -1 0, 0;
     -fx-padding: 0.25em 0.3125em 0.25em 0.3125em; /* 3 3.75 3 3.75 */
     -fx-shape: "M 0 0 L 4 3.5 L 0 7 z";
 }
 
-.pagination .pagination-control > .arrow-button .bullet-indicator {
+.pagination .pagination-control > .page-navigation .bullet {
    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-insets: 0 0 -1 0,  0,  1,  2;
    -fx-background-radius: 1.0em; /* large value to make sure this remains circular */
    -fx-padding: 0.333333em; /* 4 -- padding from outside edge to the inner black dot */
 }
 
-.pagination .pagination-control > .arrow-button:selected .bullet-indicator {
+.pagination .pagination-control > .page-navigation:selected .bullet {
    -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
    -fx-background-insets: 0 0 -1 0, 0;    
 }
 
-.pagination .pagination-control > .arrow-button .number-indicator {
+.pagination .pagination-control > .page-navigation .number {
     -fx-font-size: 10px;
-    -fx-background-color: -fx-mark-highlight-color, white;
-    -fx-background-insets: 1 0 -1 0, 0;    
-    -fx-padding: 2 10 2 10; /*0.25em 0.25em 0.25em 0.25em; /* 3 3 3 3*/
-    -fx-shape: "M0 0 L0 0 L5 0 L5 5 L0 5 Z";
-}
-
-.pagination .pagination-control > .arrow-button:selected .number-indicator {
-    -fx-background-color: transparent, red;    
-}
+    -fx-background-color: -fx-mark-highlight-color, white;    
+    -fx-padding: 0 2 0 2;
+}
+
+.pagination .pagination-control > .page-navigation:selected .number {
+    -fx-background-color: transparent, red;        
+}
+
+.pagination .list-view .scroll-bar:horizontal, 
+.pagination .list-view .scroll-bar:horizontal .thumb, 
+.pagination .list-view .scroll-bar:horizontal .track, 
+.pagination .list-view .scroll-bar:horizontal .increment-button, 
+.pagination .list-view .scroll-bar:horizontal .decrement-button, 
+.pagination .list-view .scroll-bar:horizontal .increment-arrow,
+.pagination .list-view .scroll-bar:horizontal .decrement-arrow {
+    -fx-background-color: null;
+    -fx-background-insets: 0;
+    -fx-padding: 0;
+}
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/center-btn-selected.png has changed
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/center-btn.png has changed
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/left-btn-selected.png has changed
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/left-btn.png has changed
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/right-btn-selected.png has changed
Binary file javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/right-btn.png has changed
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/resources/controls.properties	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/resources/controls.properties	Mon Apr 09 13:05:50 2012 -0700
@@ -9,8 +9,184 @@
 
 # TextField, PasswordField, TextArea
 # The keys must match the action strings in TextInputControlBehavior
+TextInputControl.menu.Undo=Undo
+TextInputControl.menu.Redo=Redo
 TextInputControl.menu.Cut=Cut
 TextInputControl.menu.Copy=Copy
 TextInputControl.menu.Paste=Paste
 TextInputControl.menu.DeleteSelection=Delete
 TextInputControl.menu.SelectAll=Select All
+
+# Virtual Keyboard for embedded FX. This will not be localized for FX 2.2.
+#
+# Note: There can at most be only one of each of the special keys: BACKSPACE ENTER SHIFT SYM
+#
+# TODO: use sect, deg, reg, tm only once
+#
+FXVK.Text.row1.key01=q 1 [
+FXVK.Text.row1.key02=w 2 ]
+FXVK.Text.row1.key03=e 3 { egrave eacute ecirc euml
+FXVK.Text.row1.key04=r 4 } reg
+FXVK.Text.row1.key05=t 5 \\ tm
+FXVK.Text.row1.key06=y 6 | ygrave yacute ycirc ymacron yuml yhook
+FXVK.Text.row1.key07=u 7 \" ugrave uacute ucirc uuml
+FXVK.Text.row1.key08=i 8 < igrave iacute icirc iuml
+FXVK.Text.row1.key09=o 9 > ograve oacute ocirc otilde ouml oslash deg
+FXVK.Text.row1.key10=p 0 _ sect para pi
+
+FXVK.Text.row2.key01=a @ ~ agrave aacute acirc atilde auml aring
+FXVK.Text.row2.key02=s # ` scedil scaron szlig sigma
+FXVK.Text.row2.key03=d $ euro eth
+FXVK.Text.row2.key04=f % pound
+FXVK.Text.row2.key05=g ^ yen
+FXVK.Text.row2.key06=h & sect
+FXVK.Text.row2.key07=j * middot
+FXVK.Text.row2.key08=k ( deg
+FXVK.Text.row2.key09=l ) neq
+
+FXVK.Text.row3.key01=SHIFT
+FXVK.Text.row3.key02=z - iexcl
+FXVK.Text.row3.key03=x = iquest
+FXVK.Text.row3.key04=c + permil ccedil copy cent
+FXVK.Text.row3.key05=v ; reg
+FXVK.Text.row3.key06=b : tm
+FXVK.Text.row3.key07=n / laquo ntilde
+FXVK.Text.row3.key08=m ' raquo micro
+FXVK.Text.row3.key09=' "
+FXVK.Text.row3.key10=BACKSPACE
+
+FXVK.Text.row4.key01=SYM
+FXVK.Text.row4.key01.width=1.5
+FXVK.Text.row4.key02=space
+FXVK.Text.row4.key02.width=5
+FXVK.Text.row4.key03=, !
+FXVK.Text.row4.key04=. ?
+FXVK.Text.row4.key05=ENTER
+FXVK.Text.row4.key05.width=1.5
+
+
+
+FXVK.Numeric.row1.key01=1
+FXVK.Numeric.row1.key01.width=1.5
+FXVK.Numeric.row1.key02=2
+FXVK.Numeric.row1.key02.width=1.5
+FXVK.Numeric.row1.key03=3
+FXVK.Numeric.row1.key03.width=1.5
+FXVK.Numeric.row1.key04=/
+FXVK.Numeric.row1.key04.width=1.5
+
+FXVK.Numeric.row2.key01=4
+FXVK.Numeric.row2.key01.width=1.5
+FXVK.Numeric.row2.key02=5
+FXVK.Numeric.row2.key02.width=1.5
+FXVK.Numeric.row2.key03=6
+FXVK.Numeric.row2.key03.width=1.5
+FXVK.Numeric.row2.key04=-
+FXVK.Numeric.row2.key04.width=1.5
+
+FXVK.Numeric.row3.key01=7
+FXVK.Numeric.row3.key01.width=1.5
+FXVK.Numeric.row3.key02=8
+FXVK.Numeric.row3.key02.width=1.5
+FXVK.Numeric.row3.key03=9
+FXVK.Numeric.row3.key03.width=1.5
+FXVK.Numeric.row3.key04=BACKSPACE
+FXVK.Numeric.row3.key04.width=1.5
+
+FXVK.Numeric.row4.key01=.
+FXVK.Numeric.row4.key01.width=1.5
+FXVK.Numeric.row4.key02=0
+FXVK.Numeric.row4.key02.width=1.5
+FXVK.Numeric.row4.key03=,
+FXVK.Numeric.row4.key03.width=1.5
+FXVK.Numeric.row4.key04=ENTER
+FXVK.Numeric.row4.key04.width=1.5
+
+
+FXVK.URL.row1.key01=q 1
+FXVK.URL.row1.key02=w 2
+FXVK.URL.row1.key03=e 3
+FXVK.URL.row1.key04=r 4
+FXVK.URL.row1.key05=t 5
+FXVK.URL.row1.key06=y 6
+FXVK.URL.row1.key07=u 7
+FXVK.URL.row1.key08=i 8
+FXVK.URL.row1.key09=o 9
+FXVK.URL.row1.key10=p 0 _
+
+FXVK.URL.row2.key01=a @ ~
+FXVK.URL.row2.key02=s #
+FXVK.URL.row2.key03=d $
+FXVK.URL.row2.key04=f %
+FXVK.URL.row2.key05=g ^
+FXVK.URL.row2.key06=h &
+FXVK.URL.row2.key07=j *
+FXVK.URL.row2.key08=k (
+FXVK.URL.row2.key09=l )
+
+FXVK.URL.row3.key01=SHIFT
+FXVK.URL.row3.key02=z -
+FXVK.URL.row3.key03=x =
+FXVK.URL.row3.key04=c +
+FXVK.URL.row3.key05=v ;
+FXVK.URL.row3.key06=b :
+FXVK.URL.row3.key07=n "
+FXVK.URL.row3.key08=m '
+FXVK.URL.row3.key09=- _
+FXVK.URL.row3.key10=BACKSPACE
+
+FXVK.URL.row4.key01=SYM
+FXVK.URL.row4.key01.width=1.5
+FXVK.URL.row4.key02=www.
+FXVK.URL.row4.key02.width=1.5
+FXVK.URL.row4.key03=.
+FXVK.URL.row4.key03.width=1.5
+FXVK.URL.row4.key04=.com .org .net .gov .edu
+FXVK.URL.row4.key04.width=1.5
+FXVK.URL.row4.key05=/ \\
+FXVK.URL.row4.key05.width=1.5
+FXVK.URL.row4.key06=ENTER
+FXVK.URL.row4.key06.width=2.5
+
+
+FXVK.Email.row1.key01=q 1 !
+FXVK.Email.row1.key02=w 2
+FXVK.Email.row1.key03=e 3
+FXVK.Email.row1.key04=r 4
+FXVK.Email.row1.key05=t 5
+FXVK.Email.row1.key06=y 6
+FXVK.Email.row1.key07=u 7
+FXVK.Email.row1.key08=i 8
+FXVK.Email.row1.key09=o 9
+FXVK.Email.row1.key10=p 0 _
+
+FXVK.Email.row2.key01=a @ ~
+FXVK.Email.row2.key02=s #
+FXVK.Email.row2.key03=d $
+FXVK.Email.row2.key04=f %
+FXVK.Email.row2.key05=g ^
+FXVK.Email.row2.key06=h &
+FXVK.Email.row2.key07=j *
+FXVK.Email.row2.key08=k (
+FXVK.Email.row2.key09=l )
+
+FXVK.Email.row3.key01=SHIFT
+FXVK.Email.row3.key02=z -
+FXVK.Email.row3.key03=x =
+FXVK.Email.row3.key04=c +
+FXVK.Email.row3.key05=v ;
+FXVK.Email.row3.key06=b :
+FXVK.Email.row3.key07=n /
+FXVK.Email.row3.key08=m '
+FXVK.Email.row3.key09=@ ?
+FXVK.Email.row3.key10=BACKSPACE
+
+FXVK.Email.row4.key01=SYM
+FXVK.Email.row4.key01.width=1.5
+FXVK.Email.row4.key02=space
+FXVK.Email.row4.key02.width=4
+FXVK.Email.row4.key03=. ,
+FXVK.Email.row4.key04=- _
+FXVK.Email.row4.key05=.com .org .net .gov .edu
+FXVK.Email.row4.key06=ENTER
+FXVK.Email.row4.key06.width=1.5
--- a/javafx-ui-controls/src/javafx/scene/control/Tab.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/Tab.java	Mon Apr 09 13:05:50 2012 -0700
@@ -46,6 +46,7 @@
 import java.lang.ref.Reference;
 import java.util.Collections;
 import java.util.List;
+import javafx.beans.DefaultProperty;
 import javafx.beans.property.ReadOnlyBooleanProperty;
 import javafx.beans.property.ReadOnlyBooleanWrapper;
 import javafx.beans.property.ReadOnlyObjectProperty;
@@ -59,6 +60,7 @@
  * <p>When the user clicks
  * on a Tab in the TabPane the Tab content becomes visible to the user.</p>
  */
+@DefaultProperty("content")
 public class Tab implements EventTarget {
 
     /***************************************************************************
--- a/javafx-ui-controls/src/javafx/scene/control/TextInputControl.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TextInputControl.java	Mon Apr 09 13:05:50 2012 -0700
@@ -31,6 +31,7 @@
 import javafx.beans.binding.IntegerBinding;
 import javafx.beans.binding.StringBinding;
 import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.IntegerProperty;
 import javafx.beans.property.ReadOnlyIntegerProperty;
 import javafx.beans.property.ReadOnlyIntegerWrapper;
 import javafx.beans.property.ReadOnlyObjectProperty;
@@ -38,6 +39,7 @@
 import javafx.beans.property.ReadOnlyStringProperty;
 import javafx.beans.property.ReadOnlyStringWrapper;
 import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleIntegerProperty;
 import javafx.beans.property.StringProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableStringValue;
@@ -1039,6 +1041,42 @@
         }
     }
 
+    /*
+     * Virtual keyboard API. This is not yet final.
+     *
+     * 0: Text, 1: Numeric, 2: URL, 3: Email address
+     *
+     * TODO: Use mnemonics until VK API is avaiulable in Quantum.
+     */
+
+    /** @treatAsPrivate implementation detail */
+    private IntegerProperty impl_virtualKeyboardType = new SimpleIntegerProperty(this, "impl_virtualKeyboardType", 0);
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public final void setImpl_virtualKeyboardType(int value) { impl_virtualKeyboardType.set(value); }
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public final int getImpl_virtualKeyboardType() { return impl_virtualKeyboardType.get(); }
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public final IntegerProperty impl_virtualKeyboardTypePoperty() { return impl_virtualKeyboardType; }
+
+
+
+    @Deprecated
+    public void impl_enableVirtualKeyboard(int type) {
+    }
+
     /***************************************************************************
      *                                                                         *
      * Stylesheet Handling                                                     *
--- a/javafx-ui-controls/test/com/sun/javafx/scene/control/skin/ScrollPaneSkinTest.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/skin/ScrollPaneSkinTest.java	Mon Apr 09 13:05:50 2012 -0700
@@ -12,13 +12,19 @@
 import javafx.event.EventType;
 import javafx.scene.Group;
 import javafx.scene.Scene;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.RadioButton;
 import javafx.scene.control.ScrollPane;
 import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.input.ScrollEvent;
+import javafx.scene.layout.HBox;
 import javafx.scene.layout.Pane;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.StackPane;
+import javafx.scene.layout.VBox;
 import javafx.scene.shape.Rectangle;
 import javafx.stage.Stage;
 
@@ -567,6 +573,50 @@
     }
 
     
+    /*
+    ** check if 'reduced-size' scrollbars leave a gap
+    ** at the right edge 
+    */
+    @Test public void checkForScrollBarGaps() {
+   
+        HBox hbox1 = new HBox(20);
+        VBox vbox1a = new VBox(10);
+        vbox1a.getChildren().addAll(new Label("one"), new Button("two"), new CheckBox("three"), new RadioButton("four"), new Label("five"));
+        VBox vbox1b = new VBox(10);
+        vbox1b.getChildren().addAll(new Label("one"), new Button("two"), new CheckBox("three"), new RadioButton("four"), new Label("five"));
+        hbox1.getChildren().addAll(vbox1a, vbox1b);
+        scrollPane.setContent(hbox1);
+        scrollPane.setStyle("-fx-background-color: red;-fx-border-color:green;");
+        scrollPane.setFocusTraversable(false);
+        scrollPane.setPrefSize(50, 50);
+
+
+        Scene scene = new Scene(new Group(), 400, 400);
+        ((Group) scene.getRoot()).getChildren().clear();
+        ((Group) scene.getRoot()).getChildren().add(scrollPane);
+        Stage stage = new Stage();
+        stage.setScene(scene);
+        stage.show();
+        
+
+        /*
+        ** did it work?
+        ** check that the scrollbar is right at the border of the scrollpane, and
+        ** not at the padding.
+        */
+        ScrollPaneSkinMock skin = (ScrollPaneSkinMock) scrollPane.getSkin();
+
+        double skinWidth = skin.getWidth();
+        double vsbPosAndWidth = skin.getVsbX()+skin.getVsbWidth()+(skin.getInsets().getRight() - skin.getPadding().getRight());
+        assertEquals(skinWidth,  vsbPosAndWidth, 0.1);
+
+        double skinHeight = skin.getHeight();
+        double hsbPosAndHeight = skin.getHsbY()+skin.getHsbHeight()+(skin.getInsets().getBottom() - skin.getPadding().getBottom());
+        assertEquals(skinHeight,  hsbPosAndHeight, 0.1);
+
+    }
+
+    
     public static final class ScrollPaneSkinMock extends ScrollPaneSkin {
         boolean propertyChanged = false;
         int propertyChangeCount = 0;
@@ -587,7 +637,18 @@
         boolean isVSBarVisible() {
             return vsb.isVisible();
         }
-
+        double getVsbX() {
+            return vsb.getLayoutX();
+        }
+        double getVsbWidth() {
+            return vsb.getWidth();
+        }
+        double getHsbY() {
+            return hsb.getLayoutY();
+        }
+        double getHsbHeight() {
+            return hsb.getHeight();
+        }
     }
 
     private static class MouseEventGenerator {
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Mon Apr 09 13:05:50 2012 -0700
@@ -152,14 +152,14 @@
         testInitialState();
     }
     
-    /* test 19
+    // test 19
     @Test public void testCtrlDownMovesFocusButLeavesSelectionAlone() {
         assertTrue(fm.isFocused(0));
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
         assertTrue(fm.isFocused(1));
         assertTrue(sm.isSelected(0));
         assertFalse(sm.isSelected(1));
-    } */
+    }
     
     // test 20
     @Test public void testCtrlUpDoesNotMoveFocus() {
@@ -185,7 +185,7 @@
         assertTrue(sm.isSelected(0));
     }
     
-    /* test 23
+    // test 23
     @Test public void testCtrlUpMovesFocus() {
         sm.clearAndSelect(1);
         assertTrue(fm.isFocused(1));
@@ -193,7 +193,7 @@
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
         assertTrue(fm.isFocused(0));
 
-    } */
+    } 
     
     // test 24
     @Test public void testCtrlDownDoesNotMoveFocusWhenAtLastIndex() {
@@ -206,7 +206,7 @@
         assertTrue(sm.isSelected(endIndex));
     }
     
-    /* test 25
+    // test 25
     @Test public void testCtrlDownArrowWithSpaceChangesAnchor() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -215,9 +215,9 @@
         assertTrue(isSelected(0, 2));
         assertTrue(isNotSelected(1));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 26
+    // test 26
     @Test public void testCtrlUpArrowWithSpaceChangesAnchor() {
         sm.clearAndSelect(2);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -226,7 +226,7 @@
         assertTrue(isSelected(0, 2));
         assertTrue(isNotSelected(1));
         assertTrue(isAnchor(0));
-    } */
+    } 
     
     // test 44
     @Test public void testHomeKey() {
@@ -244,23 +244,23 @@
         assertTrue(isNotSelected(1,2,3));
     }
     
-    /* test 53
+    // test 53
     @Test public void testCtrlHome() {
         sm.clearAndSelect(5);
         keyboard.doKeyPress(KeyCode.HOME, KeyModifier.getShortcutKey());
         assertTrue(isSelected(5));
         assertTrue(fm.isFocused(0));
-    } */
+    } 
     
-    /* test 54
+    // test 54
     @Test public void testCtrlEnd() {
         sm.clearAndSelect(5);
         keyboard.doKeyPress(KeyCode.END, KeyModifier.getShortcutKey());
         assertTrue(isSelected(5));
         assertTrue(fm.isFocused(listView.getItems().size() - 1));
-    } */
+    } 
     
-    /* test 68
+    // test 68
     @Test public void testCtrlSpaceToClearSelection() {
         sm.clearAndSelect(5);
         assertTrue(isSelected(5));
@@ -269,7 +269,7 @@
         assertTrue(isNotSelected(5));
         assertTrue(debug(), fm.isFocused(5));
         assertTrue(isAnchor(5));
-    } */
+    } 
     
     
     
@@ -382,7 +382,7 @@
         assertFalse(sm.isSelected(endIndex - 2));
     }
     
-    /* test 27
+    // test 27
     @Test public void testCtrlDownArrowWithSpaceChangesAnchor_extended() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -395,9 +395,9 @@
         assertTrue(isSelected(2));
         assertTrue(isNotSelected(0, 1));
         assertTrue(isAnchor(0));
-    } */
+    } 
     
-    /* test 28
+    // test 28
     @Test public void testCtrlUpArrowWithSpaceChangesAnchor_extended() {
         sm.clearAndSelect(2);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -410,9 +410,9 @@
         assertTrue(isSelected(0));
         assertTrue(isNotSelected(1, 2));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 29
+    // test 29
     @Test public void testCtrlDownArrowWithSpaceChangesAnchor_extended2() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -425,9 +425,9 @@
         assertTrue(isSelected(0, 2, 4));
         assertTrue(isNotSelected(1, 3, 5));
         assertTrue(isAnchor(4));
-    } */
+    } 
     
-    /* test 30
+    // test 30
     @Test public void testCtrlUpArrowWithSpaceChangesAnchor_extended2() {
         sm.clearAndSelect(4);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 3
@@ -440,9 +440,9 @@
         assertTrue(isSelected(0, 2, 4));
         assertTrue(isNotSelected(1, 3));
         assertTrue(isAnchor(0));
-    } */
+    } 
     
-    /* test 31
+    // test 31
     @Test public void testCtrlDownArrowThenShiftSpaceToSelectRange() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -451,9 +451,9 @@
         assertTrue(isSelected(0, 1, 2));
         assertTrue(isNotSelected(3));
         assertTrue(isAnchor(0));
-    } */
+    } 
     
-    /* test 32
+    // test 32
     @Test public void testCtrlUpArrowThenShiftSpaceToSelectRange() {
         sm.clearAndSelect(2);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -462,9 +462,9 @@
         assertTrue(isSelected(0, 1, 2));
         assertTrue(isNotSelected(3));
         assertTrue(debug(), isAnchor(2));
-    } */
+    } 
     
-    /* test 33
+    // test 33
     @Test public void testCtrlDownArrowThenSpaceToChangeSelection() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -480,9 +480,9 @@
         assertTrue(isSelected(2, 3, 4));
         assertTrue(isNotSelected(0, 1));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 34
+    // test 34
     @Test public void testCtrlUpArrowThenSpaceToChangeSelection() {
         sm.clearAndSelect(4);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 3
@@ -498,18 +498,18 @@
         assertTrue(isSelected(0, 1, 2));
         assertTrue(isNotSelected(3, 4));
         assertTrue(debug(), isAnchor(2));
-    } */
+    } 
     
-    /* test 35
+    // test 35
     @Test public void testCtrlDownTwiceThenShiftDown() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 2
         keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);  // select 0,1,2,3
         assertTrue(isSelected(0, 1, 2, 3));
-    } */
+    } 
     
-    /* test 36
+    // test 36
     @Test public void testCtrlUpTwiceThenShiftDown() {
         sm.clearAndSelect(3);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 2
@@ -518,9 +518,9 @@
         keyboard.doKeyPress(KeyCode.DOWN, KeyModifier.SHIFT);  // select 1,2,3
         assertTrue(isSelected(1, 2, 3));
         assertTrue(isNotSelected(0));
-    } */
+    } 
     
-    /* test 37
+    // test 37
     @Test public void testCtrlDownThriceThenShiftUp() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -529,9 +529,9 @@
         keyboard.doKeyPress(KeyCode.UP, KeyModifier.SHIFT);  // select 0,1,2
         assertTrue(isSelected(0, 1, 2));
         assertTrue(isNotSelected(3, 4));
-    } */
+    } 
     
-    /* test 38
+    // test 38
     @Test public void testCtrlUpTwiceThenShiftUp() {
         sm.clearAndSelect(3);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 2
@@ -539,9 +539,9 @@
         keyboard.doKeyPress(KeyCode.UP, KeyModifier.SHIFT);  // select 0,1,2,3
         assertTrue(isSelected(0, 1, 2, 3));
         assertTrue(isNotSelected(4));
-    } */
+    } 
     
-    /* test 39
+    // test 39
     @Test public void testCtrlDownTwiceThenSpace_extended() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -557,9 +557,9 @@
         assertTrue(isSelected(2, 3, 4, 5));
         assertTrue(isNotSelected(0, 1));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 40
+    // test 40
     @Test public void testCtrlUpTwiceThenSpace_extended() {
         sm.clearAndSelect(5);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 4
@@ -576,9 +576,9 @@
         assertTrue(isSelected(1,2,3));
         assertTrue(isNotSelected(0,4,5));
         assertTrue(isAnchor(3));
-    } */
+    } 
     
-    /* test 41
+    // test 41
     @Test public void testCtrlDownTwiceThenSpace_extended2() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -595,9 +595,9 @@
         assertTrue(isSelected(2,3,4));
         assertTrue(isNotSelected(0,1,5));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 50
+    // test 50
     @Test public void testCtrlDownThenShiftHome() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());    // move focus to 1
@@ -613,9 +613,9 @@
         assertTrue(isSelected(0,1,2));
         assertTrue(isNotSelected(3,4));
         assertTrue(debug(),isAnchor(2));
-    } */
+    } 
     
-    /* test 51
+    // test 51
     @Test public void testCtrlUpThenShiftEnd() {
         sm.clearAndSelect(5);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 4
@@ -631,9 +631,9 @@
         assertTrue(isSelected(3,4,5,6,7,8,9));
         assertTrue(isNotSelected(0,1,2));
         assertTrue(debug(),isAnchor(3));
-    } */
+    } 
     
-    /* test 42
+    // test 42
     @Test public void testCtrlUpTwiceThenSpace_extended2() {
         sm.clearAndSelect(5);
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());    // move focus to 4
@@ -649,7 +649,7 @@
         assertTrue(isSelected(0,1,2,3));
         assertTrue(isNotSelected(4,5));
         assertTrue(isAnchor(3));
-    } */
+    } 
     
     // test 46
     @Test public void testHomeKey_withSelectedItems() {
@@ -699,7 +699,7 @@
         assertTrue(isAnchor(5));
     }
     
-    /* test 65
+    // test 65
     @Test public void testShiftPageUp() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
@@ -713,14 +713,161 @@
         keyboard.doKeyPress(KeyCode.PAGE_UP, KeyModifier.SHIFT);
         assertTrue(isSelected(0,1,2));
         assertTrue(isAnchor(2));
-    } */
+    } 
     
-    /* test 67
+    // test 67
     @Test public void testCtrlAToSelectAll() {
         sm.clearAndSelect(5);
         keyboard.doKeyPress(KeyCode.A, KeyModifier.getShortcutKey());
         assertTrue(isSelected(0,1,2,3,4,5,6,7,8,9));
-    } */
+    } 
+    
+    
+    
+    /***************************************************************************
+     * Tests for discontinuous multiple selection (RT-18953)
+     **************************************************************************/  
+    
+    // Test 1
+    @Test public void test_rt18593_1() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(debug(),isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 2
+    @Test public void test_rt18593_2() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // Test 3
+    @Test public void test_rt18593_3() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 4
+    @Test public void test_rt18593_4() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,4,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // TODO skipped some tests here (5-8)
+    
+    // Test 9
+    @Test public void test_rt18593_9() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 10
+    @Test public void test_rt18593_10() {
+        sm.clearAndSelect(9);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(7,9));
+        assertTrue(isAnchor(7));
+        
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,9));
+        assertTrue(isAnchor(7));
+    }
+    
+    // Test 11
+    @Test public void test_rt18593_11() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5));
+        assertTrue(isAnchor(5));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(5));
+    }
+    
+    // Test 12
+    @Test public void test_rt18593_12() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4));
+        assertTrue(isAnchor(2));
+
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE,KeyModifier.getShortcutKey());
+        assertTrue(isSelected(1,2,3,4));
+        assertTrue(fm.isFocused(0));
+        assertTrue(isAnchor(0));
+    }
     
     
     /***************************************************************************
@@ -754,7 +901,7 @@
      * Tests for specific bug reports
      **************************************************************************/
     
-    /* @Test public void test_rt18642() {
+    @Test public void test_rt18642() {
         sm.clearAndSelect(1);                          // select 1
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // shift focus to 2
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // shift focus to 3
@@ -770,5 +917,5 @@
         keyboard.doUpArrowPress(KeyModifier.SHIFT);   
         assertTrue(isSelected(4, 5));
         assertTrue(isNotSelected(0, 1, 2, 3));
-    } */
+    } 
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Mon Apr 09 13:05:50 2012 -0700
@@ -31,11 +31,11 @@
     private Scene scene;
     private Group group;
     
+    private final TableColumn<String, String> col0 = new TableColumn<String, String>("col0");
     private final TableColumn<String, String> col1 = new TableColumn<String, String>("col1");
     private final TableColumn<String, String> col2 = new TableColumn<String, String>("col2");
     private final TableColumn<String, String> col3 = new TableColumn<String, String>("col3");
     private final TableColumn<String, String> col4 = new TableColumn<String, String>("col4");
-    private final TableColumn<String, String> col5 = new TableColumn<String, String>("col5");
     
     @Before public void setup() {
         tableView = new TableView<String>();
@@ -45,8 +45,8 @@
         sm.setSelectionMode(SelectionMode.MULTIPLE);
         sm.setCellSelectionEnabled(false);
         
-        tableView.getItems().setAll("1", "2", "3", "4", "5", "6", "7", "8", "9", "10");
-        tableView.getColumns().setAll(col1, col2, col3, col4, col5);
+        tableView.getItems().setAll("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12");
+        tableView.getColumns().setAll(col0, col1, col2, col3, col4);
         
         sm.clearAndSelect(0);
         
@@ -750,33 +750,33 @@
         // select horizontally, then select two items vertically, then go back
         // in opposite direction
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col1);
+        sm.clearAndSelect(1, col0);
         
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col2)
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col3)
         keyboard.doDownArrowPress(KeyModifier.SHIFT);    // select (2, col3)
         keyboard.doDownArrowPress(KeyModifier.SHIFT);    // select (3, col3)
-        assertTrue(sm.isSelected(1, col3));
-        assertTrue(sm.isSelected(2, col3));
-        assertTrue(sm.isSelected(3, col3));
+        assertTrue(sm.isSelected(1, col2));
+        assertTrue(sm.isSelected(2, col2));
+        assertTrue(sm.isSelected(3, col2));
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT);    // deselect (3, col3)
-        assertTrue(sm.isSelected(1, col3));
-        assertTrue(sm.isSelected(2, col3));
-        assertFalse(sm.isSelected(3, col3));
+        assertTrue(sm.isSelected(1, col2));
+        assertTrue(sm.isSelected(2, col2));
+        assertFalse(sm.isSelected(3, col2));
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT);    // deselect (2, col3)
-        assertTrue(sm.isSelected(1, col3));
-        assertFalse(sm.isSelected(2, col3));
-        assertFalse(sm.isSelected(3, col3));
+        assertTrue(sm.isSelected(1, col2));
+        assertFalse(sm.isSelected(2, col2));
+        assertFalse(sm.isSelected(3, col2));
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT);    // deselect (1, col3)
-        assertFalse(debug(), sm.isSelected(1, col3));
-        assertFalse(sm.isSelected(2, col3));
-        assertFalse(sm.isSelected(3, col3));
+        assertFalse(debug(), sm.isSelected(1, col2));
+        assertFalse(sm.isSelected(2, col2));
+        assertFalse(sm.isSelected(3, col2));
         
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);    // deselect (1, col2)
-        assertFalse(sm.isSelected(1, col2));
+        assertFalse(sm.isSelected(1, col1));
     }
     
     
@@ -810,8 +810,8 @@
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
         keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
-        assertTrue(isSelected(0,2,3,4));
-        assertTrue(isAnchor(2));
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
     }
     
     // Test 3
@@ -849,14 +849,14 @@
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
         keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
-        assertTrue(isSelected(0,2,3,4));
-        assertTrue(isAnchor(2));
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
         // end of similarities
         
         keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
         keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
         keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
-        assertTrue(isSelected(0,1,2,3,4,5));
+        assertTrue(isSelected(1,2,3,4,5));
         assertTrue(isAnchor(3));
     }
     
@@ -882,7 +882,7 @@
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
-        assertTrue(isSelected(8,10));
+        assertTrue(debug(), isSelected(8,10));
         assertTrue(isAnchor(8));
         
         keyboard.doKeyPress(KeyCode.PAGE_UP, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
@@ -987,20 +987,342 @@
     // Test 1
     @Test public void test_rt18591_cell_1() {
         sm.setSelectionMode(SelectionMode.MULTIPLE);
-        sm.select(0, col1);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col0);
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(sm.isSelected(0,col3));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+    }
+    
+    // Test 2
+    @Test public void test_rt18591_cell_2() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col4);
+        keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
         assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+    }
+    
+    // Test 3
+    @Test public void test_rt18591_cell_3() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col0);
+        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col2));
         assertTrue(sm.isSelected(0,col3));
-        assertTrue(isAnchor(2));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(sm.isSelected(0,col3));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+    }
+    
+    // Test 4
+    @Test public void test_rt18591_cell_4() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col4);
+        keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doLeftArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+        
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col0));
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(0,col2));
+        assertTrue(sm.isSelected(0,col3));
+        assertTrue(sm.isSelected(0,col4));
+        assertTrue(isAnchor(0,2));
+    }
+    
+    // Test 5
+    @Test public void test_rt18591_cell_5() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col1);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(isAnchor(2,1));
         
         keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
         keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
-        assertTrue(isSelected(0,2,3,4));
-        assertTrue(isAnchor(2));
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(2,1));
     }
     
+    // Test 6
+    @Test public void test_rt18591_cell_6() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(5, col1);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(5,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(isAnchor(3,1));
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(5,col1));
+        assertTrue(isAnchor(3,1));
+    }
+    
+    // Test 7
+    @Test public void test_rt18591_cell_7() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col1);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(2,1));
+    }
+    
+    // Test 8
+    @Test public void test_rt18591_cell_8() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(5, col1);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(5,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(isAnchor(3,1));
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(5,col1));
+        assertTrue(isAnchor(3,1));
+        
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(sm.isSelected(5,col1));
+        assertTrue(isAnchor(3,1));
+    }
+    
+    // Skipped tests 9 - 12 as they require Page Up/Down support
+    
+    // Test 13
+    @Test public void test_rt18591_cell_13() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col1);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        for (int i = 2; i < tableView.getItems().size(); i++) {
+            assertTrue(debug(),sm.isSelected(i,col1));
+        }
+        assertTrue(isAnchor(2,1));
+    }
+    
+    // Test 14
+    @Test public void test_rt18591_cell_14() {
+        int n = tableView.getItems().size() - 1;
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(n, col1);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(n,col1));
+        assertTrue(sm.isSelected(n - 2,col1));
+        assertTrue(isAnchor(n - 2,1));
+        
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(n,col1));
+        for (int i = 0; i < n - 2; i++) {
+            assertTrue(sm.isSelected(i,col1));
+        }
+        assertTrue(isAnchor(n - 2,1));
+    }
+    
+    // Test 15
+    @Test public void test_rt18591_cell_15() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(5, col1);
+
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        for (int i = 0; i <= 5; i++) {
+            assertTrue(sm.isSelected(i,col1));
+        }
+        assertTrue(isAnchor(5,1));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        for (int i = 0; i < tableView.getItems().size() - 1; i++) {
+            assertTrue(sm.isSelected(i,col1));
+        }
+        assertTrue(isAnchor(5,1));
+    }
+    
+    // Test 16
+    @Test public void test_rt18591_cell_16() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.setCellSelectionEnabled(true);
+        sm.select(0, col1);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(2,1));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertFalse(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(1,col1));
+        assertTrue(sm.isSelected(2,col1));
+        assertTrue(sm.isSelected(3,col1));
+        assertTrue(sm.isSelected(4,col1));
+        assertTrue(isAnchor(0,1));
+        assertTrue(fm.isFocused(0,col1));
+    }
+    
+//    // Test 17
+//    @Test public void test_rt18591_cell_17() {
+//        sm.setSelectionMode(SelectionMode.MULTIPLE);
+//        sm.setCellSelectionEnabled(true);
+//        sm.select(3, col1);
+//        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+//        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+//        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+//        assertTrue(sm.isSelected(3,col1));
+//        assertTrue(sm.isSelected(3,col3));
+//        assertTrue(isAnchor(3,3));
+//        
+//        keyboard.doRightArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        assertTrue(sm.isSelected(3,col1));
+//        assertTrue(sm.isSelected(3,col3));
+//        assertTrue(sm.isSelected(3,col4));
+//        assertTrue(isAnchor(3,3));
+//        
+//        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        assertTrue(sm.isSelected(3,col1));
+//        assertTrue(sm.isSelected(3,col3));
+//        assertTrue(sm.isSelected(3,col4));
+//        assertTrue(sm.isSelected(4,col3));
+//        assertTrue(sm.isSelected(5,col3));
+//        assertTrue(isAnchor(3,3));
+//    }
+    
     
     /***************************************************************************
      * Tests for specific bug reports
@@ -1008,51 +1330,51 @@
     
     @Test public void test_rt18488_selectToLeft() {
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col5);
+        sm.clearAndSelect(1, col4);
         
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);   // select (1, col4)
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);   // select (1, col3)
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);   // select (1, col2)
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);   // select (1, col1)
-        assertTrue(sm.isSelected(1, col5));
         assertTrue(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
         
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // deselect (1, col1)
-        assertTrue(sm.isSelected(1, col5));
         assertTrue(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
-        assertTrue(debug(), sm.isSelected(1, col2));
-        assertFalse(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col2));
+        assertTrue(debug(), sm.isSelected(1, col1));
+        assertFalse(sm.isSelected(1, col0));
     }
     
     @Test public void test_rt18488_selectToRight() {
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col1);
+        sm.clearAndSelect(1, col0);
         
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col2)
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col3)
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col4)
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col5)
-        assertTrue(sm.isSelected(1, col5));
         assertTrue(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
         
         keyboard.doLeftArrowPress(KeyModifier.SHIFT);   // deselect (1, col5)
-        assertFalse(sm.isSelected(1, col5));
-        assertTrue(sm.isSelected(1, col4));
+        assertFalse(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
     }
     
     @Test public void test_rt18488_comment1() {
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col1);
+        sm.clearAndSelect(1, col0);
         
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col2)
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col3)
@@ -1060,106 +1382,106 @@
         keyboard.doRightArrowPress(KeyModifier.SHIFT);   // select (1, col5)
         keyboard.doDownArrowPress(KeyModifier.SHIFT);    // select (2, col5)
         
-        assertTrue(sm.isSelected(2, col5));
-        assertTrue(sm.isSelected(1, col5));
+        assertTrue(sm.isSelected(2, col4));
         assertTrue(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
         
         keyboard.doUpArrowPress(KeyModifier.SHIFT);     // deselect (2, col5)
-        assertFalse(sm.isSelected(2, col5));
-        assertTrue(sm.isSelected(1, col5));
+        assertFalse(sm.isSelected(2, col4));
         assertTrue(sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
     }
     
     @Test public void test_rt18536_positive_horizontal() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col1);
+        sm.clearAndSelect(1, col0);
         
         // move focus by holding down ctrl button
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col2)
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col3)
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col4)
         keyboard.doRightArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col5)
-        assertTrue(fm.isFocused(1, col5));
+        assertTrue(fm.isFocused(1, col4));
         
         // press shift + space to select all cells between (1, col1) and (1, col5)
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
-        assertTrue(sm.isSelected(1, col5));
-        assertTrue(debug(), sm.isSelected(1, col4));
-        assertTrue(sm.isSelected(1, col3));
+        assertTrue(sm.isSelected(1, col4));
+        assertTrue(debug(), sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
     }
     
     @Test public void test_rt18536_negative_horizontal() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col5);
+        sm.clearAndSelect(1, col4);
         
         // move focus by holding down ctrl button
         keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col4)
         keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col3)
         keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col2)
         keyboard.doLeftArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col1)
-        assertTrue(fm.isFocused(1, col1));
+        assertTrue(fm.isFocused(1, col0));
         
         // press shift + space to select all cells between (1, col1) and (1, col5)
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
-        assertTrue(debug(), sm.isSelected(1, col5));
-        assertTrue(sm.isSelected(1, col4));
+        assertTrue(debug(), sm.isSelected(1, col4));
         assertTrue(sm.isSelected(1, col3));
         assertTrue(sm.isSelected(1, col2));
         assertTrue(sm.isSelected(1, col1));
+        assertTrue(sm.isSelected(1, col0));
     }
 
     //
     @Test public void test_rt18536_positive_vertical() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(1, col5);
+        sm.clearAndSelect(1, col4);
         
         // move focus by holding down ctrl button
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // move focus to (2, col5)
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // move focus to (3, col5)
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // move focus to (4, col5)
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());   // move focus to (5, col5)
-        assertTrue(fm.isFocused(5, col5));
+        assertTrue(fm.isFocused(5, col4));
         
         // press shift + space to select all cells between (1, col5) and (5, col5)
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
-        assertTrue(sm.isSelected(1, col5));
-        assertTrue(sm.isSelected(2, col5));
-        assertTrue(sm.isSelected(3, col5));
-        assertTrue(sm.isSelected(4, col5));
-        assertTrue(sm.isSelected(5, col5));
+        assertTrue(sm.isSelected(1, col4));
+        assertTrue(sm.isSelected(2, col4));
+        assertTrue(sm.isSelected(3, col4));
+        assertTrue(sm.isSelected(4, col4));
+        assertTrue(sm.isSelected(5, col4));
     }
     
     //
     @Test public void test_rt18536_negative_vertical() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
-        sm.clearAndSelect(5, col5);
+        sm.clearAndSelect(5, col4);
         
         // move focus by holding down ctrl button
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());   // move focus to (4, col5)
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());   // move focus to (3, col5)
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());   // move focus to (2, col5)
         keyboard.doUpArrowPress(KeyModifier.getShortcutKey());   // move focus to (1, col5)
-        assertTrue(fm.isFocused(1, col5));
+        assertTrue(fm.isFocused(1, col4));
         
         // press shift + space to select all cells between (1, col5) and (5, col5)
         keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.SHIFT);
-        assertTrue(sm.isSelected(1, col5));
-        assertTrue(sm.isSelected(2, col5));
-        assertTrue(sm.isSelected(3, col5));
-        assertTrue(sm.isSelected(4, col5));
-        assertTrue(sm.isSelected(5, col5));
+        assertTrue(sm.isSelected(1, col4));
+        assertTrue(sm.isSelected(2, col4));
+        assertTrue(sm.isSelected(3, col4));
+        assertTrue(sm.isSelected(4, col4));
+        assertTrue(sm.isSelected(5, col4));
     }
     
     //
--- a/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Thu Apr 05 14:43:38 2012 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Mon Apr 09 13:05:50 2012 -0700
@@ -763,6 +763,153 @@
     
     
     /***************************************************************************
+     * Tests for discontinuous multiple selection (RT-18952)
+     **************************************************************************/  
+    
+    // Test 1
+    @Test public void test_rt18952_1() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(debug(),isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 2
+    @Test public void test_rt18952_2() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // Test 3
+    @Test public void test_rt18952_3() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 4
+    @Test public void test_rt18952_4() {
+        sm.clearAndSelect(5);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,5));
+        assertTrue(isAnchor(3));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(1,2,3,4,5));
+        assertTrue(isAnchor(3));
+    }
+    
+    // TODO skipped some tests here (5-8)
+    
+    // Test 9
+    @Test public void test_rt18952_9() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 10
+    @Test public void test_rt18952_10() {
+        sm.clearAndSelect(9);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(7,9));
+        assertTrue(isAnchor(7));
+        
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,9));
+        assertTrue(isAnchor(7));
+    }
+    
+    // Test 11
+    @Test public void test_rt18952_11() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5));
+        assertTrue(isAnchor(5));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(5));
+    }
+    
+    // Test 12
+    @Test public void test_rt18952_12() {
+        sm.clearAndSelect(0);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doDownArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey(), KeyModifier.SHIFT);
+        assertTrue(isSelected(0,1,2,3,4));
+        assertTrue(isAnchor(2));
+
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE,KeyModifier.getShortcutKey());
+        assertTrue(isSelected(1,2,3,4));
+        assertTrue(fm.isFocused(0));
+        assertTrue(isAnchor(0));
+    }
+    
+    
+    
+    /***************************************************************************
      * Tests for editing
      **************************************************************************/