changeset 2894:e784a2f6b6a3

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/8.0/MASTER/jfx/rt
author David Grieve<david.grieve@oracle.com>
date Tue, 12 Mar 2013 20:00:53 -0400
parents 9f21025a5563 ecc8529fdc5b
children f114202c0208
files javafx-ui-common/src/javafx/scene/Node.java
diffstat 58 files changed, 3060 insertions(+), 1372 deletions(-) [+]
line wrap: on
line diff
--- a/apps/experiments/Modena/src/modena/Modena.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/apps/experiments/Modena/src/modena/Modena.java	Tue Mar 12 20:00:53 2013 -0400
@@ -31,6 +31,9 @@
  */
 package modena;
 
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -55,6 +58,7 @@
 import javafx.event.EventHandler;
 import javafx.fxml.FXMLLoader;
 import javafx.geometry.NodeOrientation;
+import javafx.geometry.Rectangle2D;
 import javafx.scene.Node;
 import javafx.scene.Scene;
 import javafx.scene.SnapshotParameters;
@@ -124,6 +128,7 @@
     
     private final BorderPane outerRoot = new BorderPane();
     private BorderPane root;
+    private SamplePageNavigation samplePageNavigation;
     private SamplePage samplePage;
     private Node mosaic;
     private Node heightTest;
@@ -147,7 +152,8 @@
                 @Override public void run() {
                     updateUserAgentStyleSheet();
                     rebuildUI(modenaButton.isSelected(), retinaButton.isSelected(), 
-                            contentTabs.getSelectionModel().getSelectedIndex());
+                            contentTabs.getSelectionModel().getSelectedIndex(),
+                            samplePageNavigation.getCurrentSection());
                 }
             });
         }
@@ -198,7 +204,7 @@
         outerRoot.setTop(buildMenuBar());
         outerRoot.setCenter(root);
         // build UI
-        rebuildUI(true,false,0);
+        rebuildUI(true,false,0, null);
         // show UI
         Scene scene = new Scene(outerRoot, 1024, 768);
         scene.getStylesheets().add(testAppCssUrl);
