changeset 652:3e46780eb7b9 2.2-b01

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/2.2/MASTER/rt
author leifs
date Mon, 19 Mar 2012 09:26:08 -0700
parents 4e886a3a798b fa715362eea5
children 684d25e44fc5 cfd096bf6562 46aa9545efc2 959a4c7636d7
files javafx-ui-controls/test/javafx/scene/control/MenuBarTest.java
diffstat 25 files changed, 2752 insertions(+), 269 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPicker.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,26 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.sun.javafx.scene.control;
+
+import javafx.scene.control.ComboBoxBase;
+
+/**
+ *
+ * @author paru
+ */
+public class ColorPicker<Color> extends ComboBoxBase<Color> {
+    
+    public ColorPicker() {
+        getStyleClass().add(DEFAULT_STYLE_CLASS);
+    }
+    
+    /***************************************************************************
+     *                                                                         *
+     * Stylesheet Handling                                                     *
+     *                                                                         *
+     **************************************************************************/
+
+    private static final String DEFAULT_STYLE_CLASS = "color-picker";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/ColorPickerPanel.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,282 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.sun.javafx.scene.control;
+
+import java.util.List;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.FXCollections;
+import javafx.event.EventHandler;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Region;
+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;
+import javafx.scene.shape.MoveTo;
+import javafx.scene.shape.Path;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.StrokeType;
+
+
+public class ColorPickerPanel extends Region {
+    
+    private static final int ARROW_SIZE = 10;
+    private static final int RADIUS = 8;
+    
+    ColorPickerGrid cpg;
+    Path path;
+    
+    private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.WHITE);
+    public ObjectProperty<Color> colorProperty() { return color; }
+    public Color getColor() { return color.get(); }
+    public void setColor(Color newColor) { color.set(newColor);}
+    
+    public ColorPickerPanel(Color initPaint) {
+        getStyleClass().add("color-panel");
+        cpg = new ColorPickerGrid(initPaint);
+        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.setStroke(null);
+        path.setEffect(new DropShadow(15, 0, 1, Color.gray(0, 0.6)));
+        path.setCache(true);
+        getChildren().addAll(path, cpg);
+    }
+    
+    @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), 
+                new ArcTo(RADIUS, RADIUS, 90, paddingX + RADIUS, paddingX + ARROW_SIZE, false, true), 
+                new LineTo(paddingX + arrowX - (ARROW_SIZE * 0.8), paddingX + ARROW_SIZE), 
+                new LineTo(paddingX + arrowX, paddingX), 
+                new LineTo(paddingX + arrowX + (ARROW_SIZE * 0.8), paddingX + ARROW_SIZE), 
+                new LineTo(paddingX + popupWidth - RADIUS, paddingX + ARROW_SIZE), 
+                new ArcTo(RADIUS, RADIUS, 90, paddingX + popupWidth, paddingX + ARROW_SIZE + RADIUS, false, true), 
+                new LineTo(paddingX + popupWidth, paddingX + ARROW_SIZE + popupHeight - RADIUS), 
+                new ArcTo(RADIUS, RADIUS, 90, paddingX + popupWidth - RADIUS, paddingX + ARROW_SIZE + popupHeight, false, true), 
+                new LineTo(paddingX + RADIUS, paddingX + ARROW_SIZE + popupHeight), 
+                new ArcTo(RADIUS, RADIUS, 90, paddingX, paddingX + ARROW_SIZE + popupHeight - RADIUS, false, true), 
+                new ClosePath());
+        cpg.relocate(paddingX*2, 2*getInsets().getTop()+ARROW_SIZE);
+    }
+}
+
+class ColorPickerGrid extends GridPane {
+    private static final int SQUARE_SIZE = 15;
+    private static final int NUM_OF_COLUMNS = 12;
+    
+    private Color currentColor = null;
+    private final List<ColorSquare> squares;
+    
+    public ColorPickerGrid(Color initPaint) {
+        setId("ColorCustomizerColorGrid");
+        setGridLinesVisible(true);
+        int columnIndex = 0, rowIndex = 0;
+        
+        squares = FXCollections.observableArrayList();
+        int numColors = rawValues.length / 3;
+        Color[] colors = new Color[numColors];
+        for (int i = 0; i < numColors; i++) {
+            colors[i] = new Color(rawValues[(i * 3)] / 255,
+                    rawValues[(i * 3) + 1] / 255, rawValues[(i * 3) + 2] / 255,
+                    1.0);
+            ColorSquare cs = new ColorSquare(colors[i]);
+            squares.add(cs);
+        }
+
+        for (ColorSquare square : squares) {
+            add(square, columnIndex, rowIndex);
+            columnIndex++;
+            if (columnIndex == NUM_OF_COLUMNS) {
+                columnIndex = 0;
+                rowIndex++;
+            }
+        }
+        
+        setColor(initPaint);
+    }
+    
+    private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.RED) {
+        @Override protected void invalidated() {
+             for (ColorSquare cs : squares) {
+            if (cs.getFill().equals(get())) {
+                // Check css rule has not been already added
+                if (!cs.getStyleClass().contains("selected")) {
+                    cs.getStyleClass().add("selected");
+                }
+            } else {
+                cs.getStyleClass().remove("selected");
+            }
+        }
+        currentColor = get(); 
+        }
+    };
+    public ObjectProperty<Color> colorProperty() { return color; }
+    public Color getColor() { return color.get(); }
+    public void setColor(Color newColor) { color.set(newColor);}
+    
+    private class ColorSquare extends Rectangle {
+        public ColorSquare(Color color) {
+            setFill(color);
+            setSmooth(false);
+//            Utils.setBlocksMouse(this, true);
+            setWidth(SQUARE_SIZE);
+            setHeight(SQUARE_SIZE);
+            setStrokeType(StrokeType.INSIDE);
+            // Add style class to handle selected color square
+            getStyleClass().add("color-square");
+            addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
+                @Override
+                public void handle(MouseEvent event) {
+                    if (event.getClickCount() == 1) {
+                        if (getFill() != null) {
+                            if (getFill() instanceof Color) {
+                                setColor((Color) getFill());
+                            }
+                            event.consume();
+                        }
+                    }
+                }
+            });
+        }
+    }
+    
+    double[] rawValues = {
+        255, 255, 255, // first row
+        242, 242, 242,
+        230, 230, 230,
+        204, 204, 204,
+        179, 179, 179,
+        153, 153, 153,
+        128, 128, 128,
+        102, 102, 102,
+        77, 77, 77,
+        51, 51, 51,
+        26, 26, 26,
+        0, 0, 0,
+        0, 51, 51, // second row
+        0, 26, 128,
+        26, 0, 104,
+        51, 0, 51,
+        77, 0, 26,
+        153, 0, 0,
+        153, 51, 0,
+        153, 77, 0,
+        153, 102, 0,
+        153, 153, 0,
+        102, 102, 0,
+        0, 51, 0,
+        26, 77, 77, // third row
+        26, 51, 153,
+        51, 26, 128,
+        77, 26, 77,
+        102, 26, 51,
+        179, 26, 26,
+        179, 77, 26,
+        179, 102, 26,
+        179, 128, 26,
+        179, 179, 26,
+        128, 128, 26,
+        26, 77, 26,
+        51, 102, 102, // fourth row
+        51, 77, 179,
+        77, 51, 153,
+        102, 51, 102,
+        128, 51, 77,
+        204, 51, 51,
+        204, 102, 51,
+        204, 128, 51,
+        204, 153, 51,
+        204, 204, 51,
+        153, 153, 51,
+        51, 102, 51,
+        77, 128, 128, // fifth row
+        77, 102, 204,
+        102, 77, 179,
+        128, 77, 128,
+        153, 77, 102,
+        230, 77, 77,
+        230, 128, 77,
+        230, 153, 77,
+        230, 179, 77,
+        230, 230, 77,
+        179, 179, 77,
+        77, 128, 77,
+        102, 153, 153, // sixth row
+        102, 128, 230,
+        128, 102, 204,
+        153, 102, 153,
+        179, 102, 128,
+        255, 102, 102,
+        255, 153, 102,
+        255, 179, 102,
+        255, 204, 102,
+        255, 255, 77,
+        204, 204, 102,
+        102, 153, 102,
+        128, 179, 179, // seventh row
+        128, 153, 255,
+        153, 128, 230,
+        179, 128, 179,
+        204, 128, 153,
+        255, 128, 128,
+        255, 153, 128,
+        255, 204, 128,
+        255, 230, 102,
+        255, 255, 102,
+        230, 230, 128,
+        128, 179, 128,
+        153, 204, 204, // eigth row
+        153, 179, 255,
+        179, 153, 255,
+        204, 153, 204,
+        230, 153, 179,
+        255, 153, 153,
+        255, 179, 128,
+        255, 204, 153,
+        255, 230, 128,
+        255, 255, 128,
+        230, 230, 153,
+        153, 204, 153,
+        179, 230, 230, // ninth row
+        179, 204, 255,
+        204, 179, 255,
+        230, 179, 230,
+        230, 179, 204,
+        255, 179, 179,
+        255, 179, 153,
+        255, 230, 179,
+        255, 230, 153,
+        255, 255, 153,
+        230, 230, 179,
+        179, 230, 179,
+        204, 255, 255, // tenth row
+        204, 230, 255,
+        230, 204, 255,
+        255, 204, 255,
+        255, 204, 230,
+        255, 204, 204,
+        255, 204, 179,
+        255, 230, 204,
+        255, 255, 179,
+        255, 255, 204,
+        230, 230, 204,
+        204, 255, 204
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/Pagination.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,328 @@
+/*
+ * Copyright (c) 2009, 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.
+ * 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javafx.scene.control;
+
+import com.sun.javafx.css.StyleableIntegerProperty;
+import com.sun.javafx.css.StyleableProperty;
+import com.sun.javafx.css.Stylesheet.Origin;
+import com.sun.javafx.css.converters.SizeConverter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javafx.beans.DefaultProperty;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.beans.value.WeakChangeListener;
+import javafx.beans.value.WritableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ListChangeListener.Change;
+import javafx.collections.ObservableList;
+import javafx.scene.control.Control;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.control.SingleSelectionModel;
+import javafx.util.Callback;
+
+@DefaultProperty("pages")
+public class Pagination<T> extends Control {
+
+    private static final int DEFAULT_NUMBER_OF_VISIBLE_PAGES = 10;
+    
+    public static final String STYLE_CLASS_BULLET = "bullet";
+
+    /**
+     * Constructs a new Pagination.
+     */
+    public Pagination() {
+        this(FXCollections.<T>observableArrayList());
+    }
+
+    public Pagination(ObservableList<T> items) {
+        getStyleClass().setAll(DEFAULT_STYLE_CLASS);
+        setSelectionModel(new PaginationSelectionModel(this));
+        setItems(items);
+    }
+
+    /***************************************************************************
+     *                                                                         *
+     * Properties                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    // The number of visible page indicators
+    public final IntegerProperty numberOfVisiblePagesProperty() {
+        if (numberOfVisiblePages == null) {
+            numberOfVisiblePages = new StyleableIntegerProperty(DEFAULT_NUMBER_OF_VISIBLE_PAGES) {
+                @Override
+                public StyleableProperty getStyleableProperty() {
+                    return StyleableProperties.NUMBER_OF_VISIBLE_PAGES;
+                }
+
+                @Override
+                public Object getBean() {
+                    return Pagination.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "numberOfVisiblePages";
+                }
+            };
+        }
+        return numberOfVisiblePages;
+    }
+
+    private IntegerProperty numberOfVisiblePages;
+    public final void setNumberOfVisiblePages(int value) {
+        numberOfVisiblePagesProperty().set(value);
+    }
+
+    public final int getNumberOfVisiblePages() {        
+        return numberOfVisiblePages == null ? DEFAULT_NUMBER_OF_VISIBLE_PAGES : numberOfVisiblePages.get();
+    }
+
+    // --- Pages
+    private ObjectProperty<ObservableList<T>> items;
+
+    public final void setItems(ObservableList<T> value) {
+        itemsProperty().set(value);
+    }
+
+    public final ObservableList<T> getItems() {
+        return items == null ? null : items.get();
+    }
+
+    public final ObjectProperty<ObservableList<T>> itemsProperty() {
+        if (items == null) {
+            items = new SimpleObjectProperty<ObservableList<T>>(this, "items") {
+                @Override protected void invalidated() {
+                }
+            };
+        }
+        return items;
+    }
+
+    private ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactory =
+            new SimpleObjectProperty<Callback<ListView<T>, ListCell<T>>>(this, "cellFactory");
+    public final void setCellFactory(Callback<ListView<T>, ListCell<T>> value) { cellFactoryProperty().set(value); }
+    public final Callback<ListView<T>, ListCell<T>> getCellFactory() {return cellFactoryProperty().get(); }
+    public ObjectProperty<Callback<ListView<T>, ListCell<T>>> cellFactoryProperty() { return cellFactory; }
+
+    private ObjectProperty<SingleSelectionModel<T>> selectionModel = new SimpleObjectProperty<SingleSelectionModel<T>>(this, "selectionModel");
+
+    /**
+     * <p>Sets the model used for page selection.  By changing the model you can alter
+     * how the pages are selected and which pages are first or last.</p>
+     */
+    public final void setSelectionModel(SingleSelectionModel<T> value) { selectionModel.set(value); }
+
+    /**
+     * <p>Gets the model used for page selection.</p>
+     */
+    public final SingleSelectionModel<T> getSelectionModel() { return selectionModel.get(); }
+
+    /**
+     * The selection model used for selecting pages.
+     */
+    public final ObjectProperty<SingleSelectionModel<T>> selectionModelProperty() { return selectionModel; }
+
+    static class PaginationSelectionModel<T> extends SingleSelectionModel<T> {
+        private final Pagination pagination;
+
+        public PaginationSelectionModel(final Pagination<T> pagination) {
+            if (pagination == null) {
+                throw new NullPointerException("Pagination can not be null");
+            }
+            this.pagination = pagination;
+
+            selectedIndexProperty().addListener(new InvalidationListener() {
+                @Override public void invalidated(Observable valueModel) {
+                    // we used to lazily retrieve the selected item, but now we just
+                    // do it when the selection changes.
+                    setSelectedItem(getModelItem(getSelectedIndex()));
+                }
+            });
+
+            /*
+             * The following two listeners are used in conjunction with
+             * SelectionModel.select(T obj) to allow for a developer to select
+             * an item that is not actually in the data model. When this occurs,
+             * we actively try to find an index that matches this object, going
+             * so far as to actually watch for all changes to the items list,
+             * rechecking each time.
+             */
+
+            this.pagination.itemsProperty().addListener(weakItemsObserver);
+            if (pagination.getItems() != null) {
+                this.pagination.getItems().addListener(weakItemsContentObserver);
+            }
+        }
+
+        // watching for changes to the items list content
+        private final ListChangeListener<T> itemsContentObserver = new ListChangeListener<T>() {
+            @Override public void onChanged(Change<? extends T> c) {
+                if (pagination.getItems() == null || pagination.getItems().isEmpty()) {
+                    setSelectedIndex(-1);
+                } else if (getSelectedIndex() == -1 && getSelectedItem() != null) {
+                    int newIndex = pagination.getItems().indexOf(getSelectedItem());
+                    if (newIndex != -1) {
+                        setSelectedIndex(newIndex);
+                    }
+                }
+
+                while (c.next()) {
+                    if (c.getFrom() <= getSelectedIndex() && getSelectedIndex()!= -1 && (c.wasAdded() || c.wasRemoved())) {
+                        int shift = c.wasAdded() ? c.getAddedSize() : -c.getRemovedSize();
+                        clearAndSelect(getSelectedIndex() + shift);
+                    }
+                }
+            }
+        };
+
+        // watching for changes to the items list
+        private final ChangeListener<ObservableList<T>> itemsObserver = new ChangeListener<ObservableList<T>>() {
+            @Override
+            public void changed(ObservableValue<? extends ObservableList<T>> valueModel,
+                ObservableList<T> oldList, ObservableList<T> newList) {
+                    updateItemsObserver(oldList, newList);
+            }
+        };
+
+        private WeakListChangeListener weakItemsContentObserver =
+                new WeakListChangeListener(itemsContentObserver);
+
+        private WeakChangeListener weakItemsObserver =
+                new WeakChangeListener(itemsObserver);
+
+        private void updateItemsObserver(ObservableList<T> oldList, ObservableList<T> newList) {
+            // update listeners
+            if (oldList != null) {
+                oldList.removeListener(weakItemsContentObserver);
+            }
+            if (newList != null) {
+                newList.addListener(weakItemsContentObserver);
+            }
+
+            // when the items list totally changes, we should clear out
+            // the selection and focus
+            setSelectedIndex(-1);
+        }
+
+        @Override protected T getModelItem(int index) {
+            final ObservableList<T> items = pagination.getItems();
+            if (items == null) return null;
+            if (index < 0 || index >= items.size()) return null;
+            return items.get(index);
+        }
+
+        @Override protected int getItemCount() {
+            final ObservableList<T> items = pagination.getItems();
+            return items == null ? 0 : items.size();
+        }
+    }
+
+    /***************************************************************************
+     *                                                                         *
+     *                         Stylesheet Handling                             *
+     *                                                                         *
+     **************************************************************************/
+
+    private static final String DEFAULT_STYLE_CLASS = "pagination";
+
+    private static class StyleableProperties {
+        private static final StyleableProperty<Pagination,Number> NUMBER_OF_VISIBLE_PAGES =
+            new StyleableProperty<Pagination,Number>("-fx-number-of-visible-pages",
+                SizeConverter.getInstance(), DEFAULT_NUMBER_OF_VISIBLE_PAGES) {
+
+            @Override
+            public void set(Pagination node, Number value, Origin origin) {
+                super.set(node, value.intValue(), origin);
+            }
+
+            @Override
+            public boolean isSettable(Pagination n) {
+                return n.numberOfVisiblePages == null || !n.numberOfVisiblePages.isBound();
+            }
+
+            @Override
+            public WritableValue<Number> getWritableValue(Pagination n) {
+                return n.numberOfVisiblePagesProperty();
+            }
+        };
+        private static final List<StyleableProperty> STYLEABLES;
+        static {
+            final List<StyleableProperty> styleables =
+                new ArrayList<StyleableProperty>(Control.impl_CSS_STYLEABLES());
+            Collections.addAll(styleables,
+                NUMBER_OF_VISIBLE_PAGES
+            );
+            STYLEABLES = Collections.unmodifiableList(styleables);
+        }
+    }
+
+    /**
+     * @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 static List<StyleableProperty> impl_CSS_STYLEABLES() {
+        return Pagination.StyleableProperties.STYLEABLES;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an experimental API that is not intended for general use and is subject to change in future versions
+     */
+    @Deprecated
+    public List<StyleableProperty> impl_getStyleableProperties() {
+        return impl_CSS_STYLEABLES();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/PaginationCell.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2009, 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.
+ * 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javafx.scene.control;
+
+import javafx.scene.control.ListCell;
+
+public class PaginationCell<T> extends ListCell<T> {
+
+    public PaginationCell() {
+        getStyleClass().addAll("pagination-cell");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/ColorPickerBehavior.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,96 @@
+/*
+ * 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.behavior;
+
+import javafx.scene.control.ComboBox;
+import static javafx.scene.input.KeyCode.*;
+import static javafx.scene.input.KeyEvent.*;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import com.sun.javafx.scene.control.ColorPicker;
+
+import javafx.scene.control.SelectionModel;
+
+public class ColorPickerBehavior<T> extends ComboBoxBaseBehavior<T> {
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
+    
+    /**
+     * 
+     */
+    public ColorPickerBehavior(final ColorPicker<T> colorPicker) {
+        super(colorPicker);
+    }
+
+    /***************************************************************************
+     *                                                                         *
+     * Key event handling                                                      *
+     *                                                                         *
+     **************************************************************************/
+
+    protected static final List<KeyBinding> COMBO_BOX_BINDINGS = new ArrayList<KeyBinding>();
+    static {
+        COMBO_BOX_BINDINGS.add(new KeyBinding(UP, KEY_PRESSED, "selectPrevious"));
+        COMBO_BOX_BINDINGS.add(new KeyBinding(DOWN, "selectNext"));
+        COMBO_BOX_BINDINGS.addAll(COMBO_BOX_BASE_BINDINGS);
+    }
+
+    @Override protected List<KeyBinding> createKeyBindings() {
+        return COMBO_BOX_BINDINGS;
+    }
+
+    @Override protected void callAction(String name) {
+        if ("selectPrevious".equals(name)) {
+            selectPrevious();
+        } else if ("selectNext".equals(name)) {
+            selectNext();
+        } else {
+            super.callAction(name);
+        }
+    }
+    
+    private ComboBox<T> getComboBox() {
+        return (ComboBox<T>) getControl();
+    }
+
+    private void selectPrevious() {
+        SelectionModel sm = getComboBox().getSelectionModel();
+        if (sm == null) return;
+        sm.selectPrevious();
+    }
+    
+    private void selectNext() {
+        SelectionModel sm = getComboBox().getSelectionModel();
+        if (sm == null) return;
+        sm.selectNext();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/PaginationBehavior.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009, 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.
+ * 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javafx.scene.control.behavior;
+
+import com.sun.javafx.scene.control.Pagination;
+import java.util.ArrayList;
+import java.util.List;
+import javafx.scene.input.MouseEvent;
+
+public class PaginationBehavior<T> extends BehaviorBase<Pagination<T>> {
+
+    /**************************************************************************
+     *                          Setup KeyBindings                             *
+     *************************************************************************/
+
+    protected static final List<KeyBinding> PAGINATION_BINDINGS = new ArrayList<KeyBinding>();
+    static {
+        PAGINATION_BINDINGS.addAll(TRAVERSAL_BINDINGS);
+    }
+
+    @Override protected List<KeyBinding> createKeyBindings() {
+        return PAGINATION_BINDINGS;
+    }
+
+    /***************************************************************************
+     *                                                                         *
+     * Mouse event handling                                                    *
+     *                                                                         *
+     **************************************************************************/
+
+    @Override public void mousePressed(MouseEvent e) {
+        super.mousePressed(e);
+        Pagination p = getControl();
+        p.requestFocus();
+    }
+
+    /**************************************************************************
+     *                         State and Functions                            *
+     *************************************************************************/
+
+    public PaginationBehavior(Pagination pagination) {
+        super(pagination);
+    }
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ChoiceBoxSkin.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ChoiceBoxSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -67,6 +67,7 @@
         registerChangeListener(control.showingProperty(), "SHOWING");
         registerChangeListener(control.itemsProperty(), "ITEMS");
         registerChangeListener(control.getSelectionModel().selectedItemProperty(), "SELECTION_CHANGED");
+        registerChangeListener(control.converterProperty(), "CONVERTER");
     }
 
     private ObservableList<?> choiceBoxItems;
@@ -238,6 +239,9 @@
             } else {
                 popup.hide();
             }
+        } else if ("CONVERTER".equals(p)) {
+            updateChoiceBoxItems();
+            updatePopupItems();
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,117 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.sun.javafx.scene.control.skin;
+
+import com.sun.javafx.scene.control.behavior.ColorPickerBehavior;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import com.sun.javafx.scene.control.ColorPicker;
+import com.sun.javafx.scene.control.ColorPickerPanel;
+import java.util.List;
+import javafx.beans.binding.ObjectBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
+import javafx.event.EventTarget;
+import javafx.geometry.HPos;
+import javafx.geometry.Point2D;
+import javafx.geometry.VPos;
+import javafx.scene.Parent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.StackPane;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+import javafx.scene.shape.RectangleBuilder;
+
+/**
+ *
+ * @author paru
+ */
+public class ColorPickerSkin<T> extends ComboBoxPopupControl<T> {
+
+    private StackPane displayNode = new StackPane();
+    private Rectangle colorRect; 
+    private ColorPickerPanel popup = new ColorPickerPanel(Color.WHITE);
+    
+    private ObjectProperty<Color> color = new SimpleObjectProperty<Color>(Color.RED);
+    public ObjectProperty<Color> colorProperty() { return color; }
+    public Color getColor() { return color.get(); }
+    public void setColor(Color newColor) { color.set(newColor); }
+    
+    public ColorPickerSkin(final ColorPicker<T> colorPicker) {
+        super(colorPicker, new ColorPickerBehavior<T>(colorPicker));
+        initialize();
+    }
+    
+    private void initialize() {
+        displayNode.getStyleClass().add("picker-color");
+        colorRect = RectangleBuilder.create()
+                .width(16).height(16)
+                .build();
+        popup.colorProperty().addListener(new ChangeListener<Color>() {
+            @Override public void changed(ObservableValue<? extends Color> ov, Color t, Color t1) {
+                 setColor(t1);
+            }
+        });
+        colorRect.fillProperty().bind(new ObjectBinding<Paint>() {
+            { bind(color); }
+            @Override protected Paint computeValue() {
+                return getColor();
+            }
+        });          
+        displayNode.getChildren().add(colorRect);
+        getChildren().setAll(displayNode, arrowButton);
+        
+        popup.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
+            @Override public void handle(MouseEvent t) {
+                getSkinnable().hide();
+            }
+        });
+    }
+    
+    @Override
+    protected Node getPopupContent() {
+       return popup;
+    }
+    
+    @Override
+    public Node getDisplayNode() {
+        return displayNode;
+    }
+    
+    @Override protected void layoutChildren() {
+        final Insets padding = getInsets();
+        final Insets arrowButtonPadding = arrowButton.getInsets();
+        
+        final double arrowWidth = snapSize(arrow.prefWidth(-1));
+        final double arrowButtonWidth = snapSpace(arrowButtonPadding.getLeft()) + 
+                                        arrowWidth + 
+                                        snapSpace(arrowButtonPadding.getRight());
+        
+        // x, y, w, h are the content area that will hold the label and arrow */
+        final double x = padding.getLeft();
+        final double y = padding.getTop();
+        final double w = getSkinnable().getWidth() - (padding.getLeft() + padding.getRight()+arrowButtonWidth);
+        final double h = getSkinnable().getHeight() - (padding.getTop() + padding.getBottom());
+        
+        if (displayNode != null) {
+            displayNode.resizeRelocate(x, y, w, h);
+        }
+        
+        arrowButton.resize(arrowButtonWidth, getHeight());
+        positionInArea(arrowButton, getWidth() - padding.getRight() - arrowButtonWidth, 0, 
+                arrowButtonWidth, getHeight(), 0, HPos.CENTER, VPos.CENTER);
+    }
+    @Override protected double computePrefWidth(double height) {
+        final double boxWidth = displayNode.prefWidth(-1)
+                + arrowButton.prefWidth(-1);
+        return getInsets().getLeft() + boxWidth +
+                + getInsets().getRight();
+    }
+    
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ComboBoxListViewSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -217,7 +217,7 @@
                 comboBox.requestFocus();
             }
         });
