changeset 1834:ddd73130f154 tip

Further work on TreeTableView: now with improved CSS styling and behavior implementation (for keyboard navigation)
author jgiles
date Wed, 21 Nov 2012 13:02:09 +1300
parents 33c215edd891
children
files javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableCellBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehaviorBase.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableViewBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableViewSkin.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TreeTableViewSkin.java javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css javafx-ui-controls/src/javafx/scene/control/TablePosition.java javafx-ui-controls/src/javafx/scene/control/TablePositionBase.java javafx-ui-controls/src/javafx/scene/control/TreeTablePosition.java
diffstat 12 files changed, 1806 insertions(+), 1395 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableCellBehavior.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableCellBehavior.java	Wed Nov 21 13:02:09 2012 +1300
@@ -46,16 +46,16 @@
     // global map used to store the focus cell for a table view when it is first
     // shift-clicked. This allows for proper keyboard interactions, in particular
     // resolving RT-11446
-    private static final WeakHashMap<TableView, TablePosition> map = new WeakHashMap<TableView, TablePosition>();
+    private static final WeakHashMap<TableView, TablePositionBase> map = new WeakHashMap<TableView, TablePositionBase>();
     
-    static TablePosition getAnchor(TableView table) {
+    static TablePositionBase getAnchor(TableView table) {
         TableViewFocusModel fm = table.getFocusModel();
         if (fm == null) return null;
         
         return hasAnchor(table) ? map.get(table) : fm.getFocusedCell();
     }
     
-    static void setAnchor(TableView table, TablePosition anchor) {
+    static void setAnchor(TableView table, TablePositionBase anchor) {
         if (table != null && anchor == null) {
             map.remove(table);
         } else {
@@ -207,7 +207,7 @@
                 } else if (e.isShiftDown()) {
                     // we add all cells/rows between the current selection focus and
                     // this cell/row (inclusive) to the current selection.
-                    TablePosition focusedCell = map.containsKey(tableView) ? map.get(tableView) : fm.getFocusedCell();
+                    TablePositionBase focusedCell = map.containsKey(tableView) ? map.get(tableView) : fm.getFocusedCell();
 
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(focusedCell.getRow(), row);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehavior.java	Wed Nov 21 13:02:09 2012 +1300
@@ -1,1168 +1,160 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.sun.javafx.scene.control.behavior;
-
-import static javafx.scene.input.KeyCode.A;
-import static javafx.scene.input.KeyCode.BACK_SLASH;
-import static javafx.scene.input.KeyCode.DOWN;
-import static javafx.scene.input.KeyCode.END;
-import static javafx.scene.input.KeyCode.ENTER;
-import static javafx.scene.input.KeyCode.ESCAPE;
-import static javafx.scene.input.KeyCode.F2;
-import static javafx.scene.input.KeyCode.HOME;
-import static javafx.scene.input.KeyCode.KP_DOWN;
-import static javafx.scene.input.KeyCode.KP_LEFT;
-import static javafx.scene.input.KeyCode.KP_RIGHT;
-import static javafx.scene.input.KeyCode.KP_UP;
-import static javafx.scene.input.KeyCode.LEFT;
-import static javafx.scene.input.KeyCode.PAGE_DOWN;
-import static javafx.scene.input.KeyCode.PAGE_UP;
-import static javafx.scene.input.KeyCode.RIGHT;
-import static javafx.scene.input.KeyCode.SPACE;
-import static javafx.scene.input.KeyCode.TAB;
-import static javafx.scene.input.KeyCode.UP;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javafx.collections.ObservableList;
-import javafx.scene.control.SelectionMode;
-import javafx.scene.control.TableColumn;
-import javafx.scene.control.TablePosition;
-import javafx.scene.control.TableView;
-import javafx.scene.control.TableView.TableViewFocusModel;
-import javafx.scene.input.KeyEvent;
-import javafx.scene.input.MouseEvent;
-
-import com.sun.javafx.PlatformUtil;
-import javafx.beans.value.ChangeListener;
-import javafx.beans.value.ObservableValue;
-import javafx.beans.value.WeakChangeListener;
-import javafx.collections.ListChangeListener;
-import javafx.collections.WeakListChangeListener;
-import javafx.scene.control.*;
-import javafx.util.Callback;
-
-public class TableViewBehavior<T> extends BehaviorBase<TableView<T>> {
-    /**************************************************************************
-     *                          Setup KeyBindings                             *
-     *************************************************************************/
-    protected static final List<KeyBinding> TABLE_VIEW_BINDINGS = new ArrayList<KeyBinding>();
-
-    static {
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraverseNext"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraversePrevious").shift());
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectFirstRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectLastRow"));
-        
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "ScrollUp"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "ScrollDown"));
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "SelectLeftCell"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "SelectLeftCell"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectRightCell"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectRightCell"));
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "SelectPreviousRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "SelectPreviousRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "SelectNextRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "SelectNextRow"));
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "TraverseLeft"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "TraverseLeft"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectNextRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectNextRow"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "TraverseUp"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "TraverseUp"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "TraverseDown"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "TraverseDown"));
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectAllToFirstRow").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectAllToLastRow").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "SelectAllPageUp").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "SelectAllPageDown").shift());
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPrevious").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPrevious").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNext").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNext").shift());
-        
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "SelectAllToFocus").shift());
-
-//        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPreviousCell").shift());
-//        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPreviousCell").shift());
-//        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNextCell").shift());
-//        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNextCell").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "AlsoSelectLeftCell").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "AlsoSelectLeftCell").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "AlsoSelectRightCell").shift());
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "AlsoSelectRightCell").shift());
-
-        if (PlatformUtil.isMac()) {
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "FocusRightCell").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "FocusRightCell").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "FocusLeftCell").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "FocusLeftCell").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl().meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").meta());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").meta());
-            
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").meta().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").meta().shift());
-        } else {
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "FocusRightCell").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "FocusRightCell").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "FocusLeftCell").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "FocusLeftCell").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").ctrl());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").ctrl());
-            
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").ctrl().shift());
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").ctrl().shift());
-        }
-
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(ENTER, "Activate"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate"));
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(F2, "Activate"));
-//        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate").ctrl());
-        
-        TABLE_VIEW_BINDINGS.add(new KeyBinding(ESCAPE, "CancelEdit"));
-
-        if (PlatformUtil.isMac()) {
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(BACK_SLASH, "ClearSelection").meta());
-        } else {
-            TABLE_VIEW_BINDINGS.add(new KeyBinding(BACK_SLASH, "ClearSelection").ctrl());
-        }
-    }
-
-    @Override protected void callAction(String name) {
-        if ("SelectPreviousRow".equals(name)) selectPreviousRow();
-        else if ("SelectNextRow".equals(name)) selectNextRow();
-        else if ("SelectLeftCell".equals(name)) selectLeftCell();
-        else if ("SelectRightCell".equals(name)) selectRightCell();
-        else if ("SelectFirstRow".equals(name)) selectFirstRow();
-        else if ("SelectLastRow".equals(name)) selectLastRow();
-        else if ("SelectAll".equals(name)) selectAll();
-        else if ("SelectAllPageUp".equals(name)) selectAllPageUp();
-        else if ("SelectAllPageDown".equals(name)) selectAllPageDown();
-        else if ("SelectAllToFirstRow".equals(name)) selectAllToFirstRow();
-        else if ("SelectAllToLastRow".equals(name)) selectAllToLastRow();
-        else if ("AlsoSelectNext".equals(name)) alsoSelectNext();
-        else if ("AlsoSelectPrevious".equals(name)) alsoSelectPrevious();
-        else if ("AlsoSelectLeftCell".equals(name)) alsoSelectLeftCell();
-        else if ("AlsoSelectRightCell".equals(name)) alsoSelectRightCell();
-        else if ("ClearSelection".equals(name)) clearSelection();
-        else if ("ScrollUp".equals(name)) scrollUp();
-        else if ("ScrollDown".equals(name)) scrollDown();
-        else if ("FocusPreviousRow".equals(name)) focusPreviousRow();
-        else if ("FocusNextRow".equals(name)) focusNextRow();
-        else if ("FocusLeftCell".equals(name)) focusLeftCell();
-        else if ("FocusRightCell".equals(name)) focusRightCell();
-        else if ("Activate".equals(name)) activate();
-        else if ("CancelEdit".equals(name)) cancelEdit();
-        else if ("FocusFirstRow".equals(name)) focusFirstRow();
-        else if ("FocusLastRow".equals(name)) focusLastRow();
-        else if ("toggleFocusOwnerSelection".equals(name)) toggleFocusOwnerSelection();
-        else if ("SelectAllToFocus".equals(name)) selectAllToFocus();
-        else if ("FocusPageUp".equals(name)) focusPageUp();
-        else if ("FocusPageDown".equals(name)) focusPageDown();
-        else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow();
-        else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow();
-        else if ("DiscontinuousSelectNextColumn".equals(name)) discontinuousSelectNextColumn();
-        else if ("DiscontinuousSelectPreviousColumn".equals(name)) discontinuousSelectPreviousColumn();
-        else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp();
-        else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown();
-        else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow();
-        else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow();
-        else super.callAction(name);
-    }
-
-    @Override protected List<KeyBinding> createKeyBindings() {
-        return TABLE_VIEW_BINDINGS;
-    }
-    
-    @Override protected void callActionForEvent(KeyEvent e) {
-        // RT-12751: we want to keep an eye on the user holding down the shift key, 
-        // so that we know when they enter/leave multiple selection mode. This
-        // changes what happens when certain key combinations are pressed.
-        isShiftDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isShiftDown();
-        isCtrlDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isControlDown();
-        
-        super.callActionForEvent(e);
-    }
-
-    /**************************************************************************
-     *                         State and Functions                            *
-     *************************************************************************/
-    
-    private boolean selectionChanging = false;
-    
-    private final ListChangeListener<TablePosition> selectedCellsListener = new ListChangeListener<TablePosition>() {
-        @Override public void onChanged(ListChangeListener.Change c) {
-            while (c.next()) {
-                TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-                if (sm == null) return;
-                
-                TablePosition anchor = getAnchor();
-                boolean cellSelectionEnabled = sm.isCellSelectionEnabled();
-                
-                if (! selectionChanging) {
-                    // there are no selected items, so lets clear out the anchor
-                    if (c.getList().isEmpty()) {
-                        setAnchor(null);
-                    } else if (! c.getList().contains(getAnchor())) {
-                        setAnchor(null);
-                    }
-                } 
-                
-                int addedSize = c.getAddedSize();
-                List<TablePosition> addedSubList = (List<TablePosition>) c.getAddedSubList();
-                
-                if (! hasAnchor() && addedSize > 0) {
-                    for (int i = 0; i < addedSize; i++) {
-                        TablePosition tp = addedSubList.get(i);
-                        if (tp.getRow() >= 0) {
-                            setAnchor(tp);
-                            break;
-                        }
-                    }
-                }
-                
-                if (!hasAnchor() && cellSelectionEnabled && ! selectionPathDeviated) {
-                    // check if the selection is on the same row or column, 
-                    // otherwise set selectionPathDeviated to true
-                    for (int i = 0; i < addedSize; i++) {
-                        TablePosition tp = addedSubList.get(i);
-                        if (anchor.getRow() != -1 && tp.getRow() != anchor.getRow() && tp.getColumn() != anchor.getColumn()) {
-                            selectionPathDeviated = true;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    };
-    
-    private final ChangeListener<TableView.TableViewSelectionModel<T>> selectionModelListener = 
-            new ChangeListener<TableView.TableViewSelectionModel<T>>() {
-        @Override
-        public void changed(ObservableValue<? extends TableView.TableViewSelectionModel<T>> observable, 
-                    TableView.TableViewSelectionModel<T> oldValue, 
-                    TableView.TableViewSelectionModel<T> newValue) {
-            if (oldValue != null) {
-                oldValue.getSelectedCells().removeListener(weakSelectedCellsListener);
-            }
-            if (newValue != null) {
-                newValue.getSelectedCells().addListener(weakSelectedCellsListener);
-            }
-        }
-    };
-    
-    private final WeakListChangeListener<TablePosition> weakSelectedCellsListener = 
-            new WeakListChangeListener<TablePosition>(selectedCellsListener);
-    private final WeakChangeListener<TableView.TableViewSelectionModel<T>> weakSelectionModelListener = 
-            new WeakChangeListener<TableView.TableViewSelectionModel<T>>(selectionModelListener);
-
-    public TableViewBehavior(TableView control) {
-        super(control);
-        
-        // Fix for RT-16565
-        getControl().selectionModelProperty().addListener(weakSelectionModelListener);
-        if (getControl().getSelectionModel() != null) {
-            getControl().getSelectionModel().getSelectedCells().addListener(selectedCellsListener);
-        }
-    }
-
-    @Override public void mousePressed(MouseEvent e) {
-        super.mousePressed(e);
-        
-        // FIXME can't assume (yet) cells.get(0) is necessarily the lead cell
-        ObservableList<TablePosition> cells = getControl().getSelectionModel().getSelectedCells();
-        setAnchor(cells.isEmpty() ? null : cells.get(0));
-        
-        if (!getControl().isFocused() && getControl().isFocusTraversable()) {
-            getControl().requestFocus();
-        }
-    }
-    
-    private boolean isCtrlDown = false;
-    private boolean isShiftDown = false;
-    private boolean selectionPathDeviated = false;
-    
-    
-    /*
-     * Anchor is created upon
-     * - initial selection of an item (by mouse or keyboard)
-     * 
-     * Anchor is changed when you
-     * - move the selection to an item by UP/DOWN/LEFT/RIGHT arrow keys
-     * - select an item by mouse click
-     * - add/remove an item to/from an existing selection by CTRL+SPACE shortcut
-     * - add/remove an items to/from an existing selection by CTRL+mouse click
-     * 
-     * Note that if an item is removed from an existing selection by 
-     * CTRL+SPACE/CTRL+mouse click, anchor still remains on this item even 
-     * though it is not selected.
-     * 
-     * Anchor is NOT changed when you
-     * - create linear multi-selection by SHIFT+UP/DOWN/LEFT/RIGHT arrow keys
-     * - create linear multi-selection by SHIFT+SPACE arrow keys
-     * - create linear multi-selection by SHIFT+mouse click
-     * 
-     * In case there is a discontinuous selection in the list, creating linear 
-     * multi-selection between anchor and focused item will cancel the 
-     * discontinuous selection. It means that only items that are located between
-     * anchor and focused item will be selected. 
-     */
-    private void setAnchor(int row, TableColumn col) {
-        setAnchor(row == -1 && col == null ? null : 
-                new TablePosition(getControl(), row, col));
-        
-        selectionPathDeviated = false;
-    }
-    private void setAnchor(TablePosition tp) {
-        TableCellBehavior.setAnchor(getControl(), tp);
-        selectionPathDeviated = false;
-    }
-    private TablePosition getAnchor() {
-        return TableCellBehavior.getAnchor(getControl());
-    }
-    
-    private boolean hasAnchor() {
-        return TableCellBehavior.hasAnchor(getControl());
-    }
-    
-//    private void shiftAnchor(boolean rowDirection, int delta) {
-//        if (anchor == null) return;
-//        if (rowDirection) {
-//            int currentRow = anchor.getRow();
-//            int newRow = currentRow + delta;
-//            if (newRow >= 0 && newRow < getItemCount()) {
-//                setAnchor(newRow, anchor.getTableColumn());
-//            }
-//        } else {
-//            System.err.println("can not shift in column direction yet");
-//        }
-//    }
-    
-    private int getItemCount() {
-        return getControl().getItems() == null ? 0 : getControl().getItems().size();
-    }
-    
-    
-    
-//    // Support for RT-13826:
-//    // set when focus is moved by keyboard to allow for proper selection positions
-//    private int selectPos = -1;
-
-    private Callback<Void, Integer> onScrollPageUp;
-    public void setOnScrollPageUp(Callback<Void, Integer> c) { onScrollPageUp = c; }
-
-    private Callback<Void, Integer> onScrollPageDown;
-    public void setOnScrollPageDown(Callback<Void, Integer> c) { onScrollPageDown = c; }
-
-    private Runnable onFocusPreviousRow;
-    public void setOnFocusPreviousRow(Runnable r) { onFocusPreviousRow = r; }
-
-    private Runnable onFocusNextRow;
-    public void setOnFocusNextRow(Runnable r) { onFocusNextRow = r; }
-
-    private Runnable onSelectPreviousRow;
-    public void setOnSelectPreviousRow(Runnable r) { onSelectPreviousRow = r; }
-
-    private Runnable onSelectNextRow;
-    public void setOnSelectNextRow(Runnable r) { onSelectNextRow = r; }
-
-    private Runnable onMoveToFirstCell;
-    public void setOnMoveToFirstCell(Runnable r) { onMoveToFirstCell = r; }
-
-    private Runnable onMoveToLastCell;
-    public void setOnMoveToLastCell(Runnable r) { onMoveToLastCell = r; }
-
-    private Runnable onSelectRightCell;
-    public void setOnSelectRightCell(Runnable r) { onSelectRightCell = r; }
-
-    private Runnable onSelectLeftCell;
-    public void setOnSelectLeftCell(Runnable r) { onSelectLeftCell = r; }
-    
-    private void scrollUp() {
-        TableView.TableViewSelectionModel<T> sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectedCells().isEmpty()) return;
-        
-        TablePosition selectedCell = sm.getSelectedCells().get(0);
-        
-        int newSelectedIndex = -1;
-        if (onScrollPageUp != null) {
-            newSelectedIndex = onScrollPageUp.call(null);
-        }
-        if (newSelectedIndex == -1) return;
-        
-        sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn());
-    }
-
-    private void scrollDown() {
-        TableView.TableViewSelectionModel<T> sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectedCells().isEmpty()) return;
-        
-        TablePosition selectedCell = sm.getSelectedCells().get(0);
-        
-        int newSelectedIndex = -1;
-        if (onScrollPageDown != null) {
-            newSelectedIndex = onScrollPageDown.call(null);
-        }
-        if (newSelectedIndex == -1) return;
-        
-        sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn());
-    }
-    
-    private void focusFirstRow() {
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        TableColumn tc = fm.getFocusedCell() == null ? null : fm.getFocusedCell().getTableColumn();
-        fm.focus(0, tc);
-        
-        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
-    }
-    
-    private void focusLastRow() {
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        TableColumn tc = fm.getFocusedCell() == null ? null : fm.getFocusedCell().getTableColumn();
-        fm.focus(getItemCount() - 1, tc);
-        
-        if (onMoveToLastCell != null) onMoveToLastCell.run();
-    }
-
-    private void focusPreviousRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        if (sm.isCellSelectionEnabled()) {
-            fm.focusAboveCell();
-        } else {
-            fm.focusPrevious();
-        }
-        
-        if (! isCtrlDown || getAnchor() == null) {
-            setAnchor(fm.getFocusedIndex(), null);
-        }
-
-        if (onFocusPreviousRow != null) onFocusPreviousRow.run();
-    }
-
-    private void focusNextRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        if (sm.isCellSelectionEnabled()) {
-            fm.focusBelowCell();
-        } else {
-            fm.focusNext();
-        }
-        
-        if (! isCtrlDown || getAnchor() == null) {
-            setAnchor(fm.getFocusedIndex(), null);
-        }
-        
-        if (onFocusNextRow != null) onFocusNextRow.run();
-    }
-
-    private void focusLeftCell() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        fm.focusLeftCell();
-        if (onFocusPreviousRow != null) onFocusPreviousRow.run();
-    }
-
-    private void focusRightCell() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        fm.focusRightCell();
-        if (onFocusNextRow != null) onFocusNextRow.run();
-    }
-    
-    private void focusPageUp() {
-        int newFocusIndex = onScrollPageUp.call(null);
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        TableColumn tc = fm.getFocusedCell() == null ? null : fm.getFocusedCell().getTableColumn();
-        fm.focus(newFocusIndex, tc);
-    }
-    
-    private void focusPageDown() {
-        int newFocusIndex = onScrollPageDown.call(null);
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        TableColumn tc = fm.getFocusedCell() == null ? null : fm.getFocusedCell().getTableColumn();
-        fm.focus(newFocusIndex, tc);
-    }
-
-    private void clearSelection() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        sm.clearSelection();
-    }
-    
-    private void clearSelectionOutsideRange(int start, int end) {
-        TableView.TableViewSelectionModel<T> sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        int min = Math.min(start, end);
-        int max = Math.max(start, end);
-        
-        List<Integer> indices = new ArrayList<Integer>(sm.getSelectedIndices());
-        
-        selectionChanging = true;
-        for (int i = 0; i < indices.size(); i++) {
-            int index = indices.get(i);
-            if (index < min || index >= max) {
-                sm.clearSelection(index);
-            }
-        }
-        selectionChanging = false;
-    }
-
-    private void alsoSelectPrevious() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        if (sm.isCellSelectionEnabled()) {
-            updateCellVerticalSelection(-1, new Runnable() {
-                @Override public void run() {
-                    getControl().getSelectionModel().selectAboveCell();
-                }
-            });
-        } else {
-            if (isShiftDown && hasAnchor()) {
-                updateRowSelection(-1);
-            } else {
-                sm.selectPrevious();
-            }
-        }
-        onSelectPreviousRow.run();
-    }
-    
-    private void alsoSelectNext() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        if (sm.isCellSelectionEnabled()) {
-            updateCellVerticalSelection(1, new Runnable() {
-                @Override public void run() {
-                    getControl().getSelectionModel().selectBelowCell();
-                }
-            });
-        } else {
-            if (isShiftDown && hasAnchor()) {
-                updateRowSelection(1);
-            } else {
-                sm.selectNext();
-            }
-        }
-        onSelectNextRow.run();
-    }
-    
-    private void alsoSelectLeftCell() {
-        updateCellHorizontalSelection(-1, new Runnable() {
-            @Override public void run() { 
-                getControl().getSelectionModel().selectLeftCell();
-            }
-        });
-    }
-
-    private void alsoSelectRightCell() {
-        updateCellHorizontalSelection(1, new Runnable() {
-            @Override public void run() { 
-                getControl().getSelectionModel().selectRightCell();
-            }
-        });
-    }
-    
-    private void updateRowSelection(int delta) {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        int newRow = fm.getFocusedIndex() + delta;
-        TablePosition anchor = getAnchor();
-        
-        if (! hasAnchor()) {
-            setAnchor(fm.getFocusedCell());
-        } 
-
-        clearSelectionOutsideRange(anchor.getRow(), newRow);
-
-        if (anchor.getRow() > newRow) {
-            sm.selectRange(anchor.getRow(), newRow - 1);
-        } else {
-            sm.selectRange(anchor.getRow(), newRow + 1);
-        }
-    }
-    
-    private void updateCellVerticalSelection(int delta, Runnable defaultAction) {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        TablePosition focusedCell = fm.getFocusedCell();
-        if (isShiftDown && sm.isSelected(focusedCell.getRow() + delta, focusedCell.getTableColumn())) {
-            int newFocusOwner = focusedCell.getRow() + delta;
-            sm.clearSelection(selectionPathDeviated ? newFocusOwner : focusedCell.getRow(), focusedCell.getTableColumn());
-            fm.focus(newFocusOwner, focusedCell.getTableColumn());
-        } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) {
-            int newRow = fm.getFocusedIndex() + delta;
-
-            int start = Math.min(getAnchor().getRow(), newRow);
-            int end = Math.max(getAnchor().getRow(), newRow);
-            for (int _row = start; _row <= end; _row++) {
-                sm.select(_row, focusedCell.getTableColumn());
-            }
-            fm.focus(newRow, focusedCell.getTableColumn());
-        } else {
-            final int focusIndex = fm.getFocusedIndex();
-            if (! sm.isSelected(focusIndex, focusedCell.getTableColumn())) {
-                sm.select(focusIndex, focusedCell.getTableColumn());
-            }
-            defaultAction.run();
-        }
-    }
-    
-    private void updateCellHorizontalSelection(int delta, Runnable defaultAction) {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        TablePosition focusedCell = fm.getFocusedCell();
-        if (focusedCell == null || focusedCell.getTableColumn() == null) return;
-        
-        TableColumn adjacentColumn = getColumn(focusedCell.getTableColumn(), delta);
-        if (adjacentColumn == null) return;
-        
-        if (isShiftDown && getAnchor() != null && 
-            sm.isSelected(focusedCell.getRow(), adjacentColumn) &&
-            ! (focusedCell.getRow() == getAnchor().getRow() && focusedCell.getTableColumn().equals(adjacentColumn))) {
-                sm.clearSelection(focusedCell.getRow(),selectionPathDeviated ? adjacentColumn : focusedCell.getTableColumn());
-                fm.focus(focusedCell.getRow(), adjacentColumn);
-        } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) {
-            int newColumn = focusedCell.getColumn() + delta;
-
-            int start = Math.min(getAnchor().getColumn(), newColumn);
-            int end = Math.max(getAnchor().getColumn(), newColumn);
-            for (int _col = start; _col <= end; _col++) {
-                sm.select(focusedCell.getRow(), getColumn(_col));
-            }
-            fm.focus(focusedCell.getRow(), getColumn(newColumn));
-        } else {
-            defaultAction.run();
-        }
-    }
-    
-    private TableColumn getColumn(int index) {
-        return getControl().getVisibleLeafColumn(index);
-    }
-    
-    private TableColumn getColumn(TableColumn tc, int delta) {
-        return getControl().getVisibleLeafColumn(getControl().getVisibleLeafIndex(tc) + delta);
-    }
-
-    private void selectFirstRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        ObservableList<TablePosition> selection = sm.getSelectedCells();
-        TableColumn<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn();
-        sm.clearAndSelect(0, selectedColumn);
-
-        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
-    }
-
-    private void selectLastRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        ObservableList<TablePosition> selection = sm.getSelectedCells();
-        TableColumn<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn();
-        sm.clearAndSelect(getItemCount() - 1, selectedColumn);
-
-        if (onMoveToLastCell != null) onMoveToLastCell.run();
-    }
-
-    private void selectPreviousRow() {
-        selectCell(-1, 0);
-        if (onSelectPreviousRow != null) onSelectPreviousRow.run();
-    }
-
-    private void selectNextRow() {
-        selectCell(1, 0);
-        if (onSelectNextRow != null) onSelectNextRow.run();
-    }
-
-    private void selectLeftCell() {
-        selectCell(0, -1);
-        if (onSelectLeftCell != null) onSelectLeftCell.run();
-    }
-
-    private void selectRightCell() {
-        selectCell(0, 1);
-        if (onSelectRightCell != null) onSelectRightCell.run();
-    }
-
-    private void selectCell(int rowDiff, int columnDiff) {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        int currentRow = focusedCell.getRow();
-        int currentColumn = focusedCell.getColumn();
-        if (rowDiff < 0 && currentRow == 0) return;
-        else if (rowDiff > 0 && currentRow == getItemCount() - 1) return;
-        else if (columnDiff < 0 && currentColumn == 0) return;
-        else if (columnDiff > 0 && currentColumn == getControl().getVisibleLeafColumns().size() - 1) return;
-
-        TableColumn tc = focusedCell.getTableColumn();
-        tc = getColumn(tc, columnDiff);
-        
-        int row = focusedCell.getRow() + rowDiff;
-        sm.clearAndSelect(row, tc);
-        setAnchor(row, tc);
-    }
-    
-    private void cancelEdit() {
-        getControl().edit(-1, null);
-    }
-
-    private void activate() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition cell = fm.getFocusedCell();
-        sm.select(cell.getRow(), cell.getTableColumn());
-
-        // edit this row also
-        getControl().edit(cell.getRow(), cell.getTableColumn());
-    }
-    
-    private void selectAllToFocus() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        int focusRow = focusedCell.getRow();
-        
-        TablePosition anchor = getAnchor();
-        int anchorRow = anchor.getRow();
-        
-        sm.clearSelection();
-        if (! sm.isCellSelectionEnabled()) {
-            int startPos = anchorRow;
-            int endPos = anchorRow > focusRow ? focusRow - 1 : focusRow + 1;
-            sm.selectRange(startPos, endPos);
-        } else {
-            // we add all cells/rows between the current selection focus and
-            // the acnhor (inclusive) to the current selection.
-
-            // and then determine all row and columns which must be selected
-            int minRow = Math.min(focusedCell.getRow(), anchorRow);
-            int maxRow = Math.max(focusedCell.getRow(), anchorRow);
-            int minColumn = Math.min(focusedCell.getColumn(), anchor.getColumn());
-            int maxColumn = Math.max(focusedCell.getColumn(), anchor.getColumn());
-
-            // clear selection
-            sm.clearSelection();
-
-            // and then perform the selection
-            for (int _row = minRow; _row <= maxRow; _row++) {
-                for (int _col = minColumn; _col <= maxColumn; _col++) {
-                    sm.select(_row, getControl().getVisibleLeafColumn(_col));
-                }
-            }
-        }
-        
-        setAnchor(anchor);
-    }
-    
-    private void selectAll() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        sm.selectAll();
-    }
-
-    private void selectAllToFirstRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        
-        int leadIndex = focusedCell.getRow();
-        
-        if (isShiftDown) {
-            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
-        }
-
-        sm.clearSelection();
-        if (! sm.isCellSelectionEnabled()) {
-            // we are going from 0 to one before the focused cell as that is
-            // the requirement of selectRange, so we call focus on the 0th row
-            sm.selectRange(0, leadIndex + 1);
-            getControl().getFocusModel().focus(0);
-//            setAnchor(leadIndex, null);
-        } else {
-            // TODO
-            
-//            setAnchor(leadIndex, );
-        }
-        
-        if (isShiftDown) {
-            setAnchor(leadIndex, null);
-        }
-
-        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
-    }
-
-    private void selectAllToLastRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        
-        int leadIndex = focusedCell.getRow();
-        
-        if (isShiftDown) {
-            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
-        }
-        
-        sm.clearSelection();
-        if (! sm.isCellSelectionEnabled()) {
-            sm.selectRange(leadIndex, getItemCount());
-        } else {
-            // TODO
-        }
-        
-        if (isShiftDown) {
-            setAnchor(leadIndex, null);
-        }
-
-        if (onMoveToLastCell != null) onMoveToLastCell.run();
-    }
-    
-    private void selectAllPageUp() {
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        int leadIndex = fm.getFocusedIndex();
-        if (isShiftDown) {
-            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
-            setAnchor(leadIndex, null);
-        }
-        
-        int leadSelectedIndex = onScrollPageUp.call(null);
-        
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        selectionChanging = true;
-        sm.clearSelection();
-        sm.selectRange(leadSelectedIndex, leadIndex + 1);
-        selectionChanging = false;
-    }
-    
-    private void selectAllPageDown() {
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        int leadIndex = fm.getFocusedIndex();
-        if (isShiftDown) {
-            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
-            setAnchor(leadIndex, null);
-        }
-        
-        int leadSelectedIndex = onScrollPageDown.call(null);
-        
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        selectionChanging = true;
-        sm.clearSelection();
-        sm.selectRange(leadIndex, leadSelectedIndex + 1);
-        selectionChanging = false;
-    }
-    
-    private void toggleFocusOwnerSelection() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        
-        if (sm.isSelected(focusedCell.getRow(), focusedCell.getTableColumn())) {
-            sm.clearSelection(focusedCell.getRow(), focusedCell.getTableColumn());
-            fm.focus(focusedCell.getRow(), focusedCell.getTableColumn());
-        } else {
-            sm.select(focusedCell.getRow(), focusedCell.getTableColumn());
-        }
-        
-        setAnchor(focusedCell.getRow(), focusedCell.getTableColumn());
-    }
-    
-    // This functionality was added, but then removed when it was realised by 
-    // UX that TableView should not include 'spreadsheet-like' functionality.
-    // When / if we ever introduce this kind of control, this functionality can
-    // be re-enabled then.
-    /*
-    private void moveToLeftMostColumn() {
-        // Functionality as described in RT-12752
-        if (onMoveToLeftMostColumn != null) onMoveToLeftMostColumn.run();
-        
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || ! sm.isCellSelectionEnabled()) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        
-        TableColumn endColumn = getControl().getVisibleLeafColumn(0);
-        sm.clearAndSelect(focusedCell.getRow(), endColumn);
-    }
-    
-    private void moveToRightMostColumn() {
-        // Functionality as described in RT-12752
-        if (onMoveToRightMostColumn != null) onMoveToRightMostColumn.run();
-        
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || ! sm.isCellSelectionEnabled()) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TablePosition focusedCell = fm.getFocusedCell();
-        
-        TableColumn endColumn = getControl().getVisibleLeafColumn(getControl().getVisibleLeafColumns().size() - 1);
-        sm.clearAndSelect(focusedCell.getRow(), endColumn);
-    }
-     */
-    
-    
-    /**************************************************************************
-     * Discontinuous Selection                                                *
-     *************************************************************************/
-    
-    private void discontinuousSelectPreviousRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        int index = fm.getFocusedIndex() - 1;
-        if (index < 0) return;
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.select(index);
-        } else {
-            sm.select(index, fm.getFocusedCell().getTableColumn());
-        }
-    }
-    
-    private void discontinuousSelectNextRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        int index = fm.getFocusedIndex() + 1;
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.select(index);
-        } else {
-            sm.select(index, fm.getFocusedCell().getTableColumn());
-        }
-    }
-    
-    private void discontinuousSelectPreviousColumn() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || ! sm.isCellSelectionEnabled()) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TableColumn tc = getColumn(fm.getFocusedCell().getTableColumn(), -1);
-        sm.select(fm.getFocusedIndex(), tc);
-    }
-    
-    private void discontinuousSelectNextColumn() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null || ! sm.isCellSelectionEnabled()) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        TableColumn tc = getColumn(fm.getFocusedCell().getTableColumn(), 1);
-        sm.select(fm.getFocusedIndex(), tc);
-    }
-    
-    private void discontinuousSelectPageUp() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        FocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        int leadIndex = fm.getFocusedIndex();
-        int leadSelectedIndex = onScrollPageUp.call(null);
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.selectRange(leadSelectedIndex, leadIndex + 1);
-        }
-    }
-    
-    private void discontinuousSelectPageDown() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        FocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-        
-        int leadIndex = fm.getFocusedIndex();
-        int leadSelectedIndex = onScrollPageDown.call(null);
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.selectRange(leadIndex, leadSelectedIndex + 1);
-        }
-    }
-    
-    private void discontinuousSelectAllToFirstRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        int index = fm.getFocusedIndex();
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.selectRange(0, index);
-        } else {
-            for (int i = 0; i < index; i++) {
-                sm.select(i, fm.getFocusedCell().getTableColumn());
-            }
-        }
-
-        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
-    }
-    
-    private void discontinuousSelectAllToLastRow() {
-        TableView.TableViewSelectionModel sm = getControl().getSelectionModel();
-        if (sm == null) return;
-        
-        TableViewFocusModel fm = getControl().getFocusModel();
-        if (fm == null) return;
-
-        int index = fm.getFocusedIndex() + 1;
-        
-        if (! sm.isCellSelectionEnabled()) {
-            sm.selectRange(index, getItemCount());
-        } else {
-            for (int i = index; i < getItemCount(); i++) {
-                sm.select(i, fm.getFocusedCell().getTableColumn());
-            }
-        }
-
-        if (onMoveToLastCell != null) onMoveToLastCell.run();
-    }   
-}
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.scene.control.behavior;
+
+import java.util.List;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.beans.value.WeakChangeListener;
+import javafx.collections.ObservableList;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableColumnBase;
+import javafx.scene.control.TableFocusModel;
+import javafx.scene.control.TablePosition;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableSelectionModel;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TableView.TableViewSelectionModel;
+
+public class TableViewBehavior<T> extends TableViewBehaviorBase<TableView<T>, T, TableColumn<T, ?>> {
+    
+    /**************************************************************************
+     *                                                                        *
+     * Listeners                                                              *
+     *                                                                        *  
+     *************************************************************************/
+    
+    private final ChangeListener<TableViewSelectionModel<T>> selectionModelListener = 
+            new ChangeListener<TableViewSelectionModel<T>>() {
+        @Override
+        public void changed(ObservableValue<? extends TableViewSelectionModel<T>> observable, 
+                    TableViewSelectionModel<T> oldValue, 
+                    TableViewSelectionModel<T> newValue) {
+            if (oldValue != null) {
+                oldValue.getSelectedCells().removeListener(weakSelectedCellsListener);
+            }
+            if (newValue != null) {
+                newValue.getSelectedCells().addListener(weakSelectedCellsListener);
+            }
+        }
+    };
+    
+    private final WeakChangeListener<TableViewSelectionModel<T>> weakSelectionModelListener = 
+            new WeakChangeListener<TableViewSelectionModel<T>>(selectionModelListener);
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Constructors                                                           *
+     *                                                                        *  
+     *************************************************************************/
+    
+    public TableViewBehavior(TableView<T> control) {
+        super(control);
+        
+        // Fix for RT-16565
+        control.selectionModelProperty().addListener(weakSelectionModelListener);
+        TableViewSelectionModel sm = control.getSelectionModel();
+        if (sm != null) {
+            sm.getSelectedCells().addListener(selectedCellsListener);
+        }
+    }
+
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Implement TableViewBehaviorBase abstract methods                       *
+     *                                                                        *  
+     *************************************************************************/
+    
+    /** {@inheritDoc}  */
+    @Override protected void setAnchor(TablePositionBase tp) {
+        TableCellBehavior.setAnchor(getControl(), tp);
+        selectionPathDeviated = false;
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase getAnchor() {
+        return TableCellBehavior.getAnchor(getControl());
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected boolean hasAnchor() {
+        return TableCellBehavior.hasAnchor(getControl());
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected int getItemCount() {
+        return getControl().getItems() == null ? 0 : getControl().getItems().size();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TableFocusModel getFocusModel() {
+        return getControl().getFocusModel();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TableSelectionModel<T, TableColumn<T, ?>> getSelectionModel() {
+        return getControl().getSelectionModel();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected ObservableList<TablePosition> getSelectedCells() {
+        return getControl().getSelectionModel().getSelectedCells();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase getFocusedCell() {
+        return getControl().getFocusModel().getFocusedCell();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected int getVisibleLeafIndex(TableColumnBase tc) {
+        return getControl().getVisibleLeafIndex((TableColumn)tc);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TableColumn getVisibleLeafColumn(int index) {
+        return getControl().getVisibleLeafColumn(index);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected void editCell(int row, TableColumnBase tc) {
+        getControl().edit(row, (TableColumn)tc);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected ObservableList<TableColumn<T,?>> getVisibleLeafColumns() {
+        return getControl().getVisibleLeafColumns();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase<TableView<T>, TableColumn<T, ?>> 
+            getTablePosition(int row, TableColumnBase<T, ?> tc) {
+        return new TablePosition(getControl(), row, (TableColumn)tc);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TableViewBehaviorBase.java	Wed Nov 21 13:02:09 2012 +1300
@@ -0,0 +1,1170 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.sun.javafx.scene.control.behavior;
+
+import static javafx.scene.input.KeyCode.A;
+import static javafx.scene.input.KeyCode.BACK_SLASH;
+import static javafx.scene.input.KeyCode.DOWN;
+import static javafx.scene.input.KeyCode.END;
+import static javafx.scene.input.KeyCode.ENTER;
+import static javafx.scene.input.KeyCode.ESCAPE;
+import static javafx.scene.input.KeyCode.F2;
+import static javafx.scene.input.KeyCode.HOME;
+import static javafx.scene.input.KeyCode.KP_DOWN;
+import static javafx.scene.input.KeyCode.KP_LEFT;
+import static javafx.scene.input.KeyCode.KP_RIGHT;
+import static javafx.scene.input.KeyCode.KP_UP;
+import static javafx.scene.input.KeyCode.LEFT;
+import static javafx.scene.input.KeyCode.PAGE_DOWN;
+import static javafx.scene.input.KeyCode.PAGE_UP;
+import static javafx.scene.input.KeyCode.RIGHT;
+import static javafx.scene.input.KeyCode.SPACE;
+import static javafx.scene.input.KeyCode.TAB;
+import static javafx.scene.input.KeyCode.UP;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javafx.collections.ObservableList;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseEvent;
+
+import com.sun.javafx.PlatformUtil;
+import javafx.collections.ListChangeListener;
+import javafx.collections.WeakListChangeListener;
+import javafx.scene.control.Control;
+import javafx.scene.control.TableColumnBase;
+import javafx.scene.control.TableFocusModel;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableSelectionModel;
+import javafx.util.Callback;
+
+public abstract class TableViewBehaviorBase<C extends Control, T, TC extends TableColumnBase<T,?>> extends BehaviorBase<C> {
+
+    /**************************************************************************
+     *                                                                        *
+     * Setup key bindings                                                     *
+     *                                                                        *  
+     *************************************************************************/
+    protected static final List<KeyBinding> TABLE_VIEW_BINDINGS = new ArrayList<KeyBinding>();
+
+    static {
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraverseNext"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraversePrevious").shift());
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectFirstRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectLastRow"));
+        
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "ScrollUp"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "ScrollDown"));
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "SelectLeftCell"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "SelectLeftCell"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectRightCell"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectRightCell"));
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "SelectPreviousRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "SelectPreviousRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "SelectNextRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "SelectNextRow"));
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "TraverseLeft"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "TraverseLeft"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectNextRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectNextRow"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "TraverseUp"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "TraverseUp"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "TraverseDown"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "TraverseDown"));
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectAllToFirstRow").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectAllToLastRow").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "SelectAllPageUp").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "SelectAllPageDown").shift());
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPrevious").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPrevious").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNext").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNext").shift());
+        
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "SelectAllToFocus").shift());
+
+//        TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPreviousCell").shift());
+//        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPreviousCell").shift());
+//        TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNextCell").shift());
+//        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNextCell").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "AlsoSelectLeftCell").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "AlsoSelectLeftCell").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "AlsoSelectRightCell").shift());
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "AlsoSelectRightCell").shift());
+
+        if (PlatformUtil.isMac()) {
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "FocusRightCell").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "FocusRightCell").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "FocusLeftCell").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "FocusLeftCell").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl().meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").meta());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").meta());
+            
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").meta().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").meta().shift());
+        } else {
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "FocusRightCell").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "FocusRightCell").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "FocusLeftCell").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "FocusLeftCell").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").ctrl());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").ctrl());
+            
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").ctrl().shift());
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").ctrl().shift());
+        }
+
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(ENTER, "Activate"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate"));
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(F2, "Activate"));
+//        TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate").ctrl());
+        
+        TABLE_VIEW_BINDINGS.add(new KeyBinding(ESCAPE, "CancelEdit"));
+
+        if (PlatformUtil.isMac()) {
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(BACK_SLASH, "ClearSelection").meta());
+        } else {
+            TABLE_VIEW_BINDINGS.add(new KeyBinding(BACK_SLASH, "ClearSelection").ctrl());
+        }
+    }
+
+    @Override protected void callAction(String name) {
+        if ("SelectPreviousRow".equals(name)) selectPreviousRow();
+        else if ("SelectNextRow".equals(name)) selectNextRow();
+        else if ("SelectLeftCell".equals(name)) selectLeftCell();
+        else if ("SelectRightCell".equals(name)) selectRightCell();
+        else if ("SelectFirstRow".equals(name)) selectFirstRow();
+        else if ("SelectLastRow".equals(name)) selectLastRow();
+        else if ("SelectAll".equals(name)) selectAll();
+        else if ("SelectAllPageUp".equals(name)) selectAllPageUp();
+        else if ("SelectAllPageDown".equals(name)) selectAllPageDown();
+        else if ("SelectAllToFirstRow".equals(name)) selectAllToFirstRow();
+        else if ("SelectAllToLastRow".equals(name)) selectAllToLastRow();
+        else if ("AlsoSelectNext".equals(name)) alsoSelectNext();
+        else if ("AlsoSelectPrevious".equals(name)) alsoSelectPrevious();
+        else if ("AlsoSelectLeftCell".equals(name)) alsoSelectLeftCell();
+        else if ("AlsoSelectRightCell".equals(name)) alsoSelectRightCell();
+        else if ("ClearSelection".equals(name)) clearSelection();
+        else if ("ScrollUp".equals(name)) scrollUp();
+        else if ("ScrollDown".equals(name)) scrollDown();
+        else if ("FocusPreviousRow".equals(name)) focusPreviousRow();
+        else if ("FocusNextRow".equals(name)) focusNextRow();
+        else if ("FocusLeftCell".equals(name)) focusLeftCell();
+        else if ("FocusRightCell".equals(name)) focusRightCell();
+        else if ("Activate".equals(name)) activate();
+        else if ("CancelEdit".equals(name)) cancelEdit();
+        else if ("FocusFirstRow".equals(name)) focusFirstRow();
+        else if ("FocusLastRow".equals(name)) focusLastRow();
+        else if ("toggleFocusOwnerSelection".equals(name)) toggleFocusOwnerSelection();
+        else if ("SelectAllToFocus".equals(name)) selectAllToFocus();
+        else if ("FocusPageUp".equals(name)) focusPageUp();
+        else if ("FocusPageDown".equals(name)) focusPageDown();
+        else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow();
+        else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow();
+        else if ("DiscontinuousSelectNextColumn".equals(name)) discontinuousSelectNextColumn();
+        else if ("DiscontinuousSelectPreviousColumn".equals(name)) discontinuousSelectPreviousColumn();
+        else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp();
+        else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown();
+        else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow();
+        else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow();
+        else super.callAction(name);
+    }
+
+    @Override protected List<KeyBinding> createKeyBindings() {
+        return TABLE_VIEW_BINDINGS;
+    }
+    
+    @Override protected void callActionForEvent(KeyEvent e) {
+        // RT-12751: we want to keep an eye on the user holding down the shift key, 
+        // so that we know when they enter/leave multiple selection mode. This
+        // changes what happens when certain key combinations are pressed.
+        isShiftDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isShiftDown();
+        isCtrlDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isControlDown();
+        
+        super.callActionForEvent(e);
+    }
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Internal fields                                                        *
+     *                                                                        *  
+     *************************************************************************/
+    
+    protected boolean isCtrlDown = false;
+    protected boolean isShiftDown = false;
+    protected boolean selectionPathDeviated = false;
+    protected boolean selectionChanging = false;
+
+    protected final ListChangeListener<TablePositionBase> selectedCellsListener = new ListChangeListener<TablePositionBase>() {
+        @Override public void onChanged(ListChangeListener.Change c) {
+            while (c.next()) {
+                TableSelectionModel sm = getSelectionModel();
+                if (sm == null) return;
+                
+                TablePositionBase anchor = getAnchor();
+                boolean cellSelectionEnabled = sm.isCellSelectionEnabled();
+                
+                if (! selectionChanging) {
+                    // there are no selected items, so lets clear out the anchor
+                    if (c.getList().isEmpty()) {
+                        setAnchor(null);
+                    } else if (! c.getList().contains(getAnchor())) {
+                        setAnchor(null);
+                    }
+                } 
+                
+                int addedSize = c.getAddedSize();
+                List<TablePositionBase> addedSubList = (List<TablePositionBase>) c.getAddedSubList();
+                
+                if (! hasAnchor() && addedSize > 0) {
+                    for (int i = 0; i < addedSize; i++) {
+                        TablePositionBase tp = addedSubList.get(i);
+                        if (tp.getRow() >= 0) {
+                            setAnchor(tp);
+                            break;
+                        }
+                    }
+                }
+                
+                if (!hasAnchor() && cellSelectionEnabled && ! selectionPathDeviated) {
+                    // check if the selection is on the same row or column, 
+                    // otherwise set selectionPathDeviated to true
+                    for (int i = 0; i < addedSize; i++) {
+                        TablePositionBase tp = addedSubList.get(i);
+                        if (anchor.getRow() != -1 && tp.getRow() != anchor.getRow() && tp.getColumn() != anchor.getColumn()) {
+                            selectionPathDeviated = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    };
+    
+    protected final WeakListChangeListener<TablePositionBase> weakSelectedCellsListener = 
+            new WeakListChangeListener<TablePositionBase>(selectedCellsListener);
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Constructors                                                           *
+     *                                                                        *  
+     *************************************************************************/
+    
+    public TableViewBehaviorBase(C control) {
+        super(control);
+    }
+
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Abstract API                                                           *
+     *                                                                        *  
+     *************************************************************************/    
+    
+    // TODO document
+    protected abstract void setAnchor(TablePositionBase tp);
+    protected abstract TablePositionBase getAnchor();
+    protected abstract boolean hasAnchor();
+    
+    protected abstract int getItemCount();
+    
+    protected abstract TableFocusModel getFocusModel();
+    
+    protected abstract TableSelectionModel<T,TC> getSelectionModel();
+    
+    protected abstract ObservableList<? extends TablePositionBase/*<C,TC>*/> getSelectedCells();
+    
+    protected abstract TablePositionBase getFocusedCell();
+    
+    protected abstract int getVisibleLeafIndex(TableColumnBase tc);
+    
+    protected abstract TableColumnBase getVisibleLeafColumn(int index);
+    
+    protected abstract void editCell(int row, TableColumnBase tc);
+    
+    protected abstract ObservableList<? extends TableColumnBase> getVisibleLeafColumns();
+    
+    protected abstract TablePositionBase<C,TC> getTablePosition(int row, TableColumnBase<T,?> tc);
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Public API                                                             *
+     *                                                                        *  
+     *************************************************************************/     
+    
+    /*
+     * Anchor is created upon
+     * - initial selection of an item (by mouse or keyboard)
+     * 
+     * Anchor is changed when you
+     * - move the selection to an item by UP/DOWN/LEFT/RIGHT arrow keys
+     * - select an item by mouse click
+     * - add/remove an item to/from an existing selection by CTRL+SPACE shortcut
+     * - add/remove an items to/from an existing selection by CTRL+mouse click
+     * 
+     * Note that if an item is removed from an existing selection by 
+     * CTRL+SPACE/CTRL+mouse click, anchor still remains on this item even 
+     * though it is not selected.
+     * 
+     * Anchor is NOT changed when you
+     * - create linear multi-selection by SHIFT+UP/DOWN/LEFT/RIGHT arrow keys
+     * - create linear multi-selection by SHIFT+SPACE arrow keys
+     * - create linear multi-selection by SHIFT+mouse click
+     * 
+     * In case there is a discontinuous selection in the list, creating linear 
+     * multi-selection between anchor and focused item will cancel the 
+     * discontinuous selection. It means that only items that are located between
+     * anchor and focused item will be selected. 
+     */
+    protected void setAnchor(int row, TableColumnBase col) {
+        setAnchor(row == -1 && col == null ? null : getTablePosition(row, col));
+    }
+    
+    private Callback<Void, Integer> onScrollPageUp;
+    public void setOnScrollPageUp(Callback<Void, Integer> c) { onScrollPageUp = c; }
+
+    private Callback<Void, Integer> onScrollPageDown;
+    public void setOnScrollPageDown(Callback<Void, Integer> c) { onScrollPageDown = c; }
+
+    private Runnable onFocusPreviousRow;
+    public void setOnFocusPreviousRow(Runnable r) { onFocusPreviousRow = r; }
+
+    private Runnable onFocusNextRow;
+    public void setOnFocusNextRow(Runnable r) { onFocusNextRow = r; }
+
+    private Runnable onSelectPreviousRow;
+    public void setOnSelectPreviousRow(Runnable r) { onSelectPreviousRow = r; }
+
+    private Runnable onSelectNextRow;
+    public void setOnSelectNextRow(Runnable r) { onSelectNextRow = r; }
+
+    private Runnable onMoveToFirstCell;
+    public void setOnMoveToFirstCell(Runnable r) { onMoveToFirstCell = r; }
+
+    private Runnable onMoveToLastCell;
+    public void setOnMoveToLastCell(Runnable r) { onMoveToLastCell = r; }
+
+    private Runnable onSelectRightCell;
+    public void setOnSelectRightCell(Runnable r) { onSelectRightCell = r; }
+
+    private Runnable onSelectLeftCell;
+    public void setOnSelectLeftCell(Runnable r) { onSelectLeftCell = r; }
+    
+    @Override public void mousePressed(MouseEvent e) {
+        super.mousePressed(e);
+        
+        // FIXME can't assume (yet) cells.get(0) is necessarily the lead cell
+        ObservableList<? extends TablePositionBase> cells = getSelectedCells();
+        setAnchor(cells.isEmpty() ? null : cells.get(0));
+        
+        if (!getControl().isFocused() && getControl().isFocusTraversable()) {
+            getControl().requestFocus();
+        }
+    }
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Private implementation                                                 *
+     *                                                                        *  
+     *************************************************************************/ 
+    
+    protected void scrollUp() {
+        TableSelectionModel<T,TC> sm = getSelectionModel();
+        if (sm == null || getSelectedCells().isEmpty()) return;
+        
+        TablePositionBase<C,TC> selectedCell = getSelectedCells().get(0);
+        
+        int newSelectedIndex = -1;
+        if (onScrollPageUp != null) {
+            newSelectedIndex = onScrollPageUp.call(null);
+        }
+        if (newSelectedIndex == -1) return;
+        
+        sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn());
+    }
+
+    protected void scrollDown() {
+        TableSelectionModel<T,TC> sm = getSelectionModel();
+        if (sm == null || getSelectedCells().isEmpty()) return;
+        
+        TablePositionBase<C,TC> selectedCell = getSelectedCells().get(0);
+        
+        int newSelectedIndex = -1;
+        if (onScrollPageDown != null) {
+            newSelectedIndex = onScrollPageDown.call(null);
+        }
+        if (newSelectedIndex == -1) return;
+        
+        sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn());
+    }
+    
+    protected void focusFirstRow() {
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn();
+        fm.focus(0, tc);
+        
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+    
+    protected void focusLastRow() {
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn();
+        fm.focus(getItemCount() - 1, tc);
+        
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }
+
+    protected void focusPreviousRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        if (sm.isCellSelectionEnabled()) {
+            fm.focusAboveCell();
+        } else {
+            fm.focusPrevious();
+        }
+        
+        if (! isCtrlDown || getAnchor() == null) {
+            setAnchor(fm.getFocusedIndex(), null);
+        }
+
+        if (onFocusPreviousRow != null) onFocusPreviousRow.run();
+    }
+
+    protected void focusNextRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        if (sm.isCellSelectionEnabled()) {
+            fm.focusBelowCell();
+        } else {
+            fm.focusNext();
+        }
+        
+        if (! isCtrlDown || getAnchor() == null) {
+            setAnchor(fm.getFocusedIndex(), null);
+        }
+        
+        if (onFocusNextRow != null) onFocusNextRow.run();
+    }
+
+    protected void focusLeftCell() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        fm.focusLeftCell();
+        if (onFocusPreviousRow != null) onFocusPreviousRow.run();
+    }
+
+    protected void focusRightCell() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        fm.focusRightCell();
+        if (onFocusNextRow != null) onFocusNextRow.run();
+    }
+    
+    protected void focusPageUp() {
+        int newFocusIndex = onScrollPageUp.call(null);
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn();
+        fm.focus(newFocusIndex, tc);
+    }
+    
+    protected void focusPageDown() {
+        int newFocusIndex = onScrollPageDown.call(null);
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn();
+        fm.focus(newFocusIndex, tc);
+    }
+
+    protected void clearSelection() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        sm.clearSelection();
+    }
+    
+    protected void clearSelectionOutsideRange(int start, int end) {
+        TableSelectionModel<T,?> sm = getSelectionModel();
+        if (sm == null) return;
+        
+        int min = Math.min(start, end);
+        int max = Math.max(start, end);
+        
+        List<Integer> indices = new ArrayList<Integer>(sm.getSelectedIndices());
+        
+        selectionChanging = true;
+        for (int i = 0; i < indices.size(); i++) {
+            int index = indices.get(i);
+            if (index < min || index >= max) {
+                sm.clearSelection(index);
+            }
+        }
+        selectionChanging = false;
+    }
+
+    protected void alsoSelectPrevious() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        if (sm.isCellSelectionEnabled()) {
+            updateCellVerticalSelection(-1, new Runnable() {
+                @Override public void run() {
+                    getSelectionModel().selectAboveCell();
+                }
+            });
+        } else {
+            if (isShiftDown && hasAnchor()) {
+                updateRowSelection(-1);
+            } else {
+                sm.selectPrevious();
+            }
+        }
+        onSelectPreviousRow.run();
+    }
+    
+    protected void alsoSelectNext() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        if (sm.isCellSelectionEnabled()) {
+            updateCellVerticalSelection(1, new Runnable() {
+                @Override public void run() {
+                    getSelectionModel().selectBelowCell();
+                }
+            });
+        } else {
+            if (isShiftDown && hasAnchor()) {
+                updateRowSelection(1);
+            } else {
+                sm.selectNext();
+            }
+        }
+        onSelectNextRow.run();
+    }
+    
+    protected void alsoSelectLeftCell() {
+        updateCellHorizontalSelection(-1, new Runnable() {
+            @Override public void run() { 
+                getSelectionModel().selectLeftCell();
+            }
+        });
+    }
+
+    protected void alsoSelectRightCell() {
+        updateCellHorizontalSelection(1, new Runnable() {
+            @Override public void run() { 
+                getSelectionModel().selectRightCell();
+            }
+        });
+    }
+    
+    protected void updateRowSelection(int delta) {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        int newRow = fm.getFocusedIndex() + delta;
+        TablePositionBase anchor = getAnchor();
+        
+        if (! hasAnchor()) {
+            setAnchor(getFocusedCell());
+        } 
+
+        clearSelectionOutsideRange(anchor.getRow(), newRow);
+
+        if (anchor.getRow() > newRow) {
+            sm.selectRange(anchor.getRow(), newRow - 1);
+        } else {
+            sm.selectRange(anchor.getRow(), newRow + 1);
+        }
+    }
+    
+    protected void updateCellVerticalSelection(int delta, Runnable defaultAction) {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        TablePositionBase focusedCell = getFocusedCell();
+        if (isShiftDown && sm.isSelected(focusedCell.getRow() + delta, focusedCell.getTableColumn())) {
+            int newFocusOwner = focusedCell.getRow() + delta;
+            sm.clearSelection(selectionPathDeviated ? newFocusOwner : focusedCell.getRow(), focusedCell.getTableColumn());
+            fm.focus(newFocusOwner, focusedCell.getTableColumn());
+        } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) {
+            int newRow = fm.getFocusedIndex() + delta;
+
+            int start = Math.min(getAnchor().getRow(), newRow);
+            int end = Math.max(getAnchor().getRow(), newRow);
+            for (int _row = start; _row <= end; _row++) {
+                sm.select(_row, focusedCell.getTableColumn());
+            }
+            fm.focus(newRow, focusedCell.getTableColumn());
+        } else {
+            final int focusIndex = fm.getFocusedIndex();
+            if (! sm.isSelected(focusIndex, focusedCell.getTableColumn())) {
+                sm.select(focusIndex, focusedCell.getTableColumn());
+            }
+            defaultAction.run();
+        }
+    }
+    
+    protected void updateCellHorizontalSelection(int delta, Runnable defaultAction) {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        TablePositionBase focusedCell = getFocusedCell();
+        if (focusedCell == null || focusedCell.getTableColumn() == null) return;
+        
+        TableColumnBase adjacentColumn = getColumn(focusedCell.getTableColumn(), delta);
+        if (adjacentColumn == null) return;
+        
+        if (isShiftDown && getAnchor() != null && 
+            sm.isSelected(focusedCell.getRow(), adjacentColumn) &&
+            ! (focusedCell.getRow() == getAnchor().getRow() && focusedCell.getTableColumn().equals(adjacentColumn))) {
+                sm.clearSelection(focusedCell.getRow(),selectionPathDeviated ? adjacentColumn : focusedCell.getTableColumn());
+                fm.focus(focusedCell.getRow(), adjacentColumn);
+        } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) {
+            final int columnPos = getVisibleLeafIndex(focusedCell.getTableColumn());
+            final int newColumn = columnPos + delta;
+
+            int start = Math.min(columnPos, newColumn);
+            int end = Math.max(columnPos, newColumn);
+            for (int _col = start; _col <= end; _col++) {
+                sm.select(focusedCell.getRow(), getColumn(_col));
+            }
+            fm.focus(focusedCell.getRow(), getColumn(newColumn));
+        } else {
+            defaultAction.run();
+        }
+    }
+    
+    protected TableColumnBase getColumn(int index) {
+        return getVisibleLeafColumn(index);
+    }
+    
+    protected TableColumnBase getColumn(TableColumnBase tc, int delta) {
+        return getVisibleLeafColumn(getVisibleLeafIndex(tc) + delta);
+    }
+
+    protected void selectFirstRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        ObservableList<? extends TablePositionBase> selection = getSelectedCells();
+        TableColumnBase<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn();
+        sm.clearAndSelect(0, selectedColumn);
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+
+    protected void selectLastRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        ObservableList<? extends TablePositionBase> selection = getSelectedCells();
+        TableColumnBase<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn();
+        sm.clearAndSelect(getItemCount() - 1, selectedColumn);
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }
+
+    protected void selectPreviousRow() {
+        selectCell(-1, 0);
+        if (onSelectPreviousRow != null) onSelectPreviousRow.run();
+    }
+
+    protected void selectNextRow() {
+        selectCell(1, 0);
+        if (onSelectNextRow != null) onSelectNextRow.run();
+    }
+
+    protected void selectLeftCell() {
+        selectCell(0, -1);
+        if (onSelectLeftCell != null) onSelectLeftCell.run();
+    }
+
+    protected void selectRightCell() {
+        selectCell(0, 1);
+        if (onSelectRightCell != null) onSelectRightCell.run();
+    }
+
+    protected void selectCell(int rowDiff, int columnDiff) {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase<C,TC> focusedCell = getFocusedCell();
+        int currentRow = focusedCell.getRow();
+        int currentColumn = getVisibleLeafIndex(focusedCell.getTableColumn());
+        if (rowDiff < 0 && currentRow == 0) return;
+        else if (rowDiff > 0 && currentRow == getItemCount() - 1) return;
+        else if (columnDiff < 0 && currentColumn == 0) return;
+        else if (columnDiff > 0 && currentColumn == getVisibleLeafColumns().size() - 1) return;
+
+        TableColumnBase tc = focusedCell.getTableColumn();
+        tc = getColumn(tc, columnDiff);
+        
+        int row = focusedCell.getRow() + rowDiff;
+        sm.clearAndSelect(row, tc);
+        setAnchor(row, tc);
+    }
+    
+    protected void cancelEdit() {
+        editCell(-1, null);
+    }
+
+    protected void activate() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase<C,TC> cell = getFocusedCell();
+        sm.select(cell.getRow(), cell.getTableColumn());
+
+        // edit this row also
+        editCell(cell.getRow(), cell.getTableColumn());
+    }
+    
+    protected void selectAllToFocus() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase<C,TC> focusedCell = getFocusedCell();
+        int focusRow = focusedCell.getRow();
+        
+        TablePositionBase<C,TC> anchor = getAnchor();
+        int anchorRow = anchor.getRow();
+        
+        sm.clearSelection();
+        if (! sm.isCellSelectionEnabled()) {
+            int startPos = anchorRow;
+            int endPos = anchorRow > focusRow ? focusRow - 1 : focusRow + 1;
+            sm.selectRange(startPos, endPos);
+        } else {
+            // we add all cells/rows between the current selection focus and
+            // the acnhor (inclusive) to the current selection.
+
+            // and then determine all row and columns which must be selected
+            int minRow = Math.min(focusedCell.getRow(), anchorRow);
+            int maxRow = Math.max(focusedCell.getRow(), anchorRow);
+            final int focusedCellColumnPos = getVisibleLeafIndex(focusedCell.getTableColumn());
+            final int anchorColumnPos = getVisibleLeafIndex(anchor.getTableColumn());
+            int minColumn = Math.min(focusedCellColumnPos, anchorColumnPos);
+            int maxColumn = Math.max(focusedCellColumnPos, anchorColumnPos);
+
+            // clear selection
+            sm.clearSelection();
+
+            // and then perform the selection
+            for (int _row = minRow; _row <= maxRow; _row++) {
+                for (int _col = minColumn; _col <= maxColumn; _col++) {
+                    sm.select(_row, getVisibleLeafColumn(_col));
+                }
+            }
+        }
+        
+        setAnchor(anchor);
+    }
+    
+    protected void selectAll() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        sm.selectAll();
+    }
+
+    protected void selectAllToFirstRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase focusedCell = getFocusedCell();
+        
+        int leadIndex = focusedCell.getRow();
+        
+        if (isShiftDown) {
+            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
+        }
+
+        sm.clearSelection();
+        if (! sm.isCellSelectionEnabled()) {
+            // we are going from 0 to one before the focused cell as that is
+            // the requirement of selectRange, so we call focus on the 0th row
+            sm.selectRange(0, leadIndex + 1);
+            getFocusModel().focus(0);
+//            setAnchor(leadIndex, null);
+        } else {
+            // TODO
+            
+//            setAnchor(leadIndex, );
+        }
+        
+        if (isShiftDown) {
+            setAnchor(leadIndex, null);
+        }
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+
+    protected void selectAllToLastRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase focusedCell = getFocusedCell();
+        
+        int leadIndex = focusedCell.getRow();
+        
+        if (isShiftDown) {
+            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
+        }
+        
+        sm.clearSelection();
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(leadIndex, getItemCount());
+        } else {
+            // TODO
+        }
+        
+        if (isShiftDown) {
+            setAnchor(leadIndex, null);
+        }
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }
+    
+    protected void selectAllPageUp() {
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        int leadIndex = fm.getFocusedIndex();
+        if (isShiftDown) {
+            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
+            setAnchor(leadIndex, null);
+        }
+        
+        int leadSelectedIndex = onScrollPageUp.call(null);
+        
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        selectionChanging = true;
+        sm.clearSelection();
+        sm.selectRange(leadSelectedIndex, leadIndex + 1);
+        selectionChanging = false;
+    }
+    
+    protected void selectAllPageDown() {
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        int leadIndex = fm.getFocusedIndex();
+        if (isShiftDown) {
+            leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow();
+            setAnchor(leadIndex, null);
+        }
+        
+        int leadSelectedIndex = onScrollPageDown.call(null);
+        
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        selectionChanging = true;
+        sm.clearSelection();
+        sm.selectRange(leadIndex, leadSelectedIndex + 1);
+        selectionChanging = false;
+    }
+    
+    protected void toggleFocusOwnerSelection() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePositionBase focusedCell = getFocusedCell();
+        
+        if (sm.isSelected(focusedCell.getRow(), focusedCell.getTableColumn())) {
+            sm.clearSelection(focusedCell.getRow(), focusedCell.getTableColumn());
+            fm.focus(focusedCell.getRow(), focusedCell.getTableColumn());
+        } else {
+            sm.select(focusedCell.getRow(), focusedCell.getTableColumn());
+        }
+        
+        setAnchor(focusedCell.getRow(), focusedCell.getTableColumn());
+    }
+    
+    // This functionality was added, but then removed when it was realised by 
+    // UX that TableView should not include 'spreadsheet-like' functionality.
+    // When / if we ever introduce this kind of control, this functionality can
+    // be re-enabled then.
+    /*
+    protected void moveToLeftMostColumn() {
+        // Functionality as described in RT-12752
+        if (onMoveToLeftMostColumn != null) onMoveToLeftMostColumn.run();
+        
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePosition focusedCell = fm.getFocusedCell();
+        
+        TableColumn endColumn = getControl().getVisibleLeafColumn(0);
+        sm.clearAndSelect(focusedCell.getRow(), endColumn);
+    }
+    
+    protected void moveToRightMostColumn() {
+        // Functionality as described in RT-12752
+        if (onMoveToRightMostColumn != null) onMoveToRightMostColumn.run();
+        
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TablePosition focusedCell = fm.getFocusedCell();
+        
+        TableColumn endColumn = getControl().getVisibleLeafColumn(getControl().getVisibleLeafColumns().size() - 1);
+        sm.clearAndSelect(focusedCell.getRow(), endColumn);
+    }
+     */
+    
+    
+    /**************************************************************************
+     * Discontinuous Selection                                                *
+     *************************************************************************/
+    
+    protected void discontinuousSelectPreviousRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        int index = fm.getFocusedIndex() - 1;
+        if (index < 0) return;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.select(index);
+        } else {
+            sm.select(index, getFocusedCell().getTableColumn());
+        }
+    }
+    
+    protected void discontinuousSelectNextRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.select(index);
+        } else {
+            sm.select(index, getFocusedCell().getTableColumn());
+        }
+    }
+    
+    protected void discontinuousSelectPreviousColumn() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TableColumnBase tc = getColumn(getFocusedCell().getTableColumn(), -1);
+        sm.select(fm.getFocusedIndex(), tc);
+    }
+    
+    protected void discontinuousSelectNextColumn() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null || ! sm.isCellSelectionEnabled()) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        TableColumnBase tc = getColumn(getFocusedCell().getTableColumn(), 1);
+        sm.select(fm.getFocusedIndex(), tc);
+    }
+    
+    protected void discontinuousSelectPageUp() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageUp.call(null);
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(leadSelectedIndex, leadIndex + 1);
+        }
+    }
+    
+    protected void discontinuousSelectPageDown() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+        
+        int leadIndex = fm.getFocusedIndex();
+        int leadSelectedIndex = onScrollPageDown.call(null);
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(leadIndex, leadSelectedIndex + 1);
+        }
+    }
+    
+    protected void discontinuousSelectAllToFirstRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex();
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(0, index);
+        } else {
+            for (int i = 0; i < index; i++) {
+                sm.select(i, getFocusedCell().getTableColumn());
+            }
+        }
+
+        if (onMoveToFirstCell != null) onMoveToFirstCell.run();
+    }
+    
+    protected void discontinuousSelectAllToLastRow() {
+        TableSelectionModel sm = getSelectionModel();
+        if (sm == null) return;
+        
+        TableFocusModel fm = getFocusModel();
+        if (fm == null) return;
+
+        int index = fm.getFocusedIndex() + 1;
+        
+        if (! sm.isCellSelectionEnabled()) {
+            sm.selectRange(index, getItemCount());
+        } else {
+            for (int i = index; i < getItemCount(); i++) {
+                sm.select(i, getFocusedCell().getTableColumn());
+            }
+        }
+
+        if (onMoveToLastCell != null) onMoveToLastCell.run();
+    }   
+}
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableCellBehavior.java	Wed Nov 21 13:02:09 2012 +1300
@@ -25,11 +25,9 @@
 package com.sun.javafx.scene.control.behavior;
 
 import com.sun.javafx.PlatformUtil;