@@ -233,6 +239,7 @@
     }
     
     private void updateUserAgentStyleSheet(boolean modena) {
+        final SamplePage.Section scrolledSection = samplePageNavigation==null? null : samplePageNavigation.getCurrentSection();
         if (!modena &&
             (baseColor == null || baseColor == Color.TRANSPARENT) &&
             (backgroundColor == null || backgroundColor == Color.TRANSPARENT) &&
@@ -286,9 +293,16 @@
         setUserAgentStylesheet("internal:stylesheet"+Math.random()+".css");
         
         if (root != null) root.requestLayout();
+
+        // restore scrolled section
+        Platform.runLater(new Runnable() {
+            @Override public void run() {
+                samplePageNavigation.setCurrentSection(scrolledSection);
+            }
+        });
     }
     
-    private void rebuildUI(boolean modena, boolean retina, int selectedTab) {
+    private void rebuildUI(boolean modena, boolean retina, int selectedTab, final SamplePage.Section scrolledSection) {
         try {
             if (root == null) {
                 root = new BorderPane();
@@ -298,14 +312,13 @@
                 root.setTop(null);
                 root.setCenter(null);
             }
+            // Create sample page and nav
+            samplePageNavigation = new SamplePageNavigation();
+            samplePage = samplePageNavigation.getSamplePage();
             // Create Content Area
             contentTabs = new TabPane();
             contentTabs.getTabs().addAll(
-                TabBuilder.create().text("All Controls").content(
-                    ScrollPaneBuilder.create().content(
-                        samplePage = new SamplePage()
-                    ).build()
-                ).build(),
+                TabBuilder.create().text("All Controls").content( samplePageNavigation ).build(),
                 TabBuilder.create().text("UI Mosaic").content(
                     ScrollPaneBuilder.create().content(
                         mosaic = (Node)FXMLLoader.load(Modena.class.getResource("ui-mosaic.fxml"))
@@ -418,12 +431,6 @@
             // populate root
             root.setTop(toolBar);
             root.setCenter(contentGroup);
-            // move foucus out of the way
-            Platform.runLater(new Runnable() {
-                @Override public void run() {
-                    modenaButton.requestFocus();
-                }
-            });
             
             samplePage.getStyleClass().add("needs-background");
             mosaic.getStyleClass().add("needs-background");
@@ -435,6 +442,14 @@
             if (retina) {
                 contentTabs.getTransforms().setAll(new Scale(2,2));
             }
+            // update state
+            Platform.runLater(new Runnable() {
+                @Override public void run() {
+                    // move focus out of the way
+                    modenaButton.requestFocus();
+                    samplePageNavigation.setCurrentSection(scrolledSection);
+                }
+            });
         } catch (IOException ex) {
             Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex);
         }
@@ -571,8 +586,20 @@
             if (file != null) {
                 try {
                     samplePage.getStyleClass().add("root");
-                    WritableImage img = samplePage.snapshot(new SnapshotParameters(), null);
-                    ImageIO.write(SwingFXUtils.fromFXImage(img, null), "PNG", file);
+                    int width = (int)(samplePage.getLayoutBounds().getWidth()+0.5d);
+                    int height = (int)(samplePage.getLayoutBounds().getHeight()+0.5d);
+                    BufferedImage imgBuffer = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
+                    Graphics2D g2 = imgBuffer.createGraphics();
+                    for (int y=0; y<height; y+=2048) {
+                        SnapshotParameters snapshotParameters = new SnapshotParameters();
+                        int remainingHeight = Math.min(2048, height - y);
+                        snapshotParameters.setViewport(new Rectangle2D(0,y,width,remainingHeight));
+                        WritableImage img = samplePage.snapshot(snapshotParameters, null);
+                        g2.drawImage(SwingFXUtils.fromFXImage(img,null),0,y,null);
+                    }
+                    g2.dispose();
+                    ImageIO.write(imgBuffer, "PNG", file);
+                    System.out.println("Written image: "+file.getAbsolutePath());
                 } catch (IOException ex) {
                     Logger.getLogger(Modena.class.getName()).log(Level.SEVERE, null, ex);
                 }
--- a/apps/experiments/Modena/src/modena/SamplePage.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/apps/experiments/Modena/src/modena/SamplePage.java	Tue Mar 12 20:00:53 2013 -0400
@@ -31,13 +31,17 @@
  */
 package modena;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import javafx.collections.ObservableList;
+import javafx.geometry.HPos;
 import javafx.geometry.Insets;
 import javafx.geometry.Orientation;
 import javafx.geometry.Pos;
 import javafx.geometry.Side;
+import javafx.geometry.VPos;
 import javafx.scene.Node;
 import javafx.scene.control.Button;
 import javafx.scene.control.ButtonBuilder;
@@ -79,10 +83,12 @@
 import javafx.scene.layout.GridPane;
 import javafx.scene.layout.HBox;
 import javafx.scene.layout.HBoxBuilder;
+import javafx.scene.layout.Priority;
 import javafx.scene.layout.VBox;
 import javafx.scene.layout.VBoxBuilder;
 import javafx.scene.paint.Color;
 import javafx.scene.web.HTMLEditorBuilder;
+
 import static modena.SamplePageChartHelper.*;
 import static modena.SamplePageHelpers.*;
 import static modena.SamplePageTableHelper.*;
@@ -90,58 +96,18 @@
 import static modena.SamplePageTreeTableHelper.*;
 
 /**
- * Page showing every control in every state
+ * Page showing every control in every state.
  */
 public class SamplePage extends GridPane {
     private int rowIndex = 0;
-    
     private Map<String, Node> content = new HashMap<>();
-    
-    private void newSection(String name, Node ...children) {
-        newSection(name, 10, children);
-    }
-    
-    private void newSection(String name, int spacing, Node ...children) {
-        Label sectionLabel = new Label(name);
-        sectionLabel.getStyleClass().add("section-label");
-        HBox box = new HBox(spacing);
-        box.getStyleClass().add("section-border");
-        box.getChildren().addAll(children);
-        setConstraints(sectionLabel, 0, rowIndex);
-        setConstraints(box, 1, rowIndex++);
-        getChildren().addAll(sectionLabel,box);
-        content.put(name, box);
-    }
-    
-    private void newDetailedSection(String[] labels, Node ...children) {
-        Label sectionLabel = new Label(labels[0]);
-        sectionLabel.getStyleClass().add("section-label");
-        HBox hbox = new HBox(10);
-        for (int n = 0; n < children.length; n++ ) {
-            VBox vbox = new VBox(10);
-            vbox.getStyleClass().add("section-border");
-            vbox.setAlignment(Pos.CENTER);
-            Label stateLabel = new Label(labels[n+1]);
-            stateLabel.getStyleClass().add("section-label");
-            vbox.getChildren().add(stateLabel);
-            vbox.getChildren().add(children[n]);
-            hbox.getChildren().addAll(vbox);
-        }
-        setConstraints(sectionLabel, 0, rowIndex);
-        setConstraints(hbox, 1, rowIndex++);
-        getChildren().addAll(sectionLabel,hbox);
-        content.put(labels[0], hbox);
-    }
+    private List<Section> sections = new ArrayList<>();
 
-    public Map<String, Node> getContent() {
-        return content;
-    }
-    
     public SamplePage() {
         setVgap(25);
         setHgap(15);
         setPadding(new Insets(15));
-        newSection("Button:", 
+        newSection("Button:",
                 new Button("Button"),
                 withState(new Button("Hover"), "hover"),
                 withState(new Button("Armed"), "armed"),
@@ -149,7 +115,7 @@
                 withState(new Button("Focused & Hover"), "focused, hover"),
                 withState(new Button("Focused & Armed"), "focused, armed"),
                 withState(new Button("Disabled"), "disabled"));
-        newSection("Default Button:", 
+        newSection("Default Button:",
                 ButtonBuilder.create().text("Button").defaultButton(true).build(),
                 withState(new Button("Hover"), "default, hover"),
                 withState(new Button("Armed"), "default, armed"),
@@ -157,13 +123,13 @@
                 withState(new Button("Focused & Hover"), "default, focused, hover"),
                 withState(new Button("Focused & Armed"), "default, focused, armed"),
                 withState(new Button("Disabled"), "default, disabled"));
-        newSection("Nice Colors:", 
+        newSection("Nice Colors:",
                 ButtonBuilder.create().text("Button").style("-fx-base: #f3622d;").build(),
                 ButtonBuilder.create().text("Button").style("-fx-base: #fba71b;").build(),
                 ButtonBuilder.create().text("Button").style("-fx-base: #57b757;").build(),
                 ButtonBuilder.create().text("Button").style("-fx-base: #41a9c9;").build(),
                 ButtonBuilder.create().text("Button").style("-fx-base: #888;").build());
-        newSection("Greys:",0, 
+        newSection("Greys:",0,
                 createGreyButton(0),
                 createGreyButton(0.1),
                 createGreyButton(0.2),
@@ -179,7 +145,7 @@
         ToggleGroup tg2 = new ToggleGroup();
         ToggleGroup tg3 = new ToggleGroup();
         ToggleGroup tg4 = new ToggleGroup();
-        newSection("Pill Toggle\nButtons:", 
+        newSection("Pill Toggle\nButtons:",
                 HBoxBuilder.create()
                     .children(
                         ToggleButtonBuilder.create().text("Left").styleClass("left-pill").toggleGroup(tg1).build(),
@@ -212,7 +178,7 @@
         ToggleGroup tg6 = new ToggleGroup();
         ToggleGroup tg7 = new ToggleGroup();
         ToggleGroup tg8 = new ToggleGroup();
-        newSection("Pill Toggle\nButtons\nFocused:", 
+        newSection("Pill Toggle\nButtons\nFocused:",
                 HBoxBuilder.create()
                     .children(
                         ToggleButtonBuilder.create().text("#").styleClass("left-pill").toggleGroup(tg5).build(),
@@ -262,7 +228,7 @@
                         withState(ToggleButtonBuilder.create().text("R").styleClass("right-pill").toggleGroup(tg8).selected(true).build(),"focused")
                     )
                     .build());
-        newSection("ToggleButton:", 
+        newSection("ToggleButton:",
                 new ToggleButton("Button"),
                 withState(new ToggleButton("Hover"), "hover"),
                 withState(new ToggleButton("Armed"), "armed"),
@@ -270,7 +236,7 @@
                 withState(new ToggleButton("Focused & Hover"), "focused, hover"),
                 withState(new ToggleButton("Focused & Armed"), "focused, armed"),
                 withState(new ToggleButton("Disabled"), "disabled"));
-        newSection("ToggleButton Selected:", 
+        newSection("ToggleButton Selected:",
                 withState(new ToggleButton("Button"), "selected"),
                 withState(new ToggleButton("Hover"), "selected, hover"),
                 withState(new ToggleButton("Armed"), "selected, armed"),
@@ -278,7 +244,7 @@
                 withState(new ToggleButton("Focused & Hover"), "selected, focused, hover"),
                 withState(new ToggleButton("Focused & Armed"), "selected, focused, armed"),
                 withState(new ToggleButton("Disabled"), "selected, disabled"));
-        newSection("CheckBox:", 
+        newSection("CheckBox:",
                 new CheckBox("CheckBox"),
                 withState(new CheckBox("Hover"), "hover"),
                 withState(new CheckBox("Armed"), "armed"),
@@ -286,7 +252,7 @@
                 withState(new CheckBox("Focused & Hover"), "focused, hover"),
                 withState(new CheckBox("Focused & Armed"), "focused, armed"),
                 withState(new CheckBox("Disabled"), "disabled"));
-        newSection("CheckBox Selected:", 
+        newSection("CheckBox Selected:",
                 withState(new CheckBox("CheckBox"), "selected"),
                 withState(new CheckBox("Hover"), "selected, hover"),
                 withState(new CheckBox("Armed"), "selected, armed"),
@@ -294,7 +260,7 @@
                 withState(new CheckBox("Focused & Hover"), "selected, focused, hover"),
                 withState(new CheckBox("Focused & Armed"), "selected, focused, armed"),
                 withState(new CheckBox("Disabled"), "selected, disabled"));
-        newSection("CheckBox\nIndeterminate:", 
+        newSection("CheckBox\nIndeterminate:",
                 CheckBoxBuilder.create().text("CheckBox").selected(true).indeterminate(true).allowIndeterminate(true).build(),
                 withState(new CheckBox("Hover"), "indeterminate, selected, hover"),
                 withState(new CheckBox("Armed"), "indeterminate, selected, armed"),
@@ -302,7 +268,7 @@
                 withState(new CheckBox("Focused & Hover"), "indeterminate, selected, focused, hover"),
                 withState(new CheckBox("Focused & Armed"), "indeterminate, selected, focused, armed"),
                 withState(new CheckBox("Disabled"), "indeterminate, selected, disabled"));
-        newSection("RadioButton:", 
+        newSection("RadioButton:",
                 new RadioButton("RadioButton"),
                 withState(new RadioButton("Hover"), "hover"),
                 withState(new RadioButton("Armed"), "armed"),
@@ -310,7 +276,7 @@
                 withState(new RadioButton("Focused & Hover"), "focused, hover"),
                 withState(new RadioButton("Focused & Armed"), "focused, armed"),
                 withState(new RadioButton("Disabled"), "disabled"));
-        newSection("RadioButton\nSelected:", 
+        newSection("RadioButton\nSelected:",
                 withState(new RadioButton("RadioButton"), "selected"),
                 withState(new RadioButton("Hover"), "selected, hover"),
                 withState(new RadioButton("Armed"), "selected, armed"),
@@ -318,7 +284,7 @@
                 withState(new RadioButton("Focused & Hover"), "selected, focused, hover"),
                 withState(new RadioButton("Focused & Armed"), "selected, focused, armed"),
                 withState(new RadioButton("Disabled"), "selected, disabled"));
-        newSection("HyperLink:", 
+        newSection("HyperLink:",
                 new Hyperlink("Hyperlink"),
                 withState(new Hyperlink("Visited"), "visited"),
                 withState(new Hyperlink("Hover"), "hover"),
@@ -330,17 +296,17 @@
                 withState(new Hyperlink("Disabled"), "disabled"));
         ObservableList<String> choiceBoxLongList = sampleItems(200);
         choiceBoxLongList.add(100, "Long List");
-        newSection(      
-                "ChoiceBox:", 
+        newSection(
+                "ChoiceBox:",
                 ChoiceBoxBuilder.create(String.class).items(sampleItems()).value("Item A").build(),
                 ChoiceBoxBuilder.create(String.class).items(choiceBoxLongList).value("Long List").build(),
                 withState(ChoiceBoxBuilder.create(String.class).items(sampleItems()).value("Item B").build(), "hover"),
                 withState(ChoiceBoxBuilder.create(String.class).items(sampleItems()).value("Item B").build(), "showing"),
                 withState(ChoiceBoxBuilder.create(String.class).items(sampleItems()).value("Item B").build(), "focused"),
                 ChoiceBoxBuilder.create(String.class).items(sampleItems()).value("Item C").disable(true).build()
-                );
-        newSection(      
-                "ComboBox:", 
+        );
+        newSection(
+                "ComboBox:",
                 ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item A").build(),
                 ComboBoxBuilder.create(String.class).items(choiceBoxLongList).value("Long List").build(),
                 withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").build(), "hover"),
@@ -348,32 +314,35 @@
                 withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").build(), "focused"),
                 ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item C").disable(true).build()
                 );
-        newSection(      
-                "ComboBox\nEditable:", 
+        newSection(
+                "ComboBox\nEditable:",
                 ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item A").editable(true).build(),
                 withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").editable(true).build(), "editable", ".arrow-button", "hover"),
-                withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").editable(true).build(), "editable", ".arrow-button", "pressed"),
+                withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").editable(true).build(), "editable", ".arrow-button", "pressed")
+                );
+        newSection(
+                "ComboBox\nEditable\n(More):",
                 withState(ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item B").editable(true).build(), "editable,contains-focus", ".text-field", "focused"),
                 ComboBoxBuilder.create(String.class).items(sampleItems()).value("Item C").editable(true).disable(true).build()
                 );
-        newSection(      
-                "Color Picker:", 
+        newSection(
+                "Color Picker:",
                 ColorPickerBuilder.create().value(Color.RED).build(),
                 withState(ColorPickerBuilder.create().value(Color.RED).build(), "hover"),
                 withState(ColorPickerBuilder.create().value(Color.RED).build(), "showing"),
                 withState(ColorPickerBuilder.create().value(Color.RED).build(), "focused"),
                 withState(ColorPickerBuilder.create().value(Color.RED).build(), "disabled")
                 );
-        newSection(      
-                "Color Picker\n Split Button:", 
+        newSection(
+                "Color Picker\n Split Button:",
                 ColorPickerBuilder.create().value(Color.RED).styleClass(ColorPicker.STYLE_CLASS_SPLIT_BUTTON).build(),
                 withState(ColorPickerBuilder.create().value(Color.RED).styleClass(ColorPicker.STYLE_CLASS_SPLIT_BUTTON).build(), "hover"),
                 withState(ColorPickerBuilder.create().value(Color.RED).styleClass(ColorPicker.STYLE_CLASS_SPLIT_BUTTON).build(), "showing"),
                 withState(ColorPickerBuilder.create().value(Color.RED).styleClass(ColorPicker.STYLE_CLASS_SPLIT_BUTTON).build(), "focused"),
                 withState(ColorPickerBuilder.create().value(Color.RED).styleClass(ColorPicker.STYLE_CLASS_SPLIT_BUTTON).build(), "disabled")
                 );
-        newSection(      
-                "MenuButton:", 
+        newSection(
+                "MenuButton:",
                 MenuButtonBuilder.create().items(createMenuItems(20)).text("right").popupSide(Side.RIGHT).build(),
                 MenuButtonBuilder.create().items(createMenuItems(20)).text("normal").build(),
                 withState(MenuButtonBuilder.create().items(createMenuItems(20)).text("hover").build(), "openvertically,hover"),
@@ -381,15 +350,15 @@
                 withState(MenuButtonBuilder.create().items(createMenuItems(20)).text("focused").build(), "openvertically,focused"),
                 withState(MenuButtonBuilder.create().items(createMenuItems(20)).text("disabled").build(), "openvertically,disabled")
                 );
-        newSection(      
-                "SplitMenuButton:", 
+        newSection(
+                "SplitMenuButton:",
                 SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("right").popupSide(Side.RIGHT).build(),
                 SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("normal").build(),
                 withState(SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("hover").build(),"openvertically",".label", "hover"),
                 withState(SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("armed").build(),"armed,openvertically",".label", "armed")
                 );
-        newSection(      
-                "SplitMenuButton\nMore:", 
+        newSection(
+                "SplitMenuButton\nMore:",
                 withState(SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("arrow hover").build(),"openvertically",".arrow-button", "hover"),
                 withState(SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("showing").build(), "openvertically,showing"),
                 withState(SplitMenuButtonBuilder.create().items(createMenuItems(20)).text("focused").build(), "openvertically,focused"),
@@ -414,14 +383,14 @@
                 withState(SliderBuilder.create().min(0).max(100).value(50).orientation(Orientation.VERTICAL).build(), "disabled"),
                 SliderBuilder.create().min(0).max(100).value(50).showTickMarks(true).showTickLabels(true).orientation(Orientation.VERTICAL).build());
         newDetailedSection(
-                new String[] {"Scrollbar - H: ", "normal", "focused", "small", "big thumb"}, 
+                new String[] {"Scrollbar - H: ", "normal", "focused", "small", "big thumb"},
                 new ScrollBar(),
                 withState(ScrollBarBuilder.create().build(), "focused"),
                 ScrollBarBuilder.create().minWidth(30).prefWidth(30).build(),
                 ScrollBarBuilder.create().visibleAmount(60).max(100).build()
                 );
         newDetailedSection(
-                new String[] {"Scrollbar - V: ", "normal", "focused", "small", "btn hover", "btn pressed", ".thumb hover", ".thumb pressed"}, 
+                new String[] {"Scrollbar - V: ", "normal", "focused", "small", "btn hover", "btn pressed", ".thumb hover", ".thumb pressed"},
                 withState(ScrollBarBuilder.create().orientation(Orientation.VERTICAL).build(), "vertical"),
                 withState(ScrollBarBuilder.create().orientation(Orientation.VERTICAL).build(), "focused"),
                 withState(ScrollBarBuilder.create().orientation(Orientation.VERTICAL).minHeight(30).prefHeight(30).build(), "vertical"),
@@ -431,86 +400,86 @@
                 withState(ScrollBarBuilder.create().orientation(Orientation.VERTICAL).build(), "vertical", ".thumb", "pressed")
                 );
         newDetailedSection(
-                new String[] {"ScrollPane: ", "normal", "small", "focused", "empty"}, 
+                new String[] {"ScrollPane: ", "normal", "small", "focused", "empty"},
                 ScrollPaneBuilder.create().content(scrollPaneContent()).build(),
                 ScrollPaneBuilder.create().content(scrollPaneContent()).minWidth(40).prefWidth(40).minHeight(40).prefHeight(40).build(),
                 withState(ScrollPaneBuilder.create().content(scrollPaneContent()).build(), "focused"),
                 ScrollPaneBuilder.create().build()
-                ); 
+                );
         newDetailedSection(
-                new String[] {"Separator: ", "horizontal", "vertical"}, 
+                new String[] {"Separator: ", "horizontal", "vertical"},
                 SeparatorBuilder.create().prefWidth(100).build(),
                 SeparatorBuilder.create().orientation(Orientation.VERTICAL).prefHeight(50).build()
                 );
         newDetailedSection(
-                new String[] {"ProgressBar: ", "normal", "disabled", "indeterminate"}, 
+                new String[] {"ProgressBar: ", "normal", "disabled", "indeterminate"},
                 ProgressBarBuilder.create().progress(0.6).prefWidth(200).build(),
                 withState(ProgressBarBuilder.create().progress(0.2).prefWidth(200).build(), "disabled"),
                 ProgressBarBuilder.create().progress(-1).prefWidth(200).build()
                 );
         newDetailedSection(
-                new String[] {"ProgressIndicator: ", "normal 0%", "normal 60%", "normal 100%", "disabled"}, 
+                new String[] {"ProgressIndicator: ", "normal 0%", "normal 60%", "normal 100%", "disabled"},
                 new ProgressIndicator(0),
                 new ProgressIndicator(0.6),
                 new ProgressIndicator(1),
                 withState(new ProgressIndicator(0.5), "disabled")
                 );
         newDetailedSection(
-                new String[] {"ProgressIndicator\nIndeterminate: ", "normal", "small", "large", "disabled"}, 
+                new String[] {"ProgressIndicator\nIndeterminate: ", "normal", "small", "large", "disabled"},
                 ProgressIndicatorBuilder.create().progress(-1).maxWidth(USE_PREF_SIZE).maxHeight(USE_PREF_SIZE).build(),
                 ProgressIndicatorBuilder.create().progress(-1).prefWidth(30).prefHeight(30).build(),
                 ProgressIndicatorBuilder.create().progress(-1).prefWidth(60).prefHeight(60).build(),
                 ProgressIndicatorBuilder.create().progress(-1).maxWidth(USE_PREF_SIZE).maxHeight(USE_PREF_SIZE).disable(true).build()
                 );
-        newSection(      
-                "TextField:", 
+        newSection(
+                "TextField:",
                 new TextField("TextField"),
                 TextFieldBuilder.create().promptText("Prompt Text").build(),
                 withState(new TextField("Focused"), "focused"),
                 withState(new TextField("Disabled"), "disabled")
-                );
-        newSection(      
-                "PasswordField:", 
+        );
+        newSection(
+                "PasswordField:",
                 PasswordFieldBuilder.create().text("Password").build(),
                 PasswordFieldBuilder.create().promptText("Prompt Text").build(),
                 withState(PasswordFieldBuilder.create().text("Password").build(), "focused"),
                 withState(PasswordFieldBuilder.create().text("Password").build(), "disabled")
-                );
-        newSection(      
-                "TextArea:", 
+        );
+        newSection(
+                "TextArea:",
                 TextAreaBuilder.create().text("TextArea").prefColumnCount(10).prefRowCount(2).build(),
                 TextAreaBuilder.create().text("Many Lines of\nText.\n#3\n#4\n#5\n#6\n#7\n#8\n#9\n#10").prefColumnCount(10).prefRowCount(3).build(),
                 TextAreaBuilder.create().text("Many Lines of\nText.\n#3\n#4\n#5\n#6\n#7\n#8\n#9\n#10").prefColumnCount(6).prefRowCount(3).build(),
                 TextAreaBuilder.create().promptText("Prompt Text").prefColumnCount(10).prefRowCount(2).build(),
                 withState(TextAreaBuilder.create().text("Focused").prefColumnCount(7).prefRowCount(2).build(), "focused"),
                 withState(TextAreaBuilder.create().text("Disabled").prefColumnCount(8).prefRowCount(2).build(), "disabled")
-                );
-        newSection(      
-                "HTMLEditor:", 
+        );
+        newSection(
+                "HTMLEditor:",
                 HTMLEditorBuilder.create().htmlText("Hello <b>Bold</b> Text").prefWidth(650).prefHeight(120).build()
                 );
-        newSection(      
-                "HTMLEditor\nFocused:", 
+        newSection(
+                "HTMLEditor\nFocused:",
                 withState(HTMLEditorBuilder.create().htmlText("<i>Focused</i>").prefWidth(650).prefHeight(120).build(), "focused")
                 );
         newDetailedSection(
-                new String[] {"ToolBar (H|TOP):", "normal", "overflow", "disabled"},
-                createToolBar(Side.TOP,false,false),
-                createToolBar(Side.TOP,true,false),
-                createToolBar(Side.TOP,false,true)
-                );
+                new String[] { "ToolBar (H|TOP):", "normal", "overflow", "disabled" },
+                createToolBar(Side.TOP, false, false),
+                createToolBar(Side.TOP, true, false),
+                createToolBar(Side.TOP, false, true)
+        );
         newDetailedSection(
-                new String[] {"ToolBar (H|BOTTOM):", "normal", "overflow", "disabled"},
-                createToolBar(Side.BOTTOM,false,false),
-                createToolBar(Side.BOTTOM,true,false),
-                createToolBar(Side.BOTTOM,false,true)
-                );
+                new String[] { "ToolBar (H|BOTTOM):", "normal", "overflow", "disabled" },
+                createToolBar(Side.BOTTOM, false, false),
+                createToolBar(Side.BOTTOM, true, false),
+                createToolBar(Side.BOTTOM, false, true)
+        );
         newDetailedSection(
-                new String[] {"ToolBar (V|LEFT):", "normal", "overflow", "disabled"},
-                createToolBar(Side.LEFT,false,false),
-                createToolBar(Side.LEFT,true,false),
-                createToolBar(Side.LEFT,false,true)
-                );
+                new String[] { "ToolBar (V|LEFT):", "normal", "overflow", "disabled" },
+                createToolBar(Side.LEFT, false, false),
+                createToolBar(Side.LEFT, true, false),
+                createToolBar(Side.LEFT, false, true)
+        );
         newDetailedSection(
                 new String[] {"ToolBar (V|RIGHT):", "normal", "overflow", "disabled"},
                 createToolBar(Side.RIGHT,false,false),
@@ -518,136 +487,157 @@
                 createToolBar(Side.RIGHT,false,true)
                 );
         newSection(
-                "Tabs\n(Top):", 
-                wrapBdr(createTabPane(4, 250, 100,null,false, false, Side.TOP)),
-                wrapBdr(withState(createTabPane(5, 200, 100,"Tab Disabled &\nMany Tabs", false, true, Side.TOP), null, ".tab", "disabled")),
-                wrapBdr(withState(createTabPane(5, 200, 100,"Disabled", false, false, Side.TOP), "disabled"))
+                "Tabs\n(Top):",
+                wrapBdr(createTabPane(4, 250, 100, null, false, false, Side.TOP)),
+                wrapBdr(withState(createTabPane(5, 200, 100, "Tab Disabled &\nMany Tabs", false, true, Side.TOP), null, ".tab", "disabled")),
+                wrapBdr(withState(createTabPane(5, 200, 100, "Disabled", false, false, Side.TOP), "disabled"))
+        );
+        newSection(
+                "Tabs Floating\n(Top):",
+                createTabPane(4, 250, 100, null, true, false, Side.TOP),
+                withState(createTabPane(5, 200, 100, "Tab Disabled &\nMany Tabs", true, true, Side.TOP), null, ".tab", "disabled"),
+                withState(createTabPane(5, 200, 100, "Disabled", true, false, Side.TOP), "disabled")
                 );
-        newSection(      
-                "Tabs Floating\n(Top):", 
-                createTabPane(4, 250, 100,null,true, false, Side.TOP),
-                withState(createTabPane(5, 200, 100,"Tab Disabled &\nMany Tabs", true, true, Side.TOP), null, ".tab", "disabled"),
-                withState(createTabPane(5, 200, 100,"Disabled", true, false, Side.TOP), "disabled")
+        newSection(
+                "Tabs\n(Bottom):",
+                wrapBdr(createTabPane(4, 250, 100, null, false, false, Side.BOTTOM)),
+                wrapBdr(withState(createTabPane(5, 200, 100, "Tab Disabled &\nMany Tabs", false, true, Side.BOTTOM), null, ".tab", "disabled")),
+                wrapBdr(withState(createTabPane(5, 200, 100, "Disabled", false, false, Side.BOTTOM), "disabled"))
                 );
-        newSection(      
-                "Tabs\n(Bottom):", 
-                wrapBdr(createTabPane(4, 250, 100,null,false, false, Side.BOTTOM)),
-                wrapBdr(withState(createTabPane(5, 200, 100,"Tab Disabled &\nMany Tabs", false, true, Side.BOTTOM), null, ".tab", "disabled")),
-                wrapBdr(withState(createTabPane(5, 200, 100,"Disabled", false, false, Side.BOTTOM), "disabled"))
+        newSection(
+                "Tabs Floating\n(Bottom):",
+                createTabPane(4, 250, 100, null, true, false, Side.BOTTOM),
+                withState(createTabPane(5, 200, 100, "Tab Disabled &\nMany Tabs", true, true, Side.BOTTOM), null, ".tab", "disabled"),
+                withState(createTabPane(5, 200, 100, "Disabled", true, false, Side.BOTTOM), "disabled")
                 );
-        newSection(      
-                "Tabs Floating\n(Bottom):", 
-                createTabPane(4, 250, 100,null,true, false, Side.BOTTOM),
-                withState(createTabPane(5, 200, 100,"Tab Disabled &\nMany Tabs", true, true, Side.BOTTOM), null, ".tab", "disabled"),
-                withState(createTabPane(5, 200, 100,"Disabled", true, false, Side.BOTTOM), "disabled")
+        newSection(
+                "Tabs\n(Left):",
+                wrapBdr(createTabPane(4, 250, 250, null, false, false, Side.LEFT)),
+                wrapBdr(withState(createTabPane(5, 200, 250, "Tab Disabled &\nMany Tabs", false, true, Side.LEFT), null, ".tab", "disabled")),
+                wrapBdr(withState(createTabPane(5, 200, 250, "Disabled", false, false, Side.LEFT), "disabled"))
                 );
-        newSection(      
-                "Tabs\n(Left):", 
-                wrapBdr(createTabPane(4, 250, 250,null,false, false, Side.LEFT)),
-                wrapBdr(withState(createTabPane(5, 200, 250,"Tab Disabled &\nMany Tabs", false, true, Side.LEFT), null, ".tab", "disabled")),
-                wrapBdr(withState(createTabPane(5, 200, 250,"Disabled", false, false, Side.LEFT), "disabled"))
+        newSection(
+                "Tabs Floating\n(Left):",
+                createTabPane(4, 250, 250, null, true, false, Side.LEFT),
+                withState(createTabPane(5, 200, 250, "Tab Disabled &\nMany Tabs", true, true, Side.LEFT), null, ".tab", "disabled"),
+                withState(createTabPane(5, 200, 250, "Disabled", true, false, Side.LEFT), "disabled")
                 );
-        newSection(      
-                "Tabs Floating\n(Left):", 
-                createTabPane(4, 250, 250,null,true, false, Side.LEFT),
-                withState(createTabPane(5, 200, 250,"Tab Disabled &\nMany Tabs", true, true, Side.LEFT), null, ".tab", "disabled"),
-                withState(createTabPane(5, 200, 250,"Disabled", true, false, Side.LEFT), "disabled")
+        newSection(
+                "Tabs\n(Right):",
+                wrapBdr(createTabPane(4, 250, 250, null, false, false, Side.RIGHT)),
+                wrapBdr(withState(createTabPane(5, 200, 250, "Tab Disabled &\nMany Tabs", false, true, Side.RIGHT), null, ".tab", "disabled")),
+                wrapBdr(withState(createTabPane(5, 200, 250, "Disabled", false, false, Side.RIGHT), "disabled"))
                 );
-        newSection(      
-                "Tabs\n(Right):", 
-                wrapBdr(createTabPane(4, 250, 250,null,false, false, Side.RIGHT)),
-                wrapBdr(withState(createTabPane(5, 200, 250,"Tab Disabled &\nMany Tabs", false, true, Side.RIGHT), null, ".tab", "disabled")),
-                wrapBdr(withState(createTabPane(5, 200, 250,"Disabled", false, false, Side.RIGHT), "disabled"))
-                );
-        newSection(      
-                "Tabs Floating\n(Right):", 
-                createTabPane(4, 250, 250,null,true, false, Side.RIGHT),
-                withState(createTabPane(5, 200, 250,"Tab Disabled &\nMany Tabs", true, true, Side.RIGHT), null, ".tab", "disabled"),
-                withState(createTabPane(5, 200, 250,"Disabled", true, false, Side.RIGHT), "disabled")
+        newSection(
+                "Tabs Floating\n(Right):",
+                createTabPane(4, 250, 250, null, true, false, Side.RIGHT),
+                withState(createTabPane(5, 200, 250, "Tab Disabled &\nMany Tabs", true, true, Side.RIGHT), null, ".tab", "disabled"),
+                withState(createTabPane(5, 200, 250, "Disabled", true, false, Side.RIGHT), "disabled")
                 );
         newDetailedSection(
-                new String[] {"TitledPane:", "normal", "not collapsible", "hover", "focused", "disabled"}, 
+                new String[] { "TitledPane:", "normal", "not collapsible", "hover", "focused", "disabled" },
                 TitledPaneBuilder.create().text("Title").content(new Label("Content\nLine2.")).build(),
                 TitledPaneBuilder.create().text("Not Collapsible").content(new Label("Content\nLine2.")).collapsible(false).build(),
                 withState(TitledPaneBuilder.create().text("Title").content(new Label("Content\nLine2.")).build(), "hover"),
                 withState(TitledPaneBuilder.create().text("Title").content(new Label("Content\nLine2.")).build(), "focused"),
                 withState(TitledPaneBuilder.create().text("Title").content(new Label("Content\nLine2.")).build(), "disabled")
-                );
+        );
         newDetailedSection(
-                new String[] {"Accordion:", "normal", "hover", "focused", "disabled"}, 
+                new String[] {"Accordion:", "normal", "hover", "focused", "disabled"},
                 createAccordion(),
                 withState(createAccordion(), null, ".titled-pane", "hover"),
                 withState(createAccordion(), null, ".titled-pane", "focused"),
                 withState(createAccordion(), "disabled")
                 );
         newDetailedSection(
-                new String[] {"SplitPane (H):", "simple", "many", "complex"}, 
-                createSplitPane(2,false,null),
-                createSplitPane(4,false,null),
-                createSplitPane(2,false,createSplitPane(2,true,null))
+                new String[] {"SplitPane (H):", "simple", "many", "complex"},
+                createSplitPane(2, false, null),
+                createSplitPane(4, false, null),
+                createSplitPane(2, false, createSplitPane(2, true, null))
                 );
         newDetailedSection(
-                new String[] {"SplitPane (V):", "simple", "many", "complex"}, 
+                new String[] {"SplitPane (V):", "simple", "many", "complex"},
                 createSplitPane(2,true,null),
                 createSplitPane(4,true,null),
                 createSplitPane(2,true,createSplitPane(2,false,null))
                 );
         newDetailedSection(
-                new String[] {"Pagination:", "simple", "infinate"}, 
+                new String[] {"Pagination:", "simple", "infinate"},
                 createPagination(5, false, true),
                 createPagination(Integer.MAX_VALUE, false, true)
                 );
         newDetailedSection(
-                new String[] {"Pagination\nBullet Style:", "simple", "infinate"}, 
+                new String[] {"Pagination\nBullet Style:", "simple", "infinate"},
                 createPagination(5, true, true),
                 createPagination(Integer.MAX_VALUE, true, true)
                 );
         newSection(
-                "Pagination\nNo Arrows:", 
+                "Pagination\nNo Arrows:",
                 createPagination(Integer.MAX_VALUE, false, false)
-                );
+        );
         newDetailedSection(
-                new String[] {"ListView\n2 items\nsingle selection:", "normal", "focused", "disabled"}, 
+                new String[] { "ListView\n2 items\nsingle selection:", "normal", "focused", "disabled" },
                 createListView(3, false, false, false),
                 withState(createListView(3, false, false, false), "focused"),
                 createListView(3, false, true, false)
-                );
+        );
         newDetailedSection(
-                new String[] {"ListView\n10,000 items\nmultiple selection:","normal", "focused", "disabled"}, 
+                new String[] {"ListView\n10,000 items\nmultiple selection:","normal", "focused", "disabled"},
                 createListView(10000, true, false, false),
                 withState(createListView(10000, true, false, false), "focused"),
                 createListView(10000, true, true, false)
                 );
         newDetailedSection(
-                new String[] {"ListView (H)\n10,000 items\nmultiple selection:","normal", "focused", "disabled"}, 
+                new String[] {"ListView (H)\n10,000 items\nmultiple selection:","normal", "focused", "disabled"},
                 createListView(10000, true, false, true),
                 withState(createListView(10000, true, false, true), "focused"),
                 createListView(10000, true, true, true)
                 );
         newSection(
-                "TableView:", 
-                createTableView(550),
-                withState(createTableView(100), "focused")
+                "TableView Simple:\n(Row Selection)",
+                createTableViewSimple(550, true, false),
+                withState(createTableViewSimple(150, true, false), "focused")
+        );
+        newSection(
+                "TableView Simple:\n(Constrained Resize)",
+                createTableViewSimple(550, true, true),
+                withState(createTableViewSimple(150, true, true), "focused")
+        );
+        newSection(
+                "TableView:\n(Row Selection)",
+                createTableView(550, true),
+                withState(createTableView(150, true), "focused")
+        );
+        newSection(
+                "TableView:\n(Cell Selection)",
+                createTableView(550, false),
+                withState(createTableView(150, false), "focused")
                 );
         newSection(
-                "TreeView:", 
+                "TreeView:",
                 createTreeView(350),
                 withState(createTreeView(350), "focused")
                 );
         newSection(
-                "TreeTableView:", 
-                createTreeTableView(550),
-                withState(createTreeTableView(200), "focused")
+                "TreeTableView:\n" +
+                        "(Row Selection)",
+                createTreeTableView(550, false),
+                withState(createTreeTableView(200, false), "focused")
+                );
+        newSection(
+                "TreeTableView:\n(Cell Selection)",
+                createTreeTableView(550, true),
+                withState(createTreeTableView(200, true), "focused")
                 );
         newDetailedSection(
                 new String[] {"Empty:", "ListView", "TableView", "TreeView", "TreeTableView"},
                 ListViewBuilder.create(String.class).prefWidth(150).prefHeight(100).build(),
-                TableViewBuilder.create().prefWidth(150).prefHeight(100).build(),
-                TreeViewBuilder.create().prefWidth(150).prefHeight(100).build(),
-                TreeTableViewBuilder.create().prefWidth(150).prefHeight(100).build()
+                TableViewBuilder.create(Object.class).prefWidth(150).prefHeight(100).build(),
+                TreeViewBuilder.create(Object.class).prefWidth(150).prefHeight(100).build(),
+                TreeTableViewBuilder.create(Object.class).prefWidth(150).prefHeight(100).build()
                 );
         newDetailedSection(
-                new String[] {"ToolTip:","inline","inline + graphic", "popup"}, 
+                new String[] {"ToolTip:","inline","inline + graphic", "popup"},
                 LabelBuilder.create().text("This is a simple Tooltip.").styleClass("tooltip").build(),
                 LabelBuilder.create().text("This is a simple Tooltip\nwith graphic.").graphic(createGraphic()).styleClass("tooltip").build(),
                 VBoxBuilder.create().fillWidth(true).spacing(4).children(
@@ -657,66 +647,130 @@
                 ).build()
                 );
         newSection(
-                "MenuBar & ContextMenu:", 
+                "MenuBar & ContextMenu:",
                 createMenuBar(),
                 createContextMenu()
                 );
         newSection(
-                "Menus:", 
+                "Menus:",
                 createInlineMenu(false),
                 createInlineMenu(true)
                 );
         newSection(
-                "AreaChart:", 
+                "AreaChart:",
                 createAreaChart(false)
                 );
         newSection(
-                "StackedAreaChart:", 
+                "StackedAreaChart:",
                 createAreaChart(true)
                 );
         newSection(
-                "BarChart\nSimple:", 
+                "BarChart\nSimple:",
                 createBarChart(false,true)
                 );
         newSection(
-                "BarChart:", 
+                "BarChart:",
                 createBarChart(false, false)
                 );
         newSection(
-                "BarChart\n(H, Simple):", 
+                "BarChart\n(H, Simple):",
                 createBarChart(true, true)
                 );
         newSection(
-                "BarChart\n(H):", 
+                "BarChart\n(H):",
                 createBarChart(true, false)
                 );
         newSection(
-                "StackedBarChart\nSimple:", 
+                "StackedBarChart\nSimple:",
                 createStackedBarChart(false,true)
                 );
         newSection(
-                "StackedBarChart\n(H, Simple):", 
+                "StackedBarChart\n(H, Simple):",
                 createStackedBarChart(true, true)
-                );
+        );
         newSection(
-                "BubbleChart:", 
+                "BubbleChart:",
                 createBubbleChart(false)
-                );
+        );
         newSection(
-                "BubbleChart\nTop & Right Axis:", 
+                "BubbleChart\nTop & Right Axis:",
                 createBubbleChart(true)
-                );
+        );
         newSection(
-                "LineChart:", 
+                "LineChart:",
                 createLineChart()
-                );
+        );
         newSection(
-                "PieChar:", 
+                "PieChar:",
                 createPieChart()
-                );
+        );
         newSection(
-                "ScatterChart:", 
+                "ScatterChart:",
                 createScatterChart()
-                );
+        );
+    }
+
+    public List<Section> getSections() {
+        return sections;
+    }
+
+    private void newSection(String name, Node ...children) {
+        newSection(name, 10, children);
+    }
+
+    private void newSection(String name, int spacing, Node ...children) {
+        Label sectionLabel = new Label(name);
+        sectionLabel.getStyleClass().add("section-label");
+        sectionLabel.setMaxSize(Double.MAX_VALUE,Double.MAX_VALUE);
+        HBox box = new HBox(spacing);
+        box.getStyleClass().add("section-border");
+        box.getChildren().addAll(children);
+        setConstraints(sectionLabel, 0, rowIndex, 1, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
+        setConstraints(box, 1, rowIndex++);
+        getChildren().addAll(sectionLabel, box);
+        sections.add(new Section(name, sectionLabel, box));
+        content.put(name, box);
+    }
+
+    private void newDetailedSection(String[] labels, Node ...children) {
+        Label sectionLabel = new Label(labels[0]);
+        sectionLabel.getStyleClass().add("section-label");
+        sectionLabel.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
+        HBox hbox = new HBox(10);
+        for (int n = 0; n < children.length; n++ ) {
+            VBox vbox = new VBox(10);
+            vbox.getStyleClass().add("section-border");
+            vbox.setAlignment(Pos.CENTER);
+            Label stateLabel = new Label(labels[n+1]);
+            stateLabel.getStyleClass().add("section-label");
+            vbox.getChildren().add(stateLabel);
+            vbox.getChildren().add(children[n]);
+            hbox.getChildren().addAll(vbox);
+        }
+        setConstraints(sectionLabel, 0, rowIndex,1,1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS,Priority.ALWAYS);
+        setConstraints(hbox, 1, rowIndex++);
+        getChildren().addAll(sectionLabel, hbox);
+        sections.add(new Section(labels[0], sectionLabel, hbox));
+        content.put(labels[0], hbox);
+    }
+
+    public Map<String, Node> getContent() {
+        return content;
+    }
+
+    public static class Section {
+        public final String name;
+        public final Label label;
+        public final Node box;
+
+        public Section(String name, Label label, Node box) {
+            this.name = name;
+            this.label = label;
+            this.box = box;
+        }
+
+        @Override public String toString() {
+            return name.replaceAll("\n"," ");
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/experiments/Modena/src/modena/SamplePageNavigation.java	Tue Mar 12 20:00:53 2013 -0400
@@ -0,0 +1,89 @@
+package modena;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.ToolBar;
+import javafx.scene.layout.BorderPane;
+
+/**
+ * Container for samplePage that has scrolling and knows how to navigate to sections
+ */
+public class SamplePageNavigation extends BorderPane {
+    private SamplePage samplePage = new SamplePage();
+    private ScrollPane scrollPane = new ScrollPane(samplePage);
+    private boolean isLocalChange = false;
+    private SamplePage.Section currentSection;
+
+    public SamplePageNavigation() {
+        scrollPane.setId("SamplePageScrollPane");
+        setCenter(scrollPane);
+        ToolBar toolBar = new ToolBar();
+        toolBar.setId("SamplePageToolBar");
+        toolBar.getStyleClass().add("bottom");
+        toolBar.getItems().add(new Label("Go to section:"));
+        final ChoiceBox<SamplePage.Section> sectionChoiceBox = new ChoiceBox<>();
+        sectionChoiceBox.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<SamplePage.Section>() {
+            @Override public void changed(ObservableValue<? extends SamplePage.Section> observable, SamplePage.Section oldValue, SamplePage.Section newValue) {
+                setCurrentSection(newValue);
+            }
+        });
+        sectionChoiceBox.getItems().addAll(samplePage.getSections());
+        toolBar.getItems().add(sectionChoiceBox);
+        setBottom(toolBar);
+        scrollPane.vvalueProperty().addListener(new ChangeListener<Number>() {
+            @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                if (!isLocalChange) {
+                    isLocalChange = true;
+                    // calc scroll position relative to scroll pane content
+                    double posPixels = samplePage.getLayoutBounds().getHeight() * newValue.doubleValue();
+                    // move to top of view port
+                    posPixels -=  scrollPane.getLayoutBounds().getHeight() * newValue.doubleValue();
+                    // move to center of view port
+                    posPixels +=  scrollPane.getLayoutBounds().getHeight() * 0.5;
+                    // find section that contains view port center
+                    currentSection = null;
+                    for (SamplePage.Section section: samplePage.getSections()) {
+                        if (section.box.getBoundsInParent().getMaxY() > posPixels ) {
+                            currentSection = section;
+                            break;
+                        }
+                    }
+                    sectionChoiceBox.getSelectionModel().select(currentSection);
+                    isLocalChange = false;
+                }
+
+            }
+        });
+    }
+
+    public SamplePage.Section getCurrentSection() {
+        return currentSection;
+    }
+
+    public void setCurrentSection(SamplePage.Section currentSection) {
+        this.currentSection = currentSection;
+        if (!isLocalChange) {
+            isLocalChange = true;
+            double pos = 0;
+            if (currentSection != null) {
+                double sectionBoxCenterY = currentSection.box.getBoundsInParent().getMinY()
+                        + (currentSection.box.getBoundsInParent().getHeight()/2);
+                // move to center of view port
+                pos -=  scrollPane.getLayoutBounds().getHeight() * 0.5;
+                // move to top of view port
+                pos +=  scrollPane.getLayoutBounds().getHeight() * (sectionBoxCenterY / samplePage.getLayoutBounds().getHeight());
+                // find relative pos
+                pos = sectionBoxCenterY / samplePage.getLayoutBounds().getHeight();
+            }
+            scrollPane.setVvalue(pos);
+            isLocalChange = false;
+        }
+    }
+
+    public SamplePage getSamplePage() {
+        return samplePage;
+    }
+}
--- a/apps/experiments/Modena/src/modena/SamplePageTableHelper.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/apps/experiments/Modena/src/modena/SamplePageTableHelper.java	Tue Mar 12 20:00:53 2013 -0400
@@ -32,6 +32,7 @@
 package modena;
 
 import javafx.beans.binding.ObjectBinding;
+import javafx.beans.binding.StringBinding;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.SimpleBooleanProperty;
@@ -44,6 +45,7 @@
 import javafx.collections.SetChangeListener;
 import javafx.event.EventHandler;
 import javafx.scene.Node;
+import javafx.scene.control.Label;
 import javafx.scene.control.SelectionMode;
 import javafx.scene.control.TableCell;
 import javafx.scene.control.TableColumn;
@@ -68,6 +70,7 @@
         private BooleanProperty invited;
         private StringProperty firstName;
         private StringProperty lastName;
+        private StringProperty name;
         private StringProperty email;
         
         private final String country = "New Zealand";
@@ -85,7 +88,13 @@
             this.lastName = new SimpleStringProperty(lName);
             this.email = new SimpleStringProperty(email);
             this.invited = new SimpleBooleanProperty(invited);
-            
+            this.name = new SimpleStringProperty();
+            this.name.bind(new StringBinding() {
+                { bind(firstName,lastName); }
+                @Override protected String computeValue() {
+                    return firstName.get() + " " + lastName.get();
+                }
+            });
             this.invited.addListener(new ChangeListener<Boolean>() {
                 public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
                     System.out.println(getFirstName() + " invited: " + t1);
@@ -96,6 +105,14 @@
         public Boolean isInvited() { return invited.get(); }
         public BooleanProperty invitedProperty() { return invited; }
 
+        public String getName() {
+            return name.get();
+        }
+
+        public StringProperty nameProperty() {
+            return name;
+        }
+
         public String getFirstName() {
             return firstName.get();
         }
@@ -189,8 +206,50 @@
         );
 
     }
-    
-    static TableView createTableView(int width) {
+
+    static TableView createTableViewSimple(int width, boolean rowSelection, boolean constrainedResize) {
+        TableColumn<Person, String> nameCol, emailCol, countryCol;
+        // Columns
+        nameCol = new TableColumn<Person, String>();
+        nameCol.setText("Name");
+        nameCol.setCellValueFactory(new PropertyValueFactory<Person,String>("name"));
+        emailCol = new TableColumn<Person, String>();
+        emailCol.setText("Email");
+        emailCol.setMinWidth(200);
+        emailCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
+            public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
+                return p.getValue().emailProperty();
+            }
+        });
+        countryCol = new TableColumn<Person, String>();
+        countryCol.setText("Country");
+        countryCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, String>, ObservableValue<String>>() {
+            public ObservableValue<String> call(TableColumn.CellDataFeatures<Person, String> p) {
+                return new ReadOnlyObjectWrapper<String>("New Zealand");
+            }
+        });
+        // Create TableView
+        TableView<Person> tableView = new TableView<Person>();
+        tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
+        tableView.getSelectionModel().setCellSelectionEnabled(!rowSelection);
+        tableView.setTableMenuButtonVisible(false);
+        if (constrainedResize) tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+        tableView.setItems(data);
+        tableView.getColumns().addAll(nameCol, emailCol, countryCol);
+        tableView.setPrefSize(width, 300);
+        if (rowSelection) {
+            tableView.getSelectionModel().selectRange(2, 5);
+        } else {
+            tableView.getSelectionModel().select(2,emailCol);
+            tableView.getSelectionModel().select(3,nameCol);
+            tableView.getSelectionModel().select(3,countryCol);
+            tableView.getSelectionModel().select(5,nameCol);
+        }
+        tableView.getSortOrder().addAll(nameCol,emailCol,countryCol);
+        return tableView;
+    }
+
+    static TableView createTableView(int width, boolean rowSelection) {
         TableColumn<Person, String> firstNameCol, lastNameCol, nameCol, emailCol, countryCol;
         TableColumn<Person, Boolean> invitedCol;
         // Columns
@@ -242,6 +301,25 @@
                 return new ReadOnlyObjectWrapper<String>("New Zealand");
             }
         });
+        // Test case for RT-28410 MODENA: can't make tree/table cell factories change color based
+        // on background when setGraphic(...) is used
+        countryCol.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
+            @Override public TableCell<Person, String> call(TableColumn<Person, String> param) {
+                final Label label = new Label();
+                label.setStyle(
+                        "-fx-font-family: 'Times New Roman';" +
+                        "-fx-font-size: 0.8em;" +
+                        "-fx-text-fill: ladder(-fx-background, yellow 49%, red 50%);");
+                TableCell cell = new TableCell() {
+                    @Override protected void updateItem(Object item, boolean empty) {
+                        label.setText(empty ? null : item.toString());
+                    }
+                };
+                cell.setGraphic(label);
+                return cell;
+            }
+        });
+
         invitedCol = new TableColumn<Person, Boolean>();
         invitedCol.setText("Invited");
         invitedCol.setPrefWidth(55);
@@ -256,12 +334,19 @@
         
         TableView<Person> tableView = new TableView<Person>();
         tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
-        tableView.getSelectionModel().setCellSelectionEnabled(false);
+        tableView.getSelectionModel().setCellSelectionEnabled(!rowSelection);
         tableView.setTableMenuButtonVisible(true);
         tableView.setItems(data);
         tableView.getColumns().addAll(invitedCol, nameCol, emailCol, countryCol);
         tableView.setPrefSize(width, 300);
-        tableView.getSelectionModel().selectRange(2, 5);
+        if (rowSelection) {
+            tableView.getSelectionModel().selectRange(2, 5);
+        } else {
+            tableView.getSelectionModel().select(2,emailCol);
+            tableView.getSelectionModel().select(3,firstNameCol);
+            tableView.getSelectionModel().select(3,countryCol);
+            tableView.getSelectionModel().select(4,lastNameCol);
+        }
         tableView.getSortOrder().addAll(firstNameCol,lastNameCol,emailCol,countryCol);
         return tableView;
     }
--- a/apps/experiments/Modena/src/modena/SamplePageTreeTableHelper.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/apps/experiments/Modena/src/modena/SamplePageTreeTableHelper.java	Tue Mar 12 20:00:53 2013 -0400
@@ -112,12 +112,20 @@
         return sp;
     }
     
-    static TreeTableView createTreeTableView(int width) {
+    static TreeTableView createTreeTableView(int width, boolean cellSelection) {
         TreeTableView treeTableView = buildFileBrowserTreeTableView();
         treeTableView.setSortMode(TreeSortMode.ONLY_FIRST_LEVEL);
         treeTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
+        treeTableView.getSelectionModel().setCellSelectionEnabled(cellSelection);
         treeTableView.setPrefSize(width, 300);
-        treeTableView.getSelectionModel().selectRange(5, 8);
+        if (cellSelection) {
+            treeTableView.getSelectionModel().select(2,(TreeTableColumn)treeTableView.getColumns().get(0));
+            treeTableView.getSelectionModel().select(3,(TreeTableColumn)treeTableView.getColumns().get(1));
+            treeTableView.getSelectionModel().select(3,(TreeTableColumn)treeTableView.getColumns().get(2));
+            treeTableView.getSelectionModel().select(5,(TreeTableColumn)treeTableView.getColumns().get(1));
+        } else {
+            treeTableView.getSelectionModel().selectRange(5, 8);
+        }
         return treeTableView;
     } 
     
--- a/apps/experiments/Modena/src/modena/TestApp.css	Tue Mar 12 09:39:27 2013 -0700
+++ b/apps/experiments/Modena/src/modena/TestApp.css	Tue Mar 12 20:00:53 2013 -0400
@@ -32,6 +32,14 @@
  #TestAppToolbar > * > .label {
      -fx-font-size: 10px;
  }
+#SamplePageToolBar {
+    -fx-base: #666;
+    -fx-background: #444;
+}
+#SamplePageScrollPane {
+    -fx-background-insets: 0,0;
+    -fx-padding: 0;
+}
 .section-label {
     -fx-font-weight: normal;
     -fx-text-fill: derive(-fx-text-background-color,30%);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/samples/Ensemble8/Ensemble8.iml	Tue Mar 12 20:00:53 2013 -0400
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/app" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/generated" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/samples" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/compiletime" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/nbproject" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="lucene-3.2.0" level="project" />
+    <orderEntry type="module" module-name="Controls" />
+    <orderEntry type="module" module-name="decora-compiler" />
+    <orderEntry type="module" module-name="decora-d3d" />
+    <orderEntry type="module" module-name="decora-es2" />
+    <orderEntry type="module" module-name="decora-jsw" />
+    <orderEntry type="module" module-name="decora-prism" />
+    <orderEntry type="module" module-name="decora-prism-ps" />
+    <orderEntry type="module" module-name="decora-prism-sw" />
+    <orderEntry type="module" module-name="decora-runtime" />
+    <orderEntry type="module" module-name="decora-sse" />
+    <orderEntry type="module" module-name="glass-mat" />
+    <orderEntry type="module" module-name="javafx-accessible" />
+    <orderEntry type="module" module-name="javafx-anim" />
+    <orderEntry type="module" module-name="javafx-annotation-processor" />
+    <orderEntry type="module" module-name="javafx-beans" />
+    <orderEntry type="module" module-name="javafx-checkstyle" />
+    <orderEntry type="module" module-name="javafx-common" />
+    <orderEntry type="module" module-name="javafx-concurrent" />
+    <orderEntry type="module" module-name="javafx-deploy" />
+    <orderEntry type="module" module-name="javafx-embed-swing" />
+    <orderEntry type="module" module-name="javafx-embed-swt" />
+    <orderEntry type="module" module-name="javafx-font" />
+    <orderEntry type="module" module-name="javafx-fxml" />
+    <orderEntry type="module" module-name="javafx-geom" />
+    <orderEntry type="module" module-name="javafx-iio" />
+    <orderEntry type="module" module-name="javafx-logging" />
+    <orderEntry type="module" module-name="javafx-mx-common" />
+    <orderEntry type="module" module-name="javafx-sg-common" />
+    <orderEntry type="module" module-name="javafx-sg-prism" />
+    <orderEntry type="module" module-name="javafx-ui-charts" />
+    <orderEntry type="module" module-name="javafx-ui-common" />
+    <orderEntry type="module" module-name="javafx-ui-controls" />
+    <orderEntry type="module" module-name="javafx-ui-desktop" />
+    <orderEntry type="module" module-name="javafx-ui-quantum" />
+    <orderEntry type="module" module-name="javafx-ui-webnode" />
+    <orderEntry type="module" module-name="javafx-util-converter" />
+    <orderEntry type="module" module-name="jfxmedia" />
+    <orderEntry type="module" module-name="pisces" />
+    <orderEntry type="module" module-name="prism-common" />
+    <orderEntry type="module" module-name="prism-d3d" />
+    <orderEntry type="module" module-name="prism-es2" />
+    <orderEntry type="module" module-name="prism-es2-mac" />
+    <orderEntry type="module" module-name="prism-j2d" />
+    <orderEntry type="module" module-name="prism-null" />
+    <orderEntry type="module" module-name="prism-ps" />
+    <orderEntry type="module" module-name="prism-util" />
+    <orderEntry type="module" module-name="test-stub-toolkit" />
+    <orderEntry type="module" module-name="webkit" />
+  </component>
+</module>
+
--- a/javafx-ui-charts/src/javafx/scene/chart/StackedBarChart.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-charts/src/javafx/scene/chart/StackedBarChart.java	Tue Mar 12 20:00:53 2013 -0400
@@ -289,26 +289,24 @@
         if (orientation == Orientation.VERTICAL) {
             item.setYValue(getYAxis().toRealValue(getYAxis().getZeroPosition()));
             t.getKeyFrames().addAll(
-                    new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedYValueProperty(item), getCurrentDisplayedYValue(item))),
+                    new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedYValueProperty(item), 
+                    getCurrentDisplayedYValue(item))),
                     new KeyFrame(Duration.millis(700), new EventHandler<ActionEvent>() {
-
-                @Override
-                public void handle(ActionEvent actionEvent) {
-                    getPlotChildren().remove(bar);
-                }
-            },
+                        @Override public void handle(ActionEvent actionEvent) {
+                            getPlotChildren().remove(bar);
+                        }
+                    },
                     new KeyValue(currentDisplayedYValueProperty(item), item.getYValue(), Interpolator.EASE_BOTH)));
         } else {
             item.setXValue(getXAxis().toRealValue(getXAxis().getZeroPosition()));
             t.getKeyFrames().addAll(
-                    new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedXValueProperty(item), getCurrentDisplayedXValue(item))),
+                    new KeyFrame(Duration.ZERO, new KeyValue(currentDisplayedXValueProperty(item), 
+                    getCurrentDisplayedXValue(item))),
                     new KeyFrame(Duration.millis(700), new EventHandler<ActionEvent>() {
-
-                @Override
-                public void handle(ActionEvent actionEvent) {
-                    getPlotChildren().remove(bar);
-                }
-            },
+                        @Override public void handle(ActionEvent actionEvent) {
+                            getPlotChildren().remove(bar);
+                        }
+                    },
                     new KeyValue(currentDisplayedXValueProperty(item), item.getXValue(), Interpolator.EASE_BOTH)));
         }
         return t;
@@ -322,6 +320,7 @@
 
                 public void handle(ActionEvent event) {
                     removeSeriesFromDisplay(series);
+                    requestChartLayout();
                 }
             });
             for (Data<X, Y> d : series.getData()) {
@@ -355,6 +354,7 @@
                 getPlotChildren().remove(bar);
             }
             removeSeriesFromDisplay(series);
+            requestChartLayout();
         }
     }
 
--- a/javafx-ui-common/src/com/sun/javafx/css/Declaration.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Declaration.java	Tue Mar 12 20:00:53 2013 -0400
@@ -25,11 +25,14 @@
 
 package com.sun.javafx.css;
 
+import com.sun.javafx.css.converters.URLConverter;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.net.URL;
 import javafx.css.StyleOrigin;
 import javafx.css.ParsedValue;
+import javafx.css.StyleConverter;
 
 final public class Declaration {
     final String property;
@@ -131,6 +134,41 @@
         if (important) sbuf.append(" !important");
         return sbuf.toString();
     }
+    
+    //
+    // RT-21964
+    //
+    // We know when the .css file is parsed what the stylesheet URL is,
+    // but that might not be the URL of the deployed file. So for URL
+    // types, the parser inserts a null placeholder for the URL and we
+    // fix it up here. This method is called from Rule#setStylesheet
+    // and from Rule#declarations onChanged method.
+    // 
+    void fixUrl(URL stylesheetUrl) {
+        
+        if (stylesheetUrl == null) return;
+        
+        final StyleConverter converter = parsedValue.getConverter();        
+        
+        // code is tightly coupled to the way URLConverter works
+        if (converter == URLConverter.getInstance()) {
+            
+            final ParsedValue[] values = (ParsedValue[])parsedValue.getValue();
+            values[1] = new ParsedValueImpl<URL,URL>(stylesheetUrl, null);
+            
+        } else if (converter == URLConverter.SequenceConverter.getInstance()) {
+
+            final ParsedValue<ParsedValue[], String>[] layers = 
+                (ParsedValue<ParsedValue[], String>[])parsedValue.getValue();
+            
+            for (int layer = 0; layer < layers.length; layer++) {
+                final ParsedValue[] values = (ParsedValue[])layers[layer].getValue();
+                values[1] = new ParsedValueImpl<URL,URL>(stylesheetUrl, null);
+            }
+            
+        }
+                
+    }
 
     void writeBinary(final DataOutputStream os, final StringStore stringStore)
         throws IOException
--- a/javafx-ui-common/src/com/sun/javafx/css/Rule.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/Rule.java	Tue Mar 12 20:00:53 2013 -0400
@@ -29,6 +29,7 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
@@ -61,6 +62,14 @@
                     for(int i = 0, max = added.size(); i < max; i++) {
                         Declaration decl = added.get(i);
                         decl.rule = Rule.this;
+                        
+                        if (stylesheet != null && stylesheet.getUrl() != null) {
+
+                            final URL stylesheetUrl = stylesheet.getUrl();
+                            decl.fixUrl(stylesheetUrl);
+
+                        }
+                        
                     }
                 }
                 
@@ -87,6 +96,15 @@
     /* package */
     void setStylesheet(Stylesheet stylesheet) {
         this.stylesheet = stylesheet;
+        
+        if (stylesheet != null && stylesheet.getUrl() != null) {
+            
+            final URL stylesheetUrl = stylesheet.getUrl();
+            for (int d=0, dMax=declarations.size(); d<dMax; d++) {
+                declarations.get(d).fixUrl(stylesheetUrl);
+            }
+            
+        }
     }
 
     public StyleOrigin getOrigin() {
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleManager.java	Tue Mar 12 20:00:53 2013 -0400
@@ -64,6 +64,9 @@
 import javafx.css.CssMetaData;
 import javafx.css.PseudoClass;
 import javafx.css.StyleOrigin;
+import javafx.scene.image.Image;
+import javafx.stage.PopupWindow;
+import javafx.util.Pair;
 import sun.util.logging.PlatformLogger;
 
 /**
@@ -224,6 +227,10 @@
         // This list should also be fairly small
         final RefList<Key> keys;
         
+        // RT-24516 -- cache images coming from this stylesheet.
+        // This just holds a hard reference to the image. 
+        final List<Image> imageCache;
+        
         final int hash;
 
         StylesheetContainer(String fname, Stylesheet stylesheet) {
@@ -256,6 +263,9 @@
             this.sceneUsers = new RefList<Scene>();
             this.parentUsers = new RefList<Parent>();
             this.keys = new RefList<Key>();
+            
+            // this just holds a hard reference to the image
+            this.imageCache = new ArrayList<Image>();
         }
 
         @Override
@@ -328,9 +338,89 @@
      */
     public void stylesheetsChanged(Scene scene, Change<String> c) {
 
-        // annihilate the cache
-        clearCache();
-        processChange(c);
+        // annihilate the cache?
+        boolean annihilate = false; 
+        final boolean isPopup = scene.getWindow() instanceof PopupWindow;
+        
+        c.reset();
+        while (c.next()) {
+            
+            //
+            // don't care about adds from popups since popup scene gets its 
+            // stylesheets from the scene of its root window.
+            //
+            if (isPopup == false && c.wasAdded()) {
+                //
+                // if a stylesheet is added, then the cache should be cleared
+                // only if that stylesheet isn't already in the 
+                // stylesheetContainerMap. Just because one scene adds the
+                // same stylesheet as another doesn't mean that the other
+                // scene's CSS is invalid. 
+                //
+                final List<String> addedSubList = c.getAddedSubList();
+
+                for (int n=0, nMax=addedSubList.size(); n<nMax; n++) {
+                    final String akey = addedSubList.get(n);
+                    // if this stylesheet isn't in the map, then clear the cache
+                    if (stylesheetContainerMap.containsKey(akey) == false) {
+                        annihilate = true;
+                        // we only need to process one add.
+                        break;
+                    }
+                    
+                }
+                
+            } else if (c.wasRemoved()) {
+                
+                if (isPopup == false) {
+                    //
+                    // If a stylesheet was removed from the scene, then the styles
+                    // will need to be remapped. 
+                    //
+                    annihilate = true;
+                    break;
+                    
+                } else /* isPopup == true */ {
+                    //
+                    // If the scene is from a popup, then styles don't need to
+                    // be remapped but the popup scene needs to be removed from
+                    // the containers.
+                    // 
+                    final List<String> removedList = c.getRemoved();
+                    for (int n=0, nMax=removedList.size(); n<nMax; n++) {
+                        
+                        final String rkey = removedList.get(n);
+                        final StylesheetContainer sc = stylesheetContainerMap.get(rkey); 
+                        
+                        if (sc != null) {
+                            
+                            final List<Reference<Scene>> refList = sc.sceneUsers.list;                            
+                            for(int r=refList.size()-1; 0 <= r; --r) {
+                                
+                                final Reference<Scene> ref = refList.get(r);
+                                final Scene s = (ref != null) ? ref.get() : null;
+                                
+                                if (s == scene) {
+                                    refList.remove(r);
+                                    break;
+                                }
+                                
+                            }
+                            
+                            if (refList.isEmpty()) {
+                                stylesheetContainerMap.remove(rkey);
+                            }
+                            
+                        }
+                    }
+                } 
+            }
+        }
+        
+        if (isPopup == false) {
+            if (annihilate) clearCache();
+            processChange(c);
+        }
         
     }
         
@@ -398,7 +488,11 @@
     private void clearCache(StylesheetContainer sc) {
 
         removeFromCacheMap(sc);
-            
+        
+        // clean up image cache by removing images from the cache that 
+        // might have come from this stylesheet
+        cleanUpImageCache(sc.fname);
+                           
         final List<Reference<Scene>> sceneList = sc.sceneUsers.list;
         final List<Reference<Parent>> parentList = sc.parentUsers.list;
                         
@@ -435,7 +529,7 @@
     // RT-22565: Called from clearParentCache to clear the cache entries.
     private void removeFromCacheMap(StylesheetContainer sc) {
 
-        if (masterCacheMap.isEmpty()) {
+        if (masterCacheMap.isEmpty() || sc == null) {
             return;
         }
 
@@ -479,6 +573,73 @@
         }
     }
     
+    ////////////////////////////////////////////////////////////////////////////
+    //
+    // Image caching
+    //
+    ////////////////////////////////////////////////////////////////////////////
+    
+    Map<String,Image> imageCache = new HashMap<String,Image>();
+    
+    public Image getCachedImage(String url) {
+    
+        Image image = imageCache.get(url);
+        if (image == null) {
+            
+            try {
+
+                image = new Image(url);
+                imageCache.put(url, image);
+                
+            } catch (IllegalArgumentException iae) {
+                // url was empty! 
+                final PlatformLogger logger = getLogger();
+                if (logger != null && logger.isLoggable(PlatformLogger.WARNING)) {
+                        LOGGER.warning(iae.getLocalizedMessage());
+                }
+
+            } catch (NullPointerException npe) {
+                // url was null!
+                final PlatformLogger logger = getLogger();
+                if (logger != null && logger.isLoggable(PlatformLogger.WARNING)) {
+                        LOGGER.warning(npe.getLocalizedMessage());
+                }
+            }
+        } 
+        
+        return image;
+    }
+    
+    private void cleanUpImageCache(String fname) {
+
+        if (fname == null && imageCache.isEmpty()) return;
+        if (fname.trim().isEmpty()) return;
+
+        int len = fname.lastIndexOf('/');
+        final String path = (len > 0) ? fname.substring(0,len) : fname;
+        final int plen = path.length();
+        
+        final String[] entriesToRemove = new String[imageCache.size()];
+        int count = 0;
+        
+        final Set<Entry<String, Image>> entrySet = imageCache.entrySet();
+        for(Entry<String, Image> entry : entrySet) {
+            
+            final String key = entry.getKey();
+            len = key.lastIndexOf('/');
+            final String kpath = (len > 0) ? key.substring(0, len) : key;
+            final int klen = kpath.length();
+            
+            // if the longer path begins with the shorter path,
+            // then assume the image came from this path.
+            boolean match = (klen > plen) ? kpath.startsWith(path) : path.startsWith(kpath);
+            if (match) entriesToRemove[count++] = key;
+        }
+        
+        for (int n=0; n<count; n++) {
+           Image img = imageCache.remove(entriesToRemove[n]);
+       }
+    }
     
     ////////////////////////////////////////////////////////////////////////////
     //
--- a/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Tue Mar 12 20:00:53 2013 -0400
@@ -38,6 +38,7 @@
 import com.sun.javafx.css.Size;
 import com.sun.javafx.css.SizeUnits;
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 
 
 public final class PaintConverter extends StyleConverterImpl<ParsedValue<?, Paint>, Paint> {
@@ -152,7 +153,7 @@
             ParsedValue<?,?> urlParsedValue = values[0];
             String url = (String) urlParsedValue.convert(font);
             if (values.length == 1) {
-                return new ImagePattern(new Image(url));
+                return new ImagePattern(StyleManager.getInstance().getCachedImage(url));
             }
 
             Size x = (Size) values[1].convert(font);
--- a/javafx-ui-common/src/com/sun/javafx/css/converters/URLConverter.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/converters/URLConverter.java	Tue Mar 12 20:00:53 2013 -0400
@@ -63,7 +63,7 @@
             } else {
                 uriStr = com.sun.javafx.Utils.stripQuotes(uriStr);
             }
-            URL stylesheetURL = (URL) values[1].getValue();
+            URL stylesheetURL = values[1] != null ? (URL)values[1].getValue() : null;
             URL resolvedURL = null;
             if (stylesheetURL == null) {
                 try {
@@ -71,10 +71,12 @@
                 } catch (MalformedURLException malf) {
                     // This may be a relative URL, so try resolving
                     // it using the application classloader
+                    if (uriStr.startsWith("/")) uriStr = uriStr.substring(1);                    
                     final ClassLoader cl = Thread.currentThread().getContextClassLoader();
                     resolvedURL = cl.getResource(uriStr);
                 }
             } else {
+                if (uriStr.startsWith("/")) uriStr = uriStr.substring(1);
                 // resolve doesn't work with opaque URI's, but this does.
                 resolvedURL = new URL(stylesheetURL, uriStr);
             }
--- a/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Tue Mar 12 20:00:53 2013 -0400
@@ -2191,7 +2191,7 @@
         final String uri = arg.token.getText();
         ParsedValueImpl[] uriValues = new ParsedValueImpl[] {
             new ParsedValueImpl<String,String>(uri, StringConverter.getInstance()),
-            new ParsedValueImpl<URL,URL>(sourceOfStylesheet, null)
+            null // placeholder for Stylesheet URL
         };
         ParsedValueImpl parsedURI = new ParsedValueImpl<ParsedValue[],String>(uriValues, URLConverter.getInstance());
 
@@ -2266,7 +2266,7 @@
         final String uri = arg.token.getText();
         ParsedValueImpl[] uriValues = new ParsedValueImpl[] {
             new ParsedValueImpl<String,String>(uri, StringConverter.getInstance()),
-            new ParsedValueImpl<URL,URL>(sourceOfStylesheet, null)
+            null // placeholder for Stylesheet URL
         };
         ParsedValueImpl parsedURI = new ParsedValueImpl<ParsedValue[],String>(uriValues, URLConverter.getInstance());
         ParsedValueImpl[] values = new ParsedValueImpl[1];
@@ -3408,7 +3408,7 @@
         final String uri = arg.token.getText();
         ParsedValueImpl[] uriValues = new ParsedValueImpl[] {
             new ParsedValueImpl<String,String>(uri, StringConverter.getInstance()),
-            new ParsedValueImpl<URL,URL>(sourceOfStylesheet, null)
+            null // placeholder for Stylesheet URL
         };
         return new ParsedValueImpl<ParsedValue[],String>(uriValues, URLConverter.getInstance());
     }
--- a/javafx-ui-common/src/javafx/scene/CssStyleHelper.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/CssStyleHelper.java	Tue Mar 12 20:00:53 2013 -0400
@@ -133,6 +133,16 @@
                 }
                 
                 if (mightInherit == false) {
+                    
+                    // If this node had a style helper but no longer has
+                    // a StyleHelper, then reset properties to thier initial
+                    // value. If this node were to have a StyleHelper after
+                    // exiting this method, then transitionToState would take
+                    // care of resetting properties if needed. 
+                    if (node.styleHelper != null) {
+                        node.styleHelper.resetToInitialValues(node);
+                    }
+                    
                     return null;
                 }
             }
@@ -239,6 +249,7 @@
         private final int smapId;
         private final StyleCache localStyleCache;
         private final Reference<StyleCache> sharedCacheRef;
+        private StyleCacheEntry previousCacheEntry;
     }
     
     //
@@ -293,6 +304,7 @@
                 
                 localCacheEntry = new StyleCacheEntry(sharedCacheEntry);
                 cacheMetaData.localStyleCache.putStyleCacheEntry(key, localCacheEntry);
+
             }
         }
 
@@ -307,7 +319,6 @@
         // new styles, reset the properties to initial values.
         if (cacheMetaData != null) {
             
-            node.impl_cssResetInitialValues();
             cacheMetaData.localStyleCache.clear();
             
             // do we have any styles at all now?
@@ -320,6 +331,7 @@
                     // initial state so that calls to transitionToState 
                     // become a no-op.
                     cacheMetaData = null;
+                    resetToInitialValues(node);
                 }
                 
                 // If smap isn't empty, then there are styles that
@@ -355,6 +367,28 @@
         }
         
     }
