changeset 6484:748893eeb873

RT-36234: Cell behavior code is highly duplicated
author jgiles
date Mon, 17 Mar 2014 16:23:17 +1300
parents f06a098b55c4
children 93a63c9eb06f
files modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/CellBehaviorBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/DateCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListViewBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehaviorBase.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java modules/controls/src/main/java/com/sun/javafx/scene/control/skin/CellSkinBase.java modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java
diffstat 15 files changed, 549 insertions(+), 892 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/CellBehaviorBase.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/CellBehaviorBase.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,16 +26,274 @@
 package com.sun.javafx.scene.control.behavior;
 
 import javafx.scene.control.Cell;
+import javafx.scene.control.Control;
+import javafx.scene.control.FocusModel;
+import javafx.scene.control.IndexedCell;
+import javafx.scene.control.MultipleSelectionModel;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.input.ContextMenuEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+
+import java.util.ArrayList;
 import java.util.List;
 
 /**
  * Behaviors for standard cells types. Simply defines methods that subclasses
  * implement so that CellSkinBase has API to call.
- *
  */
-public class CellBehaviorBase<T extends Cell> extends BehaviorBase<T> {
+public abstract class CellBehaviorBase<T extends Cell> extends BehaviorBase<T> {
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private static implementation                                           *
+     *                                                                         *
+     **************************************************************************/
+
+    private static final String ANCHOR_PROPERTY_KEY = "anchor";
+
+    static <T> T getAnchor(Control control, T defaultResponse) {
+        return hasAnchor(control) ?
+                (T) control.getProperties().get(ANCHOR_PROPERTY_KEY) :
+                defaultResponse;
+    }
+
+    static <T> void setAnchor(Control control, T anchor) {
+        if (control != null && anchor == null) {
+            removeAnchor(control);
+        } else {
+            control.getProperties().put(ANCHOR_PROPERTY_KEY, anchor);
+        }
+    }
+
+    static boolean hasAnchor(Control control) {
+        return control.getProperties().get(ANCHOR_PROPERTY_KEY) != null;
+    }
+
+    static void removeAnchor(Control control) {
+        control.getProperties().remove(ANCHOR_PROPERTY_KEY);
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private fields                                                          *
+     *                                                                         *
+     **************************************************************************/
+
+    // To support touch devices, we have to slightly modify this behavior, such
+    // that selection only happens on mouse release, if only minimal dragging
+    // has occurred.
+    private boolean latePress = false;
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
 
     public CellBehaviorBase(T control, List<KeyBinding> bindings) {
         super(control, bindings);
     }
+
+
+    abstract Control getCellContainer(); // e.g. ListView
+    abstract MultipleSelectionModel<?> getSelectionModel();
+    abstract FocusModel<?> getFocusModel();
+    abstract void edit(T cell);
+    boolean handleDisclosureNode(double x, double y) {
+        return false;
+    }
+
+    boolean isClickPositionValid(final double x, final double y) {
+        return true;
+    }
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    int getIndex() {
+        return getControl() instanceof IndexedCell ? ((IndexedCell)getControl()).getIndex() : -1;
+    }
+
+    @Override public void mousePressed(MouseEvent e) {
+        if (e.isSynthesized()) {
+            latePress = true;
+        } else {
+            latePress  = getControl().isSelected();
+            if (!latePress) {
+                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
+                        e.isShiftDown(), e.isShortcutDown());
+            }
+        }
+    }
+
+    @Override public void mouseReleased(MouseEvent e) {
+        if (latePress) {
+            latePress = false;
+            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
+                    e.isShiftDown(), e.isShortcutDown());
+        }
+    }
+
+    @Override public void mouseDragged(MouseEvent e) {
+        latePress = false;
+    }
+
+    @Override public void contextMenuRequested(ContextMenuEvent e) {
+        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
+
+    protected void doSelect(final double x, final double y, final MouseButton button,
+                            final int clickCount, final boolean shiftDown, final boolean shortcutDown) {
+        // we update the cell to point to the new tree node
+        final T cell = getControl();
+
+        final Control cellContainer = getCellContainer();
+
+        // If the mouse event is not contained within this TreeCell, then
+        // we don't want to react to it.
+        if (cell.isEmpty() || ! cell.contains(x, y)) {
+            return;
+        }
+
+        final int index = getIndex();
+        boolean selected = cell.isSelected();
+        MultipleSelectionModel<?> sm = getSelectionModel();
+        if (sm == null) return;
+
+        FocusModel<?> fm = getFocusModel();
+        if (fm == null) return;
+
+        // if the user has clicked on the disclosure node, we do nothing other
+        // than expand/collapse the tree item (if applicable). We do not do editing!
+        if (handleDisclosureNode(x,y)) {
+            return;
+        }
+
+        // we only care about clicks in certain places (depending on the subclass)
+        if (! isClickPositionValid(x, y)) return;
+
+        // if shift is down, and we don't already have the initial focus index
+        // recorded, we record the focus index now so that subsequent shift+clicks
+        // result in the correct selection occuring (whilst the focus index moves
+        // about).
+        if (shiftDown) {
+            if (! hasAnchor(cellContainer)) {
+                setAnchor(cellContainer, fm.getFocusedIndex());
+            }
+        } else {
+            removeAnchor(cellContainer);
+        }
+
+        if (button == MouseButton.PRIMARY || (button == MouseButton.SECONDARY && !selected)) {
+            if (sm.getSelectionMode() == SelectionMode.SINGLE) {
+                simpleSelect(button, clickCount, shortcutDown);
+            } else {
+                if (shortcutDown) {
+                    if (selected) {
+                        // we remove this row from the current selection
+                        sm.clearSelection(index);
+                        fm.focus(index);
+                    } else {
+                        // We add this row to the current selection
+                        sm.select(index);
+                    }
+                } else if (shiftDown && clickCount == 1) {
+                    // we add all rows between the current selection focus and
+                    // this row (inclusive) to the current selection.
+                    final int focusedIndex = getAnchor(cellContainer, fm.getFocusedIndex());
+
+                    selectRows(focusedIndex, index);
+
+                    fm.focus(index);
+                } else {
+                    simpleSelect(button, clickCount, shortcutDown);
+                }
+            }
+        }
+    }
+
+    protected void simpleSelect(MouseButton button, int clickCount, boolean shortcutDown) {
+        final int index = getIndex();
+        MultipleSelectionModel<?> sm = getSelectionModel();
+        boolean isAlreadySelected = sm.isSelected(index);
+
+        if (isAlreadySelected && shortcutDown) {
+            sm.clearSelection(index);
+            getFocusModel().focus(index);
+            isAlreadySelected = false;
+        } else {
+            sm.clearAndSelect(index);
+        }
+
+        handleClicks(button, clickCount, isAlreadySelected);
+    }
+
+    protected void handleClicks(MouseButton button, int clickCount, boolean isAlreadySelected) {
+        // handle editing, which only occurs with the primary mouse button
+        if (button == MouseButton.PRIMARY) {
+            if (clickCount == 1 && isAlreadySelected) {
+                edit(getControl());
+            } else if (clickCount == 1) {
+                // cancel editing
+                edit(null);
+            } else if (clickCount == 2 && getControl().isEditable()) {
+                edit(getControl());
+            }
+        }
+    }
+
+    void selectRows(int focusedIndex, int index) {
+        final boolean asc = focusedIndex < index;
+
+        // and then determine all row and columns which must be selected
+        int minRow = Math.min(focusedIndex, index);
+        int maxRow = Math.max(focusedIndex, index);
+
+        // To prevent RT-32119, we make a copy of the selected indices
+        // list first, so that we are not iterating and modifying it
+        // concurrently.
+        List<Integer> selectedIndices = new ArrayList<>(getSelectionModel().getSelectedIndices());
+        for (int i = 0, max = selectedIndices.size(); i < max; i++) {
+            int selectedIndex = selectedIndices.get(i);
+            if (selectedIndex < minRow || selectedIndex > maxRow) {
+                getSelectionModel().clearSelection(selectedIndex);
+            }
+        }
+
+        if (minRow == maxRow) {
+            // RT-32560: This prevents the anchor 'sticking' in
+            // the wrong place when a range is selected and then
+            // selection goes back to the anchor position.
+            // (Refer to the video in RT-32560 for more detail).
+            getSelectionModel().select(minRow);
+        } else {
+            // RT-21444: We need to put the range in the correct
+            // order or else the last selected row will not be the
+            // last item in the selectedItems list of the selection
+            // model,
+            if (asc) {
+                getSelectionModel().selectRange(minRow, maxRow + 1);
+            } else {
+                getSelectionModel().selectRange(maxRow, minRow - 1);
+            }
+        }
+    }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/DateCellBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/DateCellBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -51,7 +51,8 @@
  * that subclasses implement so that CellSkinBase has API to call.
  *
  */
-public class DateCellBehavior extends CellBehaviorBase<DateCell> {
+public class DateCellBehavior extends BehaviorBase<DateCell> {
+
     /**************************************************************************
      *                          Setup KeyBindings                             *
      *************************************************************************/
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListCellBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,75 +25,17 @@
 
 package com.sun.javafx.scene.control.behavior;
 
-import com.sun.javafx.scene.control.Logging;
 import javafx.scene.control.FocusModel;
 import javafx.scene.control.ListCell;
 import javafx.scene.control.ListView;
 import javafx.scene.control.MultipleSelectionModel;
-import javafx.scene.control.SelectionMode;
-import javafx.scene.input.ContextMenuEvent;
-import javafx.scene.input.MouseButton;
-import javafx.scene.input.MouseEvent;
-import sun.util.logging.PlatformLogger;
-import sun.util.logging.PlatformLogger.Level;
 
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 
-/**
- */
 public class ListCellBehavior<T> extends CellBehaviorBase<ListCell<T>> {
 
     /***************************************************************************
      *                                                                         *
-     * Private static implementation                                           *
-     *                                                                         *
-     **************************************************************************/
-
-    private static final String ANCHOR_PROPERTY_KEY = "list.anchor";
-
-    static int getAnchor(ListView<?> list) {
-        FocusModel<?> fm = list.getFocusModel();
-        if (fm == null) return -1;
-
-        return hasAnchor(list) ?
-                (int)list.getProperties().get(ANCHOR_PROPERTY_KEY) :
-                fm.getFocusedIndex();
-    }
-
-    static void setAnchor(ListView<?> list, int anchor) {
-        if (list != null && anchor < 0) {
-            removeAnchor(list);
-        } else {
-            list.getProperties().put(ANCHOR_PROPERTY_KEY, anchor);
-        }
-    }
-
-    static boolean hasAnchor(ListView<?> list) {
-        return list.getProperties().get(ANCHOR_PROPERTY_KEY) != null;
-    }
-
-    static void removeAnchor(ListView<?> list) {
-        list.getProperties().remove(ANCHOR_PROPERTY_KEY);
-    }
-
-
-
-    /***************************************************************************
-     *                                                                         *
-     * Private fields                                                          *
-     *                                                                         *
-     **************************************************************************/
-
-    // To support touch devices, we have to slightly modify this behavior, such
-    // that selection only happens on mouse release, if only minimal dragging
-    // has occurred.
-    private boolean latePress = false;
-
-
-    /***************************************************************************
-     *                                                                         *
      * Constructors                                                            *
      *                                                                         *
      **************************************************************************/
@@ -110,162 +52,21 @@
      *                                                                         *
      **************************************************************************/
 
-    @Override public void mousePressed(MouseEvent e) {
-        if (e.isSynthesized()) {
-            latePress = true;
-            return;
-        } else {
-            latePress  = getControl().isSelected();
-            if (!latePress) {
-                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                         e.isShiftDown(), e.isShortcutDown());
-            }
-        }
+    @Override MultipleSelectionModel<T> getSelectionModel() {
+        return getCellContainer().getSelectionModel();
     }
 
-    @Override public void mouseReleased(MouseEvent e) {
-        if (latePress) {
-            latePress = false;
-            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                     e.isShiftDown(), e.isShortcutDown());
-        }
+    @Override FocusModel<T> getFocusModel() {
+        return getCellContainer().getFocusModel();
     }
 
-    @Override public void mouseDragged(MouseEvent e) {
-        latePress = false;
+    @Override ListView<T> getCellContainer() {
+        return getControl().getListView();
     }
 
-    @Override public void contextMenuRequested(ContextMenuEvent e) {
-        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
-    }
-
-
-
-    /***************************************************************************
-     *                                                                         *
-     * Private implementation                                                  *
-     *                                                                         *
-     **************************************************************************/
-
-    private void doSelect(final double x, final double y, final MouseButton button,
-                          final int clickCount, final boolean shiftDown,
-                          final boolean shortcutDown) {
-        // Note that list.select will reset selection
-        // for out of bounds indexes. So, need to check
-        ListCell<T> listCell = getControl();
-        ListView<T> listView = getControl().getListView();
-        if (listView == null) return;
-
-        // If the mouse event is not contained within this ListCell, then
-        // we don't want to react to it.
-        if (listCell.isEmpty() || ! listCell.contains(x, y)) {
-            return;
-        }
-
-        int rowCount = listView.getItems() == null ? 0 : listView.getItems().size();
-        if (listCell.getIndex() >= rowCount) return;
-
-        int index = listCell.getIndex();
-        boolean selected = listCell.isSelected();
-
-        MultipleSelectionModel<T> sm = listView.getSelectionModel();
-        if (sm == null) return;
-
-        FocusModel<T> fm = listView.getFocusModel();
-        if (fm == null) return;
-
-        // if shift is down, and we don't already have the initial focus index
-        // recorded, we record the focus index now so that subsequent shift+clicks
-        // result in the correct selection occuring (whilst the focus index moves
-        // about).
-        if (shiftDown) {
-            if (! hasAnchor(listView)) {
-                setAnchor(listView, fm.getFocusedIndex());
-            }
-        } else {
-            removeAnchor(listView);
-        }
-
-        if (button == MouseButton.PRIMARY || (button == MouseButton.SECONDARY && !selected)) {
-            if (sm.getSelectionMode() == SelectionMode.SINGLE) {
-                simpleSelect(button, clickCount, shortcutDown);
-            } else {
-                if (shortcutDown) {
-                    if (selected) {
-                        // we remove this row from the current selection
-                        sm.clearSelection(index);
-                        fm.focus(index);
-                    } else {
-                        // We add this row to the current selection
-                        sm.select(index);
-                    }
-                } else if (shiftDown) {
-                    // we add all rows between the current focus and
-                    // this row (inclusive) to the current selection.
-                    final int focusIndex = getAnchor(listView);
-                    final boolean asc = focusIndex < index;
-
-                    // and then determine all row and columns which must be selected
-                    int minRow = Math.min(focusIndex, index);
-                    int maxRow = Math.max(focusIndex, index);
-
-                    // and then perform the selection.
-                    // We do this by deselecting the elements that are not in
-                    // range, and then selecting all elements that are in range.
-                    // To prevent RT-32119, we make a copy of the selected indices
-                    // list first, so that we are not iterating and modifying it
-                    // concurrently.
-                    List<Integer> selectedIndices = new ArrayList<>(sm.getSelectedIndices());
-                    for (int i = 0, max = selectedIndices.size(); i < max; i++) {
-                        int selectedIndex = selectedIndices.get(i);
-                        if (selectedIndex < minRow || selectedIndex > maxRow) {
-                            sm.clearSelection(selectedIndex);
-                        }
-                    }
-
-                    // RT-21444: We need to put the range in in the correct
-                    // order or else the last selected row will not be the
-                    // last item in the selectedItems list of the selection
-                    // model,
-                    if (asc) {
-                        sm.selectRange(minRow, maxRow + 1);
-                    } else {
-                        sm.selectRange(maxRow, minRow - 1);
-                    }
-
-                    // return selection back to the focus owner
-                    fm.focus(index);
-                } else {
-                    simpleSelect(button, clickCount, shortcutDown);
-                }
-            }
-        }
-    }
-
-    private void simpleSelect(MouseButton button, int clickCount, boolean shortcutDown) {
-        ListView<T> lv = getControl().getListView();
-        int index = getControl().getIndex();
-        MultipleSelectionModel<T> sm = lv.getSelectionModel();
-        boolean isAlreadySelected = sm.isSelected(index);
-
-        if (isAlreadySelected && shortcutDown) {
-            sm.clearSelection(index);
-            lv.getFocusModel().focus(index);
-            isAlreadySelected = false;
-        } else {
-            sm.clearAndSelect(index);
-        }
-
-        // handle editing, which only occurs with the primary mouse button
-        if (button == MouseButton.PRIMARY) {
-            if (clickCount == 1 && isAlreadySelected) {
-                lv.edit(index);
-            } else if (clickCount == 1) {
-                // cancel editing
-                lv.edit(-1);
-            } else if (clickCount == 2 && getControl().isEditable()) {
-                lv.edit(index);
-            }
-        }
+    @Override
+    void edit(ListCell<T> cell) {
+        int index = cell == null ? -1 : cell.getIndex();
+        getCellContainer().edit(index);
     }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/ListViewBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -341,11 +341,11 @@
     }
 
     private void setAnchor(int anchor) {
-        ListCellBehavior.setAnchor(getControl(), anchor);
+        ListCellBehavior.setAnchor(getControl(), anchor < 0 ? null : anchor);
     }
     
     private int getAnchor() {
-        return ListCellBehavior.getAnchor(getControl());
+        return ListCellBehavior.getAnchor(getControl(), getControl().getFocusModel().getFocusedIndex());
     }
     
     private boolean hasAnchor() {
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -56,7 +56,7 @@
      **************************************************************************/          
 
     /** @{@inheritDoc} */
-    @Override TableView<S> getTableControl() {
+    @Override TableView<S> getCellContainer() {
         return getControl().getTableView();
     }
 
@@ -67,22 +67,22 @@
 
     /** @{@inheritDoc} */
     @Override int getItemCount() {
-        return getTableControl().getItems().size();
+        return getCellContainer().getItems().size();
     }
 
     /** @{@inheritDoc} */
     @Override TableView.TableViewSelectionModel<S> getSelectionModel() {
-        return getTableControl().getSelectionModel();
+        return getCellContainer().getSelectionModel();
     }
 
     /** @{@inheritDoc} */
     @Override TableViewFocusModel<S> getFocusModel() {
-        return getTableControl().getFocusModel();
+        return getCellContainer().getFocusModel();
     }
 
     /** @{@inheritDoc} */
     @Override TablePositionBase getFocusedCell() {
-        return getTableControl().getFocusModel().getFocusedCell();
+        return getCellContainer().getFocusModel().getFocusedCell();
     }
 
     /** @{@inheritDoc} */
@@ -91,13 +91,8 @@
     }
 
     /** @{@inheritDoc} */
-    @Override TableColumnBase getVisibleLeafColumn(int index) {
-        return getTableControl().getVisibleLeafColumn(index);
-    }
-
-    /** @{@inheritDoc} */
     @Override protected int getVisibleLeafIndex(TableColumnBase tc) {
-        return getTableControl().getVisibleLeafIndex((TableColumn) tc);
+        return getCellContainer().getVisibleLeafIndex((TableColumn) tc);
     }
 
     /** @{@inheritDoc} */
@@ -106,7 +101,11 @@
     }
 
     /** @{@inheritDoc} */
-    @Override void edit(int row, TableColumnBase tc) {
-        getTableControl().edit(row, (TableColumn)tc);
+    @Override void edit(TableCell<S,T> cell) {
+        if (cell == null) {
+            getCellContainer().edit(-1, null);
+        } else {
+            getCellContainer().edit(cell.getIndex(), cell.getTableColumn());
+        }
     }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableCellBehaviorBase.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,60 +32,20 @@
 import javafx.scene.control.TableFocusModel;
 import javafx.scene.control.TablePositionBase;
 import javafx.scene.control.TableSelectionModel;
-import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.MouseButton;
-import javafx.scene.input.MouseEvent;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-/**
- */
 public abstract class TableCellBehaviorBase<S, T, TC extends TableColumnBase<S, ?>, C extends IndexedCell<T>> extends CellBehaviorBase<C> {
     
     /***************************************************************************
      *                                                                         *
-     * Private static implementation                                           *
-     *                                                                         *
-     **************************************************************************/
-    
-    private static final String ANCHOR_PROPERTY_KEY = "table.anchor";
-    
-    static TablePositionBase getAnchor(Control table, TablePositionBase focusedCell) {
-        return hasAnchor(table) ? 
-                (TablePositionBase) table.getProperties().get(ANCHOR_PROPERTY_KEY) : 
-                focusedCell;
-    }
-    
-    static void setAnchor(Control table, TablePositionBase anchor) {
-        if (table != null && anchor == null) {
-            removeAnchor(table);
-        } else {
-            table.getProperties().put(ANCHOR_PROPERTY_KEY, anchor);
-        }
-    }
-    
-    static boolean hasAnchor(Control table) {
-        return table.getProperties().get(ANCHOR_PROPERTY_KEY) != null;
-    }
-    
-    static void removeAnchor(Control table) {
-        table.getProperties().remove(ANCHOR_PROPERTY_KEY);
-    }
-    
-    
-    
-    /***************************************************************************
-     *                                                                         *
      * Private fields                                                          *
      *                                                                         *
      **************************************************************************/      
 
-    // To support touch devices, we have to slightly modify this behavior, such
-    // that selection only happens on mouse release, if only minimal dragging
-    // has occurred.
-    private boolean latePress = false;
 
     /***************************************************************************
      *                                                                         *
@@ -105,14 +65,12 @@
      *                                                                        *  
      *************************************************************************/  
     
-    abstract Control getTableControl(); // tableCell.getTreeTableView()
     abstract TableColumnBase<S, T> getTableColumn(); // getControl().getTableColumn()
     abstract int getItemCount();        // tableView.impl_getTreeItemCount()
     abstract TableSelectionModel<S> getSelectionModel();
     abstract TableFocusModel<S,TC> getFocusModel();
     abstract TablePositionBase getFocusedCell();
     abstract boolean isTableRowSelected(); // tableCell.getTreeTableRow().isSelected()
-    abstract TableColumnBase<S,T> getVisibleLeafColumn(int index);
 
     /**
      * Returns the position of the given table column in the visible leaf columns
@@ -121,8 +79,7 @@
     protected abstract int getVisibleLeafIndex(TableColumnBase<S,T> tc);
     
     abstract void focus(int row, TableColumnBase<S,T> tc); //fm.focus(new TreeTablePosition(tableView, row, tableColumn));
-    abstract void edit(int row, TableColumnBase<S,T> tc); 
-    
+
     
     
     /***************************************************************************
@@ -131,35 +88,7 @@
      *                                                                         *
      **************************************************************************/    
     
-    @Override public void mousePressed(MouseEvent e) {
-        if (e.isSynthesized()) {
-            latePress = true;
-        } else {
-            latePress  = getControl().isSelected();
-            if (!latePress) {
-                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                         e.isShiftDown(), e.isShortcutDown());
-            }
-        }
-    }
-    
-    @Override public void mouseReleased(MouseEvent e) {
-        if (latePress) {
-            latePress = false;
-            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                     e.isShiftDown(), e.isShortcutDown());
-        }
-    }
-    
-    @Override public void mouseDragged(MouseEvent e) {
-        latePress = false;
-    }
 
-    @Override public void contextMenuRequested(ContextMenuEvent e) {
-        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
-    }
-
-    
     
     /***************************************************************************
      *                                                                         *
@@ -167,7 +96,7 @@
      *                                                                         *
      **************************************************************************/   
     
-    private void doSelect(final double x, final double y, final MouseButton button,
+    protected void doSelect(final double x, final double y, final MouseButton button,
                           final int clickCount, final boolean shiftDown, final boolean shortcutDown) {
         // Note that table.select will reset selection
         // for out of bounds indexes. So, need to check
@@ -177,7 +106,7 @@
         // we don't want to react to it.
         if (! tableCell.contains(x, y)) return;
 
-        final Control tableView = getTableControl();
+        final Control tableView = getCellContainer();
         if (tableView == null) return;
         
         int count = getItemCount();
@@ -198,8 +127,7 @@
 
         // if the user has clicked on the disclosure node, we do nothing other
         // than expand/collapse the tree item (if applicable). We do not do editing!
-        boolean disclosureClicked = checkDisclosureNodeClick(x, y);
-        if (disclosureClicked) {
+        if (handleDisclosureNode(x, y)) {
             return;
         }
         
@@ -236,49 +164,21 @@
                     final TablePositionBase anchor = getAnchor(tableView, focusedCell);
 
                     final int anchorRow = anchor.getRow();
-                    final boolean asc = anchorRow < row;
-                    
-                    // and then determine all row and columns which must be selected
-                    int minRow = Math.min(anchor.getRow(), row);
-                    int maxRow = Math.max(anchor.getRow(), row);
-                    TableColumnBase<S,T> minColumn = anchor.getColumn() < column ? anchor.getTableColumn() : tableColumn;
-                    TableColumnBase<S,T> maxColumn = anchor.getColumn() >= column ? anchor.getTableColumn() : tableColumn;
 
                     if (sm.isCellSelectionEnabled()) {
                         // clear selection, but maintain the anchor
                         sm.clearSelection();
 
+                        // and then determine all row and columns which must be selected
+                        int minRow = Math.min(anchor.getRow(), row);
+                        int maxRow = Math.max(anchor.getRow(), row);
+                        TableColumnBase<S,T> minColumn = anchor.getColumn() < column ? anchor.getTableColumn() : tableColumn;
+                        TableColumnBase<S,T> maxColumn = anchor.getColumn() >= column ? anchor.getTableColumn() : tableColumn;
+
                         // and then perform the selection
                         sm.selectRange(minRow, minColumn, maxRow, maxColumn);
                     } else {
-                        // To prevent RT-32119, we make a copy of the selected indices
-                        // list first, so that we are not iterating and modifying it
-                        // concurrently.
-                        List<Integer> selectedIndices = new ArrayList<>(sm.getSelectedIndices());
-                        for (int i = 0, max = selectedIndices.size(); i < max; i++) {
-                            int selectedIndex = selectedIndices.get(i);
-                            if (selectedIndex < minRow || selectedIndex > maxRow) {
-                                sm.clearSelection(selectedIndex);
-                            }
-                        }
-
-                        if (minRow == maxRow) {
-                            // RT-32560: This prevents the anchor 'sticking' in
-                            // the wrong place when a range is selected and then
-                            // selection goes back to the anchor position.
-                            // (Refer to the video in RT-32560 for more detail).
-                            sm.select(minRow);
-                        } else {
-                            // RT-21444: We need to put the range in the correct
-                            // order or else the last selected row will not be the
-                            // last item in the selectedItems list of the selection
-                            // model,
-                            if (asc) {
-                                sm.selectRange(minRow, maxRow + 1);
-                            } else {
-                                sm.selectRange(maxRow, minRow - 1);
-                            }
-                        }
+                        selectRows(anchorRow, row);
                     }
 
                     // This line of code below was disabled as a fix for RT-30394.
@@ -314,22 +214,17 @@
         // handle editing, which only occurs with the primary mouse button
         if (button == MouseButton.PRIMARY) {
             if (clickCount == 1 && isAlreadySelected) {
-                edit(row, column);
+                edit(getControl());
             } else if (clickCount == 1) {
                 // cancel editing
-                edit(-1, null);
+                edit(null);
             } else if (clickCount == 2 && getControl().isEditable()) {
                 // edit at the specified row and column
-                edit(row, column);
+                edit(getControl());
             }
         }
     }
 
-    protected boolean checkDisclosureNodeClick(double x, double y) {
-        // by default we don't care about disclosure nodes
-        return false;
-    }
-
     private int getColumn() {
         if (getSelectionModel().isCellSelectionEnabled()) {
             TableColumnBase<S,T> tc = getTableColumn();
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,29 +25,14 @@
 
 package com.sun.javafx.scene.control.behavior;
 
-import javafx.scene.control.*;
-import javafx.scene.input.ContextMenuEvent;
-import javafx.scene.input.MouseButton;
-import javafx.scene.input.MouseEvent;
+import javafx.collections.ObservableList;
+import javafx.scene.control.FocusModel;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableRow;
+import javafx.scene.control.TableSelectionModel;
+import javafx.scene.control.TableView;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class TableRowBehavior<T> extends CellBehaviorBase<TableRow<T>> {
-
-    /***************************************************************************
-     *                                                                         *
-     * Private fields                                                          *
-     *                                                                         *
-     **************************************************************************/
-
-    // To support touch devices, we have to slightly modify this behavior, such
-    // that selection only happens on mouse release, if only minimal dragging
-    // has occurred.
-    private boolean latePress = false;
-
-
+public class TableRowBehavior<T> extends TableRowBehaviorBase<TableRow<T>> {
 
     /***************************************************************************
      *                                                                         *
@@ -56,7 +41,7 @@
      **************************************************************************/
 
     public TableRowBehavior(TableRow<T> control) {
-        super(control, Collections.EMPTY_LIST);
+        super(control);
     }
 
 
@@ -67,126 +52,27 @@
      *                                                                         *
      **************************************************************************/
 
-    @Override public void mousePressed(MouseEvent e) {
-        // we only care about clicks to the right of the right-most column
-        if (! isClickOutsideCellBounds(e.getX())) return;
-
-        if (e.isSynthesized()) {
-            latePress = true;
-        } else {
-            latePress  = getControl().isSelected();
-            if (!latePress) {
-                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                         e.isShiftDown(), e.isShortcutDown());
-            }
-        }
+    @Override TableSelectionModel<T> getSelectionModel() {
+        return getCellContainer().getSelectionModel();
     }
 
-    @Override public void mouseReleased(MouseEvent e) {
-        if (latePress) {
-            latePress = false;
-            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                     e.isShiftDown(), e.isShortcutDown());
-        }
+    @Override TablePositionBase<?> getFocusedCell() {
+        return getCellContainer().getFocusModel().getFocusedCell();
     }
 
-    @Override public void mouseDragged(MouseEvent e) {
-        latePress = false;
+    @Override FocusModel<T> getFocusModel() {
+        return getCellContainer().getFocusModel();
     }
 
-    @Override public void contextMenuRequested(ContextMenuEvent e) {
-        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
+    @Override ObservableList getVisibleLeafColumns() {
+        return getCellContainer().getVisibleLeafColumns();
     }
 
-
-
-    /***************************************************************************
-     *                                                                         *
-     * Private implementation                                                  *
-     *                                                                         *
-     **************************************************************************/
-
-    private void doSelect(final double x, final double y, final MouseButton button,
-                          final int clickCount, final boolean shiftDown,
-                          final boolean shortcutDown) {
-        final TableRow<T> tableRow = getControl();
-        final TableView<T> table = tableRow.getTableView();
-        if (table == null) return;
-        final TableSelectionModel<T> sm = table.getSelectionModel();
-        if (sm == null || sm.isCellSelectionEnabled()) return;
-        
-        final int index = getControl().getIndex();
-        final boolean isAlreadySelected = sm.isSelected(index);
-        if (clickCount == 1) {
-            // we only care about clicks to the right of the right-most column
-            if (! isClickOutsideCellBounds(x)) return;
-            
-            // In the case of clicking to the right of the rightmost
-            // TreeTableCell, we should still support selection, so that
-            // is what we are doing here.
-            if (isAlreadySelected && shortcutDown) {
-                sm.clearSelection(index);
-            } else {
-                if (shortcutDown) {
-                    sm.select(tableRow.getIndex());
-                } else if (shiftDown) {
-                    // we add all rows between the current focus and
-                    // this row (inclusive) to the current selection.
-                    TablePositionBase anchor = TableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
-                    final int anchorRow = anchor.getRow();
-                    final boolean asc = anchorRow < index;
-
-                    // and then determine all row and columns which must be selected
-                    int minRow = Math.min(anchorRow, index);
-                    int maxRow = Math.max(anchorRow, index);
-
-                    // To prevent RT-32119, we make a copy of the selected indices
-                    // list first, so that we are not iterating and modifying it
-                    // concurrently.
-                    List<Integer> selectedIndices = new ArrayList<>(sm.getSelectedIndices());
-                    for (int i = 0, max = selectedIndices.size(); i < max; i++) {
-                        int selectedIndex = selectedIndices.get(i);
-                        if (selectedIndex < minRow || selectedIndex > maxRow) {
-                            sm.clearSelection(selectedIndex);
-                        }
-                    }
-
-                    if (minRow == maxRow) {
-                        // RT-32560: This prevents the anchor 'sticking' in
-                        // the wrong place when a range is selected and then
-                        // selection goes back to the anchor position.
-                        // (Refer to the video in RT-32560 for more detail).
-                        sm.select(minRow);
-                    } else {
-                        // RT-21444: We need to put the range in the correct
-                        // order or else the last selected row will not be the
-                        // last item in the selectedItems list of the selection
-                        // model,
-                        if (asc) {
-                            sm.selectRange(minRow, maxRow + 1);
-                        } else {
-                            sm.selectRange(maxRow, minRow - 1);
-                        }
-                    }
-                } else {
-                    sm.clearAndSelect(tableRow.getIndex());
-                }
-            }
-        }
+    @Override TableView<T> getCellContainer() {
+        return getControl().getTableView();
     }
 
-    private boolean isClickOutsideCellBounds(final double x) {
-        // get width of all visible columns (we only care about clicks to the
-        // right of the right-most column)
-        final TableRow<T> tableRow = getControl();
-        final TableView<T> table = tableRow.getTableView();
-        if (table == null) return false;
-        List<TableColumn<T, ?>> columns = table.getVisibleLeafColumns();
-        double width = 0.0;
-        for (int i = 0; i < columns.size(); i++) {
-            width += columns.get(i).getWidth();
-        }
-
-        return x > width;
+    @Override void edit(TableRow<T> cell) {
+        // no-op (for now)
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TableRowBehaviorBase.java	Mon Mar 17 16:23:17 2014 +1300
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.javafx.scene.control.behavior;
+
+import javafx.collections.ObservableList;
+import javafx.scene.control.Cell;
+import javafx.scene.control.Control;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableColumnBase;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableSelectionModel;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class TableRowBehaviorBase<T extends Cell> extends CellBehaviorBase<T> {
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
+
+    public TableRowBehaviorBase(T control) {
+        super(control, Collections.EMPTY_LIST);
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    @Override public void mousePressed(MouseEvent e) {
+        // we only care about clicks to the right of the right-most column
+        if (! isClickPositionValid(e.getX(), e.getY())) return;
+
+        super.mousePressed(e);
+    }
+
+    @Override abstract TableSelectionModel<?> getSelectionModel();
+
+    abstract TablePositionBase<?> getFocusedCell();
+
+    abstract ObservableList getVisibleLeafColumns();
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
+
+    @Override protected void doSelect(final double x, final double y, final MouseButton button,
+                   final int clickCount, final boolean shiftDown, final boolean shortcutDown) {
+        final Control table = getCellContainer();
+        if (table == null) return;
+
+        // if the user has clicked on the disclosure node, we do nothing other
+        // than expand/collapse the tree item (if applicable). We do not do editing!
+        if (handleDisclosureNode(x,y)) {
+            return;
+        }
+
+        final TableSelectionModel<?> sm = getSelectionModel();
+        if (sm == null || sm.isCellSelectionEnabled()) return;
+
+        final int index = getIndex();
+        final boolean isAlreadySelected = sm.isSelected(index);
+        if (clickCount == 1) {
+            // we only care about clicks to the right of the right-most column
+            if (! isClickPositionValid(x, y)) return;
+
+            // In the case of clicking to the right of the rightmost
+            // TreeTableCell, we should still support selection, so that
+            // is what we are doing here.
+            if (isAlreadySelected && shortcutDown) {
+                sm.clearSelection(index);
+            } else {
+                if (shortcutDown) {
+                    sm.select(getIndex());
+                } else if (shiftDown) {
+                    // we add all rows between the current focus and
+                    // this row (inclusive) to the current selection.
+                    TablePositionBase anchor = TableCellBehavior.getAnchor(table, getFocusedCell());
+                    final int anchorRow = anchor.getRow();
+                    selectRows(anchorRow, index);
+                } else {
+                    sm.clearAndSelect(getIndex());
+                }
+            }
+        }
+    }
+
+    @Override boolean isClickPositionValid(final double x, final double y) {
+        // get width of all visible columns (we only care about clicks to the
+        // right of the right-most column)
+        List<TableColumnBase<T, ?>> columns = getVisibleLeafColumns();
+        double width = 0.0;
+        for (int i = 0; i < columns.size(); i++) {
+            width += columns.get(i).getWidth();
+        }
+
+        return x > width;
+    }
+}
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeCellBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,78 +25,20 @@
 
 package com.sun.javafx.scene.control.behavior;
 
-import com.sun.javafx.scene.control.Logging;
 import javafx.scene.Node;
 import javafx.scene.control.FocusModel;
 import javafx.scene.control.MultipleSelectionModel;
-import javafx.scene.control.SelectionMode;
 import javafx.scene.control.TreeCell;
 import javafx.scene.control.TreeItem;
 import javafx.scene.control.TreeView;
-import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.MouseButton;
-import javafx.scene.input.MouseEvent;
-import sun.util.logging.PlatformLogger;
-import sun.util.logging.PlatformLogger.Level;
 
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.List;
 
-/**
- */
 public class TreeCellBehavior<T> extends CellBehaviorBase<TreeCell<T>> {
 
     /***************************************************************************
      *                                                                         *
-     * Private static implementation                                           *
-     *                                                                         *
-     **************************************************************************/
-
-    private static final String ANCHOR_PROPERTY_KEY = "list.anchor";
-
-    static int getAnchor(TreeView<?> tree) {
-        FocusModel<?> fm = tree.getFocusModel();
-        if (fm == null) return -1;
-
-        return hasAnchor(tree) ?
-                (int)tree.getProperties().get(ANCHOR_PROPERTY_KEY) :
-                fm.getFocusedIndex();
-    }
-
-    static void setAnchor(TreeView<?> tree, int anchor) {
-        if (tree != null && anchor < 0) {
-            removeAnchor(tree);
-        } else {
-            tree.getProperties().put(ANCHOR_PROPERTY_KEY, anchor);
-        }
-    }
-
-    static boolean hasAnchor(TreeView<?> tree) {
-        return tree.getProperties().get(ANCHOR_PROPERTY_KEY) != null;
-    }
-
-    static void removeAnchor(TreeView<?> tree) {
-        tree.getProperties().remove(ANCHOR_PROPERTY_KEY);
-    }
-
-
-
-    /***************************************************************************
-     *                                                                         *
-     * Private fields                                                          *
-     *                                                                         *
-     **************************************************************************/
-
-    // To support touch devices, we have to slightly modify this behavior, such
-    // that selection only happens on mouse release, if only minimal dragging
-    // has occurred.
-    private boolean latePress = false;
-
-
-
-    /***************************************************************************
-     *                                                                         *
      * Constructors                                                            *
      *                                                                         *
      **************************************************************************/
@@ -109,174 +51,62 @@
 
     /***************************************************************************
      *                                                                         *
-     * Public API                                                              *
+     * Private implementation                                                  *
      *                                                                         *
      **************************************************************************/
 
-    @Override public void mousePressed(MouseEvent e) {
+    @Override
+    MultipleSelectionModel<TreeItem<T>> getSelectionModel() {
+        return getCellContainer().getSelectionModel();
+    }
 
-        if (e.isSynthesized()) {
-            latePress = true;
-        } else {
-            latePress  = getControl().isSelected();
-            if (!latePress) {
-                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                         e.isShiftDown(), e.isShortcutDown());
+    @Override
+    FocusModel<TreeItem<T>> getFocusModel() {
+        return getCellContainer().getFocusModel();
+    }
+
+    @Override
+    TreeView<T> getCellContainer() {
+        return getControl().getTreeView();
+    }
+
+    @Override
+    void edit(TreeCell<T> cell) {
+        TreeItem treeItem = cell == null ? null : cell.getTreeItem();
+        getCellContainer().edit(treeItem);
+    }
+
+    @Override
+    protected void handleClicks(MouseButton button, int clickCount, boolean isAlreadySelected) {
+        // handle editing, which only occurs with the primary mouse button
+        TreeItem<T> treeItem = getControl().getTreeItem();
+        if (button == MouseButton.PRIMARY) {
+            if (clickCount == 1 && isAlreadySelected) {
+                edit(getControl());
+            } else if (clickCount == 1) {
+                // cancel editing
+                edit(null);
+            } else if (clickCount == 2 && treeItem.isLeaf()) {
+                // attempt to edit
+                edit(getControl());
+            } else if (clickCount % 2 == 0) {
+                // try to expand/collapse branch tree item
+                treeItem.setExpanded(! treeItem.isExpanded());
             }
         }
     }
 
-    @Override public void mouseReleased(MouseEvent e) {
-        if (latePress) {
-            latePress = false;
-            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                     e.isShiftDown(), e.isShortcutDown());
-        }
-    }
-
-    @Override public void mouseDragged(MouseEvent e) {
-        latePress = false;
-    }
-
-    @Override public void contextMenuRequested(ContextMenuEvent e) {
-        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
-    }
-
-    /***************************************************************************
-     *                                                                         *
-     * Private implementation                                                  *
-     *                                                                         *
-     **************************************************************************/
-
-    private void doSelect(final double x, final double y, final MouseButton button,
-                          final int clickCount, final boolean shiftDown, final boolean shortcutDown) {
-        // we update the cell to point to the new tree node
+    @Override boolean handleDisclosureNode(double x, double y) {
         TreeCell<T> treeCell = getControl();
-        TreeView<T> treeView = treeCell.getTreeView();
-        if (treeView == null) return;
-
-        // If the mouse event is not contained within this TreeCell, then
-        // we don't want to react to it.
-        if (treeCell.isEmpty() || ! treeCell.contains(x, y)) {
-            return;
-        }
-
-        int index = treeCell.getIndex();
-        boolean selected = treeCell.isSelected();
-        MultipleSelectionModel<TreeItem<T>> sm = treeView.getSelectionModel();
-        if (sm == null) return;
-
-        FocusModel<TreeItem<T>> fm = treeView.getFocusModel();
-        if (fm == null) return;
-
-        // if the user has clicked on the disclosure node, we do nothing other
-        // than expand/collapse the tree item (if applicable). We do not do editing!
         Node disclosureNode = treeCell.getDisclosureNode();
         if (disclosureNode != null) {
             if (disclosureNode.getBoundsInParent().contains(x, y)) {
                 if (treeCell.getTreeItem() != null) {
                     treeCell.getTreeItem().setExpanded(! treeCell.getTreeItem().isExpanded());
                 }
-                return;
+                return true;
             }
         }
-
-        // if shift is down, and we don't already have the initial focus index
-        // recorded, we record the focus index now so that subsequent shift+clicks
-        // result in the correct selection occuring (whilst the focus index moves
-        // about).
-        if (shiftDown) {
-            if (! hasAnchor(treeView)) {
-                setAnchor(treeView, fm.getFocusedIndex());
-            }
-        } else {
-            removeAnchor(treeView);
-        }
-
-        if (button == MouseButton.PRIMARY || (button == MouseButton.SECONDARY && !selected)) {
-            if (sm.getSelectionMode() == SelectionMode.SINGLE) {
-                simpleSelect(button, clickCount, shortcutDown);
-            } else {
-                if (shortcutDown) {
-                    if (selected) {
-                        // we remove this row from the current selection
-                        sm.clearSelection(index);
-                        fm.focus(index);
-                    } else {
-                        // We add this row to the current selection
-                        sm.select(index);
-                    }
-                } else if (shiftDown && clickCount == 1) {
-                    // we add all rows between the current selection focus and
-                    // this row (inclusive) to the current selection.
-                    final int focusedIndex = getAnchor(treeView);
-                    final boolean asc = focusedIndex < index;
-
-                    // and then determine all row and columns which must be selected
-                    int minRow = Math.min(focusedIndex, index);
-                    int maxRow = Math.max(focusedIndex, index);
-
-                    // and then perform the selection
-                    // We do this by deselecting the elements that are not in
-                    // range, and then selecting all elements that are in range
-                    // To prevent RT-32119, we make a copy of the selected indices
-                    // list first, so that we are not iterating and modifying it
-                    // concurrently.
-                    List<Integer> selectedIndices = new ArrayList<>(sm.getSelectedIndices());
-                    for (int i = 0, max = selectedIndices.size(); i < max; i++) {
-                        int selectedIndex = selectedIndices.get(i);
-                        if (selectedIndex < minRow || selectedIndex > maxRow) {
-                            sm.clearSelection(selectedIndex);
-                        }
-                    }
-
-                    // RT-21444: We need to put the range in in the correct
-                    // order or else the last selected row will not be the
-                    // last item in the selectedItems list of the selection
-                    // model,
-                    if (asc) {
-                        sm.selectRange(minRow, maxRow + 1);
-                    } else {
-                        sm.selectRange(maxRow, minRow - 1);
-                    }
-
-                    fm.focus(index);
-                } else {
-                    simpleSelect(button, clickCount, shortcutDown);
-                }
-            }
-        }
-    }
-
-    private void simpleSelect(MouseButton button, int clickCount, boolean shortcutDown) {
-        TreeView<T> tv = getControl().getTreeView();
-        TreeItem<T> treeItem = getControl().getTreeItem();
-        int index = getControl().getIndex();
-        MultipleSelectionModel<TreeItem<T>> sm = tv.getSelectionModel();
-        boolean isAlreadySelected = sm.isSelected(index);
-
-        if (isAlreadySelected && shortcutDown) {
-            sm.clearSelection(index);
-            tv.getFocusModel().focus(index);
-            isAlreadySelected = false;
-        } else {
-            sm.clearAndSelect(index);
-        }
-
-        // handle editing, which only occurs with the primary mouse button
-        if (button == MouseButton.PRIMARY) {
-            if (clickCount == 1 && isAlreadySelected) {
-                tv.edit(treeItem);
-            } else if (clickCount == 1) {
-                // cancel editing
-                tv.edit(null);
-            } else if (clickCount == 2 && treeItem.isLeaf()) {
-                // attempt to edit
-                tv.edit(treeItem);
-            } else if (clickCount % 2 == 0) {
-                // try to expand/collapse branch tree item
-                treeItem.setExpanded(! treeItem.isExpanded());
-            }
-        }
+        return false;
     }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -59,7 +59,7 @@
      **************************************************************************/          
 
     /** @{@inheritDoc} */
-    @Override TreeTableView<S> getTableControl() {
+    @Override TreeTableView<S> getCellContainer() {
         return getControl().getTreeTableView();
     }
 
@@ -70,22 +70,22 @@
 
     /** @{@inheritDoc} */
     @Override int getItemCount() {
-        return getTableControl().getExpandedItemCount();
+        return getCellContainer().getExpandedItemCount();
     }
 
     /** @{@inheritDoc} */
     @Override TreeTableView.TreeTableViewSelectionModel<S> getSelectionModel() {
-        return getTableControl().getSelectionModel();
+        return getCellContainer().getSelectionModel();
     }
 
     /** @{@inheritDoc} */
     @Override TreeTableView.TreeTableViewFocusModel<S> getFocusModel() {
-        return getTableControl().getFocusModel();
+        return getCellContainer().getFocusModel();
     }
 
     /** @{@inheritDoc} */
     @Override TablePositionBase getFocusedCell() {
-        return getTableControl().getFocusModel().getFocusedCell();
+        return getCellContainer().getFocusModel().getFocusedCell();
     }
 
     /** @{@inheritDoc} */
@@ -94,13 +94,8 @@
     }
 
     /** @{@inheritDoc} */
-    @Override TableColumnBase getVisibleLeafColumn(int index) {
-        return getTableControl().getVisibleLeafColumn(index);
-    }
-
-    /** @{@inheritDoc} */
     @Override protected int getVisibleLeafIndex(TableColumnBase tc) {
-        return getTableControl().getVisibleLeafIndex(null);
+        return getCellContainer().getVisibleLeafIndex(null);
     }
 
     /** @{@inheritDoc} */
@@ -109,11 +104,15 @@
     }
 
     /** @{@inheritDoc} */
-    @Override void edit(int row, TableColumnBase tc) {
-        getTableControl().edit(row, (TreeTableColumn)tc);
+    @Override void edit(TreeTableCell<S,T> cell) {
+        if (cell == null) {
+            getCellContainer().edit(-1, null);
+        } else {
+            getCellContainer().edit(cell.getIndex(), cell.getTableColumn());
+        }
     }
 
-    @Override protected boolean checkDisclosureNodeClick(double x, double y) {
+    @Override protected boolean handleDisclosureNode(double x, double y) {
         final TreeItem<S> treeItem = getControl().getTreeTableRow().getTreeItem();
         final Node disclosureNode = getControl().getTreeTableRow().getDisclosureNode();
         if (disclosureNode != null) {
@@ -127,33 +126,19 @@
         return false;
     }
     
-    @Override protected void simpleSelect(MouseButton button, int clickCount, boolean shortcutDown) {
-        TreeTableView<S> tv = getControl().getTreeTableView();
-        TreeItem treeItem = getControl().getTreeTableRow().getTreeItem();
-        int index = getControl().getIndex();
-        TreeTableColumn<S,T> column = getTableColumn();
-        TableSelectionModel sm = tv.getSelectionModel();
-        
-        boolean isAlreadySelected = sm.isSelected(index, column);
-
-        if (isAlreadySelected && shortcutDown) {
-            sm.clearSelection(index, column);
-            isAlreadySelected = false;
-        } else {
-            // we check if cell selection is enabled to fix RT-33897
-            sm.clearAndSelect(index, sm.isCellSelectionEnabled() ? column : null);
-        }
-
+    @Override
+    protected void handleClicks(MouseButton button, int clickCount, boolean isAlreadySelected) {
         // handle editing, which only occurs with the primary mouse button
+        TreeItem<S> treeItem = getControl().getTreeTableRow().getTreeItem();
         if (button == MouseButton.PRIMARY) {
             if (clickCount == 1 && isAlreadySelected) {
-                tv.edit(index, column);
+                edit(getControl());
             } else if (clickCount == 1) {
                 // cancel editing
-                tv.edit(-1, null);
+                edit(null);
             } else if (clickCount == 2 && treeItem.isLeaf()) {
                 // attempt to edit
-                tv.edit(index, column);
+                edit(getControl());
             } else if (clickCount % 2 == 0) {
                 // try to expand/collapse branch tree item
                 treeItem.setExpanded(! treeItem.isExpanded());
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeTableRowBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,31 +25,15 @@
 
 package com.sun.javafx.scene.control.behavior;
 
-import javafx.scene.Node;
-import javafx.scene.control.*;
-import javafx.scene.control.TreeTableView.TreeTableViewSelectionModel;
-import javafx.scene.input.ContextMenuEvent;
-import javafx.scene.input.MouseButton;
-import javafx.scene.input.MouseEvent;
+import javafx.collections.ObservableList;
+import javafx.scene.control.TableFocusModel;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableSelectionModel;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableRow;
+import javafx.scene.control.TreeTableView;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class TreeTableRowBehavior<T> extends CellBehaviorBase<TreeTableRow<T>> {
-
-    /***************************************************************************
-     *                                                                         *
-     * Private fields                                                          *
-     *                                                                         *
-     **************************************************************************/
-
-    // To support touch devices, we have to slightly modify this behavior, such
-    // that selection only happens on mouse release, if only minimal dragging
-    // has occurred.
-    private boolean latePress = false;
-
-
+public class TreeTableRowBehavior<T> extends TableRowBehaviorBase<TreeTableRow<T>> {
 
     /***************************************************************************
      *                                                                         *
@@ -58,7 +42,7 @@
      **************************************************************************/
 
     public TreeTableRowBehavior(TreeTableRow<T> control) {
-        super(control, Collections.EMPTY_LIST);
+        super(control);
     }
 
     /***************************************************************************
@@ -67,145 +51,27 @@
      *                                                                         *
      **************************************************************************/
 
-    @Override public void mousePressed(MouseEvent e) {
-        // we only care about clicks to the right of the right-most column
-        if (! isClickOutsideCellBounds(e.getX())) return;
-
-        if (e.isSynthesized()) {
-            latePress = true;
-        } else {
-            latePress  = getControl().isSelected();
-            if (!latePress) {
-                doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                         e.isShiftDown(), e.isShortcutDown());
-            }
-        }
+    @Override TableSelectionModel<TreeItem<T>> getSelectionModel() {
+        return getCellContainer().getSelectionModel();
     }
 
-    @Override public void mouseReleased(MouseEvent e) {
-        if (latePress) {
-            latePress = false;
-            doSelect(e.getX(), e.getY(), e.getButton(), e.getClickCount(),
-                     e.isShiftDown(), e.isShortcutDown());
-        }
+    @Override TableFocusModel<TreeItem<T>,?> getFocusModel() {
+        return getCellContainer().getFocusModel();
     }
 
-    @Override public void mouseDragged(MouseEvent event) {
-        latePress = false;
+    @Override TreeTableView<T> getCellContainer() {
+        return getControl().getTreeTableView();
     }
 
-    @Override public void contextMenuRequested(ContextMenuEvent e) {
-        doSelect(e.getX(), e.getY(), MouseButton.SECONDARY, 1, false, false);
+    @Override TablePositionBase<?> getFocusedCell() {
+        return getCellContainer().getFocusModel().getFocusedCell();
     }
 
-
-
-    /***************************************************************************
-     *                                                                         *
-     * Private implementation                                                  *
-     *                                                                         *
-     **************************************************************************/
-
-    private void doSelect(final double x, final double y, final MouseButton button,
-                          final int clickCount, final boolean shiftDown, final boolean shortcutDown) {
-        TreeTableRow<T> treeTableRow = getControl();
-        TreeItem<T> treeItem = treeTableRow.getTreeItem();
-        if (treeItem == null) return;
-        
-        // if the user has clicked on the disclosure node, we do nothing other
-        // than expand/collapse the tree item (if applicable). We do not do editing!
-        Node disclosureNode = treeTableRow.getDisclosureNode();
-        if (disclosureNode != null) {
-            if (disclosureNode.getBoundsInParent().contains(x, y)) {
-                treeItem.setExpanded(! treeItem.isExpanded());
-                return;
-            }
-        }
-
-        TreeTableView<T> table = treeTableRow.getTreeTableView();
-        if (table == null) return;
-        final TreeTableViewSelectionModel<T> sm = table.getSelectionModel();
-        if (sm == null || sm.isCellSelectionEnabled()) return;
-        
-        final int index = getControl().getIndex();
-        final boolean isAlreadySelected = sm.isSelected(index);
-        if (clickCount == 1) {
-            // get width of all visible columns (we only care about clicks to the
-            // right of the right-most column)
-            List<TreeTableColumn<T, ?>> columns = getControl().getTreeTableView().getVisibleLeafColumns();
-            double width = 0.0;
-            for (int i = 0; i < columns.size(); i++) {
-                width += columns.get(i).getWidth();
-            }
-            
-            if (x < width) return;
-            
-            // In the case of clicking to the right of the rightmost
-            // TreeTableCell, we should still support selection, so that
-            // is what we are doing here.
-            if (isAlreadySelected && shortcutDown) {
-                sm.clearSelection(index);
-            } else {
-                if (shortcutDown) {
-                    sm.select(treeTableRow.getIndex());
-                } else if (shiftDown) {
-                    // we add all rows between the current focus and
-                    // this row (inclusive) to the current selection.
-                    TablePositionBase anchor = TreeTableCellBehavior.getAnchor(table, table.getFocusModel().getFocusedCell());
-                    final int anchorRow = anchor.getRow();
-                    final boolean asc = anchorRow < index;
-
-                    // and then determine all row and columns which must be selected
-                    int minRow = Math.min(anchor.getRow(), index);
-                    int maxRow = Math.max(anchor.getRow(), index);
-
-                    // To prevent RT-32119, we make a copy of the selected indices
-                    // list first, so that we are not iterating and modifying it
-                    // concurrently.
-                    List<Integer> selectedIndices = new ArrayList<>(sm.getSelectedIndices());
-                    for (int i = 0, max = selectedIndices.size(); i < max; i++) {
-                        int selectedIndex = selectedIndices.get(i);
-                        if (selectedIndex < minRow || selectedIndex > maxRow) {
-                            sm.clearSelection(selectedIndex);
-                        }
-                    }
-
-                    if (minRow == maxRow) {
-                        // RT-32560: This prevents the anchor 'sticking' in
-                        // the wrong place when a range is selected and then
-                        // selection goes back to the anchor position.
-                        // (Refer to the video in RT-32560 for more detail).
-                        sm.select(minRow);
-                    } else {
-                        // RT-21444: We need to put the range in the correct
-                        // order or else the last selected row will not be the
-                        // last item in the selectedItems list of the selection
-                        // model,
-                        if (asc) {
-                            sm.selectRange(minRow, maxRow + 1);
-                        } else {
-                            sm.selectRange(maxRow, minRow - 1);
-                        }
-                    }
-                } else {
-                    sm.clearAndSelect(treeTableRow.getIndex());
-                }
-            }
-        }
+    @Override ObservableList getVisibleLeafColumns() {
+        return getCellContainer().getVisibleLeafColumns();
     }
 
-    private boolean isClickOutsideCellBounds(final double x) {
-        // get width of all visible columns (we only care about clicks to the
-        // right of the right-most column)
-        final TreeTableRow<T> tableRow = getControl();
-        final TreeTableView<T> table = tableRow.getTreeTableView();
-        if (table == null) return false;
-        List<TreeTableColumn<T, ?>> columns = table.getVisibleLeafColumns();
-        double width = 0.0;
-        for (int i = 0; i < columns.size(); i++) {
-            width += columns.get(i).getWidth();
-        }
-
-        return x > width;
+    @Override void edit(TreeTableRow<T> cell) {
+        // no-op (for now)
     }
 }
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Mon Mar 17 16:23:17 2014 +1300
@@ -289,11 +289,11 @@
     }
     
     private void setAnchor(int anchor) {
-        TreeCellBehavior.setAnchor(getControl(), anchor);
+        TreeCellBehavior.setAnchor(getControl(), anchor < 0 ? null : anchor);
     }
     
     private int getAnchor() {
-        return TreeCellBehavior.getAnchor(getControl());
+        return TreeCellBehavior.getAnchor(getControl(), getControl().getFocusModel().getFocusedIndex());
     }
     
     private boolean hasAnchor() {
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/CellSkinBase.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/CellSkinBase.java	Mon Mar 17 16:23:17 2014 +1300
@@ -29,6 +29,7 @@
 import java.util.Collections;
 import java.util.List;
 
+import com.sun.javafx.scene.control.behavior.BehaviorBase;
 import javafx.scene.control.Cell;
 
 import javafx.css.StyleableDoubleProperty;
@@ -40,6 +41,7 @@
 import javafx.beans.property.ReadOnlyDoubleProperty;
 import javafx.css.Styleable;
 import javafx.css.StyleableProperty;
+import javafx.scene.control.IndexedCell;
 import javafx.scene.control.SkinBase;
 
 
@@ -48,21 +50,20 @@
  * This might not be a suitable base class for TreeCellSkin or some other
  * such skins.
  */
-public class CellSkinBase<C extends Cell, B extends CellBehaviorBase<C>> extends LabeledSkinBase<C, B> {
+public class CellSkinBase<C extends Cell, B extends BehaviorBase<C>> extends LabeledSkinBase<C, B> {
     /**
      * The default cell size. For vertical ListView or a TreeView or TableView
      * this is the height, for a horizontal ListView this is the width. This
      * is settable from CSS
      */
     private DoubleProperty cellSize;
-//    boolean cellSizeSet = false;
 
     public final double getCellSize() {
         return cellSize == null ? DEFAULT_CELL_SIZE : cellSize.get();
     }
 
     public final ReadOnlyDoubleProperty cellSizeProperty() {
-        return (ReadOnlyDoubleProperty)cellSizePropertyImpl();
+        return cellSizePropertyImpl();
     }
 
     private DoubleProperty cellSizePropertyImpl() {
--- a/modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/ListViewAnchorRetriever.java	Mon Mar 17 16:23:17 2014 +1300
@@ -31,6 +31,6 @@
 
     // can only access the getAnchor method in ListCellBehavior from this package
     public static int getAnchor(ListView listView) {
-        return ListCellBehavior.getAnchor(listView);
+        return ListCellBehavior.getAnchor(listView, -1);
     }
 }
--- a/modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java	Mon Mar 17 13:44:38 2014 +1300
+++ b/modules/controls/src/test/java/com/sun/javafx/scene/control/behavior/TreeViewAnchorRetriever.java	Mon Mar 17 16:23:17 2014 +1300
@@ -31,6 +31,6 @@
 
     // can only access the getAnchor method in TreeCellBehavior from this package
     public static int getAnchor(TreeView treeView) {
-        return TreeCellBehavior.getAnchor(treeView);
+        return TreeCellBehavior.getAnchor(treeView, -1);
     }
 }