-import java.util.List;
 import java.util.WeakHashMap;
 import javafx.scene.control.SelectionMode;
-import javafx.scene.control.TreeTableCell;
-
+import javafx.scene.control.TablePositionBase;
 import javafx.scene.control.TreeTableView.TreeTableViewFocusModel;
 import javafx.scene.control.TreeTableCell;
 import javafx.scene.control.TreeTableColumn;
@@ -51,16 +49,16 @@
     // global map used to store the focus cell for a table view when it is first
     // shift-clicked. This allows for proper keyboard interactions, in particular
     // resolving RT-11446
-    private static final WeakHashMap<TreeTableView, TreeTablePosition> map = new WeakHashMap<TreeTableView, TreeTablePosition>();
+    private static final WeakHashMap<TreeTableView, TablePositionBase> map = new WeakHashMap<TreeTableView, TablePositionBase>();
     
-    static TreeTablePosition getAnchor(TreeTableView table) {
+    static TablePositionBase getAnchor(TreeTableView table) {
         TreeTableViewFocusModel fm = table.getFocusModel();
         if (fm == null) return null;
         
         return hasAnchor(table) ? map.get(table) : fm.getFocusedCell();
     }
     
-    static void setAnchor(TreeTableView table, TreeTablePosition anchor) {
+    static void setAnchor(TreeTableView table, TablePositionBase anchor) {
         if (table != null && anchor == null) {
             map.remove(table);
         } else {
@@ -212,7 +210,7 @@
                 } else if (e.isShiftDown()) {
                     // we add all cells/rows between the current selection focus and
                     // this cell/row (inclusive) to the current selection.
-                    TreeTablePosition focusedCell = map.containsKey(tableView) ? map.get(tableView) : fm.getFocusedCell();
+                    TablePositionBase focusedCell = map.containsKey(tableView) ? map.get(tableView) : fm.getFocusedCell();
 
                     // and then determine all row and columns which must be selected
                     int minRow = Math.min(focusedCell.getRow(), row);
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableViewBehavior.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeTableViewBehavior.java	Wed Nov 21 13:02:09 2012 +1300
@@ -24,15 +24,212 @@
  */
 package com.sun.javafx.scene.control.behavior;
 
+import static javafx.scene.input.KeyCode.*;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.beans.value.WeakChangeListener;
+import javafx.collections.ObservableList;
+import javafx.scene.control.TableColumnBase;
+import javafx.scene.control.TableFocusModel;
+import javafx.scene.control.TablePositionBase;
+import javafx.scene.control.TableSelectionModel;
+import javafx.scene.control.TreeItem;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTablePosition;
 import javafx.scene.control.TreeTableView;
+import javafx.util.Callback;
 
-/**
- *
- */
-public class TreeTableViewBehavior<T> extends BehaviorBase<TreeTableView<T>> {
+public class TreeTableViewBehavior<T> extends TableViewBehaviorBase<TreeTableView<T>, TreeItem<T>, TreeTableColumn<T, ?>> {
+    
+    /**************************************************************************
+     *                                                                        *
+     * Setup key bindings                                                     *
+     *                                                                        *  
+     *************************************************************************/
+    
+    static {
+        // Add these bindings at the front of the list, so they take precedence
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(LEFT, "CollapseRow"));
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(KP_LEFT, "CollapseRow"));
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(RIGHT, "ExpandRow"));
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(KP_RIGHT, "ExpandRow"));
+        
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(MULTIPLY, "ExpandAll"));
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(ADD, "ExpandRow"));
+        TABLE_VIEW_BINDINGS.add(0,new KeyBinding(SUBTRACT, "CollapseRow"));
+    }
 