+    
+    private void resetToInitialValues(Styleable styleable) {
+        
+        final List<CssMetaData<? extends Styleable, ?>> metaDataList = styleable.getCssMetaData();
+        final int nStyleables = metaDataList != null ? metaDataList.size() : 0;
+        for (int n=0; n<nStyleables; n++) {
+            final CssMetaData metaData = metaDataList.get(n);
+            if (metaData.isSettable(styleable) == false) continue;
+            final StyleableProperty<?> styleableProperty = metaData.getStyleableProperty(styleable);
+            if (styleableProperty != null) {
+                final StyleOrigin origin = styleableProperty.getStyleOrigin();
+                if (origin != null && origin != StyleOrigin.USER) {
+                    // If a property is never set by the user or by CSS, then 
+                    // the StyleOrigin of the property is null. So, passing null 
+                    // here makes the property look (to CSS) like it was
+                    // initialized but never used.
+                    metaData.set(styleable, metaData.getInitialValue(styleable), null);
+                }
+            }
+        }        
+    }
+    
         
     private Map<String, List<CascadingStyle>> getStyleMap() {
         if (cacheMetaData == null) return null;
@@ -362,35 +396,6 @@
         return (styleMap != null) ? styleMap.getMap() : null;
     }
     
-    /**
-     * A convenient place for holding default values for populating the
-     * List&lt;Style&gt; that is populated if the Node has a 
-     * Map&lt;WritableValue&gt;, List&lt;Style&gt;. 
-     * See handleNoStyleFound
-     */
-    private static final Map<CssMetaData<? extends Styleable, ?>,Style> stylesFromDefaults = 
-            new HashMap<CssMetaData<? extends Styleable, ?>,Style>();
-    
-    /**
-     * The List to which Declarations fabricated from StyleablePropeerty 
-     * defaults are added.
-     */
-    private static final List<Declaration> declarationsFromDefaults;
-    
-    /**
-     * The Styles in defaultsStyles need to belong to a stylesheet. 
-     */
-    private static final Stylesheet defaultsStylesheet;
-    static {
-        final Rule defaultsRule = 
-            new Rule(new ArrayList<Selector>(), Collections.<Declaration>emptyList());
-        defaultsRule.getSelectors().add(Selector.getUniversalSelector());
-        declarationsFromDefaults = defaultsRule.getDeclarations();
-        defaultsStylesheet = new Stylesheet();
-        defaultsStylesheet.setOrigin(null);
-        defaultsStylesheet.getRules().add(defaultsRule);
-    };
-        
     /** 
      * Cache of parsed, inline styles. The key is Node.style. 
      * The value is the set of property styles from parsing Node.style.
@@ -561,7 +566,6 @@
             return;
         }
         
-        
         Set<PseudoClass>[] transitionStates = getTransitionStates(node);
                 
         //
@@ -657,22 +661,67 @@
                     : null;
 
             CalculatedValue calculatedValue = null;
+            boolean isUserSet = false;
+            
             if (fastpath) {
 
                 calculatedValue = cacheEntry.get(property);
-                if (calculatedValue == null) continue;
+                if (calculatedValue == null || calculatedValue == SKIP) continue;
+                
+                // This block of code is repeated in the slowpath, but we 
+                // want to avoid calling getStyleableProperty if possible.
+                // See the use of 'if (isUserSet)' below.
+                final StyleableProperty styleableProperty = cssMetaData.getStyleableProperty(node);
+                if (styleableProperty == null) continue;
+                
+                final StyleOrigin origin = styleableProperty.getStyleOrigin();
+                
+                isUserSet = origin == StyleOrigin.USER;
+                
+            } else {
 
-            }
-
-            if (calculatedValue == null) {
-
-                boolean isUserSet = isUserSetProperty(node, cssMetaData);            
+                final StyleableProperty styleableProperty = cssMetaData.getStyleableProperty(node);
+                if (styleableProperty == null) continue;
+                
+                final StyleOrigin origin = styleableProperty.getStyleOrigin();
+                
+                isUserSet = origin == StyleOrigin.USER;
 
                 calculatedValue = lookup(node, cssMetaData, isUserSet, node.pseudoClassStates, 
                         inlineStyles, node, cacheEntry, styleList);
 
+                // lookup is not supposed to return null.
+                assert(calculatedValue != null);
+                
+                // RT-19089
+                // If the current value of the property was set by CSS 
+                // and there is no style for the property, then reset this
+                // property to its initial value. 
+                //
+                if (calculatedValue == SKIP || calculatedValue == null) {
+                    
+                    // Was value set by CSS?
+                    if (origin != StyleOrigin.USER && origin != null) {
+
+                        Object initial = cssMetaData.getInitialValue(node);
+                        
+                        calculatedValue = new CalculatedValue(initial, null, false);
+                        
+                    } else {   
+                        // was set by user or was never set
+                        continue;
+                    }
+                    
+                }
+                
+                cacheEntry.put(property, calculatedValue);
+
             }
             
+            assert (calculatedValue != SKIP);
+            if (calculatedValue == SKIP) continue;
+            
+                        
             // RT-10522:
             // If the user set the property and there is a style and
             // the style came from the user agent stylesheet, then
@@ -686,27 +735,23 @@
             // the check needs to be done on any calculated value, not just
             // calculatedValues from cache
             //
-            if (calculatedValue == SKIP
-                || (   calculatedValue != null
-                    && (   calculatedValue.getOrigin() == StyleOrigin.USER_AGENT
-                        || calculatedValue.getOrigin() == null) 
-                    && isUserSetProperty(node, cssMetaData)
-                    )
-                ) {
-                continue;
+            final StyleOrigin cvOrigin = calculatedValue.getOrigin();
+            if (isUserSet) {
+                if (cvOrigin == StyleOrigin.USER_AGENT || cvOrigin == null) { 
+                    continue;
+                }                
             }
             
-                final Object value = calculatedValue.getValue();
-                if (LOGGER.isLoggable(PlatformLogger.FINER)) {
-                    LOGGER.finer("call " + node + ".impl_cssSet(" +
-                                    property + ", " + value + ")");
-                }
+                try {
+                    
+                    final Object value = calculatedValue.getValue();
+                    if (LOGGER.isLoggable(PlatformLogger.FINER)) {
+                        LOGGER.finer(property + ", call cssMetaData.set(" + 
+                                node + ", " + String.valueOf(value) + ", " +
+                                cvOrigin + ")");
+                    }
 
-                try {
-                    cssMetaData.set(node, value, calculatedValue.getOrigin());
-                    
-                    cacheEntry.put(property, calculatedValue);
-
+                    cssMetaData.set(node, value, cvOrigin);
                     
                     if (observableStyleMap != null) {
                         StyleableProperty<?> styleableProperty = cssMetaData.getStyleableProperty(node);                            
@@ -735,6 +780,8 @@
 
             }
         
+        cacheMetaData.previousCacheEntry = cacheEntry;
+        
         // RT-20643
         CssError.setCurrentScene(null);
 
@@ -965,51 +1012,11 @@
 
             return cv;
 
-        } else if (isUserSet) {
+        } else {
 
-            // Not inherited. There is no style but we don't want to
-            // set the default value if the user set the property
+            // Not inherited. There is no style
             return SKIP;
 
-        } else {
-            
-            final Map<String, List<CascadingStyle>> smap = getStyleMap();
-            if (smap == null) return SKIP;
-            
-            if (smap.containsKey(styleable.getProperty())) {
-
-                // If there is a style in the stylemap but it just doen't apply,
-                // then it may have been set and it needs to be reset to its
-                // default value. For example, if there is a style for the hover
-                // pseudo-class state, but no style for the default state.
-                Object initialValue = styleable.getInitialValue(node);
-
-                if (styleList != null) {
-
-                    Style initialStyle = stylesFromDefaults.get(styleable);
-                    if (initialStyle != null) {
-                        if (!declarationsFromDefaults.contains(initialStyle.getDeclaration())) {
-                            declarationsFromDefaults.add(initialStyle.getDeclaration());
-                        }
-                    } else {
-                        initialStyle = new Style( 
-                            Selector.getUniversalSelector(),
-                            new Declaration(styleable.getProperty(), 
-                                        new ParsedValueImpl(initialValue, null), false));
-                        stylesFromDefaults.put(styleable, initialStyle);
-                        declarationsFromDefaults.add(initialStyle.getDeclaration());
-                    }
-
-                    styleList.add(initialStyle);
-                }
-
-                return new CalculatedValue(initialValue, null, false);
-
-            } else {
-
-                return SKIP;
-
-            }
         }
     }
     /**
@@ -1414,15 +1421,7 @@
         return new CalculatedValue(null, style.getOrigin(), false);
            
     }
-    
-    /** return true if the origin of the property is USER */
-    private boolean isUserSetProperty(Node node, CssMetaData styleable) {
-        StyleableProperty styleableProperty = node != null ? styleable.getStyleableProperty(node) : null;
-        // writable could be null if this is a sub-property
-        StyleOrigin origin = styleableProperty != null ? styleableProperty.getStyleOrigin() : null;
-        return (origin == StyleOrigin.USER);    
-    }    
-            
+                
     private static final CssMetaData dummyFontProperty =
             new FontCssMetaData<Node>("-fx-font", Font.getDefault()) {
 
--- a/javafx-ui-common/src/javafx/scene/Node.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/Node.java	Tue Mar 12 20:00:53 2013 -0400
@@ -8185,38 +8185,6 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    public void impl_cssResetInitialValues() {
-        
-        //
-        // RT-9784: reset all properties that have been set by CSS back
-        // to their default value. Called when a stylesheet is removed from
-        // a parent or from the scene. This has to be done before calling 
-        // impl_reapplyCSS.
-        //
-        final List<CssMetaData<? extends Styleable, ?>> metaDataList = getCssMetaData();
-        final int nStyleables = metaDataList != null ? metaDataList.size() : 0;
-        for (int n=0; n<nStyleables; n++) {
-            final CssMetaData metaData = metaDataList.get(n);
-            if (metaData.isSettable(this) == false) continue;
-            final StyleableProperty<?> styleableProperty = metaData.getStyleableProperty(this);
-            if (styleableProperty != null) {
-                final StyleOrigin origin = styleableProperty.getStyleOrigin();
-                if (origin != null && origin != StyleOrigin.USER) {
-                    // If a property is never set by the user or by CSS, then 
-                    // the StyleOrigin of the property is null. So, passing null 
-                    // here makes the property look (to CSS) like it was
-                    // initialized but never used.
-                    metaData.set(this, metaData.getInitialValue(this), null);
-                }
-            }
-        }        
-    }
-    
-    /**
-     * @treatAsPrivate implementation detail
-     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
-     */
-    @Deprecated
     public final void impl_reapplyCSS() {
         // If there is no scene, then we cannot make it dirty, so we'll leave
         // the flag alone
@@ -8329,9 +8297,11 @@
 
         if (cssFlag == CssFlags.REAPPLY) {
             
+            final boolean hadStyles = styleHelper != null;
+            
             // Match new styles if my own indicates I need to reapply
             styleHelper = CssStyleHelper.createStyleHelper(this);
-
+            
         } else if (cssFlag == CssFlags.RECALCULATE) {
             
             // Recalculate means that the in-line style has changed.
--- a/javafx-ui-common/src/javafx/scene/Parent.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/Parent.java	Tue Mar 12 20:00:53 2013 -0400
@@ -1080,7 +1080,6 @@
                     if (c.wasRemoved() == false) {
                         continue;
                     }
-                    impl_cssResetInitialValues();
                     break; // no point in resetting more than once...
                 }
                 
@@ -1167,21 +1166,6 @@
         }
     }
     
-    /**
-     * @treatAsPrivate implementation detail
-     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
-     */
-    @Deprecated
-    @Override public void impl_cssResetInitialValues() {
-        // RT-9784
-        super.impl_cssResetInitialValues();
-
-        for (int i=0, max=children.size(); i<max; i++) {
-            children.get(i).impl_cssResetInitialValues();
-        }
-    }
-    
-
     /***********************************************************************
      *                               Misc                                  *
      *                                                                     *
--- a/javafx-ui-common/src/javafx/scene/Scene.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/Scene.java	Tue Mar 12 20:00:53 2013 -0400
@@ -1394,7 +1394,6 @@
                 if (c.wasRemoved() == false) {
                     continue;
                 }
-                getRoot().impl_cssResetInitialValues();
                 break; // no point in resetting more than once...
             }
             getRoot().impl_reapplyCSS();
--- a/javafx-ui-common/src/javafx/scene/image/ImageView.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/image/ImageView.java	Tue Mar 12 20:00:53 2013 -0400
@@ -34,9 +34,11 @@
 import javafx.scene.Node;
 
 import com.sun.javafx.beans.event.AbstractNotifyListener;
+import com.sun.javafx.css.StyleManager;
 import javafx.css.CssMetaData;
 import javafx.css.StyleableStringProperty;
 import com.sun.javafx.css.converters.StringConverter;
+import com.sun.javafx.css.converters.URLConverter;
 import com.sun.javafx.geom.BaseBounds;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.jmx.MXNodeAlgorithm;
@@ -249,20 +251,9 @@
                 @Override
                 protected void invalidated() {
 
-                    String imageUrl = null;
-                    if (get() != null) {
-                        URL url = null;
-                        try {
-                            url = new URL(get());
-                        } catch (MalformedURLException malf) {
-                            // This may be a relative URL, so try resolving
-                            // it using the application classloader
-                            final ClassLoader cl = Thread.currentThread().getContextClassLoader();
-                            url = cl.getResource(get());
-                        }
-                        if (url != null) {
-                            setImage(new Image(url.toExternalForm())); 
-                        }
+                    final String imageUrl = get();
+                    if (imageUrl != null) {
+                        setImage(StyleManager.getInstance().getCachedImage(imageUrl)); 
                     } else {
                         setImage(null);
                     }                    
@@ -800,7 +791,7 @@
         // "preserve-ratio","smooth","viewport","fit-width","fit-height"
          private static final CssMetaData<ImageView, String> IMAGE = 
             new CssMetaData<ImageView,String>("-fx-image",
-                StringConverter.getInstance()) {
+                URLConverter.getInstance()) {
 
             @Override
             public boolean isSettable(ImageView n) {
--- a/javafx-ui-common/src/javafx/scene/layout/BackgroundConverter.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/layout/BackgroundConverter.java	Tue Mar 12 20:00:53 2013 -0400
@@ -26,6 +26,7 @@
 package javafx.scene.layout;
 
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.scene.layout.region.RepeatStruct;
 import java.util.Map;
 import javafx.css.CssMetaData;
@@ -107,7 +108,7 @@
                 // RT-21335: skip background and border images whose image url is null
                 if (imageUrls[i] == null) continue;
 
-                final Image image = new Image(imageUrls[i]);
+                final Image image = StyleManager.getInstance().getCachedImage(imageUrls[i]);
                 final RepeatStruct repeat = (repeats.length > 0) ?
                         repeats[i <= lastRepeatIndex ? i : lastRepeatIndex] : null; // min
                 final BackgroundPosition position = (positions.length > 0) ?
--- a/javafx-ui-common/src/javafx/scene/layout/BorderConverter.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/scene/layout/BorderConverter.java	Tue Mar 12 20:00:53 2013 -0400
@@ -26,6 +26,7 @@
 package javafx.scene.layout;
 
 import com.sun.javafx.css.StyleConverterImpl;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.scene.layout.region.BorderImageSlices;
 import com.sun.javafx.scene.layout.region.Margins;
 import com.sun.javafx.scene.layout.region.RepeatStruct;
@@ -163,7 +164,8 @@
                 final BorderImageSlices slice = slices.length > 0 ? slices[i <= lastSlicesIndex ? i : lastSlicesIndex] : BorderImageSlices.EMPTY;
                 final Insets inset = insets.length > 0 ? insets[i <= lastInsetsIndex ? i : lastInsetsIndex] : Insets.EMPTY;
                 final BorderWidths width = widths.length > 0 ? widths[i <= lastWidthsIndex ? i : lastWidthsIndex] : BorderWidths.DEFAULT;
-                borderImages[i] = new BorderImage(new Image(imageUrls[i]), width, inset, slice.widths, slice.filled, repeatX, repeatY);
+                final Image img = StyleManager.getInstance().getCachedImage(imageUrls[i]);
+                borderImages[i] = new BorderImage(img, width, inset, slice.widths, slice.filled, repeatX, repeatY);
             }
         }
 
--- a/javafx-ui-common/src/javafx/stage/PopupWindow.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/src/javafx/stage/PopupWindow.java	Tue Mar 12 20:00:53 2013 -0400
@@ -386,9 +386,13 @@
 
         final Scene sceneValue = getScene();
         if (sceneValue != null) {
-            SceneHelper.parentEffectiveOrientationInvalidated(sceneValue);
+            SceneHelper.parentEffectiveOrientationInvalidated(sceneValue);            
         }
         
+        // RT-28447
+        final Scene ownerScene = getRootWindow(owner).getScene();
+        sceneValue.getStylesheets().setAll(ownerScene.getStylesheets());
+        
         // It is required that the root window exist and be visible to show the popup.
         if (getRootWindow(owner).isShowing()) {
             // We do show() first so that the width and height of the
--- a/javafx-ui-common/test/unit/javafx/scene/Node_effectiveOrientation_Css_Test.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-common/test/unit/javafx/scene/Node_effectiveOrientation_Css_Test.java	Tue Mar 12 20:00:53 2013 -0400
@@ -41,7 +41,6 @@
 /**
  * Test :dir functional pseudo-class 
  */
-@org.junit.Ignore
 public class Node_effectiveOrientation_Css_Test {
     
     private Group root;
@@ -79,7 +78,7 @@
         root.getChildren().add(rect);
 
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), LEFT_TO_RIGHT);
         assertEquals(Color.web("#00ff00"), rect.getFill());
@@ -100,7 +99,7 @@
 
         scene.setNodeOrientation(RIGHT_TO_LEFT);
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), RIGHT_TO_LEFT);
         assertEquals(Color.web("#ff0000"), rect.getFill());
@@ -120,7 +119,7 @@
         root.getChildren().add(rect);
 
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), LEFT_TO_RIGHT);
         assertEquals(Color.web("#00ff00"), rect.getFill());
@@ -141,7 +140,7 @@
 
         scene.setNodeOrientation(RIGHT_TO_LEFT);
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), RIGHT_TO_LEFT);
         assertEquals(Color.web("#ff0000"), rect.getFill());
@@ -161,7 +160,7 @@
         root.getChildren().add(rect);
 
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), LEFT_TO_RIGHT);
         assertEquals(Color.web("#00ff00"), rect.getFill());
@@ -182,7 +181,7 @@
 
         scene.setNodeOrientation(RIGHT_TO_LEFT);
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), RIGHT_TO_LEFT);
         assertEquals(Color.web("#ff0000"), rect.getFill());
@@ -204,7 +203,7 @@
         rect.setNodeOrientation(RIGHT_TO_LEFT);
 
         // CSS is applied on next pulse after child is added
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), LEFT_TO_RIGHT);
         assertEquals(rect.getEffectiveNodeOrientation(), RIGHT_TO_LEFT);
@@ -214,7 +213,7 @@
         scene.setNodeOrientation(RIGHT_TO_LEFT);
         rect.setNodeOrientation(LEFT_TO_RIGHT);
         
-        Toolkit.getToolkit().firePulse();
+        root.impl_processCSS(true);
         
         assertEquals(scene.getEffectiveNodeOrientation(), RIGHT_TO_LEFT);
         assertEquals(rect.getEffectiveNodeOrientation(), LEFT_TO_RIGHT);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/TableColumnComparator.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/TableColumnComparator.java	Tue Mar 12 20:00:53 2013 -0400
@@ -23,37 +23,41 @@
  * questions.
  */
 
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-
 package com.sun.javafx.scene.control;
 
+import com.sun.javafx.collections.annotations.ReturnsUnmodifiableCollection;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
-
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
+import java.util.List;
 import javafx.scene.control.TableColumnBase;
 
-public class TableColumnComparator implements Comparator<Object> {
+public class TableColumnComparator<S,T> implements Comparator<S> {
 
-    private final ObservableList<TableColumnBase<?,?>> columns;
+    private final List<TableColumnBase<S,T>> columns;
 
-    public TableColumnComparator() {
-        this.columns = FXCollections.observableArrayList();
+    public TableColumnComparator(TableColumnBase<S,T>... columns) {
+        this(Arrays.asList(columns));
+    }
+    
+    public TableColumnComparator(List<TableColumnBase<S,T>> columns) {
+        this.columns = new ArrayList<TableColumnBase<S, T>>(columns);
     }
 
-    public ObservableList<TableColumnBase<?,?>> getColumns() {
-        return columns;
+    @ReturnsUnmodifiableCollection
+    public List<TableColumnBase<S,T>> getColumns() {
+        return Collections.unmodifiableList(columns);
     }
 
-    @Override public int compare(Object o1, Object o2) {
-        for (TableColumnBase tc : columns) {
-            Comparator c = tc.getComparator();
+    @Override public int compare(S o1, S o2) {
+        for (TableColumnBase<S,T> tc : columns) {
+            if (tc.getSortType() == null) continue;
+            
+            Comparator<T> c = tc.getComparator();
 
-            Object value1 = tc.getCellData(o1);
-            Object value2 = tc.getCellData(o2);
+            T value1 = tc.getCellData(o1);
+            T value2 = tc.getCellData(o2);
             
             int result = 0;
             switch (tc.getSortType()) {
@@ -87,4 +91,8 @@
         }
         return true;
     }
+
+    @Override public String toString() {
+        return "TableColumnComparator [ columns: " + getColumns() + "] ";
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Tue Mar 12 20:00:53 2013 -0400
@@ -121,8 +121,6 @@
         TreeTableColumn column = getTableColumn();
         TableSelectionModel sm = tv.getSelectionModel();
         
-        if (! sm.isCellSelectionEnabled()) return;
-        
         boolean isAlreadySelected = sm.isSelected(index, column);
 
         sm.clearAndSelect(index, column);
@@ -139,7 +137,6 @@
                 tv.edit(index, column);
             } else if (e.getClickCount() % 2 == 0) {
                 // try to expand/collapse branch tree item
-                System.out.println("is expanded: " + treeItem.isExpanded() + " -> " + ! treeItem.isExpanded());
                 treeItem.setExpanded(! treeItem.isExpanded());
             }
         }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Tue Mar 12 20:00:53 2013 -0400
@@ -74,9 +74,6 @@
             // TreeTableCell, we should still support selection, so that
             // is what we are doing here.
             sm.select(treeItem);
-        } else if (clickCount % 2 == 0) {
-            // try to expand/collapse branch tree item
-            treeItem.setExpanded(! treeItem.isExpanded());
         }
     }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ColorPickerSkin.java	Tue Mar 12 20:00:53 2013 -0400
@@ -25,6 +25,7 @@
 
 package com.sun.javafx.scene.control.skin;
 
+import com.sun.javafx.css.StyleManager;
 import javafx.beans.property.StringProperty;
 import javafx.css.StyleOrigin;
 import javafx.css.StyleableBooleanProperty;
@@ -96,9 +97,9 @@
             } else {
                 if (pickerColorBox.getChildren().size() == 2) {
                     ImageView imageView = (ImageView)pickerColorBox.getChildren().get(1);
-                    imageView.setImage(new Image(v));
+                    imageView.setImage(StyleManager.getInstance().getCachedImage(v));
                 } else {
-                    pickerColorBox.getChildren().add(new ImageView(new Image(v)));
+                    pickerColorBox.getChildren().add(new ImageView(StyleManager.getInstance().getCachedImage(v)));
                 }
             }
         }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledText.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/LabeledText.java	Tue Mar 12 20:00:53 2013 -0400
@@ -164,14 +164,14 @@
                 StyleOrigin propOrigin = prop.getStyleOrigin();
 
                 //
+                // if propOrigin is greater than origin, then the style should
+                //    not override
                 // if propOrigin is null, then the property is in init state
                 // if origin is null, then some code is initializing this prop
-                // if propOrigin is greater than origin, then the style should
-                //    not override
                 //
-                if (propOrigin == null ||
-                    origin == null ||
-                    propOrigin.compareTo(origin) <= 0) {
+                if ((propOrigin != null && origin != null &&  
+                     propOrigin.compareTo(origin) <= 0) ||
+                    (propOrigin == null && origin == null)) {
                     super.set(node, value, origin);
                 }
             }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListViewSkin.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ListViewSkin.java	Tue Mar 12 20:00:53 2013 -0400
@@ -218,8 +218,6 @@
     @Override protected void updateRowCount() {
         if (flow == null) return;
         
-        updatePlaceholderRegionVisibility();
-        
         int oldCount = itemCount;
         int newCount = listViewItems == null ? 0 : listViewItems.size();
         
@@ -227,6 +225,7 @@
         
         flow.setCellCount(newCount);
         
+        updatePlaceholderRegionVisibility();
         if (newCount != oldCount) {
             needCellsRebuilt = true;
         } else {
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationSkin.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/PaginationSkin.java	Tue Mar 12 20:00:53 2013 -0400
@@ -25,11 +25,14 @@
 
 package com.sun.javafx.scene.control.skin;
 
+import javafx.beans.property.DoubleProperty;
 import javafx.css.StyleableBooleanProperty;
+import javafx.css.StyleableDoubleProperty;
 import javafx.css.StyleableObjectProperty;
 import javafx.css.CssMetaData;
 import com.sun.javafx.css.converters.BooleanConverter;
 import com.sun.javafx.css.converters.EnumConverter;
+import com.sun.javafx.css.converters.SizeConverter;
 import com.sun.javafx.scene.control.behavior.PaginationBehavior;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -508,6 +511,22 @@
         }
     };
 
+    /** The size of the gap between number buttons and arrow buttons */
+    private final DoubleProperty arrowButtonGap = new StyleableDoubleProperty(60.0) {
+        @Override public Object getBean() {
+            return PaginationSkin.this;
+        }
+        @Override public String getName() {
+            return "arrowButtonGap";
+        }
+        @Override public CssMetaData<Pagination,Number> getCssMetaData() {
+            return StyleableProperties.ARROW_BUTTON_GAP;
+        }
+    };
+    private DoubleProperty arrowButtonGapProperty() {
+        return arrowButtonGap;
+    }
+
     private BooleanProperty arrowsVisible;
     public final void setArrowsVisible(boolean value) { arrowsVisibleProperty().set(value); }
     public final boolean isArrowsVisible() { return arrowsVisible == null ? DEFAULT_ARROW_VISIBLE : arrowsVisible.get(); }
@@ -739,7 +758,7 @@
             leftArrowButton.setMinSize(minButtonSize, minButtonSize);
             leftArrowButton.getStyleClass().add("left-arrow-button");
             leftArrowButton.setFocusTraversable(false);
-            HBox.setMargin(leftArrowButton, new Insets(0, 4, 0, 0));
+            HBox.setMargin(leftArrowButton, new Insets(0, snapSize(arrowButtonGap.get()), 0, 0));
             leftArrow = new StackPane();
             leftArrow.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
             leftArrowButton.setGraphic(leftArrow);
@@ -749,7 +768,7 @@
             rightArrowButton.setMinSize(minButtonSize, minButtonSize);
             rightArrowButton.getStyleClass().add("right-arrow-button");
             rightArrowButton.setFocusTraversable(false);
-            HBox.setMargin(rightArrowButton, new Insets(0, 0, 0, 4));
+            HBox.setMargin(rightArrowButton, new Insets(0, 0, 0, snapSize(arrowButtonGap.get())));
             rightArrow = new StackPane();
             rightArrow.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
             rightArrowButton.setGraphic(rightArrow);
@@ -764,6 +783,20 @@
             initializeNavigationHandlers();
             initializePageIndicators();
             updatePageIndex();
+
+            // listen to changes to arrowButtonGap and update margins
+            arrowButtonGap.addListener(new ChangeListener<Number>() {
+                @Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+                    if (newValue.doubleValue() == 0) {
+                        HBox.setMargin(leftArrowButton, null);
+                        HBox.setMargin(rightArrowButton, null);
+
+                    } else {
+                        HBox.setMargin(leftArrowButton, new Insets(0, snapSize(newValue.doubleValue()), 0, 0));
+                        HBox.setMargin(rightArrowButton, new Insets(0, 0, 0, snapSize(newValue.doubleValue())));
+                    }
+                }
+            });
         }
 
         private void initializeNavigationHandlers() {
@@ -877,7 +910,7 @@
                 }
 
                 x += (iw + controlBox.getSpacing());