-
+        
         return textField;
     }
     
@@ -228,6 +228,7 @@
         if (comboBox.isEditable()) {
             T value = comboBox.getValue();
             String stringValue = c.toString(value);
+            
             if (value == null || stringValue == null) {
                 textField.setText("");
             } else if (! stringValue.equals(textField.getText())) {
@@ -352,6 +353,7 @@
 
         listView.setId("list-view");
         listView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
+        listView.prefWidthProperty().bind(comboBox.widthProperty());
 
         listView.getSelectionModel().selectedIndexProperty().addListener(new InvalidationListener() {
              @Override public void invalidated(Observable o) {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ContextMenuContent.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ContextMenuContent.java	Mon Mar 19 09:26:08 2012 -0700
@@ -95,7 +95,7 @@
     private double maxLeftWidth = 0;
 
     private Rectangle clipRect;
-    private MenuBox itemsContainer;
+    MenuBox itemsContainer;
     private ArrowMenuItem upArrow;
     private ArrowMenuItem downArrow;
 
@@ -129,6 +129,21 @@
         initialize();
         setUpBinds();
     }
+    
+       //For testing purpose only
+    VBox getItemsContainer() {
+        return itemsContainer;
+    }
+    //For testing purpose only
+    int getCurrentFocusIndex() {
+        return currentFocusedIndex;
+    }
+    //For testing purpose only
+    void setCurrentFocusedIndex(int index) {
+        if (index < itemsContainer.getChildren().size()) {
+            currentFocusedIndex = index;
+        }
+    }
 
     private void computeVisualMetrics() {
         maxRightWidth = 0;
@@ -545,7 +560,7 @@
                     // if submenu for this menu is already showing then do nothing
                     // Menubar will process the right key and move to the next menu
                     if (openSubmenu == menu && submenu.isShowing()) return;
-                    showSubmenu(menu);
+                    menu.show();
                     // request focus on the first item of the submenu after it is shown
                     ContextMenuContent cmContent = (ContextMenuContent)submenu.getSkin().getNode();
                     if (cmContent != null) {
@@ -573,7 +588,7 @@
                     }
                     if (menu.isDisable()) return;
                     selectedBackground = ((MenuItemContainer)n);
-                    showSubmenu(menu);
+                    menu.show();
                 } else {
                     ((MenuItemContainer)n).doSelect();
                 }
@@ -732,6 +747,11 @@
         }
     }
 
+    // For test purpose only
+    ContextMenu getSubMenu() {
+        return submenu;
+    }
+
     private void showSubmenu(Menu menu) {
         openSubmenu = menu;
 
@@ -973,6 +993,10 @@
         protected Label getLabel(){
             return (Label) label;
         }
+        
+        protected MenuItem getItem() {
+            return item;
+        }
 
         public MenuItemContainer(MenuItem item){
             if (item == null) {
@@ -1115,6 +1139,18 @@
                             doSelect();
                         }
                     });
+                    // RT-19546 update currentFocusedIndex when MenuItemContainer gets focused.
+                    // e.g this happens when you press the Right key to open a submenu; the first
+                    // menuitem is focused.
+                    focusedProperty().addListener(new ChangeListener<Boolean>() {
+                        @Override public void changed(ObservableValue<? extends Boolean> ov,
+                                                                    Boolean t, Boolean t1) {
+                            if (t1 && !t) {
+                                currentFocusedIndex =
+                                    itemsContainer.getChildren().indexOf(MenuItemContainer.this);
+                            }
+                        }
+                    });
                 }
             }
         }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVKSkin.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/FXVKSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -28,9 +28,14 @@
 import java.lang.reflect.Field;
 import java.util.*;
 