+    @Override protected void callAction(String name) {
+        if ("ExpandRow".equals(name)) rightArrowPressed();
+        else if ("CollapseRow".equals(name)) leftArrowPressed();
+        else if ("ExpandAll".equals(name)) expandAll();
+        else super.callAction(name);
+    }
+    
+
+    
+    /**************************************************************************
+     *                                                                        *
+     * Listeners                                                              *
+     *                                                                        *  
+     *************************************************************************/
+    
+    private final ChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> selectionModelListener = 
+            new ChangeListener<TreeTableView.TreeTableViewSelectionModel<T>>() {
+        @Override
+        public void changed(ObservableValue<? extends TreeTableView.TreeTableViewSelectionModel<T>> observable, 
+                    TreeTableView.TreeTableViewSelectionModel<T> oldValue, 
+                    TreeTableView.TreeTableViewSelectionModel<T> newValue) {
+            if (oldValue != null) {
+                oldValue.getSelectedCells().removeListener(weakSelectedCellsListener);
+            }
+            if (newValue != null) {
+                newValue.getSelectedCells().addListener(weakSelectedCellsListener);
+            }
+        }
+    };
+    
+    private final WeakChangeListener<TreeTableView.TreeTableViewSelectionModel<T>> weakSelectionModelListener = 
+            new WeakChangeListener<TreeTableView.TreeTableViewSelectionModel<T>>(selectionModelListener);
+    
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Constructors                                                           *
+     *                                                                        *  
+     *************************************************************************/
+    
     public TreeTableViewBehavior(TreeTableView<T> control) {
         super(control);
+        
+        // Fix for RT-16565
+        control.selectionModelProperty().addListener(weakSelectionModelListener);
+        if (getSelectionModel() != null) {
+            control.getSelectionModel().getSelectedCells().addListener(selectedCellsListener);
+        }
+    }
+
+    
+    
+    /**************************************************************************
+     *                                                                        *
+     * Implement TableViewBehaviorBase abstract methods                       *
+     *                                                                        *  
+     *************************************************************************/
+    
+    /** {@inheritDoc}  */
+    @Override protected void setAnchor(TablePositionBase tp) {
+        TreeTableCellBehavior.setAnchor(getControl(), tp);
+        selectionPathDeviated = false;
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase getAnchor() {
+        return TreeTableCellBehavior.getAnchor(getControl());
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected boolean hasAnchor() {
+        return TreeTableCellBehavior.hasAnchor(getControl());
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected int getItemCount() {
+        return getControl().impl_getTreeItemCount();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TableFocusModel getFocusModel() {
+        return getControl().getFocusModel();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TableSelectionModel<TreeItem<T>, TreeTableColumn<T, ?>> getSelectionModel() {
+        return getControl().getSelectionModel();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected ObservableList<TreeTablePosition> getSelectedCells() {
+        return getControl().getSelectionModel().getSelectedCells();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase getFocusedCell() {
+        return getControl().getFocusModel().getFocusedCell();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected int getVisibleLeafIndex(TableColumnBase tc) {
+        return getControl().getVisibleLeafIndex((TreeTableColumn)tc);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TreeTableColumn getVisibleLeafColumn(int index) {
+        return getControl().getVisibleLeafColumn(index);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected void editCell(int row, TableColumnBase tc) {
+        getControl().edit(row, (TreeTableColumn)tc);
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected ObservableList<TreeTableColumn<T,?>> getVisibleLeafColumns() {
+        return getControl().getVisibleLeafColumns();
+    }
+
+    /** {@inheritDoc}  */
+    @Override protected TablePositionBase<TreeTableView<T>, TreeTableColumn<T, ?>> 
+            getTablePosition(int row, TableColumnBase<TreeItem<T>, ?> tc) {
+        return new TreeTablePosition(getControl(), row, (TreeTableColumn)tc);
     }
     
+    
+    
+        
+    /**************************************************************************
+     *                                                                        *
+     * Tree-related implementation                                            *
+     *                                                                        *  
+     *************************************************************************/
+    
+    /**
+     * The next methods handle the left/right arrow input differently depending
+     * on whether we are in row or cell selection.
+     */
+    private void rightArrowPressed() {
+        if (getControl().getSelectionModel().isCellSelectionEnabled()) {
+            selectRightCell();
+        } else {
+            expandRow();
+        }
+    }
+    
+    private void leftArrowPressed() {
+        if (getControl().getSelectionModel().isCellSelectionEnabled()) {
+            selectLeftCell();
+        } else {
+            collapseRow();
+        }
+    }
+    
+    private void expandRow() {
+        Callback<TreeItem<T>, Integer> getIndex = new Callback<TreeItem<T>, Integer>() {
+            @Override public Integer call(TreeItem<T> p) {
+                return getControl().getRow(p);
+            }
+        };
+        TreeViewBehavior.expandRow(getControl().getSelectionModel(), getIndex);
+    }
+    
+    private void expandAll() {
+        TreeViewBehavior.expandAll(getControl().getRoot());
+    }
+    
+    private void collapseRow() {
+        TreeTableView control = getControl();
+        TreeViewBehavior.collapseRow(control.getSelectionModel(), control.getRoot(), control.isShowRoot());
+    }
 }
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java	Wed Nov 21 13:02:09 2012 +1300
@@ -610,9 +610,26 @@
         sm.selectRange(startPos, endPos);
         setAnchor(anchor);
     }
+    
+    private void expandRow() {
+        Callback<TreeItem<T>, Integer> getIndex = new Callback<TreeItem<T>, Integer>() {
+            @Override public Integer call(TreeItem<T> p) {
+                return getControl().getRow(p);
+            }
+        };
+        TreeViewBehavior.expandRow(getControl().getSelectionModel(), getIndex);
+    }
+    
+    private void expandAll() {
+        TreeViewBehavior.expandAll(getControl().getRoot());
+    }
+    
+    private void collapseRow() {
+        TreeView control = getControl();
+        TreeViewBehavior.collapseRow(control.getSelectionModel(), control.getRoot(), control.isShowRoot());
+    }
 
-    private void expandRow() {
-        MultipleSelectionModel<TreeItem<T>> sm = getControl().getSelectionModel();
+    static <T> void expandRow(final MultipleSelectionModel<TreeItem<T>> sm, Callback<TreeItem<T>, Integer> getIndex) {
         if (sm == null) return;
         
         TreeItem<T> treeItem = sm.getSelectedItem();
@@ -622,22 +639,21 @@
             // move selection to the first child (RT-17978)
             List<TreeItem<T>> children = treeItem.getChildren();
             if (! children.isEmpty()) {
-                sm.clearAndSelect(getControl().getRow(children.get(0)));
+                sm.clearAndSelect(getIndex.call(children.get(0)));
             }
         } else {
             treeItem.setExpanded(true);
         }
     }
     
-    private void expandAll() {
-        TreeItem root = getControl().getRoot();
+    static void expandAll(final TreeItem root) {
         if (root == null) return;
         
         root.setExpanded(true);
         expandChildren(root);
     }
     
-    private void expandChildren(TreeItem node) {
+    private static void expandChildren(TreeItem node) {
         if (node == null) return;
         List<TreeItem> children = node.getChildren();
         if (children == null) return;
@@ -651,32 +667,32 @@
         }
     }
 
-    private void collapseRow() {
-        TreeItem treeItem = getControl().getSelectionModel().getSelectedItem();
-        if (treeItem == null) return;
+    static <T> void collapseRow(final MultipleSelectionModel<TreeItem<T>> sm, final TreeItem root, final boolean isShowRoot) {
+        if (sm == null) return;
         
-        TreeItem root = getControl().getRoot();
+        TreeItem selectedItem = sm.getSelectedItem();
+        if (selectedItem == null) return;
         if (root == null) return;
         
         // Fix for RT-17233 where we could hide all items in a tree with no visible
         // root by pressing the left-arrow key too many times
-        if (! getControl().isShowRoot() && ! treeItem.isExpanded() && root.equals(treeItem.getParent())) {
+        if (! isShowRoot && ! selectedItem.isExpanded() && root.equals(selectedItem.getParent())) {
             return;
         }
         
         // Fix for RT-17833 where the selection highlight could disappear unexpectedly from
         // the root node in certain circumstances
-        if (root.equals(treeItem) && (! root.isExpanded() || root.getChildren().isEmpty())) {
+        if (root.equals(selectedItem) && (! root.isExpanded() || root.getChildren().isEmpty())) {
             return;
         }
         
         // If we're on a leaf or the branch is not expanded, move up to the parent,
         // otherwise collapse the branch.
-        if (treeItem.isLeaf() || ! treeItem.isExpanded()) {
-            getControl().getSelectionModel().clearSelection();
-            getControl().getSelectionModel().select(treeItem.getParent());
+        if (selectedItem.isLeaf() || ! selectedItem.isExpanded()) {
+            sm.clearSelection();
+            sm.select(selectedItem.getParent());
         } else {
-            treeItem.setExpanded(false);
+            selectedItem.setExpanded(false);
         }
     }
     
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableViewSkin.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TableViewSkin.java	Wed Nov 21 13:02:09 2012 +1300
@@ -24,6 +24,7 @@
  */
 package com.sun.javafx.scene.control.skin;
 
+import com.sun.javafx.scene.control.behavior.TableViewBehavior;
 import javafx.collections.ObservableList;
 import javafx.event.EventHandler;
 import javafx.scene.Node;
@@ -35,7 +36,6 @@
 import javafx.scene.input.MouseEvent;
 import javafx.util.Callback;
 
-import com.sun.javafx.scene.control.behavior.TableViewBehavior;
 import java.util.List;
 import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.ObjectProperty;
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TreeTableViewSkin.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/TreeTableViewSkin.java	Wed Nov 21 13:02:09 2012 +1300
@@ -93,39 +93,38 @@
         flow.getVbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml);
         flow.getHbar().addEventFilter(MouseEvent.MOUSE_PRESSED, ml);
 
-        // TODO enable
-//        // init the behavior 'closures'
-//        TreeTableViewBehavior behavior = getBehavior();
-//        behavior.setOnFocusPreviousRow(new Runnable() {
-//            @Override public void run() { onFocusPreviousCell(); }
-//        });
-//        behavior.setOnFocusNextRow(new Runnable() {
-//            @Override public void run() { onFocusNextCell(); }
-//        });
-//        behavior.setOnMoveToFirstCell(new Runnable() {
-//            @Override public void run() { onMoveToFirstCell(); }
-//        });
-//        behavior.setOnMoveToLastCell(new Runnable() {
-//            @Override public void run() { onMoveToLastCell(); }
-//        });
-//        behavior.setOnScrollPageDown(new Callback<Void, Integer>() {
-//            @Override public Integer call(Void param) { return onScrollPageDown(); }
-//        });
-//        behavior.setOnScrollPageUp(new Callback<Void, Integer>() {
-//            @Override public Integer call(Void param) { return onScrollPageUp(); }
-//        });
-//        behavior.setOnSelectPreviousRow(new Runnable() {
-//            @Override public void run() { onSelectPreviousCell(); }
-//        });
-//        behavior.setOnSelectNextRow(new Runnable() {
-//            @Override public void run() { onSelectNextCell(); }
-//        });
-//        behavior.setOnSelectLeftCell(new Runnable() {
-//            @Override public void run() { onSelectLeftCell(); }
-//        });
-//        behavior.setOnSelectRightCell(new Runnable() {
-//            @Override public void run() { onSelectRightCell(); }
-//        });
+        // init the behavior 'closures'
+        TreeTableViewBehavior behavior = getBehavior();
+        behavior.setOnFocusPreviousRow(new Runnable() {
+            @Override public void run() { onFocusPreviousCell(); }
+        });
+        behavior.setOnFocusNextRow(new Runnable() {
+            @Override public void run() { onFocusNextCell(); }
+        });
+        behavior.setOnMoveToFirstCell(new Runnable() {
+            @Override public void run() { onMoveToFirstCell(); }
+        });
+        behavior.setOnMoveToLastCell(new Runnable() {
+            @Override public void run() { onMoveToLastCell(); }
+        });
+        behavior.setOnScrollPageDown(new Callback<Void, Integer>() {
+            @Override public Integer call(Void param) { return onScrollPageDown(); }
+        });
+        behavior.setOnScrollPageUp(new Callback<Void, Integer>() {
+            @Override public Integer call(Void param) { return onScrollPageUp(); }
+        });
+        behavior.setOnSelectPreviousRow(new Runnable() {
+            @Override public void run() { onSelectPreviousCell(); }
+        });
+        behavior.setOnSelectNextRow(new Runnable() {
+            @Override public void run() { onSelectNextCell(); }
+        });
+        behavior.setOnSelectLeftCell(new Runnable() {
+            @Override public void run() { onSelectLeftCell(); }
+        });
+        behavior.setOnSelectRightCell(new Runnable() {
+            @Override public void run() { onSelectRightCell(); }
+        });
         
         registerChangeListener(treeTableView.rootProperty(), "ROOT");
         registerChangeListener(treeTableView.showRootProperty(), "SHOW_ROOT");
--- a/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/com/sun/javafx/scene/control/skin/caspian/caspian.css	Wed Nov 21 13:02:09 2012 +1300
@@ -2142,7 +2142,7 @@
 }
 
 .table-view > .virtual-flow > .corner,
-.tree-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;
 }
@@ -2163,7 +2163,7 @@
 }
 
 .table-row-cell:focused,
-.tree-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;
 }
@@ -2183,19 +2183,19 @@
 }
 
 .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 > .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:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:hover: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:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:focused:selected:hover,
-.tree-table-view:focused > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:focused:selected:hover{
+.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;
@@ -2216,13 +2216,19 @@
     -fx-opacity: -fx-disabled-opacity;
 }
 
-.table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:hover,
-.tree-table-view:row-selection > .virtual-flow > .clipped-container > .sheet > .tree-table-row-cell:filled:hover { 
+.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;
@@ -2245,7 +2251,7 @@
    a right-border, as it is not possible to get this cleanly out of view without
    introducing horizontal scrollbars (see RT-14886). */
 .table-view:constrained-resize > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .table-cell:last-visible,
-.tree-table-view:constrained-resize > .virtual-flow > .clipped-container > .sheet > .table-row-cell > .tree-table-cell:last-visible {
+.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,
@@ -2306,7 +2312,7 @@
 }
 
 .table-cell:selected:disabled,
-.tree-table-cell:selected:disabled{
+.tree-table-cell:selected:disabled {
     -fx-opacity: -fx-disabled-opacity;
 }
 
@@ -2394,7 +2400,7 @@
 }
 
 .table-view .show-hide-column-image,
-.tree-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;
 
@@ -2411,7 +2417,7 @@
    column will be dropped. This region can be styled using the .column-drag-header
    name. */
 .table-view .column-drag-header,
-.tree-table-view .column-drag-header{
+.tree-table-view .column-drag-header {
     -fx-background: -fx-accent;
     -fx-background-color: -fx-selection-bar;
     -fx-border-color: transparent;
@@ -2420,13 +2426,13 @@
 
 /* Semi-transparent overlay to indicate the column that is currently being moved */
 .table-view .column-overlay,
-.tree-table-view .column-overlay{
+.tree-table-view .column-overlay {
     -fx-background-color: darkgray;
     -fx-opacity: 0.3;
 }
 
 .table-view /*> column-header-background > nested-column-header >*/ .arrow,
-.tree-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-padding: 0.25em 0.3125em 0.25em 0.3125em; /* 3 3.75 3 3.75 */
--- a/javafx-ui-controls/src/javafx/scene/control/TablePosition.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/javafx/scene/control/TablePosition.java	Wed Nov 21 13:02:09 2012 +1300
@@ -24,8 +24,6 @@
  */
 package javafx.scene.control;
 
-import java.lang.ref.WeakReference;
-
 /**
  * This class is used to represent a single row/column/cell in a TableView.
  * This is used throughout the TableView API to represent which rows/columns/cells
@@ -43,7 +41,7 @@
  * @see TableView
  * @see TableColumn
  */
-public class TablePosition<S,T> {
+public class TablePosition<S,T> extends TablePositionBase<TableView<S>, TableColumn<S,T>> {
     
     /***************************************************************************
      *                                                                         *
@@ -62,9 +60,7 @@
      * @param tableColumn The TableColumn instance that this TablePosition represents.
      */
     public TablePosition(TableView<S> tableView, int row, TableColumn<S,T> tableColumn) {
-        this.row = row;
-        this.tableColumnRef = new WeakReference<TableColumn<S, T>>(tableColumn);
-        this.tableViewRef = new WeakReference<TableView<S>>(tableView);
+        super(tableView, row, tableColumn);
     }
     
     
@@ -75,10 +71,6 @@
      *                                                                         *
      **************************************************************************/
 
-    private final int row;
-    private final WeakReference<TableColumn<S,T>> tableColumnRef;
-    private final WeakReference<TableView<S>> tableViewRef;
-
 
 
     /***************************************************************************
@@ -88,17 +80,10 @@
      **************************************************************************/
     
     /**
-     * The row that this TablePosition represents in the TableView.
-     */
-    public final int getRow() {
-        return row;
-    }
-    
-    /**
      * The column index that this TablePosition represents in the TableView. It
      * is -1 if the TableView or TableColumn instances are null.
      */
-    public final int getColumn() {
+    @Override public int getColumn() {
         TableView tableView = getTableView();
         TableColumn tableColumn = getTableColumn();
         return tableView == null || tableColumn == null ? -1 : 
@@ -109,64 +94,11 @@
      * The TableView that this TablePosition is related to.
      */
     public final TableView<S> getTableView() {
-        return tableViewRef.get();
+        return getControl();
     }
     
-    /**
-     * The TableColumn that this TablePosition represents in the TableView.
-     */
-    public final TableColumn<S,T> getTableColumn() {
-        return tableColumnRef.get();
-    }
-
-    /**
-     * Indicates whether some other object is "equal to" this one.
-     * @param obj the reference object with which to compare.
-     * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
-     */
-    @Override public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        @SuppressWarnings("unchecked")
-        final TablePosition<S,T> other = (TablePosition<S,T>) obj;
-        if (this.row != other.row) {
-            return false;
-        }
-        TableColumn tableColumn = getTableColumn();
-        TableColumn otherTableColumn = other.getTableColumn();
-        if (tableColumn != otherTableColumn && (tableColumn == null || !tableColumn.equals(otherTableColumn))) {
-            return false;
-        }
-        TableView tableView = getTableView();
-        TableView otherTableView = other.getTableView();
-        if (tableView != otherTableView && (tableView == null || !tableView.equals(otherTableView))) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a hash code for this {@code TablePosition} object.
-     * @return a hash code for this {@code TablePosition} object.
-     */ 
-    @Override public int hashCode() {
-        int hash = 5;
-        hash = 79 * hash + this.row;
-        hash = 79 * hash + (getTableColumn() != null ? getTableColumn().hashCode() : 0);
-        hash = 79 * hash + (getTableView() != null ? getTableView().hashCode() : 0);
-        return hash;
-    }
-
-    /**
-     * Returns a string representation of this {@code TablePosition} object.
-     * @return a string representation of this {@code TablePosition} object.
-     */ 
-    @Override public String toString() {
-        return "TablePosition [ row: " + row + ", column: " + getTableColumn() + ", "
-                + "tableView: " + getTableView() + " ]";
+    @Override public final TableColumn<S,T> getTableColumn() {
+        // Forcing the return type to be TableColumn<S,T>, not TableColumnBase<S,T>
+        return super.getTableColumn();
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-controls/src/javafx/scene/control/TablePositionBase.java	Wed Nov 21 13:02:09 2012 +1300
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package javafx.scene.control;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is used to represent a single row/column/cell in a TableView.
+ * This is used throughout the TableView API to represent which rows/columns/cells
+ * are currently selected, focused, being edited, etc. Note that this class is
+ * immutable once it is created.
+ *
+ * <p>Because the TableView can have different
+ * {@link SelectionMode selection modes}, the row and column properties in
+ * TablePosition can be 'disabled' to represent an entire row or column. This is
+ * done by setting the unrequired property to -1 or null.
+ *
+ * @param <S> The type of the items contained within the TableView (i.e. the same
+ *      generic type as the S in TableView&lt;S&gt;).
+ * @param <T> The type of the items contained within the TableColumn.
+ * @see TableView
+ * @see TableColumn
+ */
+public abstract class TablePositionBase<C,TC extends TableColumnBase> {
+    
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/  
+
+    /**
+     * Constructs a TablePosition instance to represent the given row/column
+     * position in the given TableView instance. Both the TableView and 
+     * TableColumn are referenced weakly in this class, so it is possible that
+     * they will be null when their respective getters are called.
+     * 
+     * @param tableView The TableView that this position is related to.
+     * @param row The row that this TablePosition is representing.
+     * @param tableColumn The TableColumn instance that this TablePosition represents.
+     */
+    public TablePositionBase(C control, int row, TC tableColumn) {
+        this.row = row;
+        this.tableColumnRef = new WeakReference<TC>(tableColumn);
+        this.controlRef = new WeakReference<C>(control);
+    }
+    
+    
+    
+    /***************************************************************************
+     *                                                                         *
+     * Instance Variables                                                      *
+     *                                                                         *
+     **************************************************************************/
+
+    private final int row;
+    private final WeakReference<TC> tableColumnRef;
+    private final WeakReference<C> controlRef;
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+    
+    /**
+     * The row that this TablePosition represents in the TableView.
+     */
+    public int getRow() {
+        return row;
+    }
+    
+    /**
+     * The column index that this TablePosition represents in the TableView. It
+     * is -1 if the TableView or TableColumn instances are null.
+     */
+    public abstract int getColumn();
+    
+    /**
+     * The TableView that this TablePosition is related to.
+     */
+    public C getControl() {
+        return controlRef.get();
+    }
+    
+    /**
+     * The TableColumn that this TablePosition represents in the TableView.
+     */
+    public TC getTableColumn() {
+        return tableColumnRef.get();
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this one.
+     * @param obj the reference object with which to compare.
+     * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
+     */
+    @Override public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        @SuppressWarnings("unchecked")
+        final TablePositionBase other = (TablePositionBase) obj;
+        if (this.row != other.row) {
+            return false;
+        }
+        TC tableColumn = getTableColumn();
+        TableColumnBase otherTableColumn = other.getTableColumn();
+        if (tableColumn != otherTableColumn && (tableColumn == null || !tableColumn.equals(otherTableColumn))) {
+            return false;
+        }
+        C control = getControl();
+        Object otherControl = other.getControl();
+        if (control != otherControl && (control == null || !control.equals(otherControl))) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns a hash code for this {@code TablePosition} object.
+     * @return a hash code for this {@code TablePosition} object.
+     */ 
+    @Override public int hashCode() {
+        int hash = 5;
+        hash = 79 * hash + this.row;
+        hash = 79 * hash + (getTableColumn() != null ? getTableColumn().hashCode() : 0);
+        hash = 79 * hash + (getControl() != null ? getControl().hashCode() : 0);
+        return hash;
+    }
+
+    /**
+     * Returns a string representation of this {@code TablePosition} object.
+     * @return a string representation of this {@code TablePosition} object.
+     */ 
+    @Override public String toString() {
+        return "TablePosition [ row: " + row + ", column: " + getTableColumn() + ", "
+                + "control: " + getControl() + " ]";
+    }
+}
--- a/javafx-ui-controls/src/javafx/scene/control/TreeTablePosition.java	Sat Nov 10 19:55:31 2012 +1300
+++ b/javafx-ui-controls/src/javafx/scene/control/TreeTablePosition.java	Wed Nov 21 13:02:09 2012 +1300
@@ -43,7 +43,7 @@
  * @see TableView
  * @see TableColumn
  */
-public class TreeTablePosition<S,T> {
+public class TreeTablePosition<S,T> extends TablePositionBase<TreeTableView<S>, TreeTableColumn<S,T>> {
     
     /***************************************************************************
      *                                                                         *
@@ -62,9 +62,7 @@
      * @param tableColumn The TableColumn instance that this TablePosition represents.
      */
     public TreeTablePosition(TreeTableView<S> treeTableView, int row, TreeTableColumn<S,T> tableColumn) {
-        this.row = row;
-        this.treeTableColumnRef = new WeakReference<TreeTableColumn<S, T>>(tableColumn);
-        this.treeTableViewRef = new WeakReference<TreeTableView<S>>(treeTableView);
+        super(treeTableView, row, tableColumn);
     }
     
     
@@ -75,10 +73,6 @@
      *                                                                         *
      **************************************************************************/
 
-    private final int row;
-    private final WeakReference<TreeTableColumn<S,T>> treeTableColumnRef;
-    private final WeakReference<TreeTableView<S>> treeTableViewRef;
-
 
 
     /***************************************************************************
@@ -88,17 +82,10 @@
      **************************************************************************/
     
     /**
-     * The row that this TablePosition represents in the TableView.
-     */
-    public final int getRow() {
-        return row;
-    }
-    
-    /**
      * The column index that this TablePosition represents in the TableView. It
      * is -1 if the TableView or TableColumn instances are null.
      */
-    public final int getColumn() {
+    @Override public int getColumn() {
         TreeTableView tableView = getTreeTableView();
         TreeTableColumn tableColumn = getTableColumn();
         return tableView == null || tableColumn == null ? -1 : 
@@ -109,64 +96,11 @@
      * The TableView that this TablePosition is related to.
      */
     public final TreeTableView<S> getTreeTableView() {
-        return treeTableViewRef.get();
+        return getControl();
     }
     
-    /**
-     * The TableColumn that this TablePosition represents in the TableView.
-     */
-    public final TreeTableColumn<S,T> getTableColumn() {
-        return treeTableColumnRef.get();
-    }
-
-    /**
-     * Indicates whether some other object is "equal to" this one.
-     * @param obj the reference object with which to compare.
-     * @return {@code true} if this object is equal to the {@code obj} argument; {@code false} otherwise.
-     */
-    @Override public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        @SuppressWarnings("unchecked")
-        final TreeTablePosition<S,T> other = (TreeTablePosition<S,T>) obj;
-        if (this.row != other.row) {
-            return false;
-        }
-        TreeTableColumn tableColumn = getTableColumn();
-        TreeTableColumn otherTableColumn = other.getTableColumn();
-        if (tableColumn != otherTableColumn && (tableColumn == null || !tableColumn.equals(otherTableColumn))) {
-            return false;
-        }
-        TreeTableView tableView = getTreeTableView();
-        TreeTableView otherTableView = other.getTreeTableView();
-        if (tableView != otherTableView && (tableView == null || !tableView.equals(otherTableView))) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns a hash code for this {@code TablePosition} object.
-     * @return a hash code for this {@code TablePosition} object.
-     */ 
-    @Override public int hashCode() {
-        int hash = 5;
-        hash = 79 * hash + this.row;
-        hash = 79 * hash + (getTableColumn() != null ? getTableColumn().hashCode() : 0);
-        hash = 79 * hash + (getTreeTableView() != null ? getTreeTableView().hashCode() : 0);
-        return hash;
-    }
-
-    /**
-     * Returns a string representation of this {@code TablePosition} object.
-     * @return a string representation of this {@code TablePosition} object.
-     */ 
-    @Override public String toString() {
-        return "TreeTablePosition [ row: " + row + ", column: " + getTableColumn() + ", "
-                + "treeTableView: " + getTreeTableView() + " ]";
+    @Override public final TreeTableColumn<S,T> getTableColumn() {
+        // Forcing the return type to be TreeTableColumn<S,T>, not TableColumnBase<S,T>
+        return super.getTableColumn();
     }
 }