-                if (x >= w) {
+                if (x > w) {
                     break;
                 }
                 indicatorCount++;
@@ -1294,6 +1327,18 @@
                 return (StyleableProperty<Boolean>)skin.tooltipVisibleProperty();
             }
         };
+        private static final CssMetaData<Pagination,Number> ARROW_BUTTON_GAP =
+            new CssMetaData<Pagination,Number>("-fx-arrow-button-gap", SizeConverter.getInstance(), 4) {
+                @Override public boolean isSettable(Pagination n) {
+                    final PaginationSkin skin = (PaginationSkin) n.getSkin();
+                    return skin.arrowButtonGap == null ||
+                            !skin.arrowButtonGap.isBound();
+                }
+                @Override public StyleableProperty<Number> getStyleableProperty(Pagination n) {
+                    final PaginationSkin skin = (PaginationSkin) n.getSkin();
+                    return (StyleableProperty<Number>)skin.arrowButtonGapProperty();
+                }
+            };
 
         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
         static {
@@ -1303,6 +1348,7 @@
             styleables.add(PAGE_INFORMATION_VISIBLE);
             styleables.add(PAGE_INFORMATION_ALIGNMENT);
             styleables.add(TOOLTIP_VISIBLE);
+            styleables.add(ARROW_BUTTON_GAP);
             STYLEABLES = Collections.unmodifiableList(styleables);
         }
     }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ProgressIndicatorSkin.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ProgressIndicatorSkin.java	Tue Mar 12 20:00:53 2013 -0400
@@ -113,7 +113,7 @@
                     spinner = new IndeterminateSpinner(getSkinnable(), ProgressIndicatorSkin.this, spinEnabled.get(), progressColor.get());
                     getChildren().add(spinner);
                 }
-                
+
                 if (spinner != null) {
                     if (getSkinnable().impl_isTreeVisible() && getSkinnable().getScene() != null) {
                         spinner.indeterminateTimeline.play();
@@ -184,7 +184,7 @@
             getChildren().add(determinateIndicator);
         }
     }
-    
+
     @Override public void dispose() {
         super.dispose();
         if (spinner != null) {
@@ -194,11 +194,11 @@
     }
 
     @Override protected void layoutChildren(final double x, final double y,
-            final double w, final double h) {
-        if (spinner != null && getSkinnable().isIndeterminate()) { 
+                                            final double w, final double h) {
+        if (spinner != null && getSkinnable().isIndeterminate()) {
             spinner.layoutChildren();
             spinner.resizeRelocate(0, 0, w, h);
-        } else if (determinateIndicator != null) { 
+        } else if (determinateIndicator != null) {
             determinateIndicator.layoutChildren();
             determinateIndicator.resizeRelocate(0, 0, w, h);
         }
@@ -224,10 +224,11 @@
         private StackPane progress;
         private StackPane tick;
         private Arc arcShape;
+        private Circle indicatorCircle;
 
         public DeterminateIndicator(ProgressIndicator control, ProgressIndicatorSkin s, Paint fillOverride) {
             this.control = control;
-            
+
             getStyleClass().add("determinate-indicator");
 
             intProgress = (int) Math.round(control.getProgress() * 100.0) ;
@@ -248,7 +249,11 @@
 
             // The circular background for the progress pie piece
             indicator = new StackPane();
+            indicator.setScaleShape(false);
+            indicator.setCenterShape(false);
             indicator.getStyleClass().setAll("indicator");
+            indicatorCircle = new Circle();
+            indicator.setShape(indicatorCircle);
 
             // The shape for our progress pie piece
             arcShape = new Arc();
@@ -301,76 +306,116 @@
             // Position and size the circular background
             double doneTextHeight = doneText.getLayoutBounds().getHeight();
             final Insets controlInsets = control.getInsets();
-            
+            final double left = snapSize(controlInsets.getLeft());
+            final double right = snapSize(controlInsets.getRight());
+            final double top = snapSize(controlInsets.getTop());
+            final double bottom = snapSize(controlInsets.getBottom());
+
             /*
             ** use the min of width, or height, keep it a circle
             */
-            double areaW = (control.getWidth() - (controlInsets.getLeft() + controlInsets.getRight()));
-            double areaH = (control.getHeight() - (controlInsets.getTop() + controlInsets.getBottom()));
+            final double areaW = control.getWidth() - left - right;
+            final double areaH = control.getHeight() - top - bottom - textGap - doneTextHeight;
+            final double radiusW = areaW / 2;
+            final double radiusH = areaH / 2;
+            final double radius = Math.floor(Math.min(radiusW, radiusH));
+            final double centerX = snapPosition(left + radiusW);
+            final double centerY = snapPosition(top + radius);
 
-            double radiusW = areaW / 2;
-            double radiusH = (areaH-(textGap+doneTextHeight)) / 2;
-            double radius = Math.min(radiusW, radiusH);
+            // find radius that fits inside radius - insetsPadding
+            final Insets indicatorInsets = indicator.getInsets();
+            final double iLeft = snapSize(indicatorInsets.getLeft());
+            final double iRight = snapSize(indicatorInsets.getRight());
+            final double iTop = snapSize(indicatorInsets.getTop());
+            final double iBottom = snapSize(indicatorInsets.getBottom());
+            final double progressRadius = snapSize(Math.min(
+                    Math.min(radius - iLeft, radius - iRight),
+                    Math.min(radius - iTop, radius - iBottom)));
 
-            indicator.setShape(new Circle(radius));
-            indicator.resize(2 * radius, 2 * radius);
+            indicatorCircle.setRadius(radius);
+            indicator.setLayoutX(centerX);
+            indicator.setLayoutY(centerY);
 
-            /*
-            ** we need to work out the available space between the padding,
-            ** and centre the indicator inside it
-            */
-            indicator.setLayoutX(controlInsets.getLeft()+(radiusW - radius));
-            indicator.setLayoutY(controlInsets.getTop()+(radiusH - radius));
+            arcShape.setRadiusX(progressRadius);
+            arcShape.setRadiusY(progressRadius);
+            progress.setLayoutX(centerX);
+            progress.setLayoutY(centerY);
 
+            // find radius that fits inside progressRadius - progressInsets
+            final Insets progressInsets = progress.getInsets();
+            final double pLeft = snapSize(progressInsets.getLeft());
+            final double pRight = snapSize(progressInsets.getRight());
+            final double pTop = snapSize(progressInsets.getTop());
+            final double pBottom = snapSize(progressInsets.getBottom());
+            final double indicatorRadius = snapSize(Math.min(
+                    Math.min(progressRadius - pLeft, progressRadius - pRight),
+                    Math.min(progressRadius - pTop, progressRadius - pBottom)));
 
-            arcShape.setRadiusX(((indicator.getWidth() - indicator.getInsets().getLeft() - indicator.getInsets().getRight()) / 2));
-            arcShape.setRadiusY(arcShape.getRadiusX());
+            // find size of spare box that fits inside indicator radius
+            double squareBoxHalfWidth = Math.ceil(Math.sqrt((indicatorRadius * indicatorRadius) / 2));
+            double squareBoxHalfWidth2 = indicatorRadius * (Math.sqrt(2)/2);
 
-
-            progress.setLayoutX(indicator.getLayoutX() + radius);
-            progress.setLayoutY(indicator.getLayoutY() + radius);
-            progress.resize(2 * arcShape.getRadiusX(), 2 * arcShape.getRadiusY());
-
-            tick.setLayoutX(indicator.getLayoutX() + (indicator.getWidth() / 2) - (tick.getWidth() / 2));
-            tick.setLayoutY(indicator.getLayoutY() + (indicator.getHeight() / 2) - (tick.getHeight() / 2));
+            tick.setLayoutX(centerX - squareBoxHalfWidth);
+            tick.setLayoutY(centerY - squareBoxHalfWidth);
+            tick.resize(squareBoxHalfWidth + squareBoxHalfWidth, squareBoxHalfWidth + squareBoxHalfWidth);
             tick.setVisible(control.getProgress() >= 1);
 
-            /*
-            ** if the % text can't fit anywhere in the bounds then don't display it
-            */
-            double textWidth = com.sun.javafx.scene.control.skin.Utils.computeTextWidth(text.getFont(), text.getText(), 0.0);
-            double textHeight = com.sun.javafx.scene.control.skin.Utils.computeTextHeight(text.getFont(), text.getText(), 0.0, text.getBoundsType());
+            // if the % text can't fit anywhere in the bounds then don't display it
+            double textWidth = text.getLayoutBounds().getWidth();
+            double textHeight = text.getLayoutBounds().getHeight();
             if (control.getWidth() >= textWidth && control.getHeight() >= textHeight) {
-                if (!text.isVisible()) {
-                    text.setVisible(true);
-                }
-                text.setLayoutY(indicator.getLayoutY()+indicator.getHeight() + textGap);
-                /*
-                ** try to centre the text at the indicators radius.
-                ** but if it can't then use the padding
-                */
-                if (textWidth > (radiusW*2)) {
-                    text.setLayoutX(controlInsets.getLeft()+(radiusW - radius));
-                }
-                else {
-                    text.setLayoutX(controlInsets.getLeft()+((radiusW*2 - textWidth)/2));
-                }
-            }
-            else {
-                if (text.isVisible()) {
-                    text.setVisible(false);
-                }
+                if (!text.isVisible()) text.setVisible(true);
+                text.setLayoutY(snapPosition(centerY + radius + textGap));
+                text.setLayoutX(snapPosition(centerX - (textWidth/2)));
+            } else {
+                if (text.isVisible()) text.setVisible(false);
             }
         }
 
         @Override protected double computePrefWidth(double height) {
-            final double indW = indicator.getInsets().getLeft() + indicator.getInsets().getRight() + progress.getInsets().getLeft() + progress.getInsets().getRight();
-            return getInsets().getLeft() + Math.max(indW, doneText.getLayoutBounds().getWidth()) + getInsets().getRight();
+            final Insets controlInsets = control.getInsets();
+            final double left = snapSize(controlInsets.getLeft());
+            final double right = snapSize(controlInsets.getRight());
+            final Insets indicatorInsets = indicator.getInsets();
+            final double iLeft = snapSize(indicatorInsets.getLeft());
+            final double iRight = snapSize(indicatorInsets.getRight());
+            final double iTop = snapSize(indicatorInsets.getTop());
+            final double iBottom = snapSize(indicatorInsets.getBottom());
+            final double indicatorMax = snapSize(Math.max(Math.max(iLeft, iRight), Math.max(iTop, iBottom)));
+            final Insets progressInsets = progress.getInsets();
+            final double pLeft = snapSize(progressInsets.getLeft());
+            final double pRight = snapSize(progressInsets.getRight());
+            final double pTop = snapSize(progressInsets.getTop());
+            final double pBottom = snapSize(progressInsets.getBottom());
+            final double progressMax = snapSize(Math.max(Math.max(pLeft, pRight), Math.max(pTop, pBottom)));
+            final Insets tickInsets = tick.getInsets();
+            final double tLeft = snapSize(tickInsets.getLeft());
+            final double tRight = snapSize(tickInsets.getRight());
+            final double indicatorWidth = indicatorMax + progressMax + tLeft + tRight + progressMax + indicatorMax;
+            return left + Math.max(indicatorWidth, doneText.getLayoutBounds().getWidth()) + right;
         }
 
         @Override protected double computePrefHeight(double width) {
-            double indH = indicator.getInsets().getTop() + indicator.getInsets().getBottom() + progress.getInsets().getTop() + progress.getInsets().getBottom();
-            return getInsets().getTop() + indH + textGap + doneText.getLayoutBounds().getHeight() + getInsets().getBottom();
+            final Insets controlInsets = control.getInsets();
+            final double top = snapSize(controlInsets.getTop());
+            final double bottom = snapSize(controlInsets.getBottom());
+            final Insets indicatorInsets = indicator.getInsets();
+            final double iLeft = snapSize(indicatorInsets.getLeft());
+            final double iRight = snapSize(indicatorInsets.getRight());
+            final double iTop = snapSize(indicatorInsets.getTop());
+            final double iBottom = snapSize(indicatorInsets.getBottom());
+            final double indicatorMax = snapSize(Math.max(Math.max(iLeft, iRight), Math.max(iTop, iBottom)));
+            final Insets progressInsets = progress.getInsets();
+            final double pLeft = snapSize(progressInsets.getLeft());
+            final double pRight = snapSize(progressInsets.getRight());
+            final double pTop = snapSize(progressInsets.getTop());
+            final double pBottom = snapSize(progressInsets.getBottom());
+            final double progressMax = snapSize(Math.max(Math.max(pLeft, pRight), Math.max(pTop, pBottom)));
+            final Insets tickInsets = tick.getInsets();
+            final double tTop = snapSize(tickInsets.getTop());
+            final double tBottom = snapSize(tickInsets.getBottom());
+            final double indicatorHeight = indicatorMax + progressMax + tTop + tBottom + progressMax + indicatorMax;
+            return top + indicatorHeight + textGap + doneText.getLayoutBounds().getHeight() + bottom;
         }
 
         @Override protected double computeMaxWidth(double height) {
@@ -461,15 +506,15 @@
                 super();
                 piSkin = pi;
                 InvalidationListener treeVisibilityListener = new InvalidationListener() {
-                        @Override public void invalidated(Observable valueModel) {
-                            if (piSkin.skin.getSkinnable().impl_isTreeVisible()) {
-                                piSkin.pauseIndicator(false);
-                            }
-                            else {
-                                piSkin.pauseIndicator(true);
-                            }
+                    @Override public void invalidated(Observable valueModel) {
+                        if (piSkin.skin.getSkinnable().impl_isTreeVisible()) {
+                            piSkin.pauseIndicator(false);
                         }
-                    };
+                        else {
+                            piSkin.pauseIndicator(true);
+                        }
+                    }
+                };
                 impl_treeVisibleProperty().addListener(treeVisibilityListener);
             }
 
@@ -511,8 +556,8 @@
                         Region region = (Region)child;
                         if (region.getShape() != null) {
                             region.resize(
-                                region.getShape().getLayoutBounds().getMaxX(),
-                                region.getShape().getLayoutBounds().getMaxY()
+                                    region.getShape().getLayoutBounds().getMaxX(),
+                                    region.getShape().getLayoutBounds().getMaxY()
                             );
                             region.getTransforms().setAll(new Scale(scale,scale,0,0));
                         } else {
@@ -658,60 +703,60 @@
      */
     private static class StyleableProperties {
         private static final CssMetaData<ProgressIndicator,Paint> PROGRESS_COLOR =
-            new CssMetaData<ProgressIndicator,Paint>("-fx-progress-color",
-                PaintConverter.getInstance(), null) {
+                new CssMetaData<ProgressIndicator,Paint>("-fx-progress-color",
+                                                         PaintConverter.getInstance(), null) {
 
-            @Override
-            public boolean isSettable(ProgressIndicator n) {
-                final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
-                return skin.progressColor == null ||
-                        !skin.progressColor.isBound();
-            }
+                    @Override
+                    public boolean isSettable(ProgressIndicator n) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
+                        return skin.progressColor == null ||
+                                !skin.progressColor.isBound();
+                    }
 
-            @Override
-            public StyleableProperty<Paint> getStyleableProperty(ProgressIndicator n) {
-                final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
-                return (StyleableProperty<Paint>)skin.progressColor;
-            }
-        };
+                    @Override
+                    public StyleableProperty<Paint> getStyleableProperty(ProgressIndicator n) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
+                        return (StyleableProperty<Paint>)skin.progressColor;
+                    }
+                };
         private static final CssMetaData<ProgressIndicator,Number> INDETERMINATE_SEGMENT_COUNT =
-            new CssMetaData<ProgressIndicator,Number>("-fx-indeterminate-segment-count",
-                                                     SizeConverter.getInstance(), 8) {
+                new CssMetaData<ProgressIndicator,Number>("-fx-indeterminate-segment-count",
+                                                          SizeConverter.getInstance(), 8) {
 
-            @Override public void set(ProgressIndicator node, Number value, StyleOrigin origin) {
-                super.set(node, value.intValue(), origin);
-            }
+                    @Override public void set(ProgressIndicator node, Number value, StyleOrigin origin) {
+                        super.set(node, value.intValue(), origin);
+                    }
 
-            @Override public boolean isSettable(ProgressIndicator n) {
-                final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
-                return skin.indeterminateSegmentCount == null ||
-                        !skin.indeterminateSegmentCount.isBound();
-            }
+                    @Override public boolean isSettable(ProgressIndicator n) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
+                        return skin.indeterminateSegmentCount == null ||
+                                !skin.indeterminateSegmentCount.isBound();
+                    }
 
-            @Override public StyleableProperty<Number> getStyleableProperty(ProgressIndicator n) {
-                final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
-                return (StyleableProperty<Number>)skin.indeterminateSegmentCount;
-            }
-        };
+                    @Override public StyleableProperty<Number> getStyleableProperty(ProgressIndicator n) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) n.getSkin();
+                        return (StyleableProperty<Number>)skin.indeterminateSegmentCount;
+                    }
+                };
         private static final CssMetaData<ProgressIndicator,Boolean> SPIN_ENABLED =
-            new CssMetaData<ProgressIndicator,Boolean>("-fx-spin-enabled",
-                                           BooleanConverter.getInstance(), Boolean.FALSE) {
+                new CssMetaData<ProgressIndicator,Boolean>("-fx-spin-enabled",
+                                                           BooleanConverter.getInstance(), Boolean.FALSE) {
 
-                @Override public boolean isSettable(ProgressIndicator node) {
-                    final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) node.getSkin();
-                    return skin.spinEnabled == null || !skin.spinEnabled.isBound();
-                }
+                    @Override public boolean isSettable(ProgressIndicator node) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) node.getSkin();
+                        return skin.spinEnabled == null || !skin.spinEnabled.isBound();
+                    }
 
-                @Override public StyleableProperty<Boolean> getStyleableProperty(ProgressIndicator node) {
-                    final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) node.getSkin();
-                    return (StyleableProperty<Boolean>)skin.spinEnabled;
-                }
-            };
+                    @Override public StyleableProperty<Boolean> getStyleableProperty(ProgressIndicator node) {
+                        final ProgressIndicatorSkin skin = (ProgressIndicatorSkin) node.getSkin();
+                        return (StyleableProperty<Boolean>)skin.spinEnabled;
+                    }
+                };
 
         public static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
         static {
-            final List<CssMetaData<? extends Styleable, ?>> styleables = 
-                new ArrayList<CssMetaData<? extends Styleable, ?>>(SkinBase.getClassCssMetaData());
+            final List<CssMetaData<? extends Styleable, ?>> styleables =
+                    new ArrayList<CssMetaData<? extends Styleable, ?>>(SkinBase.getClassCssMetaData());
             styleables.add(PROGRESS_COLOR);
             styleables.add(INDETERMINATE_SEGMENT_COUNT);
             styleables.add(SPIN_ENABLED);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollBarSkin.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/ScrollBarSkin.java	Tue Mar 12 20:00:53 2013 -0400
@@ -210,6 +210,10 @@
                     */
                     if (trackLength > thumbLength) {
                         Point2D cur = thumb.localToParent(me.getX(), me.getY());
+                        if (dragStart == null) {
+                            // we're getting dragged without getting a mouse press
+                            dragStart = thumb.localToParent(me.getX(), me.getY());
+                        }
                         double dragPos = getSkinnable().getOrientation() == Orientation.VERTICAL ? cur.getY() - dragStart.getY(): cur.getX() - dragStart.getX();
                         getBehavior().thumbDragged(me, preDragThumbPos + dragPos / (trackLength - thumbLength));
                     }
@@ -247,6 +251,10 @@
                         */
                         if (trackLength > thumbLength) {
                             Point2D cur = thumb.localToParent(event.getX(), event.getY());
+                            if (dragStart == null) {
+                                // we're getting dragged without getting a mouse press
+                                dragStart = thumb.localToParent(event.getX(), event.getY());
+                            }
                             double dragPos = getSkinnable().getOrientation() == Orientation.VERTICAL ? cur.getY() - dragStart.getY(): cur.getX() - dragStart.getX();
                             getBehavior().thumbDragged(null/*todo*/, preDragThumbPos + dragPos / (trackLength - thumbLength));
                         }
@@ -578,17 +586,14 @@
 
         @Override protected void layoutChildren() {
             final Insets insets = getInsets();
-            final double top = insets.getTop();
-            final double left = insets.getLeft();
-            final double bottom = insets.getBottom();
-            final double right = insets.getRight();
-            
-            double aw = arrow.prefWidth(-1);
-            double ah = arrow.prefHeight(-1);
-            
-            double yPos = (getHeight() - (top + bottom + ah)) / 2.0;
-            double xPos = (getWidth() - (left + right + aw)) / 2.0;
-
+            final double top = snapSize(insets.getTop());
+            final double left = snapSize(insets.getLeft());
+            final double bottom = snapSize(insets.getBottom());
+            final double right = snapSize(insets.getRight());
+            final double aw = snapSize(arrow.prefWidth(-1));
+            final double ah = snapSize(arrow.prefHeight(-1));
+            final double yPos = snapPosition((getHeight() - (top + bottom + ah)) / 2.0);
+            final double xPos = snapPosition((getWidth() - (left + right + aw)) / 2.0);
             arrow.resizeRelocate(xPos + left, yPos + top, aw, ah);
         }
 
@@ -601,11 +606,19 @@
         }
 
         @Override protected double computePrefWidth(double height) {
-            return arrow.prefWidth(height) + getInsets().getLeft() + getInsets().getRight();
+            final Insets insets = getInsets();
+            final double left = snapSize(insets.getLeft());
+            final double right = snapSize(insets.getRight());
+            final double aw = snapSize(arrow.prefWidth(-1));
+            return left + aw + right;
         }
         
         @Override protected double computePrefHeight(double width) {
-            return arrow.prefHeight(width) + getInsets().getTop() + getInsets().getBottom();
+            final Insets insets = getInsets();
+            final double top = snapSize(insets.getTop());
+            final double bottom = snapSize(insets.getBottom());
+            final double ah = snapSize(arrow.prefHeight(-1));
+            return top + ah + bottom;
         }
     }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableColumnHeader.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableColumnHeader.java	Tue Mar 12 20:00:53 2013 -0400
@@ -110,6 +110,7 @@
             changeListenerHandler.registerChangeListener(column.widthProperty(), "TABLE_COLUMN_WIDTH");
             changeListenerHandler.registerChangeListener(column.visibleProperty(), "TABLE_COLUMN_VISIBLE");
             changeListenerHandler.registerChangeListener(column.sortNodeProperty(), "TABLE_COLUMN_SORT_NODE");
+            changeListenerHandler.registerChangeListener(column.sortableProperty(), "TABLE_COLUMN_SORTABLE");
         }
     }
     
@@ -149,6 +150,8 @@
             }
         } else if ("TABLE_COLUMN_SORT_NODE".equals(p)) {
             updateSortGrid();
+        } else if ("TABLE_COLUMN_SORTABLE".equals(p)) {
+            setSortPos(! column.isSortable() ? -1 : skin.getSortOrder().indexOf(column));
         }
     }
     
@@ -207,7 +210,12 @@
             if (header.getTableHeaderRow().isReordering() && header.isColumnReorderingEnabled()) {
                 header.columnReorderingComplete(me);
             } else {
-                header.sortColumn(tableColumn, me.isShiftDown());
+                TableColumnHeader.sortColumn(
+                        header.getTableViewSkin().getSortOrder(), 
+                        tableColumn, 
+                        header.isSortingEnabled(),
+                        header.isSortColumn,
+                        me.isShiftDown());
             }
             me.consume();
         }
@@ -551,8 +559,12 @@
         pseudoClassStateChanged(PSEUDO_CLASS_LAST_VISIBLE, isLastVisibleColumn);
     }
 
-    private void sortColumn(TableColumnBase column, boolean addColumn) {
-        if (! isSortingEnabled()) return;
+    public static void sortColumn(final ObservableList<TableColumnBase<?,?>> sortOrder, 
+            final TableColumnBase column, 
+            final boolean isSortingEnabled, 
+            final boolean isSortColumn, 
+            final boolean addColumn) {
+        if (! isSortingEnabled) return;
         
         // we only allow sorting on the leaf columns and columns
         // that actually have comparators defined, and are sortable
@@ -564,19 +576,19 @@
         if (addColumn) {
             if (!isSortColumn) {
                 column.setSortType(ASCENDING);
-                getTableViewSkin().getSortOrder().add(column);
+                sortOrder.add(column);
             } else if (column.getSortType() == ASCENDING) {
                 column.setSortType(DESCENDING);
             } else {
-                int i = getTableViewSkin().getSortOrder().indexOf(column);
+                int i = sortOrder.indexOf(column);
                 if (i != -1) {
-                    getTableViewSkin().getSortOrder().remove(i);
+                    sortOrder.remove(i);
                 }
             }
         } else {
             // the user has clicked on a column header - we should add this to
             // the TableView sortOrder list if it isn't already there.
-            if (isSortColumn && getTableViewSkin().getSortOrder().size() == 1) {
+            if (isSortColumn && sortOrder.size() == 1) {
                 // the column is already being sorted, and it's the only column.
                 // We therefore move through the 2nd or 3rd states:
                 //   1st click: sort ascending
@@ -586,7 +598,7 @@
                     column.setSortType(DESCENDING);
                 } else {
                     // remove from sort
-                    getTableViewSkin().getSortOrder().remove(column);
+                    sortOrder.remove(column);
                 }
             } else if (isSortColumn) {
                 // the column is already being used to sort, so we toggle its
@@ -600,20 +612,21 @@
                 // to prevent multiple sorts, we make a copy of the sort order
                 // list, moving the column value from the current position to 
                 // its new position at the front of the list
-                List<TableColumnBase> sortOrder = new ArrayList<TableColumnBase>(getTableViewSkin().getSortOrder());
-                sortOrder.remove(column);
-                sortOrder.add(0, column);
-                getTableViewSkin().getSortOrder().setAll(column);
+                List<TableColumnBase<?,?>> sortOrderCopy = new ArrayList<TableColumnBase<?,?>>(sortOrder);
+                sortOrderCopy.remove(column);
+                sortOrderCopy.add(0, column);
+                sortOrder.setAll(column);
             } else {
                 // add to the sort order, in ascending form
                 column.setSortType(ASCENDING);
-                getTableViewSkin().getSortOrder().setAll(column);
+                sortOrder.setAll(column);
             }
         }
     }
     
     
     
+    
     /***************************************************************************
      *                                                                         *
      * Layout                                                                  *
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableHeaderRow.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableHeaderRow.java	Tue Mar 12 20:00:53 2013 -0400
@@ -91,9 +91,9 @@
     public double getTableWidth() { return tableWidth; }
     private void updateTableWidth() {
         // snapping added for RT-19428
-        double padding = snapSpace(getTablePadding().getLeft()) + snapSpace(getTablePadding().getRight());
+        double padding = snapSize(getTablePadding().getLeft()) + snapSize(getTablePadding().getRight());
         this.tableWidth = snapSize(tableSkin.getSkinnable().getWidth()) - padding;
-        clip.setWidth(tableWidth + 1);
+        clip.setWidth(tableWidth);
     }
 
     private Rectangle clip;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableRowSkinBase.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableRowSkinBase.java	Tue Mar 12 20:00:53 2013 -0400
@@ -683,23 +683,6 @@
     @Override protected double computePrefWidth(double height) {
         double prefWidth = 0.0F;
 
-        boolean isIndentationRequired = isIndentationRequired();
-        if (isIndentationRequired) {
-            // Do calculations for disclosure node and indentation.
-            // Firstly, indentation
-            int indentationLevel = getIndentationLevel(getSkinnable());
-            if (! isShowRoot()) indentationLevel--;
-            final double indentationPerLevel = getIndentationPerLevel();
-            prefWidth += indentationLevel * indentationPerLevel;
-
-            // Secondl, the disclosure node width
-            Control c = getVirtualFlowOwner();
-            final double defaultDisclosureWidth = 
-                maxDisclosureWidthMap.containsKey(c) ? maxDisclosureWidthMap.get(c) : 0;
-            Node disclosureNode = getDisclosureNode();
-            prefWidth += Math.max(defaultDisclosureWidth, disclosureNode == null ? 0 : disclosureNode.prefWidth(-1));
-        }
-
         List<? extends TableColumnBase/*<T,?>*/> visibleLeafColumns = getVisibleLeafColumns();
         for (int i = 0, max = visibleLeafColumns.size(); i < max; i++) {
             TableColumnBase<T,?> tableColumn = visibleLeafColumns.get(i);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/VirtualFlow.java	Tue Mar 12 20:00:53 2013 -0400
@@ -145,7 +145,7 @@
                     viewportBreadth = viewportLength = lastPosition = 0;
                     hbar.setValue(0);
                     vbar.setValue(0);
-                    adjustPosition(0.0f);
+                    setPosition(0.0f);
                     setNeedsLayout(true);
                     requestLayout();
                 }
@@ -238,11 +238,10 @@
         return position;
     }
 
-    public void setPosition(double position) {
-        boolean needsUpdate = this.position != position;
-        this.position = position;
+    public void setPosition(double newPosition) {
+        boolean needsUpdate = this.position != newPosition;
+        this.position = com.sun.javafx.Utils.clamp(0, newPosition, 1);;
         if (needsUpdate) {
-            adjustPosition(position);
             requestLayout();
         }
     }
@@ -1016,7 +1015,7 @@
                 // Update the item count
 //                setItemCount(cellCount);
             } else if (currentIndex >= cellCount) {
-                adjustPosition(1.0f);
+                setPosition(1.0f);
 //                setItemCount(cellCount);
             } else if (firstCell != null) {
                 double firstCellOffset = getCellPosition(firstCell);
@@ -1118,7 +1117,7 @@
         int firstIndex = cell.getIndex();
         double firstCellPos = getCellPosition(cell);
         if (firstIndex == 0 && firstCellPos > 0) {
-            adjustPosition(0.0f);
+            setPosition(0.0f);
             offset = 0;
             for (int i = 0; i < cells.size(); i++) {
                 cell = cells.get(i);
@@ -1214,9 +1213,9 @@
             // to be at 0 instead of 1.
             start = getCellPosition(firstCell);
             if (firstCell.getIndex() == 0 && start == 0) {
-                adjustPosition(0);
+                setPosition(0);
             } else if (getPosition() != 1) {
-                adjustPosition(1);
+                setPosition(1);
             }
         }
 
@@ -2019,7 +2018,7 @@
                         T cell = cells.get(i);
                         positionCell(cell, getCellPosition(cell) + emptySize);
                     }
-                    adjustPosition(1.0f);
+                    setPosition(1.0f);
                 }
             }
 
@@ -2128,20 +2127,12 @@
         return pixelOffset - viewportOffset;
     }
 