+import javafx.animation.Interpolator;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
 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;
@@ -41,6 +46,7 @@
 import javafx.geometry.Point2D;
 import javafx.geometry.VPos;
 import javafx.scene.Node;
+import javafx.scene.Parent;
 import javafx.scene.Scene;
 import javafx.scene.control.*;
 import javafx.scene.input.KeyCode;
@@ -48,34 +54,50 @@
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.*;
 import javafx.stage.Popup;
-
+import javafx.util.Duration;
 
 import com.sun.javafx.robot.impl.FXRobotHelper;
 import com.sun.javafx.robot.impl.FXRobotHelper.FXRobotInputAccessor;
 import com.sun.javafx.scene.control.behavior.BehaviorBase;
 
+import javafx.animation.Animation.Status;
 import static javafx.scene.input.KeyCode.*;
+import static javafx.scene.input.MouseEvent.*;
 
 public class FXVKSkin extends SkinBase<FXVK, BehaviorBase<FXVK>> {
 
+    private static Region oldRoot;
+    private static Pane newRoot;
     private static Popup secondaryPopup;
     private static FXVK primaryVK;
     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;
+
     private Node attachedNode;
-//     private Scene scene;
 
     FXVK fxvk;
     Control[][] keyRows;
 
     enum State { NORMAL, SHIFTED, SHIFT_LOCK, NUMERIC; };
 
-    State state = State.NORMAL;
+    static State state = State.NORMAL;
 
-    static double KEY_WIDTH = 40;
-    static double KEY_HEIGHT = 30;
-    final static double hGap = 2;
-    final static double vGap = 3;
+    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;
+    static final double PREF_KEY_WIDTH = 40;
+    static final double PREF_KEY_HEIGHT = 30;
+    static final double hGap = 2;
+    static final double vGap = 3;
+
+    double keyWidth = PREF_KEY_WIDTH;
+    double keyHeight = PREF_KEY_HEIGHT;
 
     private ShiftKey shiftKey;
     private SymbolKey symbolKey;
@@ -84,6 +106,8 @@
         super(fxvk, new BehaviorBase<FXVK>(fxvk));
         this.fxvk = fxvk;
 
+        fxvk.setFocusTraversable(false);
+
         createKeys();
 
         fxvk.attachedNodeProperty().addListener(new InvalidationListener() {
@@ -91,39 +115,69 @@
                 Node oldNode = attachedNode;
                 attachedNode = fxvk.getAttachedNode();
 
-                if (oldNode != null) {
-                    oldNode.getScene().getRoot().setTranslateY(0);
+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) {
                     final Scene scene = attachedNode.getScene();
 
+                    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);
+                                }
+                            }
+                        });
+                        secondaryVKDelay.getKeyFrames().add(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);
+                        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) {
-                            scene.getRoot().setTranslateY(y - nodeBottom);
+                            translatePane(scene.getRoot(), y - nodeBottom);
                         }
                     } 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);
+                                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);
 
 
                                 double nodeBottom = nodePoint.getY();
                                 if (y < nodeBottom) {
-                                    scene.getRoot().setTranslateY(y - nodeBottom);
+                                    translatePane(scene.getRoot(), y - nodeBottom);
                                 }
                             }
                         });
@@ -131,61 +185,129 @@
 
                     if (oldNode == null || oldNode.getScene() != attachedNode.getScene()) {
                         fxvk.setPrefWidth(scene.getWidth());
-                        fxvk.setMaxWidth(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();
+                    }
+
+                    fxvk.setVisible(true);
+                    if (fxvk != secondaryVK && fxvk.getHeight() > 0 &&
+                        (fxvk.getLayoutY() == 0 || fxvk.getLayoutY() > scene.getHeight() - fxvk.getHeight())) {
+
+                        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()) {
+                                    translatePane(oldRoot, fxvk.getLayoutY() - nodeBottom);
+                                }
+                            }
+                        });
+
+                        if (oldNode == null || oldNode.getScene() != attachedNode.getScene()) {
+                            fxvk.setPrefWidth(VK_WIDTH);
+                            fxvk.setMaxWidth(USE_PREF_SIZE);
+                            fxvk.setPrefHeight(VK_HEIGHT);
+                            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();
+                    }
                     return;
                 }
             }
         });
     }
 
+    private void translatePane(Parent pane, double y) {
+        if (slideRootTimeline == null) {
+            slideRootTimeline = new Timeline();
+        } else {
+            slideRootTimeline.stop();
+        }
+
+        slideRootTimeline.getKeyFrames().setAll(
+            new KeyFrame(Duration.millis(VK_SLIDE_MILLIS),
+                         new KeyValue(pane.translateYProperty(), y, Interpolator.EASE_BOTH)));
+        slideRootTimeline.playFromStart();
+    }
+
     private void createKeys() {
         getChildren().clear();
 
         if (fxvk.chars != null) {
-            keyRows = new Control[][] {
-                makeKeyRow((Object[])fxvk.chars)
-            };
+            // 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)));
+            }
         } else {
+            // TODO: Move this to a resource bundle.
             keyRows = new Control[][] {
                 makeKeyRow("q 1 [",
                            "w 2 ]",
-                           "e 3 {",
-                           "r 4 }",
-                           "t 5 \\",
-                           "y 6 |",
-                           "u 7 \"",
-                           "i 8 <",
-                           "o 9 >",
-                           "p 0 _"),
-                makeKeyRow("a @ ~",
-                           "s # `",
-                           "d $ \u20ac",
-                           "f % \u00a3",
-                           "g ^ \u00a5",
-                           "h & \u00a7",
-                           "j * \u00b7",
-                           "k ( \u00b0",
-                           "l ) \u2260"),
-                makeKeyRow(shiftKey = new ShiftKey(KEY_WIDTH * 1.5),
-                           "z - \u00a1",
-                           "x = \u00bf",
-                           "c + \u2030",
-                           "v ; \u00ae",
-                           "b : \u2122",
-                           "n / \u00ab",
-                           "m ' \u00bb",
-                           new CommandKey("\u232b", BACK_SPACE, KEY_WIDTH * 1.5)),
-                makeKeyRow(symbolKey = new SymbolKey("!#123 ABC", KEY_WIDTH * 2.5 + (9-4) * hGap / 2),
-                           ", !",
-                           " ",
-                           ". ?",
-                           new CommandKey("\u21b5", ENTER, KEY_WIDTH * 2.5 + (9-4) * hGap / 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))
             };
         }
 
@@ -193,13 +315,27 @@
         vbox.setFillWidth(true);
         getChildren().add(vbox);
 
+        double primaryFontSize = 16 * keyWidth / PREF_KEY_WIDTH;
+        double secondaryFontSize = 8 * keyWidth / PREF_KEY_WIDTH;
+
         for (Control[] row : keyRows) {
             HBox hbox = new HBox(hGap);
-            hbox.setAlignment(Pos.CENTER);
+            // Primary keyboard has centered keys, secondary has left aligned keys.
+            hbox.setAlignment((fxvk.chars != null) ? Pos.CENTER_LEFT : Pos.CENTER);
             vbox.getChildren().add(hbox);
-            for (Control key : row) {
-                hbox.getChildren().add(key);
-                HBox.setHgrow(key, Priority.ALWAYS);
+            for (Control c : row) {
+                hbox.getChildren().add(c);
+                HBox.setHgrow(c, Priority.ALWAYS);
+                if (c instanceof Key) {
+                    Key key = (Key)c;
+                    if (fxvk.chars != null) {
+                        key.getStyleClass().add("secondary-key");
+                    }
+                    key.setStyle("-fx-font-size: "+primaryFontSize+"px;");
+                    if (key.getGraphic() instanceof Label) {
+                        ((Label)key.getGraphic()).setStyle("-fx-font-size: "+secondaryFontSize+"px;");
+                    }
+                }
             }
         }
     }