-    /**
-     * Simply adjusts the position to the given value, clamped between 0 and 1
-     * inclusive.
-     */
-    private void adjustPosition(double pos) {
-        setPosition(com.sun.javafx.Utils.clamp(0, pos, 1));
-    }
-
     private void adjustPositionToIndex(int index) {
         int cellCount = getCellCount();
         if (cellCount <= 0) {
             setPosition(0.0f);
         } else {            
-            adjustPosition(((double)index) / cellCount);
+            setPosition(((double)index) / cellCount);
         }
     }
 
@@ -2240,14 +2231,14 @@
         return -(viewportLength * p);
     }
     
-    /**
-     * Adjust the position based on a chunk of pixels. The position is based
-     * on the start of the scrollbar position.
-     */
-    private void adjustByPixelChunk(double numPixels) {
-        setPosition(0);
-        adjustByPixelAmount(numPixels);
-    }
+//    /**
+//     * Adjust the position based on a chunk of pixels. The position is based
+//     * on the start of the scrollbar position.
+//     */
+//    private void adjustByPixelChunk(double numPixels) {
+//        setPosition(0);
+//        adjustByPixelAmount(numPixels);
+//    }
     // end of old PositionMapper code
     
     
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Tue Mar 12 20:00:53 2013 -0400
@@ -2007,7 +2007,7 @@
         -fx-inner-border,
         -fx-body-color;
     -fx-background-insets: 1 0 -1 0, 0, 1, 2;
-    -fx-padding: 0.083333em; /* 1 */
+    -fx-padding: 1px;
 }
 
 .progress-indicator > .determinate-indicator > .progress {
@@ -2015,7 +2015,7 @@
         linear-gradient(to bottom, derive(-fx-accent,38%), derive(-fx-accent,-60%)),
         linear-gradient(to bottom, derive(-fx-accent,20%), -fx-accent);
     -fx-background-insets: 0, 1;
-    -fx-padding: 0.75em; /* 9 */
+    -fx-padding: 0.25em; /* 3px */
 }
 
 /* TODO: scaling the shape seems to make it disappear */
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/modena/modena.css	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/modena/modena.css	Tue Mar 12 20:00:53 2013 -0400
@@ -121,7 +121,7 @@
      * -fx-selection-bar-text for text on top of -fx-selection-bar
      */
     -fx-dark-text-color: black;
-    -fx-mid-text-color: #292929;
+    -fx-mid-text-color: #333;
     -fx-light-text-color: white;
 
     /* A bright blue for highlighting/accenting objects.  For example: selected
@@ -301,8 +301,7 @@
     );
 
     /* The small thin light "shadow" for mark-like objects. Typically used in
-     * conjunction with -fx-mark-color with an insets of 1 0 -1 0.
-     */
+     * conjunction with -fx-mark-color with an insets of 1 0 -1 0. */
     -fx-mark-highlight-color: ladder(
         -fx-color,
         derive(-fx-color,80%) 60%,
@@ -310,24 +309,19 @@
     );
 
     /* Background for items in list like things such as menus, lists, trees,
-     * and tables.
-     *
-     * TODO: it seems like this should be based upon -fx-accent and we should
-     * remove the setting -fx-background in all the sections that use
-     * -fx-selection-bar.
-     */
+     * and tables. */
     -fx-selection-bar: -fx-accent;
 
+    /* Background color to use for selection of list cells etc. This is when
+     * the control doesn't have focus or the row of a previously selected item. */
+    -fx-selection-bar-non-focused: lightgrey;
+
     /* The color to use as -fx-text-fill when painting text on top of
      * backgrounds filled with -fx-selection-bar.
      *
-     * TODO: it seems like this should be derived from -fx-selection-bar.
+     * TODO: this can be removed
      */
-    -fx-selection-bar-text: ladder(
-        -fx-background,
-        -fx-light-text-color 50%,
-        -fx-mid-text-color   51%
-    );
+    -fx-selection-bar-text: -fx-text-background-color;
 
     /* These are needed for Popup */
     -fx-background-radius: inherit;
@@ -337,7 +331,8 @@
     /* The color to use in ListView/TreeView/TableView to indicate hover. */
     -fx-cell-hover-color: #cce3f4;
 
-    -fx-cell-focus-inner-border: #85b9de;
+    /** Focus line for keyboard focus traversal on cell based controls */
+    -fx-cell-focus-inner-border: derive(-fx-selection-bar,30%);
 
     /* The colors to use in Pagination */
     -fx-page-bullet-border: #acacac;
@@ -466,7 +461,15 @@
 .menu-item:disabled > .label,
 .menu-item:disabled,
 .list-cell:filled:selected:focused:disabled,
-.list-cell:filled:selected:disabled {
+.list-cell:filled:selected:disabled,
+.tree-cell:filled:selected:focused:disabled,
+.tree-cell:filled:selected:disabled,
+.tree-cell > .tree-disclosure-node:disabled,
+.tree-table-row-cell > .tree-disclosure-node:disabled,
+.table-row-cell:selected:disabled,
+.tree-table-row-cell:selected:disabled,
+.table-cell:selected:disabled,
+.tree-table-cell:selected:disabled {
     -fx-opacity: 0.4;
 }
 
@@ -547,6 +550,7 @@
 .split-pane:focused,
 .list-view:focused,
 .tree-view:focused,
+.table-view:focused,
 .tree-table-view:focused,
 .html-editor:focused {
     -fx-background-color: -fx-faint-focus-color, -fx-focus-color, -fx-control-inner-background; 
@@ -699,17 +703,10 @@
 }
 .check-box > .box {
     -fx-background-radius: 3, 2, 1;
-    -fx-padding: 0.666667em; /* 16x16 = 8+8 */
-}
-.check-box:selected > .box {
-    /* Carefully picked numbers to remain square as scaling */
-    /*-fx-padding: -0.208333em 0 0.208333em 0.23em;  -2.5 0 2.5 2.76 */
-    /*-fx-padding: -0.208333em 0.083333em 0.208333em 0.23em;  -1.5 0 2.5 2.76 */
     -fx-padding: 0.166667em 0.166667em 0.25em 0.25em; /* 2 2 3 3 */
 }
-.check-box:selected > .box > .mark {
-    -fx-background-color: transparent;
-    /*-fx-padding: 0.666667em 0.583333em 0.666667em 0.5em;  7 7 6 5 */
+.check-box > .box > .mark {
+    -fx-background-color: null;
     -fx-padding: 0.416667em 0.416667em 0.5em 0.5em; /* 5 5 6 6 */
     -fx-shape: "M-0.25,6.083c0.843-0.758,4.583,4.833,5.75,4.833S14.5-1.5,15.917-0.917c1.292,0.532-8.75,17.083-10.5,17.083C3,16.167-1.083,6.833-0.25,6.083z";
 }
@@ -808,9 +805,14 @@
     -fx-alignment: TOP_LEFT;
 }
 .tool-bar { /* top */
+    TOP-COLOR: ladder(
+        -fx-base,
+        derive(-fx-base,0%) 0%,
+        derive(-fx-base,46%) 100%
+    );
     -fx-background-color:
-        linear-gradient(to bottom, derive(-fx-base,75%) 0%, -fx-outer-border 90%),
-        linear-gradient(to bottom, derive(-fx-base,46.9%) 2%, derive(-fx-base,-2.1%) 95%);
+        linear-gradient(to bottom, derive(TOP-COLOR,25%) 0%, -fx-outer-border 90%),
+        linear-gradient(to bottom, TOP-COLOR 2%, derive(-fx-base,-2.1%) 95%);
     -fx-background-insets: 0 0 0 0, 1 0 1 0;
     -fx-padding: 0.416667em 0.5em 0.416667em 0.5em; /* 5 6  5 6 */
     -fx-spacing: 0.333em; /* 4 */
@@ -822,8 +824,8 @@
 .tool-bar.bottom {
     -fx-background-color:
         -fx-outer-border,
-        derive(-fx-base,75%),
-        linear-gradient(to bottom, derive(-fx-base,46.9%) 2%, derive(-fx-base,-2.1%) 95%);
+        derive(TOP-COLOR,25%),
+        linear-gradient(to bottom, TOP-COLOR 2%, derive(-fx-base,-2.1%) 95%);
     -fx-background-insets: 0, 1 0 0 0, 2 0 0 0;
 }
 .tool-bar > .container > .separator {
@@ -953,39 +955,39 @@
 }
 .scroll-bar > .increment-button > .increment-arrow,
 .scroll-bar > .decrement-button > .decrement-arrow {
-    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-35%);
+    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-45%);
 }
 .scroll-bar > .increment-button:hover > .increment-arrow,
 .scroll-bar > .decrement-button:hover > .decrement-arrow {
-    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-40%);
+    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-50%);
 }
 .scroll-bar > .increment-button:pressed > .increment-arrow,
 .scroll-bar > .decrement-button:pressed > .decrement-arrow {
-    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-45%);
+    -fx-background-color: -fx-mark-highlight-color,derive(-fx-base,-55%);
+}
+.scroll-bar:horizontal > .decrement-button > .decrement-arrow {
+    -fx-padding: 0.333em 0.167em 0.333em 0.167em; /* 4 2 4 2 */
+    -fx-shape: "M5.997,5.072L5.995,6.501l-2.998-4l2.998-4l0.002,1.43l-1.976,2.57L5.997,5.072z";
+    -fx-effect: dropshadow(two-pass-box , -fx-shadow-highlight-color, 1, 0.0 , 0, 1.4);
+    /*-fx-background-insets: 2 0 -2 0, 0;*/
 }
 .scroll-bar:horizontal > .increment-button > .increment-arrow {
-    /*-fx-padding: 0.583333em 0.416667em 0 0; *//* 7 5 0 0 */
-    -fx-padding: 0.75em 0.416667em 0 0; /* 9 5 0 0 */
-    -fx-shape: "M0.315,1.457l1.414-1.414L5.686,4L1.729,7.957L0.315,6.543L2.857,4L0.315,1.457z";
-    -fx-background-insets: 2 0 -2 0, 0;
+    -fx-padding: 0.333em 0.167em 0.333em 0.167em; /* 4 2 4 2 */
+    -fx-shape: "M2.998-0.07L3-1.499l2.998,4L3,6.501l-0.002-1.43l1.976-2.57L2.998-0.07z";
+    -fx-effect: dropshadow(two-pass-box , -fx-shadow-highlight-color, 1, 0.0 , 0, 1.4);
+    /*-fx-background-insets: 2 0 -2 0, 0;*/
+}
+.scroll-bar:vertical > .decrement-button > .decrement-arrow {
+    -fx-padding: 0.167em 0.333em 0.167em 0.333em; /* 2 4 2 4 */
+    -fx-shape: "M1.929,4L0.5,3.998L4.5,1l4,2.998L7.07,4L4.5,2.024L1.929,4z";
+    -fx-effect: dropshadow(two-pass-box , -fx-shadow-highlight-color, 1, 0.0 , 0, 1.4);
+    /*-fx-background-insets: 2 0 -2 0, 0;*/
 }
 .scroll-bar:vertical > .increment-button > .increment-arrow {
-    /*-fx-padding: 0.416667em 0.583333em 0 0 ; *//* 5 7 0 0 */
-    -fx-padding: 0.416667em 0.75em 0 0 ; /* 5 9 0 0 */
-    -fx-shape: "M6.543,0.315l1.414,1.414L4,5.686L0.043,1.729l1.414-1.414L4,2.858L6.543,0.315z";
-    -fx-background-insets: 2 0 -2 0, 0;
-}
-.scroll-bar:horizontal > .decrement-button > .decrement-arrow {
-    /*-fx-padding: 0.583333em 0.416667em 0 0; *//* 7 5 0 0 */
-    -fx-padding: 0.75em 0.416667em 0 0; /* 9 5 0 0 */
-    -fx-shape: "M5.686,6.543L4.271,7.957L0.314,4l3.957-3.957l1.414,1.414L3.143,4L5.686,6.543z";
-    -fx-background-insets: 2 0 -2 0, 0;
-}
-.scroll-bar:vertical > .decrement-button > .decrement-arrow {
-    /*-fx-padding: 0.416667em 0.583333em 0 0; *//* 5 7 0 0 */
-    -fx-padding: 0.416667em 0.75em 0 0; /* 5 9 0 0 */
-    -fx-shape: "M1.457,5.563L0.042,4.149L4,0.193l3.957,3.957L6.543,5.563L4,3.021L1.457,5.563z";
-    -fx-background-insets: 2 0 -2 0, 0;
+    -fx-padding: 0.167em 0.333em 0.167em 0.333em; /* 2 4 2 4 */
+    -fx-shape: "M7.071,1L8.5,1.002L4.5,4l-4-2.998L1.93,1L4.5,2.976L7.071,1z";
+    -fx-effect: dropshadow(two-pass-box , -fx-shadow-highlight-color, 1, 0.0 , 0, 1.4);
+    /*-fx-background-insets: 2 0 -2 0, 0;*/
 }
 
 /*******************************************************************************
@@ -998,13 +1000,13 @@
     -fx-background-insets: 0 1 1 1, 1;
     -fx-padding: 0 1 0 1;
 }
-.scroll-pane > .scroll-bar:horizontal > .increment-button, 
+.scroll-pane > .scroll-bar:horizontal > .increment-button,
 .scroll-pane > .scroll-bar:horizontal > .decrement-button {
-    -fx-padding: 0.25em 0.25em 0.166667em 0.25em; /* 3 3 2 3 */
-}
-.scroll-pane > .scroll-bar:vertical > .increment-button, 
+    -fx-padding: 0.166667em 0.25em 0.25em  0.25em; /* 2 3 3 3 */
+}
+.scroll-pane > .scroll-bar:vertical > .increment-button,
 .scroll-pane > .scroll-bar:vertical > .decrement-button {
-    -fx-padding: 0.25em 0.166667em 0.25em 0.25em; /* 3 2 3 3 */
+    -fx-padding: 0.25em 0.25em 0.25em 0.166667em; /* 3 3 3 2 */
 }
 .scroll-pane > .scroll-bar:vertical {
     -fx-background-insets: 1 1 1 0, 1;
@@ -1105,24 +1107,17 @@
         radial-gradient(center 50% 50%, radius 50%, -fx-control-inner-background 70%, derive(-fx-control-inner-background, -9%) 100%), 
         -fx-control-inner-background;
     -fx-background-insets: 0, 1, 5 2 1 2;
-    -fx-padding: 1;
+    -fx-padding: 0.166667em; /* 2px */
 }
 .progress-indicator > .determinate-indicator > .progress {
     -fx-background-color: -fx-accent;
-    /*-fx-background-insets: 2;*/
-    -fx-background-insets: 1;
-    -fx-padding: 1em; /* 9 */
-}
-/* TODO: scaling the shape seems to make it disappear */
+    -fx-padding: 0.083333em; /* 1px */
+}
 .progress-indicator > .determinate-indicator > .tick {
     -fx-background-color: white;
     -fx-background-insets: 0;
-    -fx-padding: 0.416667em; /* 5 */
+    -fx-padding: 0.666667em; /* 8 */
     -fx-shape: "M-0.25,6.083c0.843-0.758,4.583,4.833,5.75,4.833S14.5-1.5,15.917-0.917c1.292,0.532-8.75,17.083-10.5,17.083C3,16.167-1.083,6.833-0.25,6.083z";
-    -fx-scale-shape: false;
-}
-.progress-indicator:indeterminate > .spinner {
-    -fx-padding: 0.833333em; /* 10 */
 }
 .progress-indicator > .percentage {
     -fx-font-size: 0.916667em; /* 11pt - 1 less than the default font */
@@ -1132,40 +1127,40 @@
     -fx-background-color: -fx-accent;
 }
 .progress-indicator:indeterminate .segment0 {
-    -fx-shape:"M10,0C9.998,0,9.995,0,9.992,0C9.991,0,9.991,0,9.99,0C9.988,0,9.986,0,9.985,0S9.982,0,9.981,0S9.979,0,9.978,0S9.975,0,9.974,0S9.972,0,9.971,0C9.969,0,9.968,0,9.966,0H9.965c-0.007,0-0.014,0-0.02,0C9.944,0,9.944,0,9.944,0C9.941,0,9.939,0,9.937,0c-0.77,0.007-1.389,0.634-1.384,1.404C8.557,2.176,9.185,2.8,9.956,2.8c0.001,0,0.003,0,0.004,0H10c0.773-0.002,1.4-0.63,1.4-1.404c0-0.77-0.622-1.393-1.392-1.396C10.006,0,10.003,0,10,0L10,0z";
-}
+     -fx-shape:"M45.0 26.0 a3.5,3.5 0 1,1 0,1 Z";
+ }
 .progress-indicator:indeterminate .segment1 {
-    -fx-shape:"M5.688,1.156c-0.236,0-0.476,0.06-0.696,0.187C4.98,1.349,4.969,1.356,4.958,1.363c0,0-0.001,0-0.001,0C4.955,1.364,4.954,1.365,4.952,1.366c-0.001,0-0.002,0.001-0.004,0.002c0,0,0,0-0.001,0C4.944,1.371,4.94,1.373,4.936,1.375c0,0,0,0-0.001,0C4.933,1.377,4.931,1.378,4.929,1.38C4.267,1.772,4.046,2.624,4.438,3.288c0.261,0.444,0.73,0.692,1.212,0.692c0.24,0,0.484-0.062,0.706-0.192l0.034-0.02C7.058,3.378,7.283,2.52,6.896,1.851C6.636,1.405,6.168,1.156,5.688,1.156L5.688,1.156z";
+    -fx-shape:"M41.98 37.25 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment2 {
-    -fx-shape:"M2.534,4.326c-0.482,0-0.951,0.25-1.209,0.697C1.323,5.027,1.321,5.029,1.32,5.031l0,0C1.319,5.033,1.318,5.034,1.317,5.036S1.315,5.039,1.314,5.04c0,0.001,0,0.002-0.001,0.002C1.312,5.044,1.311,5.046,1.31,5.048c0,0,0,0,0,0.001C1.309,5.051,1.308,5.053,1.307,5.055C1.302,5.063,1.297,5.071,1.292,5.079l0,0C1.291,5.082,1.29,5.084,1.288,5.087c-0.376,0.67-0.141,1.519,0.529,1.898c0.218,0.123,0.456,0.182,0.69,0.182c0.488,0,0.963-0.255,1.222-0.708l0.02-0.035c0.383-0.671,0.149-1.527-0.521-1.912C3.008,4.386,2.769,4.326,2.534,4.326L2.534,4.326z";
+    -fx-shape:"M33.75 45.48 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment3 {
-    -fx-shape:"M1.396,8.648c-0.002,0-0.005,0-0.008,0C0.619,8.652-0.001,9.278,0,10.047c0,0.002,0,0.006,0,0.008l0,0c0,0.019,0,0.037,0,0.056c0,0.001,0,0.002,0,0.003s0,0.003,0,0.004c0.01,0.765,0.632,1.378,1.396,1.378c0.005,0,0.01,0,0.015,0c0.773-0.009,1.395-0.642,1.389-1.415v-0.04C2.794,9.27,2.166,8.648,1.396,8.648L1.396,8.648z";
+    -fx-shape:"M22.5 48.5 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment4 {
-    -fx-shape:"M2.579,12.955c-0.242,0-0.487,0.062-0.71,0.194c-0.664,0.391-0.885,1.242-0.499,1.906c0.013,0.021,0.025,0.043,0.038,0.063c0.262,0.436,0.724,0.678,1.197,0.678c0.243,0,0.49-0.063,0.714-0.197c0.665-0.396,0.883-1.257,0.489-1.922l-0.02-0.034C3.526,13.201,3.059,12.955,2.579,12.955L2.579,12.955z";
+    -fx-shape:"M11.25 45.48 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment5 {
-    -fx-shape:"M5.772,16.09c-0.489,0-0.965,0.257-1.223,0.712c-0.38,0.671-0.146,1.52,0.523,1.901c0.002,0.001,0.004,0.002,0.007,0.004h0c0.004,0.002,0.008,0.004,0.012,0.007c0,0,0,0,0.001,0c0.001,0.001,0.002,0.002,0.004,0.002c0.001,0.001,0.003,0.002,0.004,0.003c0,0,0.001,0,0.001,0.001c0.012,0.006,0.023,0.013,0.035,0.019c0.214,0.119,0.446,0.176,0.675,0.176c0.489,0,0.963-0.258,1.22-0.716c0.377-0.675,0.137-1.529-0.537-1.908l-0.035-0.02C6.242,16.149,6.006,16.09,5.772,16.09L5.772,16.09z";
+    -fx-shape:"M3.01 37.25 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment6 {
-    -fx-shape:"M10.14,17.198c-0.006,0-0.013,0-0.02,0h-0.039c-0.773,0.011-1.394,0.646-1.385,1.419c0.008,0.767,0.631,1.382,1.396,1.382c0.003,0,0.006,0,0.009-0.001c0.024,0,0.051,0,0.075-0.001c0.769-0.016,1.38-0.648,1.367-1.418C11.53,17.813,10.904,17.198,10.14,17.198L10.14,17.198z";
+    -fx-shape:"M0.0 26.0 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment7 {
-    -fx-shape:"M14.433,15.97c-0.245,0-0.494,0.064-0.72,0.2l-0.034,0.021c-0.663,0.397-0.88,1.258-0.483,1.922c0.261,0.439,0.725,0.683,1.2,0.683c0.24,0,0.484-0.062,0.707-0.194c0.022-0.013,0.044-0.025,0.065-0.039c0.656-0.399,0.866-1.254,0.469-1.913C15.373,16.212,14.909,15.97,14.433,15.97L14.433,15.97z";
+    -fx-shape:"M3.01 14.74 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment8 {
-    -fx-shape:"M17.539,12.748c-0.493,0-0.973,0.261-1.229,0.723l-0.02,0.034c-0.376,0.676-0.133,1.53,0.542,1.907c0.216,0.121,0.45,0.178,0.681,0.178c0.487,0,0.96-0.256,1.217-0.71c0.003-0.006,0.007-0.012,0.01-0.019c0.007-0.013,0.015-0.025,0.021-0.038l0,0c0.002-0.003,0.003-0.005,0.004-0.008c0.369-0.675,0.124-1.521-0.55-1.893C18.001,12.805,17.769,12.748,17.539,12.748L17.539,12.748z";
+    -fx-shape:"M11.24 6.51 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment9 {
-    -fx-shape:"M18.603,8.408c-0.011,0-0.021,0-0.031,0c-0.773,0.018-1.388,0.657-1.373,1.431l0.001,0.04c0.015,0.765,0.641,1.377,1.403,1.377c0.008,0,0.016,0,0.023,0c0.77-0.013,1.383-0.646,1.373-1.414c0-0.003,0-0.006,0-0.009l0,0c-0.001-0.019-0.001-0.037-0.001-0.055c0-0.001,0-0.001-0.001-0.002c0-0.002,0-0.004,0-0.006C19.979,9.012,19.358,8.408,18.603,8.408L18.603,8.408z";
+    -fx-shape:"M22.49 3.5 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment10 {
-    -fx-shape:"M17.345,4.121c-0.248,0-0.5,0.066-0.728,0.205c-0.659,0.403-0.869,1.266-0.468,1.927l0.021,0.034c0.265,0.435,0.728,0.675,1.202,0.675c0.247,0,0.497-0.065,0.724-0.202c0.659-0.397,0.871-1.252,0.477-1.912c-0.007-0.011-0.013-0.021-0.02-0.032c-0.001-0.002-0.002-0.003-0.002-0.004c-0.001,0-0.001-0.001-0.001-0.002c-0.004-0.005-0.008-0.011-0.011-0.017c0-0.001,0-0.001-0.001-0.001c-0.001-0.002-0.002-0.004-0.004-0.007C18.271,4.358,17.813,4.121,17.345,4.121L17.345,4.121z";
+    -fx-shape:"M33.75 6.51 a3.5,3.5 0 1,1 0,1 Z";
 }
 .progress-indicator:indeterminate .segment11 {
-    -fx-shape:"M14.104,1.039c-0.494,0-0.974,0.264-1.227,0.729c-0.37,0.679-0.12,1.531,0.559,1.903l0.034,0.019c0.214,0.117,0.445,0.173,0.673,0.173c0.495,0,0.976-0.262,1.231-0.726c0.371-0.674,0.129-1.519-0.542-1.894c-0.012-0.006-0.024-0.013-0.036-0.02c-0.007-0.004-0.014-0.008-0.021-0.012c-0.003-0.001-0.006-0.003-0.009-0.005C14.556,1.094,14.329,1.039,14.104,1.039L14.104,1.039z";
+    -fx-shape:"M41.98 14.74 a3.5,3.5 0 1,1 0,1 Z";
 }
 
 /*******************************************************************************
@@ -1344,9 +1339,9 @@
     -fx-background-color: transparent;
 }
 .context-menu > .scroll-arrow:hover {
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
+    -fx-background: -fx-seleection-bar;
+    -fx-background-color: -fx-background;
+    -fx-text-fill: -fx-text-background-color;
 }
 .context-menu:show-mnemonics > .mnemonic-underline {
     -fx-stroke: -fx-text-fill;
@@ -1396,16 +1391,10 @@
 .menu-bar {
     -fx-padding: 0.0em 0.666667em 0.0em 0.666667em; /* 0 8 0 8 */
     -fx-spacing: 0.166667em; /* 2 */
-    /*-fx-base: derive(#d0d0d0,-70%);*/
-    /*-fx-background-color: linear-gradient(to bottom, derive(-fx-color,50%), derive(-fx-color,-30%)), -fx-body-color;*/
-    /*-fx-background-insets: 0, 1 0 1 0;*/
     -fx-background-color:
         linear-gradient(to bottom, derive(-fx-base,75%) 0%, -fx-outer-border 90%),
         linear-gradient(to bottom, derive(-fx-base,46.9%) 2%, derive(-fx-base,-2.1%) 95%);
     -fx-background-insets: 0 0 0 0, 1 0 1 0;
-    /*-fx-background-color:*/
-        /*linear-gradient(to bottom, derive(-fx-base,46.9%) 2%, derive(-fx-base,5%) 95%);*/
-    /*-fx-background-insets: 0;*/
     -fx-background-radius: 0, 0 ;
 }
 /* Show nothing for background of normal menu button in a menu bar */
@@ -1431,12 +1420,12 @@
 .menu-bar > .container > .menu-button:focused,
 .menu-bar > .container > .menu-button:showing {
     -fx-background: -fx-selection-bar;
-    -fx-background-color: -fx-selection-bar;
+    -fx-background-color: -fx-background;
 }
 .menu-bar > .container > .menu-button:hover > .label,
 .menu-bar > .container > .menu-button:focused > .label,
 .menu-bar > .container > .menu-button:showing  > .label {
-    -fx-text-fill: -fx-selection-bar-text !important;
+    -fx-text-fill: -fx-text-background-color !important;
 }
 .menu-bar:show-mnemonics > .mnemonic-underline {
     -fx-stroke: -fx-text-fill;
@@ -1463,9 +1452,9 @@
     -fx-text-fill: -fx-text-base-color;
 }
 .menu-item:focused {
-     -fx-background: -fx-accent;
-     -fx-background-color: -fx-selection-bar;
-     -fx-text-fill: -fx-selection-bar-text;
+     -fx-background: -fx-selection-bar;
+     -fx-background-color: -fx-background;
+     -fx-text-fill:  -fx-text-background-color;
 }
 .menu-item:focused > .label {
     -fx-text-fill: white;
@@ -1617,10 +1606,12 @@
 .tab-pane > .tab-header-area > .control-buttons-tab > .container > .tab-down-button {
     -fx-background-color: -fx-outer-border, -fx-inner-border, -fx-body-color;
     -fx-background-insets: -1 0 5 0, 0 1 6 1, 1 2 7 2;
-    -fx-padding: 4 4 9 4; /* TODO convert to ems */
-    /*-fx-padding: 0.416667em 0.416667em 0.833333em 0.416667em;*/ 
+    -fx-padding: 4 4 9 4;
     -fx-background-radius: 10;
 }
+.tab-pane:bottom > .tab-header-area > .control-buttons-tab > .container > .tab-down-button {
+    -fx-padding: -5 4 4 4; /* TODO convert to ems */
+}
 /* FLOATING TABS CUSTOMISATION */
 .tab-pane.floating > .tab-header-area > .tab-header-background {
     -fx-background-color: null;
@@ -1709,17 +1700,16 @@
 .combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell {
     -fx-padding: 4 0 4 5;
     /* No alternate highlighting */
-    -fx-background-color: -fx-control-inner-background;
-}
-.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, 
+    -fx-background: -fx-control-inner-background;
+}
+.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
+    -fx-background:  -fx-selection-bar-non-focused;
+    -fx-background-color:  -fx-background !important;
+}
+.combo-box-popup  > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover,
 .combo-box-popup  > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
     -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.combo-box-popup  > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover {
-    -fx-background-color: -fx-cell-hover-color;
-    -fx-text-fill: -fx-text-inner-color;
+    -fx-background-color: -fx-selection-bar !important;
 }
 .combo-box-popup > .list-view > .placeholder > .label {
     -fx-text-fill: derive(-fx-control-inner-background,-30%);
@@ -2073,28 +2063,30 @@
     -fx-arrows-visible: true;
     -fx-tooltip-visible: true;
     -fx-page-information-visible: true;
-    -fx-page-information-alignment: bottom;        
+    -fx-page-information-alignment: bottom;
+    -fx-arrow-button-gap: 0;
 }
 .pagination > .page {
     -fx-background-color: transparent;
 }
 .pagination > .pagination-control {
-    -fx-background-color: transparent;    
-    -fx-padding: 0.833333em 0em 0.833333em 0em;
+    -fx-background-color: transparent;
+    -fx-font-size: 0.82em;
 }
 .pagination > .pagination-control > .control-box {
+    -fx-padding: 5px 0 0 0;
     -fx-spacing: 2;
     -fx-alignment: center;
 }
 .pagination > .pagination-control > .control-box > .left-arrow-button {
     -fx-background-radius: 3 0 0 3, 3 0 0 3, 2 0 0 2, 1 0 0 1;
-    -fx-background-insets: 0 -4 -1 11, 0 -4 0 11, 1 -3 1 12, 2 -2 2 13;
-    -fx-padding: 0.166667em 0em 0.25em 1.167em;
+    -fx-background-insets: 0 0 -1 5, 0 0 0 5, 1 1 1 6, 2 2 2 7;
+    -fx-padding: 0.166667em 0em 0.25em 0.9em;
 }
 .pagination > .pagination-control > .control-box > .right-arrow-button {
     -fx-background-radius: 0 3 3 0, 0 3 3 0, 0 2 2 0, 0 1 1 0;
-    -fx-background-insets: 0 11 -1 -4, 0 11 0 -4, 1 12 1 -3, 2 13 2 -2;
-    -fx-padding: 0.166667em 0em 0.25em 0.083em;
+    -fx-background-insets: 0 5 -1 0, 0 5 0 0, 1 6 1 1, 2 7 2 2;
+    -fx-padding: 0.166667em 0em 0.25em 0.4em;
 }
 .pagination > .pagination-control .left-arrow {
     -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
@@ -2171,77 +2163,80 @@
 
 /*******************************************************************************
  *                                                                             *
+ * List, Tree, Table COMMON                                                    *
+ *                                                                             *
+ ******************************************************************************/
+
+/* remove double borders from scrollbars */
+.list-view > .virtual-flow > .scroll-bar:vertical,
+.tree-view > .virtual-flow > .scroll-bar:vertical,
+.table-view > .virtual-flow > .scroll-bar:vertical,
+.tree-table-view > .virtual-flow > .scroll-bar:vertical {
+    -fx-background-insets: 0, 0 0 0 1;
+    -fx-padding: -1 -1 -1 0;
+}
+.list-view > .virtual-flow > .scroll-bar:horizontal,
+.tree-view > .virtual-flow > .scroll-bar:horizontal,
+.table-view > .virtual-flow > .scroll-bar:horizontal,
+.tree-table-view > .virtual-flow > .scroll-bar:horizontal {
+    -fx-background-insets: 0, 1 0 0 0;
+    -fx-padding: 0 -1 -1 -1;
+}
+.list-view > .virtual-flow > .corner,
+.tree-view > .virtual-flow > .corner,
+.table-view > .virtual-flow > .corner,
+.tree-table-view > .virtual-flow > .corner {
+    -fx-background-color: derive(-fx-base,-1%);
+}
+/* standard cell */
+.list-cell,
+.tree-cell {
+    -fx-background: -fx-control-inner-background;
+    -fx-background-color: -fx-background;
+    -fx-text-fill: -fx-text-background-color;
+}
+/* Selected rows */
+.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected,
+.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell .tree-table-cell:selected {
+    -fx-background: -fx-selection-bar;
+    -fx-table-cell-border-color: derive(-fx-selection-bar, 20%);
+}
+/* Selected when control is not focused */
+.list-cell:filled:selected,
+.tree-cell:filled:selected,
+.table-row-cell:filled:selected,
+.tree-table-row-cell:filled:selected,
+.table-row-cell:filled > .table-cell:selected,
+.tree-table-row-cell:filled > .tree-table-cell:selected {
+    -fx-background: -fx-selection-bar-non-focused;
+    -fx-table-cell-border-color: derive(-fx-selection-bar-non-focused, 20%);
+}
+/* focused cell (keyboard navigation) */
+.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused,
+.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:focused,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:focused,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:focused,
+.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .table-cell:focused,
+.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell > .tree-table-cell:focused {
+    -fx-background-color: -fx-background, -fx-cell-focus-inner-border, -fx-background !important;
+    -fx-background-insets: 0, 1, 2 !important;
+}
+
+/*******************************************************************************
+ *                                                                             *
  * ListView and ListCell                                                       *
  *                                                                             *
  ******************************************************************************/
 
-.list-view > .virtual-flow > .scroll-bar:vertical{
-    -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -1 -1 -1 0;
-}
-.list-view > .virtual-flow > .scroll-bar:horizontal{
-    -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0 -1 -1 -1;
-}
-.list-view > .virtual-flow > .corner {
-    -fx-background-color: -fx-box-border, -fx-base;
-    -fx-background-insets: 0, 1 0 0 1;
-}
 .list-cell {
-    -fx-background-color: -fx-control-inner-background;
     -fx-padding: 0.25em 0.583em 0.25em 0.583em; /* 3 7 3 7 */
-    -fx-text-fill: -fx-text-inner-color;
-    -fx-opacity: 1;
 }
 .list-cell:odd {
-    -fx-background-color: -fx-control-inner-background-alt;
-}
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background;
-    -fx-background-insets: 0, 1, 2;
-}
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused:odd {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background-alt;
-    -fx-background-insets: 0, 1, 2;
-}
-/* When the list-cell is selected and focused */
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:focused:selected {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-background: -fx-accent;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, 
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:focused:selected:hover {
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-/* When the ListView is _not_ focused, we show alternate selection colors */
-.list-cell:filled:selected:focused,
-.list-cell:filled:selected,
-.list-view:horizontal > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
-    -fx-background-color: lightgray;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-/*.list-cell:filled:hover {
-    -fx-background-color: -fx-cell-hover-color;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:focused:hover {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0, 1, 2;
-    -fx-text-fill: -fx-text-inner-color;
-}*/
-.list-view:horizontal > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected,
-.list-view:horizontal > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
-    -fx-background-color: -fx-selection-bar;
+    -fx-background: -fx-control-inner-background-alt;
 }
 
 /*******************************************************************************
@@ -2250,69 +2245,21 @@
  *                                                                             *
  ******************************************************************************/
 
-.tree-view > .virtual-flow > .scroll-bar:vertical{
-    -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -1 -1 -1 0;
-}
-.tree-view > .virtual-flow > .scroll-bar:horizontal{
-    -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0 -1 -1 -1;
-}
-.tree-view > .virtual-flow > .corner {
-    -fx-background-color: -fx-box-border, -fx-base;
-    -fx-background-insets: 0, 1 0 0 1;
-}
 .tree-cell {
-    -fx-background-color: -fx-control-inner-background;
     -fx-padding: 0.25em; /* 3 */
-    -fx-text-fill: -fx-text-inner-color;
     -fx-indent: 1em;
 }
 .tree-cell .label {
     -fx-padding: 0.0em 0.0em 0.0em 0.25em; /* 0 0 0 3 */
 }
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:focused {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background;
-    -fx-background-insets: 0, 1, 2;
-}
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:focused:selected {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-background: -fx-accent;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected, 
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:selected:hover {
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:focused:selected:hover {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-/* When the TreeView is _not_ focused, we show alternate selection colors */
-.tree-cell:filled:selected:focused, 
-.tree-cell:filled:selected {
-    -fx-background-color: lightgray;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.tree-cell:filled:selected:focused:disabled, 
-.tree-cell:filled:selected:disabled {
-    -fx-opacity: -fx-disabled-opacity;
-}
 .tree-cell > .tree-disclosure-node,
 .tree-table-row-cell > .tree-disclosure-node {
     -fx-padding: 4 2 4 8;
     -fx-background-color: transparent;
 }
-.tree-cell > .tree-disclosure-node:disabled,
-.tree-table-row-cell > .tree-disclosure-node:disabled {
-    -fx-opacity: -fx-disabled-opacity;
-}
 .tree-cell > .tree-disclosure-node > .arrow,
 .tree-table-row-cell > .tree-disclosure-node > .arrow {
-    -fx-background-color: -fx-mark-color;
+    -fx-background-color: -fx-text-background-color;
     -fx-padding: 0.333333em; /* 4 */
     -fx-shape: "M 0 -4 L 8 0 L 0 4 z";
 }
@@ -2320,29 +2267,6 @@
 .tree-table-row-cell:expanded > .tree-disclosure-node > .arrow {
     -fx-rotate: 90;
 }
-.tree-cell:filled:selected > .tree-disclosure-node > .arrow,
-.tree-table-row-cell:filled:selected > .tree-disclosure-node > .arrow {
-    -fx-background-color: -fx-selection-bar-text;
-}
-.tree-cell:filled:hover,
-.tree-table-row-cell:filled:hover {
-    -fx-background-color: -fx-cell-hover-color;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.tree-cell:filled:hover > .tree-disclosure-node > .arrow,
-.tree-table-row-cell:filled:hover > .tree-disclosure-node > .arrow {
-    -fx-background-color: -fx-mark-color;
-}
-.tree-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-cell:filled:focused:hover,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:hover {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0, 1, 2;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.tree-cell:filled:selected:hover > .tree-disclosure-node > .arrow,
-.tree-table-row-cell:filled:selected:hover > .tree-disclosure-node > .arrow {
-    -fx-background-color: -fx-selection-bar-text;
-}
 
 /*******************************************************************************
  *                                                                             *
@@ -2354,112 +2278,33 @@
 .tree-table-view {
     /* Constants used throughout the tableview. */
     -fx-table-header-border-color: -fx-box-border;
-    -fx-table-cell-border-color: -fx-box-border;
-}
-/** Draws focus border around tableview */
-.table-view:focused,
-.tree-table-view:focused {
-    -fx-background-color: -fx-faint-focus-color, -fx-focus-color,-fx-control-inner-background;
-    -fx-background-insets: -2, -0.3, 1;
-    -fx-background-radius: 2, 0, 0;
-}
-.table-view > .virtual-flow > .scroll-bar:vertical,
-.tree-table-view > .virtual-flow > .scroll-bar:vertical {
-    -fx-background-insets: 0, 0 0 0 1;
-    -fx-padding: -1 -1 -1 0;
-}
-.table-view > .virtual-flow > .scroll-bar:horizontal,
-.tree-table-view > .virtual-flow > .scroll-bar:horizontal {
-    -fx-background-insets: 0, 1 0 0 0;
-    -fx-padding: 0 -1 -1 -1;
-}
-.table-view > .virtual-flow > .corner,
-.tree-table-view > .virtual-flow > .corner {
-    -fx-background-color: -fx-box-border, -fx-base;
-    -fx-background-insets: 0, 1 0 0 1;
-}
+    -fx-table-cell-border-color: derive(-fx-color,5%);
+}
+/***** ROW CELLS **************************************************************/
 /* Each row in the table is a table-row-cell. Inside a table-row-cell is any
    number of table-cell. */
 .table-row-cell {
-    -fx-background-color: -fx-table-cell-border-color, -fx-control-inner-background;
+    -fx-background: -fx-control-inner-background;
+    -fx-background-color: -fx-table-cell-border-color, -fx-background;
     -fx-background-insets: 0, 0 0 1 0;
-    -fx-padding: 0.0em; /* 0 */
-    -fx-text-fill: -fx-text-inner-color;
+    -fx-padding: 0;
+    -fx-text-fill: -fx-text-background-color;
 }
 .table-row-cell:odd {
-    -fx-background-color: -fx-table-cell-border-color, -fx-control-inner-background-alt;
-    -fx-background-insets: 0, 0 0 1 0;
-}
-.table-row-cell:focused,
-.tree-table-row-cell:focused {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background;
-    -fx-background-insets: 0, 1, 2;
-}
-.table-row-cell:focused:odd {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background-alt;
-    -fx-background-insets: 0, 1, 2;
-}
-/* When the table-row-cell is selected and focused */
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:focused:selected,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:selected {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-background: -fx-accent;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected > .table-cell,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected > .tree-table-cell {
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected, 
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected {
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-view:row-selection:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:focused:selected:hover,
-.tree-table-view:row-selection:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:selected:hover{
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0, 1, 2;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-/* When the TableView is _not_ focused, we show alternate selection colors */
-.table-row-cell:filled:selected:focused, 
-.table-row-cell:filled:selected,
-.tree-table-row-cell:filled:selected:focused, 
-.tree-table-row-cell:filled:selected {
-    -fx-background-color: lightgray;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-row-cell:selected:disabled,
-.tree-table-row-cell:selected:disabled {
-    -fx-opacity: -fx-disabled-opacity;
-}
-.table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:hover { 
-    -fx-background-color: -fx-table-cell-border-color, -fx-cell-hover-color;
-    -fx-background-insets: 0, 0 0 1 0;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.tree-table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:hover {
-    /* No 1-pixel bottom border for the TreeTableView (unlike the TableView above) */
-    -fx-background-color: -fx-cell-hover-color;
-    -fx-background-insets: 0;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:focused:hover,
-.tree-table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:hover { 
-    -fx-background-color: -fx-table-cell-border-color, -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0, 0 0 1 0, 1 1 2 1, 2 2 3 2, 3 3 4 3;
-    -fx-text-fill: -fx-text-inner-color;
-}
+    -fx-background: -fx-control-inner-background-alt;
+}
+/***** INDIVIDUAL CELLS ********************************************************/
 .table-cell {
     -fx-padding: 0.166667em; /* 2px, plus border adds 1px */
-    -fx-background-color: transparent;
+    -fx-background-color: null;
     -fx-border-color: transparent -fx-table-cell-border-color transparent transparent;
-    -fx-border-width: 0.083333em; /* 1 */
     -fx-cell-size: 2.0em; /* 24 */
-    -fx-text-fill: -fx-text-inner-color;
+    -fx-text-fill: -fx-text-background-color;
+}
+.table-view > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected,
+.tree-table-view > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell .tree-table-cell:selected {
+    -fx-background-color: -fx-table-cell-border-color, -fx-background;
+    -fx-background-insets: 0, 0 0 1 0;
 }
 /* When in constrained resize mode, the right-most visible cell should not have
    a right-border, as it is not possible to get this cleanly out of view without
@@ -2468,162 +2313,70 @@
 .tree-table-view:constrained-resize > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell > .tree-table-cell:last-visible {
     -fx-border-color: transparent;
 }
-.table-view:constrained-resize > .column-header:last-visible,
-.tree-table-view:constrained-resize > .column-header:last-visible {
-    -fx-border-width: 0.083333em 0.0em 0.083333em 0.083333em, 0.083333em 0.0em 0.083333em 0.083333em;
-}
-.table-view:constrained-resize .filler,
-.tree-table-view:constrained-resize .filler {
-    -fx-border-color: 
-        derive(-fx-base, 80%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%)
-        derive(-fx-base, 10%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%),
-        /* Outer border: */
-        transparent -fx-table-header-border-color -fx-table-header-border-color -fx-table-header-border-color;
-    -fx-border-insets: 0 1 1 1, 0 0 0 0;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .table-cell:focused,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell > .tree-table-cell:focused {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-control-inner-background;
-    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled .table-cell:focused:selected,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled .tree-table-cell:focused:selected {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
-    -fx-background: -fx-accent;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:selected, 
-.table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:hover:selected,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:selected, 
-.tree-table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:hover:selected {
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
-    -fx-text-fill: -fx-selection-bar-text;
-    -fx-background-insets: 0 0 1 0;
-}
-.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:selected:hover,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:focused:selected:hover{
-    -fx-background: -fx-accent;
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-selection-bar;
-    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-/* When the TableView is _not_ focused, we show alternate selection colors */
-.table-row-cell:filled > .table-cell:selected:focused, 
-.table-row-cell:filled > .table-cell:selected,
-.tree-table-row-cell:filled > .tree-table-cell:selected:focused, 
-.tree-table-row-cell:filled > .tree-table-cell:selected {
-    -fx-background-color: lightgray;
-    -fx-text-fill: -fx-selection-bar-text;
-}
-.table-cell:selected:disabled,
-.tree-table-cell:selected:disabled {
-    -fx-opacity: -fx-disabled-opacity;
-}
-.table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:hover,
-.tree-table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:hover {
-    -fx-background-color: -fx-cell-hover-color;
-    -fx-text-fill: -fx-text-inner-color;
-    -fx-background-insets: 0 0 1 0;
-}
-.table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled > .table-cell:focused:hover,
-.tree-table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:focused:hover {
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
-    -fx-text-fill: -fx-text-inner-color;
-}
+/***** HEADER **********************************************************************/
 /* The column-resize-line is shown when the user is attempting to resize a column. */
 .table-view .column-resize-line,
 .tree-table-view .column-resize-line {
     -fx-background: -fx-accent;
-    -fx-background-color: -fx-selection-bar;
+    -fx-background-color: -fx-background;
     -fx-padding: 0.0em 0.0416667em 0.0em 0.0416667em; /* 0 0.571429 0 0.571429 */
 }
 /* This is the area behind the column headers. An ideal place to specify background
    and border colors for the whole area (not individual column-header's). */
 .table-view .column-header-background,
 .tree-table-view > .column-header-background {
-    -fx-background-color: -fx-body-color;
-    -fx-padding: 0;
-}
-/*.tree-table-view > .column-header-background {
-    -fx-background-color: -fx-body-color;
-    -fx-padding: 0;
-}*/
+    -fx-background-color: -fx-inner-border, -fx-body-color;
+    -fx-background-insets: 0, 1;
+}
 /* The column header row is made up of a number of column-header, one for each
    TableColumn, and a 'filler' area that extends from the right-most column
    to the edge of the tableview, or up to the 'column control' button. */
 .table-view .column-header,
-.tree-table-view .column-header {
+.tree-table-view .column-header,
+.table-view .filler,
+.tree-table-view .filler,
+.table-view > .column-header-background > .show-hide-columns-button,
+.tree-table-view > .column-header-background > .show-hide-columns-button,
+.table-view:constrained-resize .filler,
+.tree-table-view:constrained-resize .filler {
+    -fx-background-color: -fx-box-border, -fx-inner-border, -fx-body-color;
+    -fx-background-insets: 0, 0 1 1 0, 1 2 2 1;
+    -fx-font-weight: bold;
+    -fx-size: 2em;
     -fx-text-fill: -fx-selection-bar-text;
-    /* TODO: for some reason, this doesn't scale. */
-    -fx-font-size: 1.083333em; /* 13pt - 1 more than the default font */
-    -fx-size: 25;
-    -fx-border-style: solid;
-    -fx-border-color: 
-        /* 
-          Inner border: we have different colours along the top, right, bottom and left.
-          Refer to RT-12298 for the spec.
-        */
-        derive(-fx-base, 80%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%)
-        derive(-fx-base, 10%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%),
-        /* Outer border: */
-        transparent -fx-table-header-border-color -fx-table-header-border-color transparent;
-    -fx-border-insets: 0 1 1 0, 0 0 0 0;
-    -fx-border-width: 0.083333em, 0.083333em;
+    -fx-padding: 0.166667em;
 }
 .table-view .filler,
-.tree-table-view .filler {
-    -fx-size: 25;
-    -fx-border-style: solid;
-    -fx-border-color: 
-        /* 
-          Inner border: we have different colours along the top, right, bottom and left.
-          Refer to RT-12298 for the spec.
-        */
-        derive(-fx-base, 80%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%)
-        derive(-fx-base, 10%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%),
-        /* Outer border: */
-        transparent transparent -fx-table-header-border-color transparent;
-    -fx-border-insets: 0 0 1 0, 0 0 0 0;
-    -fx-border-width: 0.083333em, 0.083333em 0 0.083333em 0.083333em;
+.tree-table-view .filler,
+.table-view:constrained-resize .filler,
+.tree-table-view:constrained-resize .filler {
+    -fx-background-insets: 0, 0 0 1 0, 1 1 2 1;
+}
+.table-view > .column-header-background > .show-hide-columns-button,
+.tree-table-view > .column-header-background > .show-hide-columns-button {
+    -fx-background-insets: 0, 0 0 1 1, 1 1 2 2;
+}
+.table-view .column-header .sort-order-dots-container,
+.tree-table-view .column-header .sort-order-dots-container{
+    -fx-padding: 2 0 2 0;
 }
 .table-view .column-header .sort-order,
 .tree-table-view .column-header .sort-order{
     -fx-font-size: 0.916667em; /* 11pt - 1 less than the default font */
 }
-.table-view > .column-header-background > .show-hide-columns-button,
-.tree-table-view > .column-header-background > .show-hide-columns-button{
-    -fx-background-color: -fx-body-color;
-
-    -fx-border-color:
-        /* inner border: A copy of the inner border used above in the general column header area. */
-        derive(-fx-base, 80%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%),
-        derive(-fx-base, 10%) 
-        linear-gradient(to bottom, derive(-fx-base,80%) 20%, derive(-fx-base,-10%) 90%),
-        /* outer border: Slightly different to the above*/
-        transparent transparent -fx-table-header-border-color -fx-table-header-border-color;
-    -fx-border-insets: 0 0 0 1, 0 0 0 0;
-}
+.table-view .column-header .sort-order-dot,
+.tree-table-view .column-header .sort-order-dot {
+    -fx-background-color: -fx-mark-color;
+    -fx-padding: 0.115em;
+    -fx-background-radius: 0.115em;
+}
+/* Plus Symbol */
 .table-view .show-hide-column-image,
 .tree-table-view .show-hide-column-image {
-    -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
-    -fx-background-insets: 1 0 -1 0, 0;
-
+    -fx-background-color: -fx-mark-color;
     -fx-padding: 0.25em; /* 3px */
     -fx-shape: "M398.902,298.045c0.667,0,1.333,0,2,0c0,0.667,0,1.333,0,2c0.667,0,1.333,0,2,0c0,0.667,0,1.333,0,2c-0.667,0-1.333,0-2,0c0,0.666,0,1.332,0,1.999c-0.667,0-1.333,0-2,0c0-0.667,0-1.333,0-1.999c-0.666,0-1.333,0-1.999,0c0-0.667,0-1.334,0-2c0.666,0,1.333,0,1.999,0C398.902,299.378,398.902,298.711,398.902,298.045z"; 
 }
-/*.nested-column-header .column-header {
-    -fx-background-color: transparent;
-}*/
 /* When a column is being 'dragged' to be placed in a different position, there
    is a region that follows along the column header area to indicate where the
    column will be dropped. This region can be styled using the .column-drag-header
@@ -2641,10 +2394,10 @@
     -fx-background-color: darkgray;
     -fx-opacity: 0.3;
 }
+/* Header Sort Arrows */
 .table-view /*> column-header-background > nested-column-header >*/ .arrow,
 .tree-table-view /*> column-header-background > nested-column-header >*/ .arrow {
-    -fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
-    -fx-background-insets: 1 0 -1 0, 0;
+    -fx-background-color: -fx-mark-color;
     -fx-padding: 0.25em 0.3125em 0.25em 0.3125em; /* 3 3.75 3 3.75 */
     -fx-shape: "M 0 0 h 7 l -3.5 4 z";
 }
@@ -2660,22 +2413,29 @@
  * Table Cells                                                                 *
  *                                                                             *
  ******************************************************************************/
+
 .check-box-table-cell {
     -fx-alignment: center;
+    -fx-padding: 0;
 }
 .check-box-table-cell > .check-box {
+    -fx-font-size: 0.8em;
     -fx-opacity: 1;
-}   
+    -fx-padding: 0 0 1 0;
+}
 .check-box-table-cell > .check-box > .box {
-    -fx-background-color: null;
+    -fx-background-color: -fx-outer-border, -fx-background;
+    -fx-background-insets: 0,1;
+}
+.check-box-table-cell > .check-box:selected > .box > .mark {
+    -fx-background-color: -fx-text-background-color !important;
     -fx-background-insets: 0;
-    -fx-border-color: -fx-outer-border;
-    -fx-border-radius: 3;
 }
 .table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected > .check-box-table-cell > .check-box > .box,
 .tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:selected > .check-box-table-cell > .check-box > .box {
-    -fx-border-color: derive(-fx-accent,20%);
-}
+    -fx-background-color: derive(-fx-accent,40%), -fx-background;
+}
+
 /*******************************************************************************
  *                                                                             *
  * TreeTableView                                                               *
@@ -2686,18 +2446,18 @@
  ******************************************************************************/
 
 .tree-table-row-cell {
-    -fx-background-color: -fx-control-inner-background;
-    -fx-padding: 0.0em; /* 0 */
-    -fx-text-fill: -fx-text-inner-color;
+    -fx-background: -fx-control-inner-background;
+    -fx-background-color: -fx-background;
+    -fx-padding: 0;
+    -fx-text-fill: -fx-text-background-color;
     -fx-indent: 1em;
 }
 .tree-table-cell {
     -fx-padding: 0.166667em; /* 2px, plus border adds 1px */
-    -fx-background-color: transparent;
+    -fx-background-color: null;
     -fx-border-color: transparent -fx-table-cell-border-color transparent transparent;
-    -fx-border-width: 0.083333em; /* 1 */
     -fx-cell-size: 2.0em; /* 24 */
-    -fx-text-fill: -fx-text-inner-color;
+    -fx-text-fill: -fx-text-background-color;
 }
 
 /*******************************************************************************
@@ -2728,30 +2488,6 @@
     -fx-background: -fx-accent;
     -fx-text-fill: -fx-selection-bar-text;
 }
-/** Hover styles */
-/** --- Row selection mode hover */
-.cell-span-tree-table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:hover > .tree-table-cell { 
-    -fx-background-color: -fx-table-cell-border-color, -fx-cell-hover-color;
-    -fx-background-insets: 0, 0 0 1 0;
-    -fx-text-fill: -fx-text-inner-color;
-}
-.cell-span-tree-table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:hover > .tree-table-cell { 
-    -fx-background-color: -fx-table-cell-border-color, -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0, 0 0 1 0, 1 1 2 1, 2 2 3 2, 3 3 4 3;
-    -fx-text-fill: -fx-text-inner-color;
-}
-/** --- Cell selection mode hover */
-.cell-span-tree-table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:hover{
-    -fx-background-color: -fx-table-cell-border-color, -fx-cell-hover-color;
-    -fx-text-fill: -fx-text-inner-color;
-    -fx-background-insets: 0, 0 0 1 0;
-}
-.cell-span-tree-table-view:cell-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled > .tree-table-cell:focused:hover{
-    -fx-background-color: -fx-focus-color, -fx-cell-focus-inner-border, -fx-cell-hover-color;
-    -fx-background-insets: 0 1 0 0, 1 2 1 1, 2 3 2 2;
-    -fx-text-fill: -fx-text-inner-color;
-}
-/** End of hover styles */
 
 /*******************************************************************************
  *                                                                             *
--- a/javafx-ui-controls/src/javafx/scene/chart/CategoryAxis.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/chart/CategoryAxis.java	Tue Mar 12 20:00:53 2013 -0400
@@ -75,7 +75,7 @@
                     // Ideally we should be using a Set for categories.
                     for (String addedStr : c.getAddedSubList())
                         checkAndRemoveDuplicates(addedStr);
-                }
+                    }
                 if (!isAutoRanging()) {
                     allDataCategories.clear();
                     allDataCategories.addAll(getCategories());
@@ -260,7 +260,6 @@
      * @param categories List of the categories for this axis
      */
     public CategoryAxis(ObservableList<String> categories) {
-        setAnimated(false);
         setCategories(categories);
     }
 
--- a/javafx-ui-controls/src/javafx/scene/control/Labeled.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/Labeled.java	Tue Mar 12 20:00:53 2013 -0400
@@ -28,6 +28,7 @@
 
 import com.sun.javafx.css.Selector;
 import com.sun.javafx.css.SimpleSelector;
+import com.sun.javafx.css.StyleManager;
 import com.sun.javafx.css.converters.BooleanConverter;
 import com.sun.javafx.css.converters.EnumConverter;
 import com.sun.javafx.css.converters.InsetsConverter;
@@ -326,7 +327,7 @@
     public final ObjectProperty<Font> fontProperty() {
         if (font == null) {
             font = new StyleableObjectProperty<Font>(Font.getDefault()) {
-                
+                                
                 @Override
                 public void set(Font value) {
                     final Font oldValue = get();
@@ -341,9 +342,10 @@
                     // RT-20727 - if font is changed by calling setFont, then
                     // css might need to be reapplied since font size affects
                     // calculated values for styles with relative values
-                    StyleOrigin origin = ((StyleableProperty)font).getStyleOrigin();
-                    if (origin == null || origin == StyleOrigin.USER) {
+                    if(fontSetByCss == false) {
                         Labeled.this.impl_reapplyCSS();
+                    } else {
+                        fontSetByCss = false;
                     }
                 }
                 
@@ -365,7 +367,7 @@
         }
         return font;
     }
-
+    private boolean fontSetByCss = false;
     private ObjectProperty<Font> font;
     public final void setFont(Font value) { fontProperty().setValue(value); }
     public final Font getFont() { return font == null ? Font.getDefault() : font.getValue(); }
@@ -445,7 +447,8 @@
                             url = cl.getResource(v);
                         }
                         if (url != null) {
-                            ((StyleableProperty)graphicProperty()).applyStyle(origin, new ImageView(new Image(url.toExternalForm())));
+                            final Image img = StyleManager.getInstance().getCachedImage(url.toExternalForm());
+                            ((StyleableProperty)graphicProperty()).applyStyle(origin, new ImageView(img));
                         }
                     }
                 }
@@ -734,6 +737,15 @@
             new FontCssMetaData<Labeled>("-fx-font", Font.getDefault()) {
 
             @Override
+            public void set(Labeled styleable, Font value, StyleOrigin origin) {
+                // RT-20727 - if font is changed by calling setFont, then
+                // css might need to be reapplied since font size affects
+                // calculated values for styles with relative values
+                styleable.fontSetByCss = origin != StyleOrigin.USER;
+                super.set(styleable, value, origin);                
+            }
+
+            @Override
             public boolean isSettable(Labeled n) {
                 return n.font == null || !n.font.isBound();
             }
--- a/javafx-ui-controls/src/javafx/scene/control/ListView.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/ListView.java	Tue Mar 12 20:00:53 2013 -0400
@@ -998,17 +998,32 @@
                 updateItemCount();
                 
                 while (c.next()) {
+                    final T selectedItem = getSelectedItem();
+                    final int selectedIndex = getSelectedIndex();
+                    
                     if (listView.getItems() == null || listView.getItems().isEmpty()) {
                         clearSelection();
-                    } else if (getSelectedIndex() == -1 && getSelectedItem() != null) {
-                        int newIndex = listView.getItems().indexOf(getSelectedItem());
+                    } else if (selectedIndex == -1 && selectedItem != null) {
+                        int newIndex = listView.getItems().indexOf(selectedItem);
                         if (newIndex != -1) {
                             setSelectedIndex(newIndex);
                         }
+                    } else if (c.wasRemoved() && 
+                            c.getRemovedSize() == 1 && 
+                            ! c.wasAdded() && 
+                            selectedItem != null && 
+                            selectedItem.equals(c.getRemoved().get(0))) {
+                        // Bug fix for RT-28637
+                        if (getSelectedIndex() < getItemCount()) {
+                            T newSelectedItem = getModelItem(selectedIndex);
+                            if (! selectedItem.equals(newSelectedItem)) {
+                                setSelectedItem(newSelectedItem);
+                            }
+                        }
                     }
-
-                    updateSelection(c);
                 }
+                
+                updateSelection(c);
             }
         };
         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/javafx/scene/control/SortEvent.java	Tue Mar 12 20:00:53 2013 -0400
@@ -0,0 +1,39 @@
+package javafx.scene.control;
+
+import javafx.event.Event;
+import javafx.event.EventTarget;
+import javafx.event.EventType;
+
+/**
+ * Event related to {@link TableView} and {@link TreeTableView} sorting.
+ */
+public class SortEvent<C> extends Event {
+
+    @SuppressWarnings("unchecked")
+    public static <C> EventType<SortEvent<C>> sortEvent() {
+        return (EventType<SortEvent<C>>) SORT_EVENT;
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static final EventType<?> SORT_EVENT = new EventType(Event.ANY, "SORT_EVENT");
+    
+//    /**
+//     * Construct a new {@code Event} with the specified event source, target
+//     * and type. If the source or target is set to {@code null}, it is replaced
+//     * by the {@code NULL_SOURCE_TARGET} value.
+//     * 
+//     * @param source the event source which sent the event
+//     * @param target the event source which sent the event
+//     * @param type the event type
+//     * @param target the target of the scroll to operation
+//     */
+    public SortEvent(C source, EventTarget target) {
+        super(source, target, sortEvent());
+        
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override public C getSource() {
+        return (C) super.getSource();
+    }
+}
\ No newline at end of file
--- a/javafx-ui-controls/src/javafx/scene/control/TableUtil.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TableUtil.java	Tue Mar 12 20:00:53 2013 -0400
@@ -30,6 +30,9 @@
 import java.util.List;
 import javafx.beans.InvalidationListener;
 import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import static javafx.scene.control.TableColumnBase.SortType.ASCENDING;
+import static javafx.scene.control.TableColumnBase.SortType.DESCENDING;
 
 /**
  * A package protected util class used by TableView and TreeTableView to reduce
@@ -91,9 +94,49 @@
         }
     }
     
+    static void handleSortFailure(ObservableList<? extends TableColumnBase> sortOrder, 
+            SortEventType sortEventType, final Object... supportInfo) {
+        // if the sort event is consumed we need to back out the previous
+        // action so that the UI is not in an incorrect state
+        if (sortEventType == SortEventType.COLUMN_SORT_TYPE_CHANGE) {
+            // go back to the previous sort type
+            final TableColumnBase changedColumn = (TableColumnBase) supportInfo[0];
+            final TableColumnBase.SortType sortType = changedColumn.getSortType();
+            if (sortType == ASCENDING) {
+                changedColumn.setSortType(null);
+            } else if (sortType == DESCENDING) {
+                changedColumn.setSortType(ASCENDING);
+            } else if (sortType == null) {
+                changedColumn.setSortType(DESCENDING);
+            }
+        } else if (sortEventType == SortEventType.SORT_ORDER_CHANGE) {
+            // Revert the sortOrder list to what it was previously
+            ListChangeListener.Change change = (ListChangeListener.Change) supportInfo[0];
+            
+            final List toRemove = new ArrayList();
+            final List toAdd = new ArrayList();
+            while (change.next()) {
+                if (change.wasAdded()) {
+                    toRemove.addAll(change.getAddedSubList());
+                }
+
+                if (change.wasRemoved()) {
+                    toAdd.addAll(change.getRemoved());
+                }
+            }
+            
+            sortOrder.removeAll(toRemove);
+            sortOrder.addAll(toAdd);
+        } else if (sortEventType == SortEventType.COLUMN_SORTABLE_CHANGE) {
+            // no-op - it is ok for the sortable type to remain as-is
+        }
+    }
     
-    
-    
+    static enum SortEventType {
+         SORT_ORDER_CHANGE,
+         COLUMN_SORT_TYPE_CHANGE,
+         COLUMN_SORTABLE_CHANGE
+     }
     
     
     
--- a/javafx-ui-controls/src/javafx/scene/control/TableView.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TableView.java	Tue Mar 12 20:00:53 2013 -0400
@@ -29,9 +29,7 @@
 import com.sun.javafx.collections.NonIterableChange;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
@@ -46,8 +44,6 @@
 import javafx.collections.ListChangeListener;
 import javafx.collections.MapChangeListener;
 import javafx.collections.ObservableList;
-import com.sun.javafx.collections.transformation.SortableList;
-import com.sun.javafx.collections.transformation.TransformationList;
 
 import javafx.scene.Node;
 import javafx.scene.layout.GridPane;
@@ -57,22 +53,23 @@
 import javafx.css.PseudoClass;
 import com.sun.javafx.scene.control.ReadOnlyUnbackedObservableList;
 import com.sun.javafx.scene.control.TableColumnComparator;
+import com.sun.javafx.scene.control.skin.TableColumnHeader;
 import javafx.collections.WeakListChangeListener;
-import javafx.event.Event;
 import javafx.event.EventHandler;
 import javafx.event.EventType;
 
 import com.sun.javafx.scene.control.skin.TableViewSkin;
 import com.sun.javafx.scene.control.skin.TableViewSkinBase;
-import com.sun.javafx.scene.control.skin.VirtualContainerBase;
 import java.lang.ref.WeakReference;
+import java.util.Comparator;
 import java.util.HashMap;
 import javafx.beans.DefaultProperty;
 import javafx.beans.WeakInvalidationListener;
 import javafx.beans.property.ReadOnlyObjectProperty;
 import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.value.WeakChangeListener;
-import javafx.scene.control.cell.TreeItemPropertyValueFactory;
+import static javafx.scene.control.TableColumnBase.SortType.ASCENDING;
+import static javafx.scene.control.TableColumnBase.SortType.DESCENDING;
 
 /**
  * The TableView control is designed to visualize an unlimited number of rows
@@ -319,6 +316,34 @@
         }
     };
     
+    /**
+     * The default {@link #sortPolicyProperty() sort policy} that this TableView
+     * will use if no other policy is specified. The sort policy is a simple 
+     * {@link Callback} that accepts a TableView as the sole argument and expects
+     * a Boolean response representing whether the sort succeeded or not. A Boolean
+     * response of true represents success, and a response of false (or null) will
+     * be considered to represent failure.
+     */
+    public static final Callback<TableView, Boolean> DEFAULT_SORT_POLICY = new Callback<TableView, Boolean>() {
+        @Override public Boolean call(TableView table) {
+            try {
+                FXCollections.sort(table.getItems(), table.getComparator());
+                return true;
+            } catch (UnsupportedOperationException e) {
+                // TODO might need to support other exception types including:
+                // ClassCastException - if the class of the specified element prevents it from being added to this list
+                // NullPointerException - if the specified element is null and this list does not permit null elements
+                // IllegalArgumentException - if some property of this element prevents it from being added to this list
+
+                // If we are here the list does not support sorting, so we gracefully 
+                // fail the sort request and ensure the UI is put back to its previous
+                // state. This is handled in the code that calls the sort policy.
+                
+                return false;
+            }
+        }
+    };
+    
     
     
     /***************************************************************************
@@ -395,7 +420,7 @@
         // the sort method.
         getSortOrder().addListener(new ListChangeListener<TableColumn<S,?>>() {
             @Override public void onChanged(Change<? extends TableColumn<S,?>> c) {
-                sort();
+                doSort(TableUtil.SortEventType.SORT_ORDER_CHANGE, c);
             }
         });
 
@@ -490,7 +515,7 @@
         @Override public void invalidated(Observable valueModel) {
             TableColumn col = (TableColumn) ((BooleanProperty)valueModel).getBean();
             if (! getSortOrder().contains(col)) return;
-            sort();
+            doSort(TableUtil.SortEventType.COLUMN_SORTABLE_CHANGE, col);
         }
     };
 
@@ -498,7 +523,7 @@
         @Override public void invalidated(Observable valueModel) {
             TableColumn col = (TableColumn) ((ObjectProperty)valueModel).getBean();
             if (! getSortOrder().contains(col)) return;
-            sort();
+            doSort(TableUtil.SortEventType.COLUMN_SORT_TYPE_CHANGE, col);
         }
     };
     
@@ -817,6 +842,108 @@
         return editingCell;
     }
 
+    
+    // --- Comparator (built via sortOrder list, so read-only)
+    /**
+     * The comparator property is a read-only property that is representative of the
+     * current state of the {@link #getSortOrder() sort order} list. The sort
+     * order list contains the columns that have been added to it either programmatically
+     * or via a user clicking on the headers themselves.
+     */
+    private ReadOnlyObjectWrapper<Comparator<S>> comparator;
+    private void setComparator(Comparator<S> value) {
+        comparatorPropertyImpl().set(value);
+    }
+    public final Comparator<S> getComparator() {
+        return comparator == null ? null : comparator.get();
+    }
+    public final ReadOnlyObjectProperty<Comparator<S>> comparatorProperty() {
+        return comparatorPropertyImpl().getReadOnlyProperty();
+    }
+    private ReadOnlyObjectWrapper<Comparator<S>> comparatorPropertyImpl() {
+        if (comparator == null) {
+            comparator = new ReadOnlyObjectWrapper<Comparator<S>>(this, "comparator");
+        }
+        return comparator;
+    }
+    
+    
+    // --- sortPolicy
+    /**
+     * The sort policy specifies how sorting in this TableView should be performed.
+     * For example, a basic sort policy may just call 
+     * {@code FXCollections.sort(tableView.getItems())}, whereas a more advanced
+     * sort policy may call to a database to perform the necessary sorting on the
+     * server-side.
+     * 
+     * <p>TableView ships with a {@link TableView#DEFAULT_SORT_POLICY default
+     * sort policy} that does precisely as mentioned above: it simply attempts
+     * to sort the items list in-place.
+     * 
+     * <p>It is recommended that rather than override the {@link TableView#sort() sort}
+     * method that a different sort policy be provided instead.
+     */
+    private ObjectProperty<Callback<TableView<S>, Boolean>> sortPolicy;
+    public final void setSortPolicy(Callback<TableView<S>, Boolean> callback) {
+        sortPolicyProperty().set(callback);
+    }
+    @SuppressWarnings("unchecked") 
+    public final Callback<TableView<S>, Boolean> getSortPolicy() {
+        return sortPolicy == null ? 
+                (Callback<TableView<S>, Boolean>)(Object) DEFAULT_SORT_POLICY : 
+                sortPolicy.get();
+    }
+    @SuppressWarnings("unchecked")
+    public final ObjectProperty<Callback<TableView<S>, Boolean>> sortPolicyProperty() {
+        if (sortPolicy == null) {
+            sortPolicy = new SimpleObjectProperty<Callback<TableView<S>, Boolean>>(
+                    this, "sortPolicy", (Callback<TableView<S>, Boolean>)(Object) DEFAULT_SORT_POLICY) {
+                @Override protected void invalidated() {
+                    sort();
+                }
+            };
+        }
+        return sortPolicy;
+    }
+    
+    
+    // onSort
+    /**
+     * Called when there's a request to sort the control.
+     */
+    private ObjectProperty<EventHandler<SortEvent<TableView<S>>>> onSort;
+    
+    public void setOnSort(EventHandler<SortEvent<TableView<S>>> value) {
+        onSortProperty().set(value);
+    }
+    
+    public EventHandler<SortEvent<TableView<S>>> getOnSort() {
+        if( onSort != null ) {
+            return onSort.get();
+        }
+        return null;
+    }
+    
+    public ObjectProperty<EventHandler<SortEvent<TableView<S>>>> onSortProperty() {
+        if( onSort == null ) {
+            onSort = new ObjectPropertyBase<EventHandler<SortEvent<TableView<S>>>>() {
+                @Override protected void invalidated() {
+                    EventType<SortEvent<TableView<S>>> eventType = SortEvent.sortEvent();
+                    EventHandler<SortEvent<TableView<S>>> eventHandler = get();
+                    setEventHandler(eventType, eventHandler);
+                }
+                
+                @Override public Object getBean() {
+                    return TableView.this;
+                }
+
+                @Override public String getName() {
+                    return "onSort";
+                }
+            };
+        }
+        return onSort;
+    }
 
     
     /***************************************************************************
@@ -1030,12 +1157,82 @@
     @Override protected Skin<?> createDefaultSkin() {
         return new TableViewSkin(this);
     }
+    
+    /**
+     * The sort method forces the TableView to re-run its sorting algorithm. More 
+     * often than not it is not necessary to call this method directly, as it is
+     * automatically called when the {@link #getSortOrder() sort order}, 
+     * {@link #sortPolicyProperty() sort policy}, or the state of the 
+     * TableColumn {@link TableColumn#sortTypeProperty() sort type} properties 
+     * change. In other words, this method should only be called directly when
+     * something external changes and a sort is required.
+     */
+    public void sort() {
+        final ObservableList<? extends TableColumnBase> sortOrder = getSortOrder();
+        
+        // update the Comparator property
+        final Comparator<S> oldComparator = getComparator();
+        Comparator<S> newComparator = new TableColumnComparator(sortOrder);
+        setComparator(newComparator);
+        
+//        if (sortOrder.isEmpty()) {
+//            // TODO this should eventually handle returning a SortedList back
+//            // to its unsorted state
+//            setComparator(null);
+//        }
+        
+        // fire the onSort event and check if it is consumed, if
+        // so, don't run the sort
+        SortEvent<TableView<S>> sortEvent = new SortEvent<TableView<S>>(TableView.this, TableView.this);
+        fireEvent(sortEvent);
+        if (sortEvent.isConsumed()) {
+            // if the sort is consumed we could back out the last action (the code
+            // is commented out right below), but we don't as we take it as a 
+            // sign that the developer has decided to handle the event themselves.
+            
+            // sortLock = true;
+            // TableUtil.handleSortFailure(sortOrder, lastSortEventType, lastSortEventSupportInfo);
+            // sortLock = false;
+            return;
+        }
+
+        // get the sort policy and run it
+        Callback<TableView<S>, Boolean> sortPolicy = getSortPolicy();
+        if (sortPolicy == null) return;
+        Boolean success = sortPolicy.call(this);
+        
+        if (success == null || ! success) {
+            // the sort was a failure. Need to backout if possible
+            sortLock = true;
+            TableUtil.handleSortFailure(sortOrder, lastSortEventType, lastSortEventSupportInfo);
+            setComparator(oldComparator);
+            sortLock = false;
+        }
+    }
+    
+    
 
     /***************************************************************************
      *                                                                         *
      * Private Implementation                                                  *
      *                                                                         *
      **************************************************************************/
+    
+    private boolean sortLock = false;
+    private TableUtil.SortEventType lastSortEventType = null;
+    private Object[] lastSortEventSupportInfo = null;
+    
+    private void doSort(final TableUtil.SortEventType sortEventType, final Object... supportInfo) {
+        if (sortLock) {
+            return;
+        }
+        
+        this.lastSortEventType = sortEventType;
+        this.lastSortEventSupportInfo = supportInfo;
+        sort();
+        this.lastSortEventType = null;
+        this.lastSortEventSupportInfo = null;
+    }
 
     /**
      * Call this function to force the TableView to re-evaluate itself. This is
@@ -1049,56 +1246,6 @@
         getProperties().put(TableViewSkinBase.REFRESH, Boolean.TRUE);
     }
 
-    /**
-     * Sometimes we want to force a sort to run - this is the recommended way
-     * of doing it internally. External users of the TableView API should just
-     * stick to modifying the TableView.sortOrder ObservableList (or the contents
-     * of the TableColumns within it - in particular the
-     * TableColumn.sortAscending boolean).
-     */
-    private void sort() {
-        // build up a new comparator based on the current table columms
-        TableColumnComparator comparator = new TableColumnComparator();
-        for (TableColumn<S,?> tc : getSortOrder()) {
-            comparator.getColumns().add(tc);
-        }
-
-        // If the items are a TransformationList, but not a SortableList, then we need
-        // to get the source of the TransformationList and check it.
-        if (getItems() instanceof TransformationList) {
-            // FIXME this is temporary code whilst I await for similar functionality
-            // within FXCollections.sort, such that it does the unwrapping that is
-            // shown below
-            List list = getItems();
-            while (list != null) {
-                if (list instanceof SortableList) {
-                    break;
-                } else if (list instanceof TransformationList) {
-                    list = ((TransformationList)list).getDirectSource();
-                } else {
-                    break;
-                }
-            }
-
-            if (list instanceof SortableList) {
-                SortableList sortableList = (SortableList) list;
-                // TODO review - note that we're changing the comparator based on
-                // what columns the user has set.
-                sortableList.setComparator(comparator);
-                
-                if (sortableList.getMode() == SortableList.SortMode.BATCH) {
-                    sortableList.sort();
-                }
-                
-                return;
-            }
-        }
-
-        // If we are here, we will use the default sort functionality available
-        // in FXCollections
-        FXCollections.sort(getItems(), comparator);
-    }
-
 
     // --- Content width
     private void setContentWidth(double contentWidth) {
@@ -1265,6 +1412,8 @@
      */
     // package for testing
     static class TableViewArrayListSelectionModel<S> extends TableViewSelectionModel<S> {
+        
+        private int itemCount = 0;
 
         /***********************************************************************
          *                                                                     *
@@ -1276,6 +1425,8 @@
             super(tableView);
             this.tableView = tableView;
             
+            updateItemCount();
+            
             cellSelectionEnabledProperty().addListener(new InvalidationListener() {
                 @Override public void invalidated(Observable o) {
                     isCellSelectionEnabled();
@@ -1384,14 +1535,34 @@
 
         final ListChangeListener<S> itemsContentListener = new ListChangeListener<S>() {
             @Override public void onChanged(Change<? extends S> c) {
-                if (tableView.getItems() == null || tableView.getItems().isEmpty()) {
-                    clearSelection();
-                } else if (getSelectedIndex() == -1 && getSelectedItem() != null) {
-                    int newIndex = tableView.getItems().indexOf(getSelectedItem());
-                    if (newIndex != -1) {
-                        setSelectedIndex(newIndex);
+                updateItemCount();
+                
+                while (c.next()) {
+                    final S selectedItem = getSelectedItem();
+                    final int selectedIndex = getSelectedIndex();
+                    
+                    if (tableView.getItems() == null || tableView.getItems().isEmpty()) {
+                        clearSelection();
+                    } else if (getSelectedIndex() == -1 && getSelectedItem() != null) {
+                        int newIndex = tableView.getItems().indexOf(getSelectedItem());
+                        if (newIndex != -1) {
+                            setSelectedIndex(newIndex);
+                        }
+                    } else if (c.wasRemoved() && 
+                            c.getRemovedSize() == 1 && 
+                            ! c.wasAdded() && 
+                            selectedItem != null && 
+                            selectedItem.equals(c.getRemoved().get(0))) {
+                        // Bug fix for RT-28637
+                        if (getSelectedIndex() < getItemCount()) {
+                            S newSelectedItem = getModelItem(selectedIndex);
+                            if (! selectedItem.equals(newSelectedItem)) {
+                                setSelectedItem(newSelectedItem);
+                            }
+                        }
                     }
                 }
+                
                 updateSelection(c);
             }
         };
@@ -1408,6 +1579,8 @@
             if (newList != null) {
                 newList.addListener(weakItemsContentListener);
             }
+            
+            updateItemCount();
 
             // when the items list totally changes, we should clear out
             // the selection
@@ -1455,6 +1628,7 @@
         // changes we can update the selected indices list to refer to the 
         // new indices.
         private void updateSelection(ListChangeListener.Change<? extends S> c) {
+            c.reset();
             while (c.next()) {
                 if (c.wasReplaced()) {
                     if (c.getList().isEmpty()) {
@@ -1466,28 +1640,40 @@
                         if (previousModelSize == c.getRemovedSize()) {
                             // all items were removed from the model
                             clearSelection();
-                        } else if (index < getRowCount() && index >= 0) {
+                        } else if (index < getItemCount() && index >= 0) {
                             // Fix for RT-18969: the list had setAll called on it
+                            // Use of makeAtomic is a fix for RT-20945
+                            makeAtomic = true;
                             clearSelection(index);
+                            makeAtomic = false;
                             select(index);
                         } else {
                             // Fix for RT-22079
                             clearSelection();
                         }
                     }
-                    
-                    
                 } else if (c.wasAdded() || c.wasRemoved()) {
                     int position = c.getFrom();
                     int shift = c.wasAdded() ? c.getAddedSize() : -c.getRemovedSize();
                     
                     if (position < 0) return;
+                    if (shift == 0) return;
                     
                     List<TablePosition> newIndices = new ArrayList<TablePosition>(selectedCells.size());
         
                     for (int i = 0; i < selectedCells.size(); i++) {
-                        TablePosition old = selectedCells.get(i);
-                        int newRow = old.getRow() < position ? old.getRow() : old.getRow() + shift;
+                        final TablePosition old = selectedCells.get(i);
+                        final int oldRow = old.getRow();
+                        final int newRow = oldRow < position ? oldRow : oldRow + shift;
+                        
+                        // Special case for RT-28637 (See unit test in TableViewTest).
+                        // Essentially the selectedItem was correct, but selectedItems
+                        // was empty.
+                        if (oldRow == 0 && shift == -1) {
+                            newIndices.add(new TablePosition(getTableView(), 0, old.getTableColumn()));
+                            continue;
+                        }
+                        
                         if (newRow < 0) continue;
                         newIndices.add(new TablePosition(getTableView(), newRow, old.getTableColumn()));
                     }
@@ -1545,7 +1731,7 @@
                 }
             }
             
-            previousModelSize = getRowCount();
+            previousModelSize = getItemCount();
         }
 
         /***********************************************************************
@@ -1569,7 +1755,7 @@
 
         @Override
         public void select(int row, TableColumn<S,?> column) {
-            if (row < 0 || row >= getRowCount()) return;
+            if (row < 0 || row >= getItemCount()) return;
 
             // if I'm in cell selection mode but the column is null, I don't want
             // to select the whole row instead...
@@ -1598,7 +1784,7 @@
             // first occurrence of the given object. Once we find the first one, we
             // don't proceed to select any others.
             S rowObj = null;
-            for (int i = 0; i < getRowCount(); i++) {
+            for (int i = 0; i < getItemCount(); i++) {
                 rowObj = getModelItem(i);
                 if (rowObj == null) continue;
 
@@ -1634,7 +1820,7 @@
              * Performance optimisation - if multiple selection is disabled, only
              * process the end-most row index.
              */
-            int rowCount = getRowCount();
+            int rowCount = getItemCount();
 
             if (getSelectionMode() == SelectionMode.SINGLE) {
                 quietClearSelection();
@@ -1690,7 +1876,7 @@
                 TablePosition tp = null;
                 for (int col = 0; col < getTableView().getVisibleLeafColumns().size(); col++) {
                     column = getTableView().getVisibleLeafColumns().get(col);
-                    for (int row = 0; row < getRowCount(); row++) {
+                    for (int row = 0; row < getItemCount(); row++) {
                         tp = new TablePosition(getTableView(), row, column);
                         indices.add(tp);
                     }
@@ -1703,11 +1889,11 @@
                 }
             } else {
                 List<TablePosition> indices = new ArrayList<TablePosition>();
-                for (int i = 0; i < getRowCount(); i++) {
+                for (int i = 0; i < getItemCount(); i++) {
                     indices.add(new TablePosition(getTableView(), i, null));
                 }
                 selectedCells.setAll(indices);
-                select(getRowCount() - 1);
+                select(getItemCount() - 1);
                 focus(indices.get(indices.size() - 1));
             }
         }
@@ -1779,14 +1965,14 @@
                 if (pos.getColumn() - 1 >= 0) {
                     // go to previous row
                     select(pos.getRow(), getTableColumn(pos.getTableColumn(), -1));
-                } else if (pos.getRow() < getRowCount() - 1) {
+                } else if (pos.getRow() < getItemCount() - 1) {
                     // wrap to end of previous row
                     select(pos.getRow() - 1, getTableColumn(getTableView().getVisibleLeafColumns().size() - 1));
                 }
             } else {
                 int focusIndex = getFocusedIndex();
                 if (focusIndex == -1) {
-                    select(getRowCount() - 1);
+                    select(getItemCount() - 1);
                 } else if (focusIndex > 0) {
                     select(focusIndex - 1);
                 }
@@ -1801,7 +1987,7 @@
                 if (pos.getColumn() + 1 < getTableView().getVisibleLeafColumns().size()) {
                     // go to next column
                     select(pos.getRow(), getTableColumn(pos.getTableColumn(), 1));
-                } else if (pos.getRow() < getRowCount() - 1) {
+                } else if (pos.getRow() < getItemCount() - 1) {
                     // wrap to start of next row
                     select(pos.getRow() + 1, getTableColumn(0));
                 }
@@ -1809,7 +1995,7 @@
                 int focusIndex = getFocusedIndex();
                 if (focusIndex == -1) {
                     select(0);
-                } else if (focusIndex < getRowCount() -1) {
+                } else if (focusIndex < getItemCount() -1) {
                     select(focusIndex + 1);
                 }
             }
@@ -1818,7 +2004,7 @@
         @Override public void selectAboveCell() {
             TablePosition pos = getFocusedCell();
             if (pos.getRow() == -1) {
-                select(getRowCount() - 1);
+                select(getItemCount() - 1);
             } else if (pos.getRow() > 0) {
                 select(pos.getRow() - 1, pos.getTableColumn());
             }
@@ -1829,7 +2015,7 @@
 
             if (pos.getRow() == -1) {
                 select(0);
-            } else if (pos.getRow() < getRowCount() -1) {
+            } else if (pos.getRow() < getItemCount() -1) {
                 select(pos.getRow() + 1, pos.getTableColumn());
             }
         }
@@ -1841,7 +2027,7 @@
                 quietClearSelection();
             }
 
-            if (getRowCount() > 0) {
+            if (getItemCount() > 0) {
                 if (isCellSelectionEnabled()) {
                     select(0, focusedCell.getTableColumn());
                 } else {
@@ -1857,7 +2043,7 @@
                 quietClearSelection();
             }
 
-            int numItems = getRowCount();
+            int numItems = getItemCount();
             if (numItems > 0 && getSelectedIndex() < numItems - 1) {
                 if (isCellSelectionEnabled()) {
                     select(numItems - 1, focusedCell.getTableColumn());
@@ -1940,22 +2126,26 @@
             return getTableView().getFocusModel().getFocusedCell();
         }
 
-        private int getRowCount() {
-            List items = tableView.getItems();
-            return items == null ? -1 : items.size();
-        }
-        
         /** {@inheritDoc} */
         @Override protected int getItemCount() {
-            List items = tableView.getItems();
-            if (items == null) return -1;
-            return items.size();
+            return itemCount;
+//            List<S> items = tableView.getItems();
+//            return items == null ? -1 : items.size();
         }
 
         @Override protected S getModelItem(int index) {
-            if (index < 0 || index > getRowCount()) return null;
+            if (index < 0 || index > getItemCount()) return null;
             return tableView.getItems().get(index);
         }
+        
+        private void updateItemCount() {
+            if (tableView == null) {
+                itemCount = -1;
+            } else {
+                List<S> items = tableView.getItems();
+                itemCount = items == null ? -1 : items.size();
+            }
+        } 
     }
     
     
--- a/javafx-ui-controls/src/javafx/scene/control/TextInputControl.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TextInputControl.java	Tue Mar 12 20:00:53 2013 -0400
@@ -187,9 +187,10 @@
                     // RT-20727 - if font is changed by calling setFont, then
                     // css might need to be reapplied since font size affects
                     // calculated values for styles with relative values
-                    StyleOrigin origin = ((StyleableProperty)font).getStyleOrigin();
-                    if (origin == null || origin == StyleOrigin.USER) {
+                    if(fontSetByCss == false) {
                         TextInputControl.this.impl_reapplyCSS();
+                    } else {
+                        fontSetByCss = false;
                     }
                 }
 
@@ -212,6 +213,7 @@
         return font;
     }
 
+    private boolean fontSetByCss = false;
     private ObjectProperty<Font> font;
     public final void setFont(Font value) { fontProperty().setValue(value); }
     public final Font getFont() { return font == null ? Font.getDefault() : font.getValue(); }
@@ -1117,6 +1119,15 @@
             new FontCssMetaData<TextInputControl>("-fx-font", Font.getDefault()) {
 
             @Override
+            public void set(TextInputControl styleable, Font value, StyleOrigin origin) {
+                // RT-20727 - if font is changed by calling setFont, then
+                // css might need to be reapplied since font size affects
+                // calculated values for styles with relative values
+                styleable.fontSetByCss = origin != StyleOrigin.USER;
+                super.set(styleable, value, origin);                
+            }
+
+            @Override
             public boolean isSettable(TextInputControl n) {
                 return n.font == null || !n.font.isBound();
             }
--- a/javafx-ui-controls/src/javafx/scene/control/Tooltip.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/Tooltip.java	Tue Mar 12 20:00:53 2013 -0400
@@ -26,6 +26,7 @@
 package javafx.scene.control;
 
 
+import com.sun.javafx.css.StyleManager;
 import javafx.css.StyleableBooleanProperty;
 import javafx.css.StyleableDoubleProperty;
 import javafx.css.StyleableObjectProperty;
@@ -596,7 +597,8 @@
                                 url = cl.getResource(get());
                             }
                             if (url != null) {
-                                setGraphic(new ImageView(new Image(url.toExternalForm())));                            
+                                final Image img = StyleManager.getInstance().getCachedImage(url.toExternalForm());
+                                setGraphic(new ImageView(img));                            
                             }
                         } else {
                             setGraphic(null);
--- a/javafx-ui-controls/src/javafx/scene/control/TreeItem.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeItem.java	Tue Mar 12 20:00:53 2013 -0400
@@ -799,6 +799,9 @@
     private void sort(final ObservableList<TreeItem<T>> children, 
                          final Comparator<TreeItem<T>> comparator, 
                          final TreeSortMode sortMode) {
+        
+        if (comparator == null) return;
+        
         runSort(children, comparator, sortMode);
         
         // if we're at the root node, we'll fire an event so that the control
@@ -839,7 +842,6 @@
 //            
         } else {
             // Unknown sort mode
-            System.out.println("Unknown sort mode in TreeItem.runSort()");
         }
     }
     
--- a/javafx-ui-controls/src/javafx/scene/control/TreeTableView.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeTableView.java	Tue Mar 12 20:00:53 2013 -0400
@@ -36,6 +36,7 @@
 import com.sun.javafx.scene.control.skin.VirtualContainerBase;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
 import javafx.application.Platform;
@@ -322,7 +323,7 @@
         // the sort method.
         getSortOrder().addListener(new ListChangeListener<TreeTableColumn<S,?>>() {
             @Override public void onChanged(ListChangeListener.Change<? extends TreeTableColumn<S,?>> c) {
-                sort();
+                doSort(TableUtil.SortEventType.SORT_ORDER_CHANGE, c);
             }
         });
 
@@ -480,6 +481,42 @@
         }
     };
     
+    /**
+     * The default {@link #sortPolicyProperty() sort policy} that this TreeTableView
+     * will use if no other policy is specified. The sort policy is a simple 
+     * {@link Callback} that accepts a TreeTableView as the sole argument and expects
+     * a Boolean response representing whether the sort succeeded or not. A Boolean
+     * response of true represents success, and a response of false (or null) will
+     * be considered to represent failure.
+     */
+    public static final Callback<TreeTableView, Boolean> DEFAULT_SORT_POLICY = new Callback<TreeTableView, Boolean>() {
+        @Override public Boolean call(TreeTableView table) {
+            try {
+                TreeItem rootItem = table.getRoot();
+                if (rootItem == null) return false;
+
+                TreeSortMode sortMode = table.getSortMode();
+                if (sortMode == null) return false;
+
+                rootItem.lastSortMode = sortMode;
+                rootItem.lastComparator = table.getComparator();
+                rootItem.sort();
+                return true;
+            } catch (UnsupportedOperationException e) {
+                // TODO might need to support other exception types including:
+                // ClassCastException - if the class of the specified element prevents it from being added to this list
+                // NullPointerException - if the specified element is null and this list does not permit null elements
+                // IllegalArgumentException - if some property of this element prevents it from being added to this list
+
+                // If we are here the list does not support sorting, so we gracefully 
+                // fail the sort request and ensure the UI is put back to its previous
+                // state. This is handled in the code that calls the sort policy.
+                
+                return false;
+            }
+        }
+    };
+    
     
     
     /***************************************************************************
@@ -581,7 +618,7 @@
         @Override public void invalidated(Observable valueModel) {
             TreeTableColumn col = (TreeTableColumn) ((BooleanProperty)valueModel).getBean();
             if (! getSortOrder().contains(col)) return;
-            sort();
+            doSort(TableUtil.SortEventType.COLUMN_SORTABLE_CHANGE, col);
         }
     };
 
@@ -589,7 +626,7 @@
         @Override public void invalidated(Observable valueModel) {
             TreeTableColumn col = (TreeTableColumn) ((ObjectProperty)valueModel).getBean();
             if (! getSortOrder().contains(col)) return;
-            sort();
+            doSort(TableUtil.SortEventType.COLUMN_SORT_TYPE_CHANGE, col);
         }
     };
     
@@ -1206,6 +1243,108 @@
     }
     
     
+    // --- Comparator (built via sortOrder list, so read-only)
+    /**
+     * The comparator property is a read-only property that is representative of the
+     * current state of the {@link #getSortOrder() sort order} list. The sort
+     * order list contains the columns that have been added to it either programmatically
+     * or via a user clicking on the headers themselves.
+     */
+    private ReadOnlyObjectWrapper<Comparator<S>> comparator;
+    private void setComparator(Comparator<S> value) {
+        comparatorPropertyImpl().set(value);
+    }
+    public final Comparator<S> getComparator() {
+        return comparator == null ? null : comparator.get();
+    }
+    public final ReadOnlyObjectProperty<Comparator<S>> comparatorProperty() {
+        return comparatorPropertyImpl().getReadOnlyProperty();
+    }
+    private ReadOnlyObjectWrapper<Comparator<S>> comparatorPropertyImpl() {
+        if (comparator == null) {
+            comparator = new ReadOnlyObjectWrapper<Comparator<S>>(this, "comparator");
+        }
+        return comparator;
+    }
+    
+    
+    // --- sortPolicy
+    /**
+     * The sort policy specifies how sorting in this TreeTableView should be performed.
+     * For example, a basic sort policy may just recursively sort the children of 
+     * the root tree item, whereas a more advanced sort policy may call to a 
+     * database to perform the necessary sorting on the server-side.
+     * 
+     * <p>TreeTableView ships with a {@link TableView#DEFAULT_SORT_POLICY default
+     * sort policy} that does precisely as mentioned above: it simply attempts
+     * to sort the tree hierarchy in-place.
+     * 
+     * <p>It is recommended that rather than override the {@link TreeTableView#sort() sort}
+     * method that a different sort policy be provided instead.
+     */
+    private ObjectProperty<Callback<TreeTableView<S>, Boolean>> sortPolicy;
+    public final void setSortPolicy(Callback<TreeTableView<S>, Boolean> callback) {
+        sortPolicyProperty().set(callback);
+    }
+    @SuppressWarnings("unchecked") 
+    public final Callback<TreeTableView<S>, Boolean> getSortPolicy() {
+        return sortPolicy == null ? 
+                (Callback<TreeTableView<S>, Boolean>)(Object) DEFAULT_SORT_POLICY : 
+                sortPolicy.get();
+    }
+    @SuppressWarnings("unchecked")
+    public final ObjectProperty<Callback<TreeTableView<S>, Boolean>> sortPolicyProperty() {
+        if (sortPolicy == null) {
+            sortPolicy = new SimpleObjectProperty<Callback<TreeTableView<S>, Boolean>>(
+                    this, "sortPolicy", (Callback<TreeTableView<S>, Boolean>)(Object) DEFAULT_SORT_POLICY) {
+                @Override protected void invalidated() {
+                    sort();
+                }
+            };
+        }
+        return sortPolicy;
+    }
+    
+    
+    // onSort
+    /**
+     * Called when there's a request to sort the control.
+     */
+    private ObjectProperty<EventHandler<SortEvent<TreeTableView<S>>>> onSort;
+    
+    public void setOnSort(EventHandler<SortEvent<TreeTableView<S>>> value) {
+        onSortProperty().set(value);
+    }
+    
+    public EventHandler<SortEvent<TreeTableView<S>>> getOnSort() {
+        if( onSort != null ) {
+            return onSort.get();
+        }
+        return null;
+    }
+    
+    public ObjectProperty<EventHandler<SortEvent<TreeTableView<S>>>> onSortProperty() {
+        if( onSort == null ) {
+            onSort = new ObjectPropertyBase<EventHandler<SortEvent<TreeTableView<S>>>>() {
+                @Override protected void invalidated() {
+                    EventType<SortEvent<TreeTableView<S>>> eventType = SortEvent.sortEvent();
+                    EventHandler<SortEvent<TreeTableView<S>>> eventHandler = get();
+                    setEventHandler(eventType, eventHandler);
+                }
+                
+                @Override public Object getBean() {
+                    return TreeTableView.this;
+                }
+
+                @Override public String getName() {
+                    return "onSort";
+                }
+            };
+        }
+        return onSort;
+    }
+    
+    
     
     /***************************************************************************
      *                                                                         *
@@ -1455,6 +1594,56 @@
         return visibleLeafColumns.get(column);
     }
 
+    /**
+     * The sort method forces the TreeTableView to re-run its sorting algorithm. More 
+     * often than not it is not necessary to call this method directly, as it is
+     * automatically called when the {@link #getSortOrder() sort order}, 
+     * {@link #sortPolicyProperty() sort policy}, or the state of the 
+     * TableColumn {@link TableColumn#sortTypeProperty() sort type} properties 
+     * change. In other words, this method should only be called directly when
+     * something external changes and a sort is required.
+     */
+    public void sort() {
+        final ObservableList<? extends TableColumnBase> sortOrder = getSortOrder();
+        
+        // update the Comparator property
+        final Comparator<S> oldComparator = getComparator();
+        if (sortOrder.isEmpty()) {
+            setComparator(null);
+        } else {
+            Comparator<S> newComparator = new TableColumnComparator(sortOrder);
+            setComparator(newComparator);
+        }
+        
+        // fire the onSort event and check if it is consumed, if
+        // so, don't run the sort
+        SortEvent<TreeTableView<S>> sortEvent = new SortEvent<TreeTableView<S>>(TreeTableView.this, TreeTableView.this);
+        fireEvent(sortEvent);
+        if (sortEvent.isConsumed()) {
+            // if the sort is consumed we could back out the last action (the code
+            // is commented out right below), but we don't as we take it as a 
+            // sign that the developer has decided to handle the event themselves.
+            
+            // sortLock = true;
+            // TableUtil.handleSortFailure(sortOrder, lastSortEventType, lastSortEventSupportInfo);
+            // sortLock = false;
+            return;
+        }
+
+        // get the sort policy and run it
+        Callback<TreeTableView<S>, Boolean> sortPolicy = getSortPolicy();
+        if (sortPolicy == null) return;
+        Boolean success = sortPolicy.call(this);
+        
+        if (success == null || ! success) {
+            // the sort was a failure. Need to backout if possible
+            sortLock = true;
+            TableUtil.handleSortFailure(sortOrder, lastSortEventType, lastSortEventSupportInfo);
+            setComparator(oldComparator);
+            sortLock = false;
+        }
+    }
+    
     
     
     /***************************************************************************
@@ -1463,6 +1652,22 @@
      *                                                                         *
      **************************************************************************/
     
+    private boolean sortLock = false;
+    private TableUtil.SortEventType lastSortEventType = null;
+    private Object[] lastSortEventSupportInfo = null;
+    
+    private void doSort(final TableUtil.SortEventType sortEventType, final Object... supportInfo) {
+        if (sortLock) {
+            return;
+        }
+        
+        this.lastSortEventType = sortEventType;
+        this.lastSortEventSupportInfo = supportInfo;
+        sort();
+        this.lastSortEventType = null;
+        this.lastSortEventSupportInfo = null;
+    }
+    
     private void updateExpandedItemCount(TreeItem treeItem) {
         setExpandedItemCount(TreeUtil.updateExpandedItemCount(treeItem, expandedItemCountDirty, isShowRoot()));
         expandedItemCountDirty = false;
@@ -1488,31 +1693,6 @@
         getProperties().put(TableViewSkinBase.REFRESH, Boolean.TRUE);
     }
     
-    /**
-     * Sometimes we want to force a sort to run - this is the recommended way
-     * of doing it internally. External users of the TableView API should just
-     * stick to modifying the TableView.sortOrder ObservableList (or the contents
-     * of the TreeTableColumns within it - in particular the
-     * TreeTableColumn.sortAscending boolean).
-     */
-    private void sort() {
-        TreeItem rootItem = getRoot();
-        if (rootItem == null) return;
-        
-        TreeSortMode sortMode = getSortMode();
-        if (sortMode == null) return;
-        
-        // build up a new comparator based on the current table columms
-        TableColumnComparator comparator = new TableColumnComparator();
-        for (TreeTableColumn<S,?> tc : getSortOrder()) {
-            comparator.getColumns().add(tc);
-        }
-
-        rootItem.lastSortMode = sortMode;
-        rootItem.lastComparator = comparator;
-        rootItem.sort();
-    }
-    
     // --- Content width
     private void setContentWidth(double contentWidth) {
         this.contentWidth = contentWidth;
@@ -1888,14 +2068,30 @@
                     // whilst we are here, we should check if the removed items
                     // are part of the selectedItems list - and remove them
                     // from selection if they are (as per RT-15446)
-                    List<Integer> indices = getSelectedIndices();
-                    for (int i = 0; i < indices.size() && ! getSelectedItems().isEmpty(); i++) {
-                        int index = indices.get(i);
-                        if (index > getSelectedItems().size()) break;
+                    final List<Integer> selectedIndices = getSelectedIndices();
+                    final int selectedIndex = getSelectedIndex();
+                    final List<TreeItem<S>> selectedItems = getSelectedItems();
+                    final TreeItem<S> selectedItem = getSelectedItem();
+                    final List<? extends TreeItem<S>> removedChildren = e.getRemovedChildren();
+                    
+                    for (int i = 0; i < selectedIndices.size() && ! selectedItems.isEmpty(); i++) {
+                        int index = selectedIndices.get(i);
+                        if (index > selectedItems.size()) break;
                         
-                        TreeItem<S> item = getSelectedItems().get(index);
-                        if (item == null || e.getRemovedChildren().contains(item)) {
+                        TreeItem<S> item = selectedItems.get(index);
+                        if (item == null || removedChildren.contains(item)) {
                             clearSelection(index);
+                        } else if (removedChildren.size() == 1 && 
+                                selectedItems.size() == 1 && 
+                                selectedItem != null && 
+                                selectedItem.equals(removedChildren.get(0))) {
+                            // Bug fix for RT-28637
+                            if (selectedIndex < getItemCount()) {
+                                TreeItem<S> newSelectedItem = getModelItem(selectedIndex);
+                                if (! selectedItem.equals(newSelectedItem)) {
+                                    setSelectedItem(newSelectedItem);
+                                }
+                            }
                         }
                     }
                 }
--- a/javafx-ui-controls/src/javafx/scene/control/TreeView.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeView.java	Tue Mar 12 20:00:53 2013 -0400
@@ -989,14 +989,30 @@
                     // whilst we are here, we should check if the removed items
                     // are part of the selectedItems list - and remove them
                     // from selection if they are (as per RT-15446)
-                    List<Integer> indices = getSelectedIndices();
-                    for (int i = 0; i < indices.size() && ! getSelectedItems().isEmpty(); i++) {
-                        int index = indices.get(i);
-                        if (index > getSelectedItems().size()) break;
+                    final List<Integer> selectedIndices = getSelectedIndices();
+                    final int selectedIndex = getSelectedIndex();
+                    final List<TreeItem<T>> selectedItems = getSelectedItems();
+                    final TreeItem<T> selectedItem = getSelectedItem();
+                    final List<? extends TreeItem<T>> removedChildren = e.getRemovedChildren();
+                    
+                    for (int i = 0; i < selectedIndices.size() && ! selectedItems.isEmpty(); i++) {
+                        int index = selectedIndices.get(i);
+                        if (index > selectedItems.size()) break;
                         
-                        TreeItem<T> item = getSelectedItems().get(index);
-                        if (item == null || e.getRemovedChildren().contains(item)) {
+                        TreeItem<T> item = selectedItems.get(index);
+                        if (item == null || removedChildren.contains(item)) {
                             clearSelection(index);
+                        } else if (removedChildren.size() == 1 && 
+                                selectedItems.size() == 1 && 
+                                selectedItem != null && 
+                                selectedItem.equals(removedChildren.get(0))) {
+                            // Bug fix for RT-28637
+                            if (selectedIndex < getItemCount()) {
+                                TreeItem<T> newSelectedItem = getModelItem(selectedIndex);
+                                if (! selectedItem.equals(newSelectedItem)) {
+                                    setSelectedItem(newSelectedItem);
+                                }
+                            }
                         }
                     }
                 }
--- a/javafx-ui-controls/test/com/sun/javafx/scene/control/test/ControlAsserts.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/com/sun/javafx/scene/control/test/ControlAsserts.java	Tue Mar 12 20:00:53 2013 -0400
@@ -42,6 +42,14 @@
 
 public class ControlAsserts {
     
+    public static void assertListContainsItemsInOrder(final List items, final Object... expected) {
+        assertEquals(expected.length, items.size());
+        for (int i = 0; i < expected.length; i++) {
+            Object item = items.get(i);
+            assertEquals(expected[i], item);
+        }
+    }
+    
     public static void assertRowsEmpty(final Control control, final int startRow, final int endRow) {
         assertRows(control, startRow, endRow, true);
     }
@@ -51,12 +59,15 @@
     }
     
     public static void assertCellEmpty(IndexedCell cell) {
-        assertNull("Expected null, found '" + cell.getText() + "'", cell.getText());
+        final String text = cell.getText();
+//        System.out.println("assertCellEmpty: " + cell.getIndex() + " : " + text);
+        assertTrue("Expected null, found '" + text + "'", text == null || text.isEmpty());
     }
     
     public static void assertCellNotEmpty(IndexedCell cell) {
-        assertNotNull("Expected a non-null, found '" + cell.getText() + "'", cell.getText());
-        assertFalse(cell.getText().isEmpty());
+        final String text = cell.getText();
+//        System.out.println("assertCellNotEmpty: " + cell.getIndex() + " : " + text);
+        assertTrue("Expected a non-null, found '" + text + "'", text != null && ! text.isEmpty());
     }
     
     private static void assertRows(final Control control, final int startRow, final int endRow, final boolean expectEmpty) {
@@ -76,7 +87,7 @@
                         assertCellNotEmpty(childCell);
                     }
                 }
-
+                
                 if (! hasChildrenCell) {
                     if (expectEmpty) {
                         assertCellEmpty(indexedCell);
@@ -160,30 +171,42 @@
     }
     
     public static void assertCallback(final Control control, final int startRow, final int endRow, final Callback<IndexedCell<?>, Void> callback) {
-        Group group = new Group();
-        Scene scene = new Scene(group);
+        VirtualFlow<?> flow = getVirtualFlow(control);
         
-        Stage stage = new Stage();
-        stage.setScene(scene);
+//        Region clippedContainer = (Region) flow.getChildrenUnmodifiable().get(0);
+//        Group sheet = (Group) clippedContainer.getChildrenUnmodifiable().get(0);
         
-        group.getChildren().setAll(control);
-        stage.show();
-        
-        Toolkit.getToolkit().firePulse();
-        
-        VirtualFlow<?> flow = (VirtualFlow<?>)control.lookup("#virtual-flow");
-        
-        Region clippedContainer = (Region) flow.getChildrenUnmodifiable().get(0);
-        Group sheet = (Group) clippedContainer.getChildrenUnmodifiable().get(0);
-        
-        final int sheetSize = sheet.getChildren().size();
+//        final int sheetSize = sheet.getChildren().size();
+        final int sheetSize = flow.getCellCount();
         final int end = endRow == -1 ? sheetSize : Math.min(endRow, sheetSize);
         for (int row = startRow; row < end; row++) {
             // old approach:
             // callback.call((IndexedCell<?>)sheet.getChildren().get(row));
             
             // new approach:
-            callback.call(flow.getCell(row));
+            IndexedCell cell = flow.getCell(row);
+//            System.out.println("cell index: " + cell.getIndex());
+            callback.call(cell);
         }
     }
+    
+    public static void assertCellCount(final Control control, final int expected) {
+        assertEquals(getVirtualFlow(control).getCellCount(), expected);
+    }
+    
+    private static VirtualFlow<?> getVirtualFlow(final Control control) {
+        Group group = new Group();
+        Scene scene = new Scene(group);
+
+        Stage stage = new Stage();
+        stage.setScene(scene);
+
+        group.getChildren().setAll(control);
+        stage.show();
+
+        Toolkit.getToolkit().firePulse();
+
+        VirtualFlow<?> flow = (VirtualFlow<?>)control.lookup("#virtual-flow");
+        return flow;
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewKeyInputTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -27,7 +27,6 @@
 
 import com.sun.javafx.Utils;
 import com.sun.javafx.scene.control.behavior.ListViewAnchorRetriever;
-import com.sun.javafx.tk.Toolkit;
 import static org.junit.Assert.*;
 
 import java.util.List;
@@ -42,7 +41,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Disabling tests as they fail with OOM in continuous builds")
+//@Ignore("Disabling tests as they fail with OOM in continuous builds")
 public class ListViewKeyInputTest {
     private ListView<String> listView;
     private MultipleSelectionModel<String> sm;
--- a/javafx-ui-controls/test/javafx/scene/control/ListViewTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/ListViewTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -30,7 +30,6 @@
 import com.sun.javafx.scene.control.test.ControlAsserts;
 import com.sun.javafx.scene.control.test.Person;
 import com.sun.javafx.scene.control.test.RT_22463_Person;
-import com.sun.javafx.tk.Toolkit;
 import static javafx.scene.control.ControlTestUtils.assertStyleClassContains;
 import static org.junit.Assert.*;
 import java.util.Arrays;
@@ -41,10 +40,6 @@
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
-import javafx.scene.Group;
-import javafx.scene.Scene;
-import javafx.scene.layout.Region;
-import javafx.stage.Stage;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -364,16 +359,16 @@
         assertEquals(2, sm.getSelectedIndices().size());
     }
     
-    private int hitCount = 0;
+    private int rt_18969_hitCount = 0;
     @Test public void test_rt18969() {
-        hitCount = 0;
+        rt_18969_hitCount = 0;
         ObservableList<String> emptyModel = FXCollections.observableArrayList();
         listView.setItems(emptyModel);
         assertTrue(listView.getItems().isEmpty());
         
         sm.selectedItemProperty().addListener(new ChangeListener<String>() {
             @Override public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
-                hitCount++;
+                rt_18969_hitCount++;
             }
         });
         
@@ -383,7 +378,7 @@
         
         sm.select(0);
         assertTrue(sm.isSelected(0));
-        assertEquals(1, hitCount);
+        assertEquals(1, rt_18969_hitCount);
         
         // sleep for 100ms so that the currentTimeMillis is guaranteed to be
         // a different value than the first one
@@ -401,7 +396,7 @@
         
         // it should be two, as there is no null event in between (although there
         // used to be, so the test used to be for three hits)
-        assertEquals(2, hitCount);
+        assertEquals(2, rt_18969_hitCount);
     }
     
     @Test public void test_rt21586() {
@@ -487,7 +482,6 @@
         ControlAsserts.assertCellTextEquals(list, 1, "updated name2");
     }
     
-    @Ignore
     @Test public void test_rt28637() {
         ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4");
         
@@ -504,4 +498,34 @@
         assertEquals("String2", listView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, listView.getSelectionModel().getSelectedIndex());
     }
+    
+    @Test public void test_rt28819_1() {
+        ObservableList<String> emptyModel = FXCollections.observableArrayList();
+        
+        final ListView<String> listView = new ListView<String>();
+        listView.setItems(emptyModel);
+        ControlAsserts.assertRowsEmpty(listView, 0, 5);
+        
+        ObservableList<String> mod = FXCollections.observableArrayList();
+        String value = System.currentTimeMillis()+"";
+        mod.add(value);
+        listView.setItems(mod);
+        ControlAsserts.assertCellCount(listView, 1);
+        ControlAsserts.assertCellTextEquals(listView, 0, value);
+    }
+    
+    @Test public void test_rt28819_2() {
+        ObservableList<String> emptyModel = FXCollections.observableArrayList();
+        
+        final ListView<String> listView = new ListView<String>();
+        listView.setItems(emptyModel);
+        ControlAsserts.assertRowsEmpty(listView, 0, 5);
+        
+        ObservableList<String> mod1 = FXCollections.observableArrayList();
+        String value1 = System.currentTimeMillis()+"";
+        mod1.add(value1);
+        listView.getItems().setAll(mod1);
+        ControlAsserts.assertCellCount(listView, 1);
+        ControlAsserts.assertCellTextEquals(listView, 0, value1);
+    }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewKeyInputTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -42,7 +42,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Disabling tests as they fail with OOM in continuous builds")
+//@Ignore("Disabling tests as they fail with OOM in continuous builds")
 public class TableViewKeyInputTest {
     private TableView<String> tableView;
     private TableView.TableViewSelectionModel<String> sm;
--- a/javafx-ui-controls/test/javafx/scene/control/TableViewTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TableViewTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -25,6 +25,7 @@
 
 package javafx.scene.control;
 
+import com.sun.javafx.scene.control.TableColumnComparator;
 import com.sun.javafx.scene.control.test.ControlAsserts;
 import com.sun.javafx.scene.control.test.Person;
 import com.sun.javafx.scene.control.test.RT_22463_Person;
@@ -33,18 +34,26 @@
 import static org.junit.Assert.*;
 
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
+import javafx.event.EventHandler;
 import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.util.Callback;
 
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 
+import static javafx.scene.control.TableColumnBase.SortType.ASCENDING;
+import static javafx.scene.control.TableColumnBase.SortType.DESCENDING;
+
 public class TableViewTest {
     private TableView<String> table;
     private TableView.TableViewSelectionModel sm;
@@ -70,6 +79,18 @@
     @Test public void noArgConstructorSetsNonNullItems() {
         assertNotNull(table.getItems());
     }
+    
+    @Test public void noArgConstructorSetsNonNullSortPolicy() {
+        assertNotNull(table.getSortPolicy());
+    }
+    
+    @Test public void noArgConstructorSetsNullComparator() {
+        assertNull(table.getComparator());
+    }
+    
+    @Test public void noArgConstructorSetsNullOnSort() {
+        assertNull(table.getOnSort());
+    }
 
     @Test public void noArgConstructor_selectedItemIsNull() {
         assertNull(sm.getSelectedItem());
@@ -285,11 +306,378 @@
         table.getColumns().addAll(first, second);
         table.getSortOrder().setAll(first, second);
         table.getColumns().remove(first);
-        assertEquals(false, table.getSortOrder().contains(first));
+        assertFalse(table.getSortOrder().contains(first));
     } 
     
     
     /*********************************************************************
+     * Tests for new sorting API in JavaFX 8.0                           *
+     ********************************************************************/
+    
+    // TODO test for sort policies returning null
+    // TODO test for changing column sortType out of order
+    // TODO test comparator returns to original when sort fails / is consumed
+    
+    private static final Callback<TableView<String>, Boolean> NO_SORT_FAILED_SORT_POLICY = 
+            new Callback<TableView<String>, Boolean>() {
+        @Override public Boolean call(TableView<String> tableView) {
+            return false;
+        }
+    };
+    
+    private static final Callback<TableView<String>, Boolean> SORT_SUCCESS_ASCENDING_SORT_POLICY = 
+            new Callback<TableView<String>, Boolean>() {
+        @Override public Boolean call(TableView<String> tableView) {
+            if (tableView.getSortOrder().isEmpty()) return true;
+            FXCollections.sort(tableView.getItems(), new Comparator<String>() {
+                @Override public int compare(String o1, String o2) {
+                    return o1.compareTo(o2);
+                }
+            });
+            return true;
+        }
+    };
+    
+    private TableColumn<String, String> initSortTestStructure() {
+        TableColumn<String, String> col = new TableColumn<String, String>("column");
+        col.setSortType(ASCENDING);
+        col.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<String, String>, ObservableValue<String>>() {
+            @Override public ObservableValue<String> call(TableColumn.CellDataFeatures<String, String> param) {
+                return new ReadOnlyObjectWrapper<String>(param.getValue());
+            }
+        });
+        table.getColumns().add(col);
+        table.getItems().addAll("Apple", "Orange", "Banana");
+        return col;
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeSortOrderList() {
+        TableColumn<String, String> col = initSortTestStructure();
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        
+        // the sort order list should be returned back to its original state
+        assertTrue(table.getSortOrder().isEmpty());
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeSortOrderList() {
+        TableColumn<String, String> col = initSortTestStructure();
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_AscendingToDescending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        assertEquals(ASCENDING, col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        
+        // when we change from ASCENDING to DESCENDING we don't expect the sort
+        // to actually change (and in fact we expect the sort type to resort
+        // back to being ASCENDING)
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        assertEquals(ASCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_AscendingToDescending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        assertEquals(ASCENDING, col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        assertEquals(DESCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_DescendingToNull() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        assertEquals(DESCENDING, col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        assertEquals(DESCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_DescendingToNull() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        assertEquals(DESCENDING, col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        assertNull(col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_NullToAscending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        assertNull(col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        assertNull(col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_NullToAscending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        assertNull(col.getSortType());
+        table.getSortOrder().add(col);
+        table.setOnSort(new EventHandler<SortEvent<TableView<String>>>() {
+            @Override public void handle(SortEvent<TableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        assertEquals(ASCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+
+    @Test public void testSortMethodWithNullSortPolicy() {
+        TableColumn<String, String> col = initSortTestStructure();
+        table.setSortPolicy(null);
+        assertNull(table.getSortPolicy());
+        table.sort();
+    }
+    
+    @Test public void testChangingSortPolicyUpdatesItemsList() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        table.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+    }
+    
+    @Test public void testChangingSortPolicyDoesNotUpdateItemsListWhenTheSortOrderListIsEmpty() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        
+        table.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderAddition() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        
+        table.getSortOrder().add(col);
+        
+        // no sort should be run (as we have a custom sort policy), and the 
+        // sortOrder list should be empty as the sortPolicy failed
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        assertTrue(table.getSortOrder().isEmpty());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderRemoval() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        // even though we remove the column from the sort order here, because the
+        // sort policy fails the items list should remain unchanged and the sort
+        // order list should continue to have the column in it.
+        table.getSortOrder().remove(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        ControlAsserts.assertListContainsItemsInOrder(table.getSortOrder(), col);
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_ascendingToDescending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(ASCENDING);
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Banana", "Orange");
+        assertEquals(ASCENDING, col.getSortType());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_descendingToNull() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        assertEquals(DESCENDING, col.getSortType());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_nullToAscending() {
+        TableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        assertNull(col.getSortType());
+    }
+    
+    @Test public void testComparatorChangesInSyncWithSortOrder_1() {
+        TableColumn<String, String> col = initSortTestStructure();
+        assertNull(table.getComparator());
+        assertTrue(table.getSortOrder().isEmpty());
+        
+        table.getSortOrder().add(col);
+        TableColumnComparator c = (TableColumnComparator)table.getComparator();
+        assertNotNull(c);
+        ControlAsserts.assertListContainsItemsInOrder(c.getColumns(), col);
+    }
+    
+    @Ignore
+    @Test public void testComparatorChangesInSyncWithSortOrder_2() {
+        // same as test above
+        TableColumn<String, String> col = initSortTestStructure();
+        assertNull(table.getComparator());
+        assertTrue(table.getSortOrder().isEmpty());
+        
+        table.getSortOrder().add(col);
+        TableColumnComparator c = (TableColumnComparator)table.getComparator();
+        assertNotNull(c);
+        ControlAsserts.assertListContainsItemsInOrder(c.getColumns(), col);
+        
+        // now remove column from sort order, and the comparator should go to
+        // being null
+        table.getSortOrder().remove(col);
+        assertNull(table.getComparator());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderAddition() {
+        TableColumn<String, String> col = initSortTestStructure();
+        final TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
+        
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Apple", "Orange", "Banana");
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        
+        table.getSortOrder().add(col);
+        
+        assertEquals(oldComparator, table.getComparator());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderRemoval() {
+        TableColumn<String, String> col = initSortTestStructure();
+        TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
+        assertNull(oldComparator);
+
+        col.setSortType(DESCENDING);
+        table.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(table.getItems(), "Orange", "Banana", "Apple");
+        oldComparator = (TableColumnComparator)table.getComparator();
+        ControlAsserts.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        table.getSortOrder().remove(col);
+        
+        assertTrue(table.getSortOrder().contains(col));
+        ControlAsserts.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortTypeChange() {
+        TableColumn<String, String> col = initSortTestStructure();
+        final TableColumnComparator oldComparator = (TableColumnComparator)table.getComparator();
+        assertNull(oldComparator);
+        
+        table.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        table.getSortOrder().add(col);
+        col.setSortType(ASCENDING);
+        
+        assertTrue(table.getSortOrder().isEmpty());
+        assertNull(oldComparator);
+    }
+    
+    
+    
+    /*********************************************************************
      * Tests for specific bugs                                           *
      ********************************************************************/
     @Test public void test_rt16019() {
@@ -542,21 +930,23 @@
         ControlAsserts.assertCellTextEquals(table, 1, "2", "updated name2");
     }
     
-    @Ignore
     @Test public void test_rt28637() {
         ObservableList<String> items = FXCollections.observableArrayList("String1", "String2", "String3", "String4");
         
         final TableView<String> tableView = new TableView<String>();
+        final MultipleSelectionModel sm = tableView.getSelectionModel();
         tableView.setItems(items);
         
         tableView.getSelectionModel().select(0);
-        assertEquals("String1", tableView.getSelectionModel().getSelectedItem());
-        assertEquals("String1", tableView.getSelectionModel().getSelectedItems().get(0));
-        assertEquals(0, tableView.getSelectionModel().getSelectedIndex());
+        assertEquals("String1", sm.getSelectedItem());
+        assertEquals(1, sm.getSelectedItems().size());
+        assertEquals("String1", sm.getSelectedItems().get(0));
+        assertEquals(0, sm.getSelectedIndex());
         
-        items.remove(tableView.getSelectionModel().getSelectedItem());
-        assertEquals("String2", tableView.getSelectionModel().getSelectedItem());
-        assertEquals("String2", tableView.getSelectionModel().getSelectedItems().get(0));
-        assertEquals(0, tableView.getSelectionModel().getSelectedIndex());
+        items.remove(sm.getSelectedItem());
+        assertEquals("String2", sm.getSelectedItem());
+        assertEquals(1, sm.getSelectedItems().size());
+        assertEquals("String2", sm.getSelectedItems().get(0));
+        assertEquals(0, sm.getSelectedIndex());
     }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeTableViewKeyInputTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeTableViewKeyInputTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -41,7 +41,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Disabling tests as they fail with OOM in continuous builds")
+//@Ignore("Disabling tests as they fail with OOM in continuous builds")
 public class TreeTableViewKeyInputTest {
     private TreeTableView<String> tableView;
     private TreeTableView.TreeTableViewSelectionModel<String> sm;
--- a/javafx-ui-controls/test/javafx/scene/control/TreeTableViewTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeTableViewTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -25,22 +25,28 @@
 
 package javafx.scene.control;
 
+import com.sun.javafx.scene.control.TableColumnComparator;
 import com.sun.javafx.scene.control.test.ControlAsserts;
 import com.sun.javafx.scene.control.test.Person;
 import com.sun.javafx.scene.control.test.RT_22463_Person;
 import java.util.Arrays;
+import java.util.Comparator;
 import javafx.beans.InvalidationListener;
 import javafx.beans.Observable;
 import static javafx.scene.control.ControlTestUtils.assertStyleClassContains;
 import static org.junit.Assert.*;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectWrapper;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.collections.FXCollections;
 import javafx.collections.ObservableList;
+import javafx.event.EventHandler;
 import javafx.scene.Group;
 import javafx.scene.Scene;
+import static javafx.scene.control.TableColumnBase.SortType.ASCENDING;
+import static javafx.scene.control.TableColumnBase.SortType.DESCENDING;
 import javafx.scene.control.cell.PropertyValueFactory;
 import javafx.scene.control.cell.TreeItemPropertyValueFactory;
 import javafx.stage.Stage;
@@ -163,6 +169,18 @@
     @Test public void noArgConstructor_selectedIndexIsNegativeOne() {
         assertEquals(-1, sm.getSelectedIndex());
     }
+    
+    @Test public void noArgConstructorSetsNonNullSortPolicy() {
+        assertNotNull(treeTableView.getSortPolicy());
+    }
+    
+    @Test public void noArgConstructorSetsNullComparator() {
+        assertNull(treeTableView.getComparator());
+    }
+    
+    @Test public void noArgConstructorSetsNullOnSort() {
+        assertNull(treeTableView.getOnSort());
+    }
 
     @Test public void singleArgConstructorSetsNonNullSelectionModel() {
         final TableView<String> b2 = new TableView<String>(FXCollections.observableArrayList("Hi"));
@@ -221,8 +239,7 @@
     }
     
     @Test public void testSortOrderCleanup() {
-//        ObservableList<ObservablePerson> persons = ObservablePerson.createFXPersonList();
-        TreeTableView table = new TreeTableView();
+        TreeTableView treeTableView = new TreeTableView();
         TreeTableColumn<String,String> first = new TreeTableColumn<String,String>("first");
         first.setCellValueFactory(new PropertyValueFactory("firstName"));
         TreeTableColumn<String,String> second = new TreeTableColumn<String,String>("second");
@@ -230,11 +247,387 @@
         treeTableView.getColumns().addAll(first, second);
         treeTableView.getSortOrder().setAll(first, second);
         treeTableView.getColumns().remove(first);
-        assertEquals(false, treeTableView.getSortOrder().contains(first));
+        assertFalse(treeTableView.getSortOrder().contains(first));
     } 
     
     
     /*********************************************************************
+     * Tests for new sorting API in JavaFX 8.0                           *
+     ********************************************************************/
+    
+    private TreeItem<String> apple, orange, banana;
+    
+    // TODO test for sort policies returning null
+    // TODO test for changing column sortType out of order
+    
+    private static final Callback<TreeTableView<String>, Boolean> NO_SORT_FAILED_SORT_POLICY = 
+            new Callback<TreeTableView<String>, Boolean>() {
+        @Override public Boolean call(TreeTableView<String> treeTableView) {
+            return false;
+        }
+    };
+    
+    private static final Callback<TreeTableView<String>, Boolean> SORT_SUCCESS_ASCENDING_SORT_POLICY = 
+            new Callback<TreeTableView<String>, Boolean>() {
+        @Override public Boolean call(TreeTableView<String> treeTableView) {
+            if (treeTableView.getSortOrder().isEmpty()) return true;
+            FXCollections.sort(treeTableView.getRoot().getChildren(), new Comparator<TreeItem<String>>() {
+                @Override public int compare(TreeItem<String> o1, TreeItem<String> o2) {
+                    return o1.getValue().compareTo(o2.getValue());
+                }
+            });
+            return true;
+        }
+    };
+    
+    private TreeTableColumn<String, String> initSortTestStructure() {
+        TreeTableColumn<String, String> col = new TreeTableColumn<String, String>("column");
+        col.setSortType(ASCENDING);
+        col.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<String, String>, ObservableValue<String>>() {
+            @Override public ObservableValue<String> call(TreeTableColumn.CellDataFeatures<String, String> param) {
+                return new ReadOnlyObjectWrapper<String>(param.getValue().getValue());
+            }
+        });
+        treeTableView.getColumns().add(col);
+        
+        TreeItem<String> newRoot = new TreeItem<String>("root");
+        newRoot.setExpanded(true);
+        newRoot.getChildren().addAll(
+                apple  = new TreeItem("Apple"), 
+                orange = new TreeItem("Orange"), 
+                banana = new TreeItem("Banana"));
+        
+        treeTableView.setRoot(newRoot);
+        
+        return col;
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeSortOrderList() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        
+        // the sort order list should be returned back to its original state
+        assertTrue(treeTableView.getSortOrder().isEmpty());
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeSortOrderList() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_AscendingToDescending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        assertEquals(ASCENDING, col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        
+        // when we change from ASCENDING to DESCENDING we don't expect the sort
+        // to actually change (and in fact we expect the sort type to resort
+        // back to being ASCENDING)
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        assertEquals(ASCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_AscendingToDescending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        assertEquals(ASCENDING, col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        assertEquals(DESCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_DescendingToNull() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        assertEquals(DESCENDING, col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        assertEquals(DESCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_DescendingToNull() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        assertEquals(DESCENDING, col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        assertNull(col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Ignore("This test is only valid if sort event consumption should revert changes")
+    @Test public void testSortEventCanBeConsumedToStopSortOccurring_changeColumnSortType_NullToAscending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        assertNull(col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                event.consume();
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        assertNull(col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Test public void testSortEventCanBeNotConsumedToAllowSortToOccur_changeColumnSortType_NullToAscending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        assertNull(col.getSortType());
+        treeTableView.getSortOrder().add(col);
+        treeTableView.setOnSort(new EventHandler<SortEvent<TreeTableView<String>>>() {
+            @Override public void handle(SortEvent<TreeTableView<String>> event) {
+                // do not consume here - this allows the sort to happen
+            }
+        });
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        assertEquals(ASCENDING, col.getSortType());
+        
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Test public void testSortMethodWithNullSortPolicy() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        treeTableView.setSortPolicy(null);
+        assertNull(treeTableView.getSortPolicy());
+        treeTableView.sort();
+    }
+    
+    @Test public void testChangingSortPolicyUpdatesItemsList() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        treeTableView.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+    }
+    
+    @Test public void testChangingSortPolicyDoesNotUpdateItemsListWhenTheSortOrderListIsEmpty() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        
+        treeTableView.setSortPolicy(SORT_SUCCESS_ASCENDING_SORT_POLICY);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderAddition() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        
+        treeTableView.getSortOrder().add(col);
+        
+        // no sort should be run (as we have a custom sort policy), and the 
+        // sortOrder list should be empty as the sortPolicy failed
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        assertTrue(treeTableView.getSortOrder().isEmpty());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortOrderRemoval() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        // even though we remove the column from the sort order here, because the
+        // sort policy fails the items list should remain unchanged and the sort
+        // order list should continue to have the column in it.
+        treeTableView.getSortOrder().remove(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getSortOrder(), col);
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_ascendingToDescending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(ASCENDING);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, banana, orange);
+        assertEquals(ASCENDING, col.getSortType());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_descendingToNull() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(DESCENDING);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(null);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        assertEquals(DESCENDING, col.getSortType());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutLastChange_sortTypeChange_nullToAscending() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        col.setSortType(null);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+
+        col.setSortType(ASCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        assertNull(col.getSortType());
+    }
+    
+    @Test public void testComparatorChangesInSyncWithSortOrder_1() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        assertNull(treeTableView.getComparator());
+        assertTrue(treeTableView.getSortOrder().isEmpty());
+        
+        treeTableView.getSortOrder().add(col);
+        TableColumnComparator c = (TableColumnComparator)treeTableView.getComparator();
+        assertNotNull(c);
+        ControlAsserts.assertListContainsItemsInOrder(c.getColumns(), col);
+    }
+    
+    @Test public void testComparatorChangesInSyncWithSortOrder_2() {
+        // same as test above
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        assertNull(treeTableView.getComparator());
+        assertTrue(treeTableView.getSortOrder().isEmpty());
+        
+        treeTableView.getSortOrder().add(col);
+        TableColumnComparator c = (TableColumnComparator)treeTableView.getComparator();
+        assertNotNull(c);
+        ControlAsserts.assertListContainsItemsInOrder(c.getColumns(), col);
+        
+        // now remove column from sort order, and the comparator should go to
+        // being null
+        treeTableView.getSortOrder().remove(col);
+        assertNull(treeTableView.getComparator());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderAddition() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        final TableColumnComparator oldComparator = (TableColumnComparator)treeTableView.getComparator();
+        
+        col.setSortType(DESCENDING);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), apple, orange, banana);
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        
+        treeTableView.getSortOrder().add(col);
+        
+        assertEquals(oldComparator, treeTableView.getComparator());
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortOrderRemoval() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        TableColumnComparator oldComparator = (TableColumnComparator)treeTableView.getComparator();
+        assertNull(oldComparator);
+
+        col.setSortType(DESCENDING);
+        treeTableView.getSortOrder().add(col);
+        ControlAsserts.assertListContainsItemsInOrder(treeTableView.getRoot().getChildren(), orange, banana, apple);
+        oldComparator = (TableColumnComparator)treeTableView.getComparator();
+        ControlAsserts.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        treeTableView.getSortOrder().remove(col);
+        
+        assertTrue(treeTableView.getSortOrder().contains(col));
+        ControlAsserts.assertListContainsItemsInOrder(oldComparator.getColumns(), col);
+    }
+    
+    @Test public void testFailedSortPolicyBacksOutComparatorChange_sortTypeChange() {
+        TreeTableColumn<String, String> col = initSortTestStructure();
+        final TableColumnComparator oldComparator = (TableColumnComparator)treeTableView.getComparator();
+        assertNull(oldComparator);
+        
+        treeTableView.setSortPolicy(NO_SORT_FAILED_SORT_POLICY);
+        treeTableView.getSortOrder().add(col);
+        col.setSortType(ASCENDING);
+        
+        assertTrue(treeTableView.getSortOrder().isEmpty());
+        assertNull(oldComparator);
+    }
+    
+    
+    
+    /*********************************************************************
      * Tests for specific bugs                                           *
      ********************************************************************/
     @Test public void test_rt16019() {
@@ -992,7 +1385,6 @@
         ControlAsserts.assertCellTextEquals(table, 2, "2", "updated name2");
     }
     
-    @Ignore
     @Test public void test_rt28637() {
         TreeItem<String> s1, s2, s3, s4;
         ObservableList<TreeItem<String>> items = FXCollections.observableArrayList(
@@ -1006,16 +1398,17 @@
         TreeItem<String> root = new TreeItem<String>("Root");
         root.setExpanded(true);
         treeTableView.setRoot(root);
+        treeTableView.setShowRoot(false);
         root.getChildren().addAll(items);
         
         treeTableView.getSelectionModel().select(0);
-        assertEquals(root, treeTableView.getSelectionModel().getSelectedItem());
-        assertEquals(root, treeTableView.getSelectionModel().getSelectedItems().get(0));
+        assertEquals((Object)s1, treeTableView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s1, treeTableView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, treeTableView.getSelectionModel().getSelectedIndex());
         
-        items.remove(treeTableView.getSelectionModel().getSelectedItem());
-        assertEquals(s1, treeTableView.getSelectionModel().getSelectedItem());
-        assertEquals(s1, treeTableView.getSelectionModel().getSelectedItems().get(0));
+        root.getChildren().remove(treeTableView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s2, treeTableView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s2, treeTableView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, treeTableView.getSelectionModel().getSelectedIndex());
     }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewKeyInputTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -41,7 +41,7 @@
 import org.junit.Ignore;
 import org.junit.Test;
 
-@Ignore("Disabling tests as they fail with OOM in continuous builds")
+//@Ignore("Disabling tests as they fail with OOM in continuous builds")
 public class TreeViewKeyInputTest {
     private TreeView<String> treeView;
     private MultipleSelectionModel<TreeItem<String>> sm;
--- a/javafx-ui-controls/test/javafx/scene/control/TreeViewTest.java	Tue Mar 12 09:39:27 2013 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/TreeViewTest.java	Tue Mar 12 20:00:53 2013 -0400
@@ -764,7 +764,6 @@
         ControlAsserts.assertCellTextEquals(tree, 2, "updated name2");
     }
     
-    @Ignore
     @Test public void test_rt28637() {
         TreeItem<String> s1, s2, s3, s4;
         ObservableList<TreeItem<String>> items = FXCollections.observableArrayList(
@@ -778,16 +777,17 @@
         TreeItem<String> root = new TreeItem<String>("Root");
         root.setExpanded(true);
         treeView.setRoot(root);
+        treeView.setShowRoot(false);
         root.getChildren().addAll(items);
         
         treeView.getSelectionModel().select(0);
-        assertEquals(root, treeView.getSelectionModel().getSelectedItem());
-        assertEquals(root, treeView.getSelectionModel().getSelectedItems().get(0));
+        assertEquals((Object)s1, treeView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s1, treeView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, treeView.getSelectionModel().getSelectedIndex());
         
-        items.remove(treeView.getSelectionModel().getSelectedItem());
-        assertEquals(s1, treeView.getSelectionModel().getSelectedItem());
-        assertEquals(s1, treeView.getSelectionModel().getSelectedItems().get(0));
+        root.getChildren().remove(treeView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s2, treeView.getSelectionModel().getSelectedItem());
+        assertEquals((Object)s2, treeView.getSelectionModel().getSelectedItems().get(0));
         assertEquals(0, treeView.getSelectionModel().getSelectedIndex());
     }
 }