@@ -214,7 +350,7 @@
         Control[] keyRow = new Control[keyList.size()];
         for (int i = 0; i < keyRow.length; i++) {
             if (keyList.get(i) instanceof String) {
-                keyRow[i] = new Key((String)keyList.get(i));
+                keyRow[i] = new CharKey((String)keyList.get(i));
             } else {
                 keyRow[i] = (Control)keyList.get(i);
             }
@@ -239,20 +375,26 @@
         }
         state = newState;
 
-        updateLabels();
+        if (fxvk == secondaryVK) {
+            ((FXVKSkin)primaryVK.getSkin()).updateLabels();
+        } else {
+            updateLabels();
+        }
     }
 
     private void updateLabels() {
         for (Control[] row : keyRows) {
             for (Control button : row) {
-                if (button instanceof Key) {
-                    Key key = (Key)button;
+                if (button instanceof CharKey) {
+                    CharKey key = (CharKey)button;
                     String txt = key.chars[0];
                     String alt = (key.chars.length > 1) ? key.chars[1] : "";
                     if (key.chars.length > 1 && state == State.NUMERIC) {
                         txt = key.chars[1];
                         if (key.chars.length > 2) {
                             alt = key.chars[2];
+                        } else {
+                            alt = "";
                         }
                     } else if (state == State.SHIFTED || state == State.SHIFT_LOCK) {
                         txt = txt.toUpperCase();
@@ -284,15 +426,36 @@
         }
     }
 
+
+
     private class Key extends Button {
+        private Key() {
+            this(null);
+        }
+
+        private Key(String text) {
+            super(text);
+
+            getStyleClass().add("key");
+            setFocusTraversable(false);
+
+            setMinHeight(USE_PREF_SIZE);
+            setPrefHeight(keyHeight);
+        }
+
+    }
+
+    private class CharKey extends Key {
         String str;
         String[] chars;
         Label graphic;
-        boolean pendingSecondaryVK = false;
 
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
             @Override public void handle(ActionEvent e) {
-if (fxvk != secondaryVK && secondaryPopup != null && secondaryPopup.isShowing()) return;
+                if (fxvk != secondaryVK && secondaryPopup != null && secondaryPopup.isShowing()) {
+                    return;
+                }
+
                 Node target = fxvk.getAttachedNode();
                 if (target instanceof EventTarget) {
                     String txt = getText();
@@ -311,48 +474,36 @@
                         toggleShift();
                     }
                 }
-if (fxvk == secondaryVK) {
-    showSecondaryVK(null, fxvk, state);
-}
+
+                if (fxvk == secondaryVK) {
+                    showSecondaryVK(null, fxvk, state);
+                }
             }
         };
 
-        Key(String str) {
+        CharKey(String str) {
             this.str = str;
-            setFocusTraversable(false);
             setOnAction(actionHandler);
 
-            if (fxvk != secondaryVK)
-            setOnMousePressed(new EventHandler<MouseEvent>() {
-                @Override public void handle(MouseEvent event) {
-                    showSecondaryVK(null, fxvk, state);
-                    pendingSecondaryVK = true;
-                    new Thread(new Runnable() {
-                        public void run() {
-                            try {
-                                Thread.sleep(500);
-                            } catch (InterruptedException ex) {
-                                pendingSecondaryVK = false;
-                            }
-                            if (pendingSecondaryVK) {
-                                Platform.runLater(new Runnable() {
-                                    public void run() {
-                                        showSecondaryVK(Key.this, fxvk, state);
-                                    }
-                                });
-                            }
+            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;
                         }
-                    }).start();
-                }
-            });
+                    }
+                });
 
-            if (fxvk != secondaryVK)
-            setOnMouseReleased(new EventHandler<MouseEvent>() {
-                @Override public void handle(MouseEvent event) {
-                    pendingSecondaryVK = false;
-                }
-            });
-
+                setOnMouseReleased(new EventHandler<MouseEvent>() {
+                    @Override public void handle(MouseEvent event) {
+                        secondaryVKDelay.stop();
+                    }
+                });
+            }
 
             if (str.length() == 1) {
                 chars = new String[] { str };
@@ -361,19 +512,19 @@
             }
             setContentDisplay(ContentDisplay.RIGHT);
             setText(chars[0]);
-            graphic = new Label((chars.length > 1) ? chars[1] : " ");
-            //graphic.getStyleClass().add("
-            //setGraphicTextGap(KEY_WIDTH - 20);
-            graphic.setPrefWidth(KEY_WIDTH / 2 - 10);
-            graphic.setPrefHeight(KEY_HEIGHT - 6);
-            setGraphic(graphic);
+            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 == " ") ? KEY_WIDTH * 3 : KEY_WIDTH);
-            setPrefHeight(KEY_HEIGHT);
+            setPrefWidth((str == " ") ? keyWidth * 3 : keyWidth);
         }
     }
 
-    private class CommandKey extends Button {
+    private class CommandKey extends Key {
         KeyCode code;
 
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
@@ -396,14 +547,12 @@
             super(label);
             this.code = code;
             getStyleClass().add("special-key");
-            setFocusTraversable(false);
             setOnAction(actionHandler);
             setPrefWidth(width);
-            setPrefHeight(KEY_HEIGHT);
         }
     }
 
-    private class ShiftKey extends Button {
+    private class ShiftKey extends Key {
         EventHandler<ActionEvent> actionHandler = new EventHandler<ActionEvent>() {
             @Override public void handle(ActionEvent e) {
                 showSecondaryVK(null, null, null);
@@ -417,11 +566,10 @@
             setFocusTraversable(false);
             setOnAction(actionHandler);
             setPrefWidth(width);
-            setPrefHeight(KEY_HEIGHT);
         }
     }
 
-    private class SymbolKey extends Button {
+    private class SymbolKey extends Key {
         String str;
         String[] chars;
 
@@ -437,7 +585,6 @@
         SymbolKey(String str, double width) {
             this.str = str;
             getStyleClass().add("special-key");
-            setFocusTraversable(false);
 
             if (str.length() == 1) {
                 chars = new String[] { str };
@@ -448,91 +595,180 @@
 
             setOnAction(actionHandler);
             setPrefWidth(width);
-            setPrefHeight(KEY_HEIGHT);
         }
     }
 
     @Override public void layoutChildren() {
         double kw, kh;
+        Insets insets = getInsets();
 
         if (fxvk == secondaryVK) {
-            kw = ((FXVKSkin)primaryVK.getSkin()).KEY_WIDTH;
-            kh = ((FXVKSkin)primaryVK.getSkin()).KEY_HEIGHT;
+            kw = ((FXVKSkin)primaryVK.getSkin()).keyWidth;
+            kh = ((FXVKSkin)primaryVK.getSkin()).keyHeight;
         } else {
             kw = (getWidth() / 10) - 2 * hGap;
-            kh = getHeight() / keyRows.length;
+            kh = (getHeight() - insets.getTop() - insets.getBottom() - (keyRows.length - 1) * vGap) / keyRows.length;
         }
 
-        if (KEY_WIDTH != kw || KEY_HEIGHT != kh) {
-            KEY_WIDTH = kw;
-            KEY_HEIGHT = kh;
+        if (keyWidth != kw || keyHeight != kh) {
+            keyWidth = kw;
+            keyHeight = kh;
             createKeys();
         }
 
         super.layoutChildren();
     }
 
-    private static void showSecondaryVK(final Key key, FXVK primVK, State state) {
+    private static void showSecondaryVK(final CharKey key, FXVK primVK, State state) {
         if (key != null) {
             primaryVK = primVK;
             final Node textInput = primaryVK.getAttachedNode();
 
             if (secondaryPopup == null) {
                 secondaryVK = new FXVK();
-                secondaryVK.getStyleClass().add("secondary");
-                if (state == State.NUMERIC) {
-                    secondaryVK.chars = new String[key.chars.length - 1];
-                    System.arraycopy(key.chars, 1, secondaryVK.chars, 0, secondaryVK.chars.length);
-                } else if (state == State.SHIFTED || state == State.SHIFT_LOCK) {
-                    secondaryVK.chars = new String[key.chars.length];
-                    System.arraycopy(key.chars, 0, secondaryVK.chars, 0, secondaryVK.chars.length);
-                    secondaryVK.chars[0] = key.chars[0].toUpperCase();
-                } else {
-                    secondaryVK.chars = key.chars;
-                }
+                secondaryVK.getStyleClass().add("fxvk-secondary");
                 secondaryPopup = new Popup();
                 secondaryPopup.getContent().add(secondaryVK);
+            }
+
+            if (state == State.NUMERIC) {
+                ArrayList<String> symbols = new ArrayList<String>();
+                for (String ch : key.chars) {
+                    if (!Character.isLetter(ch.charAt(0))) {
+                        symbols.add(ch);
+                    }
+                }
+                secondaryVK.chars = symbols.toArray(new String[symbols.size()]);
+            } else if (state == State.SHIFTED || state == State.SHIFT_LOCK) {
+                secondaryVK.chars = new String[key.chars.length];
+                System.arraycopy(key.chars, 0, secondaryVK.chars, 0, secondaryVK.chars.length);
+                for (int i = 0; i < secondaryVK.chars.length; i++) {
+                    secondaryVK.chars[i] = key.chars[i].toUpperCase();
+                }
             } else {
-                if (state == State.NUMERIC) {
-                    secondaryVK.chars = new String[key.chars.length - 1];
-                    System.arraycopy(key.chars, 1, secondaryVK.chars, 0, secondaryVK.chars.length);
-                } else if (state == State.SHIFTED || state == State.SHIFT_LOCK) {
-                    secondaryVK.chars = new String[key.chars.length];
-                    System.arraycopy(key.chars, 0, secondaryVK.chars, 0, secondaryVK.chars.length);
-                    secondaryVK.chars[0] = key.chars[0].toUpperCase();
-                } else {
-                    secondaryVK.chars = key.chars;
-                }
+                secondaryVK.chars = key.chars;
+            }
+
+            if (secondaryVK.getSkin() != null) {
                 ((FXVKSkin)secondaryVK.getSkin()).createKeys();
             }
 
             secondaryVK.setAttachedNode(textInput);
-            double w = key.chars.length * KEY_WIDTH + (key.chars.length - 1) * hGap;
+            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.setMaxWidth(w);
-            secondaryVK.setPrefHeight(KEY_HEIGHT);
+            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,
-//                                       secondaryPopup.getWidth(), secondaryPopup.getHeight(),
-                                      secondaryVK.prefWidth(-1), secondaryVK.prefHeight(-1),
-                                      HPos.CENTER, VPos.TOP, 0, -5, true);
-
-                    Point2D point =
-                        com.sun.javafx.Utils.pointRelativeTo(key.getScene().getRoot(),
-                                      secondaryPopup.getWidth(), secondaryPopup.getHeight(),
-                                      HPos.CENTER, VPos.TOP, 0, 0, true);
-
-                    secondaryPopup.show(key.getScene().getWindow(), nodePoint.getX(), nodePoint.getY());
+                        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.getAttachedNode() == textInput) {
             if (secondaryVK != null) {
                 secondaryVK.setAttachedNode(null);
                 secondaryPopup.hide();
             }
         }
     }
+
+    class NewRootPane extends Pane {
+        double dragStartY;
+
+        NewRootPane(final Region oldRoot) {
+            getChildren().add(oldRoot);
+            prefWidthProperty().bind(oldRoot.prefWidthProperty());
+            prefHeightProperty().bind(oldRoot.prefHeightProperty());
+
+
+            addEventHandler(MOUSE_PRESSED, new EventHandler<MouseEvent>() {
+                @Override public void handle(MouseEvent e) {
+                    dragStartY = e.getY() - oldRoot.getTranslateY();
+                    e.consume();
+                }
+            });
+
+            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);
+                    }
+                    e.consume();
+                }
+            });
+        }
+
+        @Override protected double computePrefWidth(double height) {
+            return oldRoot.prefWidth(height);
+        }
+
+        @Override protected double computePrefHeight(double width) {
+            return oldRoot.prefHeight(width);
+        }
+
+        @Override public void layoutChildren() {
+            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(
+                new KeyFrame(Duration.ZERO,
+                             new KeyValue(fxvk.visibleProperty(), true),
+                             new KeyValue(fxvk.layoutYProperty(), rootHeight)),
+                new KeyFrame(Duration.millis(VK_SLIDE_MILLIS),
+                             new KeyValue(fxvk.visibleProperty(), true),
+                             new KeyValue(fxvk.layoutYProperty(),
+                                          Math.floor(rootHeight - vkHeight),
+                                          Interpolator.EASE_BOTH)));
+
+            slideOutTimeline = new Timeline();
+            slideOutTimeline.getKeyFrames().setAll(
+                new KeyFrame(Duration.ZERO,
+                             new KeyValue(fxvk.layoutYProperty(),
+                                          Math.floor(rootHeight - vkHeight))),
+                new KeyFrame(Duration.millis(VK_SLIDE_MILLIS),
+                             new KeyValue(fxvk.layoutYProperty(),
+                                          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);
+                }
+            }
+        }
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/NestedTableColumnHeader.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/NestedTableColumnHeader.java	Mon Mar 19 09:26:08 2012 -0700
@@ -139,44 +139,19 @@
     public void setColumns(ObservableList<? extends TableColumn> newColumns) {
         this.columns = newColumns;  
         
-        // update the column headers. 
-        // We make a copy of all current headers
-        ArrayList<TableColumnHeader> copy = new ArrayList(getColumnHeaders());
+        // update the column headers....
         
         // then clear out the column headers
         getColumnHeaders().clear();
         
-        // then iterate through all columns. We try to reuse the column if it
-        // previously existed, rather than recreate it.
+        // then iterate through all columns.
         for (int i = 0; i < getColumns().size(); i++) {
-            TableColumnHeader header = null;
             TableColumn<?,?> column = getColumns().get(i);
             
             if (column == null) continue;
             
-            for (int j = 0; j < copy.size(); j++) {
-                if (column.equals(copy.get(j).getTableColumn())) {
-                    header = copy.get(j);
-                    break;
-                }
-            }
-            
-            if (header == null) {
-                header = createColumnHeader(column);
-            }
-            
-            getColumnHeaders().add(header);
+            getColumnHeaders().add(createColumnHeader(column));
         }
-        
-//        // we put all column headers into the list, even if they are not visible.
-//        // This ensures that we can accurately calculate the height of the 
-//        // table header row, even when no columns are visible. See RT-14434 for
-//        // the bug report (in addition to this change, I also changed the 
-//        // prefHeight calculations to ignore whether or not the column is 
-//        // visible).
-//        for (TableColumn c : getColumns()) {
-//            getColumnHeaders().add(createColumnHeader(c));
-//        }
 
         // update the content
         updateContent();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationCellSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2009, 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.
+ * 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javafx.scene.control.skin;
+
+import javafx.scene.control.ListView;
+import com.sun.javafx.scene.control.PaginationCell;
+
+public class PaginationCellSkin extends ListCellSkin {
+
+    public PaginationCellSkin(PaginationCell control) {
+        super(control);
+    }
+
+    @Override protected double computePrefWidth(double height) {
+        ListView listView = getSkinnable().getListView();
+        double d = listView == null ? 0 : listView.getLayoutBounds().getWidth();
+        return d;
+    }
+
+    @Override protected double computePrefHeight(double width) {
+        ListView listView = getSkinnable().getListView();
+        double d = listView == null ? 0 : listView.getLayoutBounds().getHeight();
+        return d;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2009, 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.
+ * 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.
+ *
+ * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.javafx.scene.control.skin;
+
+import com.sun.javafx.css.StyleManager;
+import com.sun.javafx.scene.control.Pagination;
+import com.sun.javafx.scene.control.PaginationCell;
+import com.sun.javafx.scene.control.WeakListChangeListener;
+import com.sun.javafx.scene.control.behavior.PaginationBehavior;
+import java.util.ArrayList;
+import java.util.List;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ListChangeListener.Change;
+import javafx.collections.ObservableList;
+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.layout.Region;
+import javafx.scene.layout.StackPane;
+import javafx.scene.shape.Rectangle;
+import javafx.util.Callback;
+
+public class PaginationSkin<T> extends SkinBase<Pagination<T>, PaginationBehavior<T>>  {
+
+    private Pagination<T> pagination;
+    private PaginationListView<T> paginationListView;
+    private ObservableList<T> pages;
+    private final ListChangeListener pageListener = new ListChangeListener() {
+        @Override public void onChanged(ListChangeListener.Change c) {
+            requestLayout();
+        }
+    };
+    private final WeakListChangeListener weakPageListener =
+            new WeakListChangeListener(pageListener);
+
+    private Rectangle clipRect;
+
+    private NavigationControl navigation;
+    private int fromIndex;
+    private int previousIndex;
+    private int currentIndex;
+    private int toIndex;
+    private int totalNumberOfPages;
+    private int numberOfPages;
+    private int numberOfVisiblePages;
+
+    public PaginationSkin(final Pagination<T> pagination) {
+        super(pagination, new PaginationBehavior(pagination));
+
+        setManaged(false);
+        clipRect = new Rectangle();
+        setClip(clipRect);
+
+        this.pagination = pagination;
+        pagination.getSelectionModel().selectFirst();
+
+        this.paginationListView = createListView();
+        updateListViewItems();
+        updateCellFactory();
+        paginationListView.setOrientation(Orientation.HORIZONTAL);
+
+        resetIndexes();
+        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);
+            }
+        });
+    }
+
+    private void resetIndexes() {
+        numberOfVisiblePages = getSkinnable().getNumberOfVisiblePages();
+        totalNumberOfPages = pages.size();
+        numberOfPages = totalNumberOfPages;
+        if (totalNumberOfPages > numberOfVisiblePages) {
+            numberOfPages = numberOfVisiblePages;
+        }
+
+        fromIndex = 0;
+        previousIndex = 0;
+        currentIndex = 0;
+        toIndex = numberOfPages - 1;
+    }
+
+    private PaginationListView<T> createListView() {
+        return new PaginationListView<T>();
+    }
+
+    private void updateCellFactory() {
+        Callback<ListView<T>, ListCell<T>> cf = pagination.getCellFactory();
+        paginationListView.setCellFactory(cf != null ? cf : getDefaultCellFactory());
+    }
+
+    private Callback<ListView<T>, ListCell<T>> getDefaultCellFactory() {
+        return new Callback<ListView<T>, ListCell<T>>() {
+            @Override public ListCell<T> call(ListView<T> listView) {
+                return new PaginationCell<T>() {
+                    @Override public void updateItem(T item, boolean empty) {
+                        super.updateItem(item, empty);
+
+                        if (empty) {
+                            setText(null);
+                            setGraphic(null);
+                        } else if (item instanceof Node) {
+                            setText(null);
+                            Node currentNode = getGraphic();
+                            Node newNode = (Node) item;
+                            if (currentNode == null || ! currentNode.equals(newNode)) {
+                                setGraphic(newNode);
+                            }
+                        } else {
+                            /**
+                            * This label is used if the item associated with this cell is to be
+                            * represented as a String. While we will lazily instantiate it
+                            * we never clear it, being more afraid of object churn than a minor
+                            * "leak" (which will not become a "major" leak).
+                            */
+                            setText(item == null ? "null" : item.toString());
+                            setGraphic(null);
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    private void updateListViewItems() {
+        if (pages != null) {
+            pages.removeListener(weakPageListener);
+        }
+
+        this.pages = getSkinnable().getItems();
+        paginationListView.setItems(pages);
+
+        if (pages != null) {
+            pages.addListener(weakPageListener);
+        }
+        requestLayout();
+    }
+
+    @Override protected void setWidth(double value) {
+        super.setWidth(value);
+        clipRect.setWidth(value);
+    }
+
+    @Override protected void setHeight(double value) {
+        super.setHeight(value);
+        clipRect.setHeight(value);
+    }
+
+    @Override protected double computePrefWidth(double height) {
+        double left = snapSpace(getInsets().getLeft());
+        double right = snapSpace(getInsets().getRight());
+        double navigationWidth = snapSize(navigation.prefWidth(height));
+        return left + Math.max(paginationListView.prefWidth(height), navigationWidth) + right;
+    }
+
+    @Override protected double computePrefHeight(double width) {
+        double top = snapSpace(getInsets().getTop());
+        double bottom = snapSpace(getInsets().getBottom());
+        double navigationHeight = snapSize(navigation.prefHeight(width));
+        return top + paginationListView.prefHeight(width) + navigationHeight + bottom;
+    }
+
+    @Override protected void layoutChildren() {
+        double left = snapSpace(getInsets().getLeft());
+        double right = snapSpace(getInsets().getRight());
+        double top = snapSpace(getInsets().getTop());
+        double bottom = snapSpace(getInsets().getBottom());
+        double width = getWidth() - (left + right);
+        double height = getHeight() - (top + bottom);
+        double navigationWidth = navigation.prefWidth(-1);
+        double navigationHeight = navigation.prefHeight(-1);
+
+        HPos hpos = navigation.getAlignment().getHpos();
+        VPos vpos = navigation.getAlignment().getVpos();
+        double x = left + Utils.computeXOffset(width, navigationWidth, hpos);
+        double y = top + Utils.computeYOffset(height, navigationHeight, vpos);
+
+        layoutInArea(paginationListView, left, top, width, height - navigationHeight, 0, HPos.CENTER, VPos.CENTER);
+        layoutInArea(navigation, x, y, navigationWidth, navigationHeight, 0, hpos, vpos);
+    }
+
+    class NavigationControl extends StackPane {
+
+        private StackPane leftArrowButton;
+        private StackPane rightArrowButton;
+        private List<IndicatorButton> indicatorButton;
+
+        public NavigationControl() {
+            getStyleClass().setAll("pagination-control");
+            StackPane leftArrow = new StackPane();
+            leftArrow.getStyleClass().add("left-arrow");
+            leftArrowButton = new StackPane();
+            leftArrowButton.getStyleClass().add("arrow-button");
+            leftArrowButton.getChildren().setAll(leftArrow);
+
+            StackPane rightArrow = new StackPane();
+            rightArrow.getStyleClass().add("right-arrow");
+            rightArrowButton = new StackPane();
+            rightArrowButton.getStyleClass().add("arrow-button");
+            rightArrowButton.getChildren().setAll(rightArrow);
+
+            indicatorButton = new ArrayList<IndicatorButton>();
+
+            getChildren().addAll(leftArrowButton, rightArrowButton);
+            setupPageIndicators();
+            setupEventHandlers();
+            indicatorButton.get(0).setSelected(true);
+            leftArrowButton.setVisible(false);
+        }
+
+        private void setupEventHandlers() {
+            leftArrowButton.setOnMousePressed(new EventHandler<MouseEvent>() {
+                @Override
+                public void handle(MouseEvent arg0) {
+                    pagination.getSelectionModel().selectPrevious();
+                    //System.out.println("LEFT BUTTON " + pagination.getSelectionModel().getSelectedIndex());
+                    paginationListView.show(pagination.getSelectionModel().getSelectedIndex());
+                    requestLayout();
+                }
+            });
+
+            rightArrowButton.setOnMousePressed(new EventHandler<MouseEvent>() {
+                @Override
+                public void handle(MouseEvent arg0) {
+                    pagination.getSelectionModel().selectNext();
+                    //System.out.println("RIGHT BUTTON " + pagination.getSelectionModel().getSelectedIndex() + " TNP " + (totalNumberOfPages - 1));
+                    paginationListView.show(pagination.getSelectionModel().getSelectedIndex());
+                    requestLayout();
+                }
+            });
+
+            getSkinnable().getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
+                @Override
+                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 + " SIZE " + pages.size());
+                    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 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();
+                }
+            });
+        }
+
+        private void setupPageIndicators() {
+            if (!indicatorButton.isEmpty()) {
+                getChildren().removeAll(indicatorButton);
+                indicatorButton.clear();
+            }
+
+            for (int i = fromIndex; i <= toIndex; i++) {
+                indicatorButton.add(new IndicatorButton(i));
+            }
+            getChildren().addAll(indicatorButton);
+        }
+
+        private void scroll() {
+            if (previousIndex < currentIndex && currentIndex % numberOfVisiblePages == 0) {
+                // Scroll to the right
+                fromIndex = currentIndex;
+                toIndex = fromIndex + (numberOfVisiblePages - 1);
+            } else if (currentIndex < previousIndex && currentIndex % numberOfVisiblePages == numberOfVisiblePages - 1) {
+                // Scroll to the left
+                toIndex = currentIndex;
+                fromIndex = toIndex - (numberOfVisiblePages - 1);
+            } else {
+                return;
+            }
+
+            // We have gone past the total number of pages
+            if (toIndex > totalNumberOfPages - 1) {
+                toIndex = totalNumberOfPages - 1;
+            }
+
+            // We have gone past the starting page
+            if (fromIndex < 0) {
+                fromIndex = 0;
+                toIndex = fromIndex + (numberOfVisiblePages - 1);
+            }
+            //System.out.println("SCROLL from " + fromIndex + " to " + toIndex + " previous " + previousIndex + " current " + currentIndex);
+            setupPageIndicators();
+        }
+
+        @Override protected double computeMinWidth(double height) {
+            return computePrefWidth(height);
+        }
+
+        @Override protected double computeMinHeight(double width) {
+            return computePrefHeight(width);
+        }
+
+        @Override protected double computePrefWidth(double height) {
+            double left = snapSpace(getInsets().getLeft());
+            double right = snapSpace(getInsets().getRight());
+            double leftArrowWidth = snapSize(leftArrowButton.prefWidth(-1));
+            double rightArrowWidth = snapSize(rightArrowButton.prefWidth(-1));
+            double indicatorWidth = snapSize(indicatorButton.get(0).prefWidth(-1));
+
+            indicatorWidth *= indicatorButton.size();
+
+            return left + leftArrowWidth + indicatorWidth + rightArrowWidth + right;
+        }
+
+        @Override protected double computePrefHeight(double width) {
+            double top = snapSpace(getInsets().getTop());
+            double bottom = snapSpace(getInsets().getBottom());
+            double leftArrowHeight = snapSize(leftArrowButton.prefWidth(-1));
+            double rightArrowHeight = snapSize(rightArrowButton.prefWidth(-1));
+            double indicatorHeight = snapSize(indicatorButton.get(0).prefHeight(-1));
+
+            return top + Math.max(leftArrowHeight, Math.max(rightArrowHeight, indicatorHeight)) + bottom;
+        }
+
+        @Override protected double computeMaxWidth(double height) {
+            return computePrefWidth(height);
+        }
+
+        @Override protected double computeMaxHeight(double width) {
+            return computePrefHeight(width);
+        }
+
+        @Override protected void layoutChildren() {
+            double top = snapSpace(getInsets().getTop());
+            double bottom = snapSpace(getInsets().getBottom());
+            double left = snapSpace(getInsets().getLeft());
+            double right = snapSpace(getInsets().getRight());
+            double width = snapSize(getWidth()) - (left + right);
+            double height = snapSize(getHeight()) - (top + bottom);
+            double leftArrowWidth = snapSize(leftArrowButton.prefWidth(-1));
+            double leftArrowHeight = snapSize(leftArrowButton.prefHeight(-1));
+            double rightArrowWidth = snapSize(rightArrowButton.prefWidth(-1));
+            double rightArrowHeight = snapSize(rightArrowButton.prefHeight(-1));
+            double indicatorWidth = snapSize(indicatorButton.get(0).prefWidth(-1));
+            double indicatorHeight = snapSize(indicatorButton.get(0).prefHeight(-1));
+            double arrowButtonY = top + Utils.computeYOffset(height, leftArrowHeight, VPos.CENTER);
+            double indicatorButtonY = top + Utils.computeYOffset(height, indicatorHeight, VPos.CENTER);
+
+            leftArrowButton.resize(leftArrowWidth, leftArrowHeight);
+
+            positionInArea(leftArrowButton, left, arrowButtonY, leftArrowWidth, leftArrowHeight, 0, HPos.CENTER, VPos.CENTER);
+
+            double indicatorX = left + leftArrowWidth;
+            for (int i = 0; i < indicatorButton.size(); i++) {
+                indicatorButton.get(i).resize(indicatorWidth, indicatorHeight);
+                positionInArea(indicatorButton.get(i), indicatorX, indicatorButtonY, indicatorWidth, indicatorHeight, 0, HPos.CENTER, VPos.CENTER);
+                indicatorX += indicatorWidth;
+            }
+
+            rightArrowButton.resize(rightArrowWidth, rightArrowHeight);
+            positionInArea(rightArrowButton, indicatorX, arrowButtonY, rightArrowWidth, rightArrowHeight, 0, HPos.CENTER, VPos.CENTER);
+        }
+    }
+
+    class IndicatorButton extends StackPane {
+        private int pageNumber;
+        private StackPane indicator;
+        private Label pageIndicator;
+        private boolean selected;
+
+        public IndicatorButton(int pageNumber) {
+            getStyleClass().add("arrow-button");
+            this.selected = false;
+            this.pageNumber = pageNumber;
+            pageIndicator = new Label(Integer.toString(this.pageNumber + 1));
+
+            indicator = new StackPane();
+            indicator.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+            indicator.setPrefSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
+            indicator.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
+
+            setIndicatorType();
+            getChildren().setAll(indicator);
+
+            getSkinnable().getStyleClass().addListener(new ListChangeListener<String>() {
+                @Override
+                public void onChanged(Change<? extends String> change) {
+                    setIndicatorType();
+                }
+            });
+
+            setOnMousePressed(new EventHandler<MouseEvent>() {
+                @Override
+                public void handle(MouseEvent arg0) {
+                    int selected = pagination.getSelectionModel().getSelectedIndex();
+                    // We do not need to update the selection if it has not changed.
+                    if (selected != IndicatorButton.this.pageNumber) {
+                        pagination.getSelectionModel().select(IndicatorButton.this.pageNumber);
+                        paginationListView.show(pagination.getSelectionModel().getSelectedIndex());
+                        requestLayout();
+                    }
+                }
+            });
+        }
+
+        private final void setIndicatorType() {
+            if (getSkinnable().getStyleClass().contains(Pagination.STYLE_CLASS_BULLET)) {
+                indicator.getStyleClass().setAll("bullet-indicator");
+                indicator.getChildren().remove(pageIndicator);
+            } else {
+                indicator.getStyleClass().setAll("number-indicator");
+                indicator.getChildren().setAll(pageIndicator);
+            }
+        }
+
+        public void setSelected(boolean selected) {
+            this.selected = selected;
+            impl_pseudoClassStateChanged("selected");
+        }
+
+        public int getPageNumber() {
+            return this.pageNumber;
+        }
+
+        @Override protected double computePrefWidth(double height) {
+            double left = snapSpace(getInsets().getLeft());
+            double right = snapSpace(getInsets().getRight());
+
+            return left + indicator.prefWidth(height) + right;
+        }
+
+        @Override protected double computePrefHeight(double width) {
+            double top = snapSpace(getInsets().getTop());
+            double bottom = snapSpace(getInsets().getBottom());
+
+            return top + indicator.prefHeight(width) + bottom;
+        }
+
+        @Override public long impl_getPseudoClassState() {
+            long mask = super.impl_getPseudoClassState();
+
+            if (selected) {
+                mask |= SELECTED_PSEUDOCLASS_STATE;
+            }
+            return mask;
+        }
+    }
+
+    private static final long SELECTED_PSEUDOCLASS_STATE =
+            StyleManager.getInstance().getPseudoclassMask("selected");
+
+    class PaginationListView<T> extends ListView<T> {
+        public PaginationListView() {
+            this(FXCollections.<T>observableArrayList());
+        }
+
+        public PaginationListView(ObservableList<T> items) {
+            super(items);
+            setId("list-view");
+        }
+
+        public void show(int index) {
+            getProperties().put(VirtualContainerBase.SHOW_INDEX, index);
+        }
+    }
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PositionMapper.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PositionMapper.java	Mon Mar 19 09:26:08 2012 -0700
@@ -163,7 +163,7 @@
     public void adjustPositionToIndex(int index) {
         if (getItemCount() <= 0) {
             setPosition(0.0f);
-        } else {
+        } else {            
             adjustPosition(((double)index) / getItemCount());
         }
     }
@@ -266,4 +266,13 @@
         double p = Utils.clamp(0, itemIndex, count) / count;
         return -(getViewportSize() * p);
     }
+    
+    /**
+     * Adjust the position based on a chunk of pixels. The position is based
+     * on the start of the scrollbar position.
+     */
+    public void adjustByPixelChunk(double numPixels) {
+        setPosition(0);
+        adjustByPixelAmount(numPixels);
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableHeaderRow.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableHeaderRow.java	Mon Mar 19 09:26:08 2012 -0700
@@ -340,7 +340,8 @@
         
         if (col.getColumns().isEmpty()) {
             CheckMenuItem item = columnMenuItems.remove(col);
-
+            if (item == null) return;
+            
             item.textProperty().unbind();
             item.selectedProperty().unbindBidirectional(col.visibleProperty());
 
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Mon Mar 19 09:26:08 2012 -0700
@@ -402,7 +402,7 @@
 
         // Add initial text content
         for (int i = 0, n = USE_MULTIPLE_NODES ? textArea.getParagraphs().size() : 1; i < n; i++) {
-            CharSequence paragraph = (n == 1) ? textArea.getText() : textArea.getParagraphs().get(i);
+            CharSequence paragraph = (n == 1) ? textArea.textProperty().getValueSafe() : textArea.getParagraphs().get(i);
             addParagraphNode(i, paragraph.toString());
         }
 
@@ -524,7 +524,7 @@
         textArea.textProperty().addListener(new InvalidationListener() {
             @Override public void invalidated(Observable observable) {
                 invalidateMetrics();
-                ((Text)paragraphNodes.getChildren().get(0)).setText(textArea.getText());
+                ((Text)paragraphNodes.getChildren().get(0)).setText(textArea.textProperty().getValueSafe());
                 contentView.requestLayout();
             }
         });
@@ -968,7 +968,7 @@
 
     public void paragraphStart(boolean previousIfAtStart, boolean select) {
         TextArea textArea = getSkinnable();
-        String text = textArea.getText();
+        String text = textArea.textProperty().getValueSafe();
         int pos = textArea.getCaretPosition();
 
         if (pos > 0) {
@@ -991,7 +991,7 @@
 
     public void paragraphEnd(boolean nextIfAtEnd, boolean select) {
         TextArea textArea = getSkinnable();
-        String text = textArea.getText();
+        String text = textArea.textProperty().getValueSafe();
         int pos = textArea.getCaretPosition();
         int len = text.length();
 
@@ -1017,7 +1017,7 @@
         int pStart = 0;
         for (Node node : paragraphNodes.getChildren()) {
             Text p = (Text)node;
-            int pEnd = pStart + p.getText().length();
+            int pEnd = pStart + p.textProperty().getValueSafe().length();
             if (pEnd >= start) {
                 return p.impl_getUnderlineShape(start - pStart, end - pStart);
             }
@@ -1030,7 +1030,7 @@
         int pStart = 0;
         for (Node node : paragraphNodes.getChildren()) {
             Text p = (Text)node;
-            int pEnd = pStart + p.getText().length();
+            int pEnd = pStart + p.textProperty().getValueSafe().length();
             if (pEnd >= start) {
                 return p.impl_getRangeShape(start - pStart, end - pStart);
             }
@@ -1044,7 +1044,7 @@
         Text paragraphNode = null;
         for (Node node : paragraphNodes.getChildren()) {
             Text p = (Text)node;
-            int pEnd = pStart + p.getText().length();
+            int pEnd = pStart + p.textProperty().getValueSafe().length();
             if (pEnd >= start) {
                 paragraphNode = p;
                 break;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualContainerBase.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualContainerBase.java	Mon Mar 19 09:26:08 2012 -0700
@@ -42,6 +42,7 @@
 public abstract class VirtualContainerBase<C extends Control, B extends BehaviorBase<C>, I extends IndexedCell> extends SkinBase<C, B> {
 
     public static final String SCROLL_TO_INDEX = "VirtualContainerBase.scrollToIndex";
+    public static final String SHOW_INDEX = "VirtualContainerBase.showIndex";
 
     public VirtualContainerBase(C control, B behavior) {
         super(control, behavior);
@@ -56,6 +57,13 @@
                     }
 
                     c.getMap().remove(SCROLL_TO_INDEX);
+                } else if (c.wasAdded() && SHOW_INDEX.equals(c.getKey())) {
+                    Object index = c.getValueAdded();
+                    if (index instanceof Integer) {
+                        flow.scrollTo((Integer)index);
+                    }
+
+                    c.getMap().remove(SHOW_INDEX);
                 }
             }
         });
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Mon Mar 19 09:26:08 2012 -0700
@@ -1762,16 +1762,16 @@
 //                layingOut = false;
                 return;
             }
-
-            // In this case, we're asked to show a random cell
-//            layingOut = true;
-            mapper.adjustPositionToIndex(index);
-            addAllToPile();
-            requestLayout();
-//            layingOut = false;
         }
     }
 
+    public void scrollTo(int index) {
+        double offset = index * getCellLength(index);
+        mapper.adjustByPixelChunk(offset);
+        addAllToPile();
+        requestLayout();        
+    }
+    
     /**
      * 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	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Mon Mar 19 09:26:08 2012 -0700
@@ -2717,30 +2717,36 @@
     -fx-padding: 0.333333em 0.083333em 0.666667em 0.083333em; /* 4 1 8 1 */
 }
 
-.fxvk .button { 
+.fxvk-secondary {
+    -fx-background-radius: 8;
+    -fx-padding: 1 3 1 3;
+}
+
+.fxvk .key { 
     -fx-body-color: linear-gradient(to bottom, #efefef, #a3a3a3);
-    -fx-font-size: 1.666667em; /* 20pt */
+    -fx-font-family: "Segoe UI";
+    -fx-font-size: 1.5em; /* 18pt */
     -fx-font-weight: bold;
     -fx-padding: 3 3 5 6;
     -fx-background-radius: 12;
 }
 
-.fxvk .button .special-key { 
-    -fx-body-color: linear-gradient(to bottom, #646464, #373737);
-}
-
-.special-key { 
-    -fx-text-fill: white;
-    -fx-font: bold 1.75em "Segoe UI Symbol";
-}
-
-.fxvk .button .label { 
-    -fx-text-fill: #b1b1b1;
-    -fx-font-size: 0.833333em; /* 10pt */
+.fxvk .secondary-key { 
+    -fx-padding: 3 3 5 1;
+}
+
+.fxvk .key .label { 
+    -fx-text-fill: #a1a1a1;
     -fx-font-weight: bold;
     -fx-alignment: TOP_RIGHT;
 }
 
+.fxvk .special-key { 
+    -fx-body-color: linear-gradient(to bottom, #646464, #373737);
+    -fx-text-fill: white;
+    -fx-font-family: "Segoe UI Symbol";
+}
+
 
 /* ====== CHART TOP LEVEL ITEMS =========================================================== */
 .chart {
@@ -3103,6 +3109,18 @@
     -fx-skin: "com.sun.javafx.scene.control.skin.ComboBoxListViewSkin";
 }
 
+.color-picker {
+    -fx-skin: "com.sun.javafx.scene.control.skin.ColorPickerSkin";
+}
+
+.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;
@@ -3135,3 +3153,63 @@
     -fx-background-color: -fx-cell-hover-color;
     -fx-text-fill: -fx-text-inner-color;
 }
+
+.pagination-cell {
+    -fx-skin: "com.sun.javafx.scene.control.skin.PaginationCellSkin";
+    -fx-backgound-color: yellow;
+}
+
+.pagination-cell:odd {
+    -fx-backgound-color: green;
+}
+
+.pagination {
+    -fx-skin: "com.sun.javafx.scene.control.skin.PaginationSkin";
+}
+
+.pagination .pagination-control {
+    -fx-alignment: BOTTOM_CENTER;
+    -fx-background-color: transparent;
+}
+
+.pagination .pagination-control > .arrow-button {    
+    -fx-padding: 0.833333em 0.416667em 0.833333em 0.416667em; /* 10 5 10 5 */    
+}
+
+.pagination .pagination-control > .arrow-button .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 {
+    -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 {
+   -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 {
+   -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 {
+    -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;    
+}
--- a/javafx-ui-controls/src/javafx/scene/control/Menu.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/javafx/scene/control/Menu.java	Mon Mar 19 09:26:08 2012 -0700
@@ -123,6 +123,13 @@
      **************************************************************************/
 
     /**
+     * Constructs a Menu with an empty string for its display text.
+     */
+    public Menu() {
+        this("");
+    }
+    
+    /**
      * Constructs a Menu and sets the display text with the specified text.
      */
     public Menu(String text) {
--- a/javafx-ui-controls/src/javafx/scene/control/PopupControl.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/src/javafx/scene/control/PopupControl.java	Mon Mar 19 09:26:08 2012 -0700
@@ -122,17 +122,42 @@
     // both here and on Node.
     
     /**
-     * The id of this {@code Node}. This simple string identifier is useful for
+     * The id of this {@code PopupControl}. This simple string identifier is useful for
      * finding a specific Node within the scene graph. While the id of a Node
      * should be unique within the scene graph, this uniqueness is not enforced.
-     * This is analogous to the "id" attribute on an HTML element.
+     * This is analogous to the "id" attribute on an HTML element 
+     * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
      *
      * @defaultValue null
      */
     private final StringProperty id = new SimpleStringProperty(this, "id");
+    public final StringProperty idProperty() { return id; }
+    
+    /**
+     * Sets the id of this {@code PopupControl}. This simple string identifier is useful for
+     * finding a specific Node within the scene graph. While the id of a Node
+     * should be unique within the scene graph, this uniqueness is not enforced.
+     * This is analogous to the "id" attribute on an HTML element 
+     * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
+     *
+     * @param value  the id assigned to this {@code PopupControl} using the {@code setId} 
+     *         method or {@code null}, if no id has been assigned.
+     * @defaultValue null
+     */
     public final void setId(String value) { id.set(value); }
+    
+    /**
+     * The id of this {@code PopupControl}. This simple string identifier is useful for
+     * finding a specific Node within the scene graph. While the id of a Node
+     * should be unique within the scene graph, this uniqueness is not enforced.
+     * This is analogous to the "id" attribute on an HTML element 
+     * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
+     *
+     * @return the id assigned to this {@code PopupControl} using the {@code setId} 
+     *         method or {@code null}, if no id has been assigned.
+     * @defaultValue null
+     */
     public final String getId() { return id.get(); }
-    public final StringProperty idProperty() { return id; }
 
     /**
      * A list of String identifiers which can be used to logically group
@@ -177,7 +202,7 @@
 
     /**
      * A string representation of the CSS style associated with this
-     * specific Node. This is analogous to the "style" attribute of an
+     * specific PopupControl. This is analogous to the "style" attribute of an
      * HTML element. Note that, like the HTML style attribute, this
      * variable contains style properties and values and not the
      * selector portion of a style rule.
@@ -188,7 +213,31 @@
      * @defaultValue empty string
      */
     private final StringProperty style = new SimpleStringProperty(this, "style");
+    
+    /**
+     * A string representation of the CSS style associated with this
+     * specific {@code PopupControl}. This is analogous to the "style" attribute of an
+     * HTML element. Note that, like the HTML style attribute, this
+     * variable contains style properties and values and not the
+     * selector portion of a style rule.
+     * @param value The inline CSS style to use for this {@code PopupControl}.
+     *         {@code null} is implicitly converted to an empty String. 
+     * @defaultValue empty string
+     */
     public final void setStyle(String value) { style.set(value); }
+    
+    // TODO: javadoc copied from property for the sole purpose of providing a return tag
+    /**
+     * A string representation of the CSS style associated with this
+     * specific {@code PopupControl}. This is analogous to the "style" attribute of an
+     * HTML element. Note that, like the HTML style attribute, this
+     * variable contains style properties and values and not the
+     * selector portion of a style rule.
+     * @defaultValue empty string
+     * @return The inline CSS style associated with this {@code PopupControl}.
+     *         If this {@code PopupControl} does not have an inline style,
+     *         an empty String is returned.
+     */
     public final String getStyle() { return style.get(); }
     public final StringProperty styleProperty() { return style; }
 
@@ -227,7 +276,35 @@
      * enabling applications to easily restrict the resizability of the control.
      */
     private DoubleProperty minWidth;
+    
+    /**
+     * Property for overriding the control's computed minimum width.
+     * This should only be set if the control's internally computed minimum width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMinWidth(forHeight)</code> will return the control's internally
+     * computed minimum width.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
+     * enabling applications to easily restrict the resizability of the control.
+     */
     public final void setMinWidth(double value) { minWidthProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed minimum width.
+     * This should only be set if the control's internally computed minimum width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMinWidth(forHeight)</code> will return the control's internally
+     * computed minimum width.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
+     * enabling applications to easily restrict the resizability of the control.
+     */
     public final double getMinWidth() { return minWidth == null ? USE_COMPUTED_SIZE : minWidth.get(); }
     public final DoubleProperty minWidthProperty() {
         if (minWidth == null) {
@@ -266,7 +343,37 @@
      *
      */
     private DoubleProperty minHeight;
+    
+    /**
+     * Property for overriding the control's computed minimum height.
+     * This should only be set if the control's internally computed minimum height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMinHeight(forWidth)</code> will return the control's internally
+     * computed minimum height.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
+     * enabling applications to easily restrict the resizability of the control.
+     *
+     */
     public final void setMinHeight(double value) { minHeightProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed minimum height.
+     * This should only be set if the control's internally computed minimum height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMinHeight(forWidth)</code> will return the control's internally
+     * computed minimum height.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
+     * enabling applications to easily restrict the resizability of the control.
+     *
+     */
     public final double getMinHeight() { return minHeight == null ? USE_COMPUTED_SIZE : minHeight.get(); }
     public final DoubleProperty minHeightProperty() {
         if (minHeight == null) {
@@ -312,10 +419,29 @@
      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
      * <code>getPrefWidth(forHeight)</code> will return the control's internally
      * computed preferred width.
-     *
      */
     private DoubleProperty prefWidth;
+    
+    /**
+     * Property for overriding the control's computed preferred width.
+     * This should only be set if the control's internally computed preferred width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getPrefWidth(forHeight)</code> will return the control's internally
+     * computed preferred width.
+     */
     public final void setPrefWidth(double value) { prefWidthProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed preferred width.
+     * This should only be set if the control's internally computed preferred width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getPrefWidth(forHeight)</code> will return the control's internally
+     * computed preferred width.
+     */
     public final double getPrefWidth() { return prefWidth == null ? USE_COMPUTED_SIZE : prefWidth.get(); }
     public final DoubleProperty prefWidthProperty() {
         if (prefWidth == null) {
@@ -349,7 +475,29 @@
      *
      */
     private DoubleProperty prefHeight;
+    
+    /**
+     * Property for overriding the control's computed preferred height.
+     * This should only be set if the control's internally computed preferred height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getPrefHeight(forWidth)</code> will return the control's internally
+     * computed preferred width.
+     *
+     */
     public final void setPrefHeight(double value) { prefHeightProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed preferred height.
+     * This should only be set if the control's internally computed preferred height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getPrefHeight(forWidth)</code> will return the control's internally
+     * computed preferred width.
+     *
+     */
     public final double getPrefHeight() { return prefHeight == null ? USE_COMPUTED_SIZE : prefHeight.get(); }
     public final DoubleProperty prefHeightProperty() {
         if (prefHeight == null) {
@@ -399,10 +547,37 @@
      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
      * enabling applications to easily restrict the resizability of the control.
-     *
      */
     private DoubleProperty maxWidth;
+    
+    /**
+     * Property for overriding the control's computed maximum width.
+     * This should only be set if the control's internally computed maximum width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMaxWidth(forHeight)</code> will return the control's internally
+     * computed maximum width.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
+     * enabling applications to easily restrict the resizability of the control.
+     */
     public final void setMaxWidth(double value) { maxWidthProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed maximum width.
+     * This should only be set if the control's internally computed maximum width
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMaxWidth(forHeight)</code> will return the control's internally
+     * computed maximum width.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
+     * enabling applications to easily restrict the resizability of the control.
+     */
     public final double getMaxWidth() { return maxWidth == null ? USE_COMPUTED_SIZE : maxWidth.get(); }
     public final DoubleProperty maxWidthProperty() {
         if (maxWidth == null) {
@@ -440,7 +615,37 @@
      *
      */
     private DoubleProperty maxHeight;
+    
+    /**
+     * Property for overriding the control's computed maximum height.
+     * This should only be set if the control's internally computed maximum height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMaxHeight(forWidth)</code> will return the control's internally
+     * computed maximum height.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
+     * enabling applications to easily restrict the resizability of the control.
+     *
+     */
     public final void setMaxHeight(double value) { maxHeightProperty().set(value); }
+    
+    /**
+     * Property for overriding the control's computed maximum height.
+     * This should only be set if the control's internally computed maximum height
+     * doesn't meet the application's layout needs.
+     * <p>
+     * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
+     * <code>getMaxHeight(forWidth)</code> will return the control's internally
+     * computed maximum height.
+     * <p>
+     * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
+     * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
+     * enabling applications to easily restrict the resizability of the control.
+     *
+     */
     public final double getMaxHeight() { return maxHeight == null ? USE_COMPUTED_SIZE : maxHeight.get(); }
     public final DoubleProperty maxHeightProperty() {
         if (maxHeight == null) {
--- a/javafx-ui-controls/test/com/sun/javafx/scene/control/skin/MenuBarMenuButtonRetriever.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/skin/MenuBarMenuButtonRetriever.java	Mon Mar 19 09:26:08 2012 -0700
@@ -3,7 +3,12 @@
  */
 package com.sun.javafx.scene.control.skin;
 
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.ContextMenu;
 import javafx.scene.control.MenuButton;
+import javafx.scene.control.Skin;
+import javafx.scene.layout.VBox;
 
 
 /**
@@ -16,4 +21,32 @@
     public static MenuButton getNodeForMenu(MenuBarSkin skin, int i) {
         return skin.getNodeForMenu(i);
     }
+    
+    public static ContextMenu getSubMenu(ContextMenuContent cmc) {
+      return cmc.getSubMenu();
+    }
+    
+    public static Skin getPopupSkin(MenuButton mb) {
+        return ((MenuButtonSkinBase)mb.getSkin()).popup.getSkin();
+    }
+    
+    public static ContextMenuContent getMenuContent(MenuButton mb) {
+        ContextMenuContent cmc = (ContextMenuContent)getPopupSkin(mb).getNode();
+        return cmc;
+    }
+    
+    public static ContextMenuContent getSubMenuContent(ContextMenuContent cmc) {
+        ContextMenu cm = cmc.getSubMenu();
+        return (cm != null) ? (ContextMenuContent)cm.getSkin().getNode() : null;
+    }
+    
+    public static ContextMenuContent.MenuItemContainer getDisplayNodeForMenuItem(ContextMenuContent cmc, int i) {
+        VBox itemsContainer = cmc.getItemsContainer();
+        return (i < itemsContainer.getChildren().size()) ? 
+            (ContextMenuContent.MenuItemContainer)itemsContainer.getChildren().get(i) : null;
+    }
+    
+    public static void setCurrentFocusedIndex(ContextMenuContent cmc, int i) {
+        cmc.setCurrentFocusedIndex(i);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/MenuBarTest.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/test/javafx/scene/control/MenuBarTest.java	Mon Mar 19 09:26:08 2012 -0700
@@ -5,10 +5,10 @@
 package javafx.scene.control;
 
 import com.sun.javafx.pgstub.StubToolkit;
+import com.sun.javafx.scene.control.skin.ContextMenuContent;
 import com.sun.javafx.scene.control.skin.MenuBarMenuButtonRetriever;
 import com.sun.javafx.scene.control.skin.MenuBarSkin;
 import com.sun.javafx.tk.Toolkit;
-import javafx.event.EventHandler;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
 import javafx.scene.input.MouseButton;
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertFalse;
 import javafx.event.EventType;
 import javafx.scene.Node;
+import javafx.scene.input.KeyCode;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -100,7 +101,80 @@
         assertTrue(menu.isShowing());
         
     }
+    
+    @Test public void testSubMenuDismissalWithKeyNavigation() {
+        final MouseEventGenerator generator = new MouseEventGenerator();
+        AnchorPane root = new AnchorPane();
+        Menu menu = new Menu("Menu");
+        Menu menu1 = new Menu("Menu With SubMenu");
+        menu.getItems().add(menu1);
 
+        MenuItem menuItem1 = new MenuItem("MenuItem1");
+        MenuItem menuItem2 = new MenuItem("MenuItem2");
+        menu1.getItems().addAll(menuItem1, menuItem2);
+        
+        menuBar.getMenus().add(menu);
+        menuBar.setLayoutX(100);
+        menuBar.setLayoutY(100);
+
+        root.getChildren().add(menuBar);
+        startApp(root);
+        tk.firePulse();
+        
+        MenuBarSkin skin = (MenuBarSkin)menuBar.getSkin();
+        assertTrue(skin != null);
+        
+        double xval = (menuBar.localToScene(menuBar.getLayoutBounds())).getMinX();
+        double yval = (menuBar.localToScene(menuBar.getLayoutBounds())).getMinY();
+   
+        MenuButton mb = MenuBarMenuButtonRetriever.getNodeForMenu(skin, 0);
+        mb.getScene().getWindow().requestFocus();
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_PRESSED, xval+20, yval+20));
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_RELEASED, xval+20, yval+20));
+        assertTrue(menu.isShowing());
+         /* ------------------------------------------------------------------ */
+        
+        // Show subMenu
+        ContextMenuContent menuContent = MenuBarMenuButtonRetriever.getMenuContent(mb); // ContextMenuContent
+        Node displayNode = MenuBarMenuButtonRetriever.getDisplayNodeForMenuItem(menuContent, 0); // MenuItemContainer
+        
+        displayNode.getScene().getWindow().requestFocus();
+        assertTrue(displayNode.getScene().getWindow().isFocused());
+        
+        displayNode.requestFocus(); // requestFocus on 1st Menu
+        assertTrue(displayNode.isFocused());
+        // update currentFocusedIndex
+        MenuBarMenuButtonRetriever.setCurrentFocusedIndex(menuContent, 0);
+        
+        // fire KeyEvent (Enter) on menu1 to show submenu
+        KeyEventFirer keyboard = new KeyEventFirer(menuContent);
+        keyboard.doKeyPress(KeyCode.ENTER);
+        tk.firePulse();     
+        assertTrue(menu1.isShowing()); // subMenu is showing
+        /* ------------------------------------------------------------------ */
+        
+        // Get 1st MenuItem from the submenu
+        ContextMenuContent subMenuContent = MenuBarMenuButtonRetriever.getSubMenuContent(menuContent);
+        subMenuContent.getScene().getWindow().requestFocus(); // requestFocus on submenu
+        assertTrue(subMenuContent.getScene().getWindow().isFocused());
+        
+        displayNode = MenuBarMenuButtonRetriever.getDisplayNodeForMenuItem(subMenuContent, 0);
+        displayNode.requestFocus();
+        assertTrue(displayNode.isFocused());
+        
+        MenuBarMenuButtonRetriever.setCurrentFocusedIndex(subMenuContent, 0);
+        // fire KeyEvent (Enter) on menuItem1 to hide all menus
+        keyboard = new KeyEventFirer(subMenuContent);
+        keyboard.doKeyPress(KeyCode.ENTER);
+        tk.firePulse();
+        
+        // confirm all menus are closed. 
+        assertTrue(!menu1.isShowing());
+        assertTrue(!menu.isShowing());
+    }
+    
     static final class MouseEventGenerator {
         private boolean primaryButtonDown = false;
 
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Fri Mar 16 09:26:59 2012 +0100
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Mon Mar 19 09:26:08 2012 -0700
@@ -400,7 +400,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
@@ -413,9 +413,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
@@ -428,9 +428,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
@@ -443,9 +443,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
@@ -458,9 +458,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
@@ -469,9 +469,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
@@ -480,9 +480,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
@@ -498,9 +498,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
@@ -516,18 +516,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
@@ -536,9 +536,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
@@ -547,9 +547,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
@@ -557,9 +557,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
@@ -575,9 +575,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
@@ -594,9 +594,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
@@ -613,9 +613,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
@@ -631,9 +631,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
@@ -649,9 +649,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
@@ -667,7 +667,7 @@
         assertTrue(isSelected(0,1,2,3));
         assertTrue(isNotSelected(4,5));
         assertTrue(isAnchor(3));
-    } */
+    }
     
     // test 46
     @Test public void testHomeKey_withSelectedItems() {
@@ -717,7 +717,7 @@
         assertTrue(isAnchor(5));
     }
     
-    /* test 65
+    // test 65
     @Test public void testShiftPageUp() {
         sm.clearAndSelect(0);
         keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
@@ -731,14 +731,14 @@
         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));
-    } */
+    }
     
     
     /***************************************************************************
@@ -781,6 +781,228 @@
     
     
     /***************************************************************************
+     * Tests for discontinuous multiple row selection (RT-18951)
+     **************************************************************************/    
+    
+    // Test 1
+    @Test public void test_rt18591_row_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.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 2
+    @Test public void test_rt18591_row_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.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 3
+    @Test public void test_rt18591_row_3() {
+        // same as test 1 above
+        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.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        // end of similarities
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,1,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 4
+    @Test public void test_rt18591_row_4() {
+        // same as test 2 above
+        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.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        // 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(isAnchor(3));
+    }
+    
+    // Test 5 (need Page down support)
+//    @Test public void test_5() {
+//        // same as test 1 above
+//        sm.clearAndSelect(0);
+//        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+//        keyboard.doDownArrowPress(KeyModifier.getShortcutKey());
+//        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+//        assertTrue(isSelected(0,2));
+//        assertTrue(isAnchor(2));
+//        // end of similarities
+//        
+//        keyboard.doKeyPress(KeyCode.PAGE_DOWN, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        assertTrue(isSelected(0,2,/*until end of page */));
+//        assertTrue(isAnchor(2));
+//    }
+    
+    // Test 6
+    @Test public void test_rt18591_row_6() {
+        sm.clearAndSelect(10);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(8,10));
+        assertTrue(isAnchor(8));
+        
+        keyboard.doKeyPress(KeyCode.PAGE_UP, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,8,10));
+        assertTrue(isAnchor(8));
+    }
+    
+//    // Test 7
+//    @Test public void test_rt18591_row_7() {
+//        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.PAGE_DOWN, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        keyboard.doKeyPress(KeyCode.PAGE_DOWN, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+//        assertTrue(isSelected(0,2,3,4,5,6,7,8,10)); // this isn't right
+//        assertTrue(isAnchor(8));
+//        
+//        // NOT COMPLETE
+//    }
+//    
+//    // Test 8
+//    @Test public void test_rt18591_row_8() {
+//        // NOT COMPLETE
+//    }
+    
+    // Test 9
+    @Test public void test_rt18591_row_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.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(2));
+    }
+    
+    // Test 10
+    @Test public void test_rt18591_row_10() {
+        sm.clearAndSelect(8);
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(6,8));
+        assertTrue(isAnchor(6));
+        
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,1,2,3,4,5,6,8));
+        assertTrue(isAnchor(6));
+    }
+    
+    // Test 11
+    @Test public void test_rt18591_row_11() {
+        sm.clearAndSelect(5);
+        keyboard.doKeyPress(KeyCode.HOME, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,1,2,3,4,5));
+        assertTrue(isAnchor(5));
+        
+        keyboard.doKeyPress(KeyCode.END, KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,1,2,3,4,5,6,7,8,9));
+        assertTrue(isAnchor(5));
+    }
+    
+    // Test 12
+    @Test public void test_rt18591_row_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.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doUpArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        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(isAnchor(0));
+        assertTrue(fm.isFocused(0));
+    }
+    
+    
+    /***************************************************************************
+     * Tests for discontinuous multiple cell selection (RT-18951)
+     **************************************************************************/  
+    
+    // Test 1
+    @Test public void test_rt18591_cell_1() {
+        sm.setSelectionMode(SelectionMode.MULTIPLE);
+        sm.select(0, col1);
+        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doRightArrowPress(KeyModifier.getShortcutKey());
+        keyboard.doKeyPress(KeyCode.SPACE, KeyModifier.getShortcutKey());
+        assertTrue(sm.isSelected(0,col1));
+        assertTrue(sm.isSelected(0,col3));
+        assertTrue(isAnchor(2));
+        
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        keyboard.doDownArrowPress(KeyModifier.SHIFT, KeyModifier.getShortcutKey());
+        assertTrue(isSelected(0,2,3,4));
+        assertTrue(isAnchor(2));
+    }
+    
+    
+    /***************************************************************************
      * Tests for specific bug reports
      **************************************************************************/
     
@@ -896,7 +1118,7 @@
         assertTrue(sm.isSelected(1, col1));
     }
 
-    /*
+    //
     @Test public void test_rt18536_positive_vertical() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
@@ -916,9 +1138,9 @@
         assertTrue(sm.isSelected(3, col5));
         assertTrue(sm.isSelected(4, col5));
         assertTrue(sm.isSelected(5, col5));
-    } */
+    }
     
-    /*
+    //
     @Test public void test_rt18536_negative_vertical() {
         // Test shift selection when focus is elsewhere (so as to select a range)
         sm.setCellSelectionEnabled(true);
@@ -938,9 +1160,9 @@
         assertTrue(sm.isSelected(3, col5));
         assertTrue(sm.isSelected(4, col5));
         assertTrue(sm.isSelected(5, col5));
-    } */
+    }
     
-    /*
+    //
     @Test public void test_rt18642() {
         sm.setCellSelectionEnabled(false);
         sm.clearAndSelect(1);                          // select 1
@@ -958,5 +1180,5 @@
         keyboard.doUpArrowPress(KeyModifier.SHIFT);   
         assertTrue(isSelected(4, 5));
         assertTrue(isNotSelected(0, 1, 2, 3));
-    } */
+    }
 }