add ignored files to the repo
diff --git a/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/client/ui/VCustomScrollTable.java b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/client/ui/VCustomScrollTable.java
new file mode 100644
index 0000000..7212104
--- /dev/null
+++ b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/client/ui/VCustomScrollTable.java
@@ -0,0 +1,9629 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.client.ui;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.Scheduler;
+import com.google.gwt.core.client.Scheduler.ScheduledCommand;
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.dom.client.Node;
+import com.google.gwt.dom.client.NodeList;
+import com.google.gwt.dom.client.Style;
+import com.google.gwt.dom.client.Style.Display;
+import com.google.gwt.dom.client.Style.Overflow;
+import com.google.gwt.dom.client.Style.Position;
+import com.google.gwt.dom.client.Style.TextAlign;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.dom.client.Style.Visibility;
+import com.google.gwt.dom.client.TableCellElement;
+import com.google.gwt.dom.client.TableRowElement;
+import com.google.gwt.dom.client.TableSectionElement;
+import com.google.gwt.dom.client.Touch;
+import com.google.gwt.event.dom.client.BlurEvent;
+import com.google.gwt.event.dom.client.BlurHandler;
+import com.google.gwt.event.dom.client.FocusEvent;
+import com.google.gwt.event.dom.client.FocusHandler;
+import com.google.gwt.event.dom.client.KeyCodes;
+import com.google.gwt.event.dom.client.KeyDownEvent;
+import com.google.gwt.event.dom.client.KeyDownHandler;
+import com.google.gwt.event.dom.client.KeyPressEvent;
+import com.google.gwt.event.dom.client.KeyPressHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.event.dom.client.ScrollEvent;
+import com.google.gwt.event.dom.client.ScrollHandler;
+import com.google.gwt.event.logical.shared.CloseEvent;
+import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.regexp.shared.MatchResult;
+import com.google.gwt.regexp.shared.RegExp;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HasWidgets;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwt.user.client.ui.Widget;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.BrowserInfo;
+import com.vaadin.client.ComponentConnector;
+import com.vaadin.client.ConnectorMap;
+import com.vaadin.client.DeferredWorker;
+import com.vaadin.client.Focusable;
+import com.vaadin.client.MouseEventDetailsBuilder;
+import com.vaadin.client.StyleConstants;
+import com.vaadin.client.TooltipInfo;
+import com.vaadin.client.UIDL;
+import com.vaadin.client.Util;
+import com.vaadin.client.VConsole;
+import com.vaadin.client.VTooltip;
+import com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow;
+import com.vaadin.client.ui.dd.DDUtil;
+import com.vaadin.client.ui.dd.VAbstractDropHandler;
+import com.vaadin.client.ui.dd.VAcceptCallback;
+import com.vaadin.client.ui.dd.VDragAndDropManager;
+import com.vaadin.client.ui.dd.VDragEvent;
+import com.vaadin.client.ui.dd.VHasDropHandler;
+import com.vaadin.client.ui.dd.VTransferable;
+import com.vaadin.shared.AbstractComponentState;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.shared.ui.dd.VerticalDropLocation;
+import com.vaadin.shared.ui.table.TableConstants;
+
+// TODO: Auto-generated Javadoc
+/**
+ * VCustomScrollTable
+ * 
+ * VCustomScrollTable is a FlowPanel having two widgets in it: * TableHead
+ * component * ScrollPanel
+ * 
+ * TableHead contains table's header and widgets + logic for resizing,
+ * reordering and hiding columns.
+ * 
+ * ScrollPanel contains VScrollTableBody object which handles content. To save
+ * some bandwidth and to improve clients responsiveness with loads of data, in
+ * VScrollTableBody all rows are not necessary rendered. There are "spacers" in
+ * VScrollTableBody to use the exact same space as non-rendered rows would use.
+ * This way we can use seamlessly traditional scrollbars and scrolling to fetch
+ * more rows instead of "paging".
+ * 
+ * In VCustomScrollTable we listen to scroll events. On horizontal scrolling we
+ * also update TableHeads scroll position which has its scrollbars hidden. On
+ * vertical scroll events we will check if we are reaching the end of area where
+ * we have rows rendered and
+ * 
+ * TODO implement unregistering for child components in Cells
+ */
+@SuppressWarnings("deprecation")
+public class VCustomScrollTable extends FlowPanel implements HasWidgets,
+        ScrollHandler, VHasDropHandler, FocusHandler, BlurHandler, Focusable,
+        ActionOwner, SubPartAware, DeferredWorker {
+
+    /** Simple interface for parts of the table capable of owning a context
+	 * menu.
+	 *
+	 * @author Vaadin Ltd
+	 * @since 7.2
+	 */
+    private interface ContextMenuOwner {
+        
+        /** Show context menu.
+		 *
+		 * @param event
+		 *            the event
+		 */
+        public void showContextMenu(Event event);
+    }
+
+    /** Handles showing context menu on "long press" from a touch screen.
+	 *
+	 * @author Vaadin Ltd
+	 * @since 7.2
+	 */
+    private class TouchContextProvider {
+        
+        /** The Constant TOUCH_CONTEXT_MENU_TIMEOUT. */
+        private static final int TOUCH_CONTEXT_MENU_TIMEOUT = 500;
+        
+        /** The context touch timeout. */
+        private Timer contextTouchTimeout;
+
+        /** The touch start. */
+        private Event touchStart;
+        
+        /** The touch start y. */
+        private int touchStartY;
+        
+        /** The touch start x. */
+        private int touchStartX;
+
+        /** The target. */
+        private ContextMenuOwner target;
+
+        /**
+         * Initializes a handler for a certain context menu owner.
+         * 
+         * @param target
+         *            the owner of the context menu
+         */
+        public TouchContextProvider(ContextMenuOwner target) {
+            this.target = target;
+        }
+
+        /**
+         * Cancels the current context touch timeout.
+         */
+        public void cancel() {
+            if (contextTouchTimeout != null) {
+                contextTouchTimeout.cancel();
+                contextTouchTimeout = null;
+            }
+            touchStart = null;
+        }
+
+        /**
+         * A function to handle touch context events in a table.
+         * 
+         * @param event
+         *            browser event to handle
+         */
+        public void handleTouchEvent(final Event event) {
+            int type = event.getTypeInt();
+
+            switch (type) {
+            case Event.ONCONTEXTMENU:
+                target.showContextMenu(event);
+                break;
+            case Event.ONTOUCHSTART:
+                // save position to fields, touches in events are same
+                // instance during the operation.
+                touchStart = event;
+
+                Touch touch = event.getChangedTouches().get(0);
+                touchStartX = touch.getClientX();
+                touchStartY = touch.getClientY();
+
+                if (contextTouchTimeout == null) {
+                    contextTouchTimeout = new Timer() {
+
+                        @Override
+                        public void run() {
+                            if (touchStart != null) {
+                                // Open the context menu if finger
+                                // is held in place long enough.
+                                target.showContextMenu(touchStart);
+                                event.preventDefault();
+                                touchStart = null;
+                            }
+                        }
+                    };
+                }
+                contextTouchTimeout.schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+                break;
+            case Event.ONTOUCHCANCEL:
+            case Event.ONTOUCHEND:
+                cancel();
+                break;
+            case Event.ONTOUCHMOVE:
+                if (isSignificantMove(event)) {
+                    // Moved finger before the context menu timer
+                    // expired, so let the browser handle the event.
+                    cancel();
+                }
+            }
+        }
+
+        /**
+         * Calculates how many pixels away the user's finger has traveled. This
+         * reduces the chance of small non-intentional movements from canceling
+         * the long press detection.
+         * 
+         * @param event
+         *            the Event for which to check the move distance
+         * @return true if this is considered an intentional move by the user
+         */
+        protected boolean isSignificantMove(Event event) {
+            if (touchStart == null) {
+                // no touch start
+                return false;
+            }
+
+            // Calculate the distance between touch start and the current touch
+            // position
+            Touch touch = event.getChangedTouches().get(0);
+            int deltaX = touch.getClientX() - touchStartX;
+            int deltaY = touch.getClientY() - touchStartY;
+            int delta = deltaX * deltaX + deltaY * deltaY;
+
+            // Compare to the square of the significant move threshold to remove
+            // the need for a square root
+            if (delta > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD
+                    * TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /** The Constant STYLENAME. */
+    public static final String STYLENAME = "v-table";
+
+    /** The Enum SelectMode.
+	 */
+    public enum SelectMode {
+        
+        /** The none. */
+        NONE(0), 
+ /** The single. */
+ SINGLE(1), 
+ /** The multi. */
+ MULTI(2);
+        
+        /** The id. */
+        private int id;
+
+        /** Instantiates a new select mode.
+		 *
+		 * @param id
+		 *            the id
+		 */
+        private SelectMode(int id) {
+            this.id = id;
+        }
+
+        /** Gets the id.
+		 *
+		 * @return the id
+		 */
+        public int getId() {
+            return id;
+        }
+    }
+
+    /** The Constant ROW_HEADER_COLUMN_KEY. */
+    private static final String ROW_HEADER_COLUMN_KEY = "0";
+
+    /** The Constant CACHE_RATE_DEFAULT. */
+    private static final double CACHE_RATE_DEFAULT = 2;
+
+    /**
+     * The default multi select mode where simple left clicks only selects one
+     * item, CTRL+left click selects multiple items and SHIFT-left click selects
+     * a range of items.
+     */
+    private static final int MULTISELECT_MODE_DEFAULT = 0;
+
+    /**
+     * The simple multiselect mode is what the table used to have before
+     * ctrl/shift selections were added. That is that when this is set clicking
+     * on an item selects/deselects the item and no ctrl/shift selections are
+     * available.
+     */
+    private static final int MULTISELECT_MODE_SIMPLE = 1;
+
+    /** multiple of pagelength which component will cache when requesting
+	 * more rows.
+	 */
+    private double cache_rate = CACHE_RATE_DEFAULT;
+    
+    /** fraction of pageLenght which can be scrolled without making new
+	 * request.
+	 */
+    private double cache_react_rate = 0.75 * cache_rate;
+
+    /** The Constant ALIGN_CENTER. */
+    public static final char ALIGN_CENTER = 'c';
+    
+    /** The Constant ALIGN_LEFT. */
+    public static final char ALIGN_LEFT = 'b';
+    
+    /** The Constant ALIGN_RIGHT. */
+    public static final char ALIGN_RIGHT = 'e';
+    
+    /** The Constant CHARCODE_SPACE. */
+    private static final int CHARCODE_SPACE = 32;
+    
+    /** The first row in view port. */
+    private int firstRowInViewPort = 0;
+    
+    /** The page length. */
+    private int pageLength = 15;
+    
+    /** The last requested firstvisible. */
+    private int lastRequestedFirstvisible = 0; // to detect "serverside scroll"
+    
+    /** The firstvisible on last page. */
+    private int firstvisibleOnLastPage = -1; // To detect if the first visible
+                                             // is on the last page
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean showRowHeaders = false;
+
+    /** The column order. */
+    private String[] columnOrder;
+
+    /** The client. */
+    protected ApplicationConnection client;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public String paintableId;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean immediate;
+
+    /** The updated req rows. */
+    private boolean updatedReqRows = true;
+
+    /** The null selection allowed. */
+    private boolean nullSelectionAllowed = true;
+
+    /** The select mode. */
+    private SelectMode selectMode = SelectMode.NONE;
+
+    /** The selected row keys. */
+    public final HashSet<String> selectedRowKeys = new HashSet<String>();
+
+    /** The un syncedselections before row fetch. */
+    /*
+     * When scrolling and selecting at the same time, the selections are not in
+     * sync with the server while retrieving new rows (until key is released).
+     */
+    private HashSet<Object> unSyncedselectionsBeforeRowFetch;
+
+    /*
+     * These are used when jumping between pages when pressing Home and End
+     */
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean selectLastItemInNextRender = false;
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean selectFirstItemInNextRender = false;
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean focusFirstItemInNextRender = false;
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean focusLastItemInNextRender = false;
+
+    /**
+     * The currently focused row.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public VScrollTableRow focusedRow;
+
+    /**
+     * Helper to store selection range start in when using the keyboard
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public VScrollTableRow selectionRangeStart;
+
+    /**
+     * Flag for notifying when the selection has changed and should be sent to
+     * the server
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public boolean selectionChanged = false;
+
+    /** The scrolling velocity. */
+    /*
+     * The speed (in pixels) which the scrolling scrolls vertically/horizontally
+     */
+    private int scrollingVelocity = 10;
+
+    /** The scrolling velocity timer. */
+    private Timer scrollingVelocityTimer = null;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public String[] bodyActionKeys;
+
+    /** The enable debug. */
+    private boolean enableDebug = false;
+
+    /** The Constant hasNativeTouchScrolling. */
+    private static final boolean hasNativeTouchScrolling = BrowserInfo.get()
+            .isTouchDevice()
+            && !BrowserInfo.get().requiresTouchScrollDelegate();
+
+    /** The noncollapsible columns. */
+    private Set<String> noncollapsibleColumns;
+
+    /**
+     * The last known row height used to preserve the height of a table with
+     * custom row heights and a fixed page length after removing the last row
+     * from the table.
+     * 
+     * A new VScrollTableBody instance is created every time the number of rows
+     * changes causing {@link VScrollTableBody#rowHeight} to be discarded and
+     * the height recalculated by {@link VScrollTableBody#getRowHeight(boolean)}
+     * to avoid some rounding problems, e.g. round(2 * 19.8) / 2 = 20 but
+     * round(3 * 19.8) / 3 = 19.66.
+     */
+    private double lastKnownRowHeight = Double.NaN;
+
+    /**
+     * Remember scroll position when getting detached to properly scroll back to
+     * the location that there is data for if getting attached again.
+     */
+    private int detachedScrollPosition = 0;
+
+    /** Represents a select range of rows.
+	 */
+    private class SelectionRange {
+        
+        /** The start row. */
+        private VScrollTableRow startRow;
+        
+        /** The length. */
+        private final int length;
+
+        /** Constuctor.
+		 *
+		 * @param row1
+		 *            the row1
+		 * @param row2
+		 *            the row2
+		 */
+        public SelectionRange(VScrollTableRow row1, VScrollTableRow row2) {
+            VScrollTableRow endRow;
+            if (row2.isBefore(row1)) {
+                startRow = row2;
+                endRow = row1;
+            } else {
+                startRow = row1;
+                endRow = row2;
+            }
+            length = endRow.getIndex() - startRow.getIndex() + 1;
+        }
+
+        /** Instantiates a new selection range.
+		 *
+		 * @param row
+		 *            the row
+		 * @param length
+		 *            the length
+		 */
+        public SelectionRange(VScrollTableRow row, int length) {
+            startRow = row;
+            this.length = length;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see java.lang.Object#toString()
+         */
+
+        @Override
+        public String toString() {
+            return startRow.getKey() + "-" + length;
+        }
+
+        /** In range.
+		 *
+		 * @param row
+		 *            the row
+		 * @return true, if successful
+		 */
+        private boolean inRange(VScrollTableRow row) {
+            return row.getIndex() >= startRow.getIndex()
+                    && row.getIndex() < startRow.getIndex() + length;
+        }
+
+        /** Split.
+		 *
+		 * @param row
+		 *            the row
+		 * @return the collection
+		 */
+        public Collection<SelectionRange> split(VScrollTableRow row) {
+            assert row.isAttached();
+            ArrayList<SelectionRange> ranges = new ArrayList<SelectionRange>(2);
+
+            int endOfFirstRange = row.getIndex() - 1;
+            if (endOfFirstRange >= startRow.getIndex()) {
+                // create range of first part unless its length is < 1
+                ranges.add(new SelectionRange(startRow, endOfFirstRange
+                        - startRow.getIndex() + 1));
+            }
+            int startOfSecondRange = row.getIndex() + 1;
+            if (getEndIndex() >= startOfSecondRange) {
+                // create range of second part unless its length is < 1
+                VScrollTableRow startOfRange = scrollBody
+                        .getRowByRowIndex(startOfSecondRange);
+                if (startOfRange != null) {
+                    ranges.add(new SelectionRange(startOfRange, getEndIndex()
+                            - startOfSecondRange + 1));
+                }
+            }
+            return ranges;
+        }
+
+        /** Gets the end index.
+		 *
+		 * @return the end index
+		 */
+        private int getEndIndex() {
+            return startRow.getIndex() + length - 1;
+        }
+
+    }
+
+    /** The selected row ranges. */
+    private final HashSet<SelectionRange> selectedRowRanges = new HashSet<SelectionRange>();
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean initializedAndAttached = false;
+
+    /**
+     * Flag to indicate if a column width recalculation is needed due update.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public boolean headerChangedDuringUpdate = false;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public final TableHead tHead = new TableHead();
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public final TableFooter tFoot = new TableFooter();
+
+    /** Handles context menu for table body. */
+    private ContextMenuOwner contextMenuOwner = new ContextMenuOwner() {
+
+        @Override
+        public void showContextMenu(Event event) {
+            int left = Util.getTouchOrMouseClientX(event);
+            int top = Util.getTouchOrMouseClientY(event);
+            boolean menuShown = handleBodyContextMenu(left, top);
+            if (menuShown) {
+                event.stopPropagation();
+                event.preventDefault();
+            }
+        }
+    };
+
+    /** Handles touch events to display a context menu for table body. */
+    private TouchContextProvider touchContextProvider = new TouchContextProvider(
+            contextMenuOwner);
+
+    /** For internal use only. May be removed or replaced in the future.
+	 * 
+	 * Overwrites onBrowserEvent function on FocusableScrollPanel to give event
+	 * access to touchContextProvider. Has to be public to give TableConnector
+	 * access to the scrollBodyPanel field.
+	 *
+	 * @author Vaadin Ltd
+	 * @since 7.2
+	 */
+    public class FocusableScrollContextPanel extends FocusableScrollPanel {
+        
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user.client.Event)
+         */
+        @Override
+        public void onBrowserEvent(Event event) {
+            super.onBrowserEvent(event);
+            touchContextProvider.handleTouchEvent(event);
+        };
+
+        /** Instantiates a new focusable scroll context panel.
+		 *
+		 * @param useFakeFocusElement
+		 *            the use fake focus element
+		 */
+        public FocusableScrollContextPanel(boolean useFakeFocusElement) {
+            super(useFakeFocusElement);
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public final FocusableScrollContextPanel scrollBodyPanel = new FocusableScrollContextPanel(
+            true);
+
+    /** The nav key press handler. */
+    private KeyPressHandler navKeyPressHandler = new KeyPressHandler() {
+
+        @Override
+        public void onKeyPress(KeyPressEvent keyPressEvent) {
+            // This is used for Firefox only, since Firefox auto-repeat
+            // works correctly only if we use a key press handler, other
+            // browsers handle it correctly when using a key down handler
+            if (!BrowserInfo.get().isGecko()) {
+                return;
+            }
+
+            NativeEvent event = keyPressEvent.getNativeEvent();
+            if (!enabled) {
+                // Cancel default keyboard events on a disabled Table
+                // (prevents scrolling)
+                event.preventDefault();
+            } else if (hasFocus) {
+                // Key code in Firefox/onKeyPress is present only for
+                // special keys, otherwise 0 is returned
+                int keyCode = event.getKeyCode();
+                if (keyCode == 0 && event.getCharCode() == ' ') {
+                    // Provide a keyCode for space to be compatible with
+                    // FireFox keypress event
+                    keyCode = CHARCODE_SPACE;
+                }
+
+                if (handleNavigation(keyCode,
+                        event.getCtrlKey() || event.getMetaKey(),
+                        event.getShiftKey())) {
+                    event.preventDefault();
+                }
+
+                startScrollingVelocityTimer();
+            }
+        }
+
+    };
+
+    /** The nav key up handler. */
+    private KeyUpHandler navKeyUpHandler = new KeyUpHandler() {
+
+        @Override
+        public void onKeyUp(KeyUpEvent keyUpEvent) {
+            NativeEvent event = keyUpEvent.getNativeEvent();
+            int keyCode = event.getKeyCode();
+
+            if (!isFocusable()) {
+                cancelScrollingVelocityTimer();
+            } else if (isNavigationKey(keyCode)) {
+                if (keyCode == getNavigationDownKey()
+                        || keyCode == getNavigationUpKey()) {
+                    /*
+                     * in multiselect mode the server may still have value from
+                     * previous page. Clear it unless doing multiselection or
+                     * just moving focus.
+                     */
+                    if (!event.getShiftKey() && !event.getCtrlKey()) {
+                        instructServerToForgetPreviousSelections();
+                    }
+                    sendSelectedRows();
+                }
+                cancelScrollingVelocityTimer();
+                navKeyDown = false;
+            }
+        }
+    };
+
+    /** The nav key down handler. */
+    private KeyDownHandler navKeyDownHandler = new KeyDownHandler() {
+
+        @Override
+        public void onKeyDown(KeyDownEvent keyDownEvent) {
+            NativeEvent event = keyDownEvent.getNativeEvent();
+            // This is not used for Firefox
+            if (BrowserInfo.get().isGecko()) {
+                return;
+            }
+
+            if (!enabled) {
+                // Cancel default keyboard events on a disabled Table
+                // (prevents scrolling)
+                event.preventDefault();
+            } else if (hasFocus) {
+                if (handleNavigation(event.getKeyCode(), event.getCtrlKey()
+                        || event.getMetaKey(), event.getShiftKey())) {
+                    navKeyDown = true;
+                    event.preventDefault();
+                }
+
+                startScrollingVelocityTimer();
+            }
+        }
+    };
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public int totalRows;
+
+    /** The collapsed columns. */
+    private Set<String> collapsedColumns;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public final RowRequestHandler rowRequestHandler;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public VScrollTableBody scrollBody;
+
+    /** The firstvisible. */
+    private int firstvisible = 0;
+    
+    /** The sort ascending. */
+    private boolean sortAscending;
+    
+    /** The sort column. */
+    private String sortColumn;
+    
+    /** The old sort column. */
+    private String oldSortColumn;
+    
+    /** The column reordering. */
+    private boolean columnReordering;
+
+    /**
+     * This map contains captions and icon urls for actions like: * "33_c" ->
+     * "Edit" * "33_i" -> "http://dom.com/edit.png"
+     */
+    private final HashMap<Object, String> actionMap = new HashMap<Object, String>();
+    
+    /** The visible col order. */
+    private String[] visibleColOrder;
+    
+    /** The initial content received. */
+    private boolean initialContentReceived = false;
+    
+    /** The scroll position element. */
+    private Element scrollPositionElement;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean enabled;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean showColHeaders;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean showColFooters;
+
+    /** flag to indicate that table body has changed. */
+    private boolean isNewBody = true;
+
+    /**
+     * Read from the "recalcWidths" -attribute. When it is true, the table will
+     * recalculate the widths for columns - desirable in some cases. For #1983,
+     * marked experimental. See also variable <code>refreshContentWidths</code>
+     * in method {@link TableHead#updateCellsFromUIDL(UIDL)}.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public boolean recalcWidths = false;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean rendering = false;
+
+    /** The has focus. */
+    private boolean hasFocus = false;
+    
+    /** The dragmode. */
+    private int dragmode;
+
+    /** The multiselectmode. */
+    private int multiselectmode;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public int tabIndex;
+
+    /** The touch scroll delegate. */
+    private TouchScrollDelegate touchScrollDelegate;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public int lastRenderedHeight;
+
+    /**
+     * Values (serverCacheFirst+serverCacheLast) sent by server that tells which
+     * rows (indexes) are in the server side cache (page buffer). -1 means
+     * unknown. The server side cache row MUST MATCH the client side cache rows.
+     * 
+     * If the client side cache contains additional rows with e.g. buttons, it
+     * will cause out of sync when such a button is pressed.
+     * 
+     * If the server side cache contains additional rows with e.g. buttons,
+     * scrolling in the client will cause empty buttons to be rendered
+     * (cached=true request for non-existing components)
+     * 
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public int serverCacheFirst = -1;
+    
+    /** The server cache last. */
+    public int serverCacheLast = -1;
+
+    /**
+     * In several cases TreeTable depends on the scrollBody.lastRendered being
+     * 'out of sync' while the update is being done. In those cases the sanity
+     * check must be performed afterwards.
+     */
+    public boolean postponeSanityCheckForLastRendered;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean sizeNeedsInit = true;
+
+    /**
+     * Used to recall the position of an open context menu if we need to close
+     * and reopen it during a row update.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public class ContextMenuDetails implements CloseHandler<PopupPanel> {
+        
+        /** The row key. */
+        public String rowKey;
+        
+        /** The left. */
+        public int left;
+        
+        /** The top. */
+        public int top;
+        
+        /** The close registration. */
+        HandlerRegistration closeRegistration;
+
+        /** Instantiates a new context menu details.
+		 *
+		 * @param menu
+		 *            the menu
+		 * @param rowKey
+		 *            the row key
+		 * @param left
+		 *            the left
+		 * @param top
+		 *            the top
+		 */
+        public ContextMenuDetails(VContextMenu menu, String rowKey, int left,
+                int top) {
+            this.rowKey = rowKey;
+            this.left = left;
+            this.top = top;
+            closeRegistration = menu.addCloseHandler(this);
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt.event.logical.shared.CloseEvent)
+         */
+        @Override
+        public void onClose(CloseEvent<PopupPanel> event) {
+            contextMenu = null;
+            closeRegistration.removeHandler();
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public ContextMenuDetails contextMenu = null;
+
+    /** The had scroll bars. */
+    private boolean hadScrollBars = false;
+
+    /** The add close handler. */
+    private HandlerRegistration addCloseHandler;
+
+    /** Changes to manage mouseDown and mouseUp. */
+    /**
+     * The element where the last mouse down event was registered.
+     */
+    private Element lastMouseDownTarget;
+
+    /**
+     * Set to true by {@link #mouseUpPreviewHandler} if it gets a mouseup at the
+     * same element as {@link #lastMouseDownTarget}.
+     */
+    private boolean mouseUpPreviewMatched = false;
+
+    /** The mouse up event preview registration. */
+    private HandlerRegistration mouseUpEventPreviewRegistration;
+
+    /**
+     * Previews events after a mousedown to detect where the following mouseup
+     * hits.
+     */
+    private final NativePreviewHandler mouseUpPreviewHandler = new NativePreviewHandler() {
+
+        @Override
+        public void onPreviewNativeEvent(NativePreviewEvent event) {
+            if (event.getTypeInt() == Event.ONMOUSEUP) {
+                mouseUpEventPreviewRegistration.removeHandler();
+
+                // Event's reported target not always correct if event
+                // capture is in use
+                Element elementUnderMouse = Util.getElementUnderMouse(event
+                        .getNativeEvent());
+                if (lastMouseDownTarget != null
+                        && lastMouseDownTarget.isOrHasChild(elementUnderMouse)) {
+                    mouseUpPreviewMatched = true;
+                } else {
+                    getLogger().log(
+                            Level.FINEST,
+                            "Ignoring mouseup from " + elementUnderMouse
+                                    + " when mousedown was on "
+                                    + lastMouseDownTarget);
+                }
+            }
+        }
+    };
+
+    /** Instantiates a new v custom scroll table.
+	 */
+    public VCustomScrollTable() {
+        setMultiSelectMode(MULTISELECT_MODE_DEFAULT);
+
+        scrollBodyPanel.addFocusHandler(this);
+        scrollBodyPanel.addBlurHandler(this);
+
+        scrollBodyPanel.addScrollHandler(this);
+
+        /*
+         * Firefox auto-repeat works correctly only if we use a key press
+         * handler, other browsers handle it correctly when using a key down
+         * handler
+         */
+        if (BrowserInfo.get().isGecko()) {
+            scrollBodyPanel.addKeyPressHandler(navKeyPressHandler);
+        } else {
+            scrollBodyPanel.addKeyDownHandler(navKeyDownHandler);
+        }
+        scrollBodyPanel.addKeyUpHandler(navKeyUpHandler);
+
+        scrollBodyPanel.sinkEvents(Event.TOUCHEVENTS | Event.ONCONTEXTMENU);
+
+        setStyleName(STYLENAME);
+
+        add(tHead);
+        add(scrollBodyPanel);
+        add(tFoot);
+
+        rowRequestHandler = new RowRequestHandler();
+    }
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
+     */
+    @Override
+    public void setStyleName(String style) {
+        updateStyleNames(style, false);
+    }
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.UIObject#setStylePrimaryName(java.lang.String)
+     */
+    @Override
+    public void setStylePrimaryName(String style) {
+        updateStyleNames(style, true);
+    }
+
+    /** Update style names.
+	 *
+	 * @param newStyle
+	 *            the new style
+	 * @param isPrimary
+	 *            the is primary
+	 */
+    private void updateStyleNames(String newStyle, boolean isPrimary) {
+        scrollBodyPanel
+                .removeStyleName(getStylePrimaryName() + "-body-wrapper");
+        scrollBodyPanel.removeStyleName(getStylePrimaryName() + "-body");
+
+        if (scrollBody != null) {
+            scrollBody.removeStyleName(getStylePrimaryName()
+                    + "-body-noselection");
+        }
+
+        if (isPrimary) {
+            super.setStylePrimaryName(newStyle);
+        } else {
+            super.setStyleName(newStyle);
+        }
+
+        scrollBodyPanel.addStyleName(getStylePrimaryName() + "-body-wrapper");
+        scrollBodyPanel.addStyleName(getStylePrimaryName() + "-body");
+
+        tHead.updateStyleNames(getStylePrimaryName());
+        tFoot.updateStyleNames(getStylePrimaryName());
+
+        if (scrollBody != null) {
+            scrollBody.updateStyleNames(getStylePrimaryName());
+        }
+    }
+
+    /** Inits the.
+	 *
+	 * @param client
+	 *            the client
+	 */
+    public void init(ApplicationConnection client) {
+        this.client = client;
+        // Add a handler to clear saved context menu details when the menu
+        // closes. See #8526.
+        addCloseHandler = client.getContextMenu().addCloseHandler(
+                new CloseHandler<PopupPanel>() {
+
+                    @Override
+                    public void onClose(CloseEvent<PopupPanel> event) {
+                        contextMenu = null;
+                    }
+                });
+    }
+
+    /**
+     * Handles a context menu event on table body.
+     * 
+     * @param left
+     *            left position of the context menu
+     * @param top
+     *            top position of the context menu
+     * @return true if a context menu was shown, otherwise false
+     */
+    private boolean handleBodyContextMenu(int left, int top) {
+        if (enabled && bodyActionKeys != null) {
+            top += Window.getScrollTop();
+            left += Window.getScrollLeft();
+            client.getContextMenu().showAt(this, left, top);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Fires a column resize event which sends the resize information to the
+     * server.
+     * 
+     * @param columnId
+     *            The columnId of the column which was resized
+     * @param originalWidth
+     *            The width in pixels of the column before the resize event
+     * @param newWidth
+     *            The width in pixels of the column after the resize event
+     */
+    private void fireColumnResizeEvent(String columnId, int originalWidth,
+            int newWidth) {
+        client.updateVariable(paintableId, "columnResizeEventColumn", columnId,
+                false);
+        client.updateVariable(paintableId, "columnResizeEventPrev",
+                originalWidth, false);
+        client.updateVariable(paintableId, "columnResizeEventCurr", newWidth,
+                immediate);
+
+    }
+
+    /**
+     * Non-immediate variable update of column widths for a collection of
+     * columns.
+     * 
+     * @param columns
+     *            the columns to trigger the events for.
+     */
+    private void sendColumnWidthUpdates(Collection<HeaderCell> columns) {
+        String[] newSizes = new String[columns.size()];
+        int ix = 0;
+        for (HeaderCell cell : columns) {
+            newSizes[ix++] = cell.getColKey() + ":" + cell.getWidth();
+        }
+        client.updateVariable(paintableId, "columnWidthUpdates", newSizes,
+                false);
+    }
+
+    /** Moves the focus one step down.
+	 *
+	 * @return Returns true if succeeded
+	 */
+    private boolean moveFocusDown() {
+        return moveFocusDown(0);
+    }
+
+    /** Moves the focus down by 1+offset rows.
+	 *
+	 * @param offset
+	 *            the offset
+	 * @return Returns true if succeeded, else false if the selection could not
+	 *         be move downwards
+	 */
+    private boolean moveFocusDown(int offset) {
+        if (isSelectable()) {
+            if (focusedRow == null && scrollBody.iterator().hasNext()) {
+                // FIXME should focus first visible from top, not first rendered
+                // ??
+                return setRowFocus((VScrollTableRow) scrollBody.iterator()
+                        .next());
+            } else {
+                VScrollTableRow next = getNextRow(focusedRow, offset);
+                if (next != null) {
+                    return setRowFocus(next);
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /** Moves the selection one step up.
+	 *
+	 * @return Returns true if succeeded
+	 */
+    private boolean moveFocusUp() {
+        return moveFocusUp(0);
+    }
+
+    /** Moves the focus row upwards.
+	 *
+	 * @param offset
+	 *            the offset
+	 * @return Returns true if succeeded, else false if the selection could not
+	 *         be move upwards
+	 */
+    private boolean moveFocusUp(int offset) {
+        if (isSelectable()) {
+            if (focusedRow == null && scrollBody.iterator().hasNext()) {
+                // FIXME logic is exactly the same as in moveFocusDown, should
+                // be the opposite??
+                return setRowFocus((VScrollTableRow) scrollBody.iterator()
+                        .next());
+            } else {
+                VScrollTableRow prev = getPreviousRow(focusedRow, offset);
+                if (prev != null) {
+                    return setRowFocus(prev);
+                } else {
+                    VConsole.log("no previous available");
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /** Selects a row where the current selection head is.
+	 *
+	 * @param ctrlSelect
+	 *            Is the selection a ctrl+selection
+	 * @param shiftSelect
+	 *            Is the selection a shift+selection
+	 * @return Returns truw
+	 */
+    private void selectFocusedRow(boolean ctrlSelect, boolean shiftSelect) {
+        if (focusedRow != null) {
+            // Arrows moves the selection and clears previous selections
+            if (isSelectable() && !ctrlSelect && !shiftSelect) {
+                deselectAll();
+                focusedRow.toggleSelection();
+                selectionRangeStart = focusedRow;
+            } else if (isSelectable() && ctrlSelect && !shiftSelect) {
+                // Ctrl+arrows moves selection head
+                selectionRangeStart = focusedRow;
+                // No selection, only selection head is moved
+            } else if (isMultiSelectModeAny() && !ctrlSelect && shiftSelect) {
+                // Shift+arrows selection selects a range
+                focusedRow.toggleShiftSelection(shiftSelect);
+            }
+        }
+    }
+
+    /**
+     * Sends the selection to the server if changed since the last update/visit.
+     */
+    protected void sendSelectedRows() {
+        sendSelectedRows(immediate);
+    }
+
+    /**
+     * Sends the selection to the server if it has been changed since the last
+     * update/visit.
+     * 
+     * @param immediately
+     *            set to true to immediately send the rows
+     */
+    protected void sendSelectedRows(boolean immediately) {
+        // Don't send anything if selection has not changed
+        if (!selectionChanged) {
+            return;
+        }
+
+        // Reset selection changed flag
+        selectionChanged = false;
+
+        // Note: changing the immediateness of this might require changes to
+        // "clickEvent" immediateness also.
+        if (isMultiSelectModeDefault()) {
+            // Convert ranges to a set of strings
+            Set<String> ranges = new HashSet<String>();
+            for (SelectionRange range : selectedRowRanges) {
+                ranges.add(range.toString());
+            }
+
+            // Send the selected row ranges
+            client.updateVariable(paintableId, "selectedRanges",
+                    ranges.toArray(new String[selectedRowRanges.size()]), false);
+            selectedRowRanges.clear();
+
+            // clean selectedRowKeys so that they don't contain excess values
+            for (Iterator<String> iterator = selectedRowKeys.iterator(); iterator
+                    .hasNext();) {
+                String key = iterator.next();
+                VScrollTableRow renderedRowByKey = getRenderedRowByKey(key);
+                if (renderedRowByKey != null) {
+                    for (SelectionRange range : selectedRowRanges) {
+                        if (range.inRange(renderedRowByKey)) {
+                            iterator.remove();
+                        }
+                    }
+                } else {
+                    // orphaned selected key, must be in a range, ignore
+                    iterator.remove();
+                }
+
+            }
+        }
+
+        // Send the selected rows
+        client.updateVariable(paintableId, "selected",
+                selectedRowKeys.toArray(new String[selectedRowKeys.size()]),
+                immediately);
+
+    }
+
+    /**
+     * Get the key that moves the selection head upwards. By default it is the
+     * up arrow key but by overriding this you can change the key to whatever
+     * you want.
+     * 
+     * @return The keycode of the key
+     */
+    protected int getNavigationUpKey() {
+        return KeyCodes.KEY_UP;
+    }
+
+    /**
+     * Get the key that moves the selection head downwards. By default it is the
+     * down arrow key but by overriding this you can change the key to whatever
+     * you want.
+     * 
+     * @return The keycode of the key
+     */
+    protected int getNavigationDownKey() {
+        return KeyCodes.KEY_DOWN;
+    }
+
+    /**
+     * Get the key that scrolls to the left in the table. By default it is the
+     * left arrow key but by overriding this you can change the key to whatever
+     * you want.
+     * 
+     * @return The keycode of the key
+     */
+    protected int getNavigationLeftKey() {
+        return KeyCodes.KEY_LEFT;
+    }
+
+    /**
+     * Get the key that scroll to the right on the table. By default it is the
+     * right arrow key but by overriding this you can change the key to whatever
+     * you want.
+     * 
+     * @return The keycode of the key
+     */
+    protected int getNavigationRightKey() {
+        return KeyCodes.KEY_RIGHT;
+    }
+
+    /** Get the key that selects an item in the table. By default it is the
+	 * space bar key but by overriding this you can change the key to whatever
+	 * you want.
+	 *
+	 * @return the navigation select key
+	 */
+    protected int getNavigationSelectKey() {
+        return CHARCODE_SPACE;
+    }
+
+    /** Get the key the moves the selection one page up in the table. By
+	 * default this is the Page Up key but by overriding this you can change the
+	 * key to whatever you want.
+	 *
+	 * @return the navigation page up key
+	 */
+    protected int getNavigationPageUpKey() {
+        return KeyCodes.KEY_PAGEUP;
+    }
+
+    /** Get the key the moves the selection one page down in the table. By
+	 * default this is the Page Down key but by overriding this you can change
+	 * the key to whatever you want.
+	 *
+	 * @return the navigation page down key
+	 */
+    protected int getNavigationPageDownKey() {
+        return KeyCodes.KEY_PAGEDOWN;
+    }
+
+    /** Get the key the moves the selection to the beginning of the table. By
+	 * default this is the Home key but by overriding this you can change the
+	 * key to whatever you want.
+	 *
+	 * @return the navigation start key
+	 */
+    protected int getNavigationStartKey() {
+        return KeyCodes.KEY_HOME;
+    }
+
+    /** Get the key the moves the selection to the end of the table. By
+	 * default this is the End key but by overriding this you can change the key
+	 * to whatever you want.
+	 *
+	 * @return the navigation end key
+	 */
+    protected int getNavigationEndKey() {
+        return KeyCodes.KEY_END;
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 * @param rowData
+	 *            the row data
+	 */
+    public void initializeRows(UIDL uidl, UIDL rowData) {
+        if (scrollBody != null) {
+            scrollBody.removeFromParent();
+        }
+
+        // Without this call the scroll position is messed up in IE even after
+        // the lazy scroller has set the scroll position to the first visible
+        // item
+        scrollBodyPanel.getScrollPosition();
+
+        scrollBody = createScrollBody();
+
+        scrollBody.renderInitialRows(rowData, uidl.getIntAttribute("firstrow"),
+                uidl.getIntAttribute("rows"));
+        scrollBodyPanel.add(scrollBody);
+
+        // New body starts scrolled to the left, make sure the header and footer
+        // are also scrolled to the left
+        tHead.setHorizontalScrollPosition(0);
+        tFoot.setHorizontalScrollPosition(0);
+
+        initialContentReceived = true;
+        sizeNeedsInit = true;
+        scrollBody.restoreRowVisibility();
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updateColumnProperties(UIDL uidl) {
+        updateColumnOrder(uidl);
+
+        updateCollapsedColumns(uidl);
+
+        UIDL vc = uidl.getChildByTagName("visiblecolumns");
+        if (vc != null) {
+            tHead.updateCellsFromUIDL(vc);
+            tFoot.updateCellsFromUIDL(vc);
+        }
+
+        updateHeader(uidl.getStringArrayAttribute("vcolorder"));
+        updateFooter(uidl.getStringArrayAttribute("vcolorder"));
+        if (uidl.hasVariable("noncollapsiblecolumns")) {
+            noncollapsibleColumns = uidl
+                    .getStringArrayVariableAsSet("noncollapsiblecolumns");
+        }
+    }
+
+    /** Update collapsed columns.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    private void updateCollapsedColumns(UIDL uidl) {
+        if (uidl.hasVariable("collapsedcolumns")) {
+            tHead.setColumnCollapsingAllowed(true);
+            collapsedColumns = uidl
+                    .getStringArrayVariableAsSet("collapsedcolumns");
+        } else {
+            tHead.setColumnCollapsingAllowed(false);
+        }
+    }
+
+    /** Update column order.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    private void updateColumnOrder(UIDL uidl) {
+        if (uidl.hasVariable("columnorder")) {
+            columnReordering = true;
+            columnOrder = uidl.getStringArrayVariable("columnorder");
+        } else {
+            columnReordering = false;
+            columnOrder = null;
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 * @return true, if successful
+	 */
+    public boolean selectSelectedRows(UIDL uidl) {
+        boolean keyboardSelectionOverRowFetchInProgress = false;
+
+        if (uidl.hasVariable("selected")) {
+            final Set<String> selectedKeys = uidl
+                    .getStringArrayVariableAsSet("selected");
+            removeUnselectedRowKeys(selectedKeys);
+
+            if (scrollBody != null) {
+                Iterator<Widget> iterator = scrollBody.iterator();
+                while (iterator.hasNext()) {
+                    /*
+                     * Make the focus reflect to the server side state unless we
+                     * are currently selecting multiple rows with keyboard.
+                     */
+                    VScrollTableRow row = (VScrollTableRow) iterator.next();
+                    boolean selected = selectedKeys.contains(row.getKey());
+                    if (!selected
+                            && unSyncedselectionsBeforeRowFetch != null
+                            && unSyncedselectionsBeforeRowFetch.contains(row
+                                    .getKey())) {
+                        selected = true;
+                        keyboardSelectionOverRowFetchInProgress = true;
+                    }
+                    if (selected && selectedKeys.size() == 1) {
+                        /*
+                         * If a single item is selected, move focus to the
+                         * selected row. (#10522)
+                         */
+                        setRowFocus(row);
+                    }
+
+                    if (selected != row.isSelected()) {
+                        row.toggleSelection();
+
+                        if (!isSingleSelectMode() && !selected) {
+                            // Update selection range in case a row is
+                            // unselected from the middle of a range - #8076
+                            removeRowFromUnsentSelectionRanges(row);
+                        }
+                    }
+                }
+
+            }
+        }
+        unSyncedselectionsBeforeRowFetch = null;
+        return keyboardSelectionOverRowFetchInProgress;
+    }
+
+    /** Removes the unselected row keys.
+	 *
+	 * @param selectedKeys
+	 *            the selected keys
+	 */
+    private void removeUnselectedRowKeys(final Set<String> selectedKeys) {
+        List<String> unselectedKeys = new ArrayList<String>(0);
+        for (String key : selectedRowKeys) {
+            if (!selectedKeys.contains(key)) {
+                unselectedKeys.add(key);
+            }
+        }
+        selectedRowKeys.removeAll(unselectedKeys);
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updateSortingProperties(UIDL uidl) {
+        oldSortColumn = sortColumn;
+        if (uidl.hasVariable("sortascending")) {
+            sortAscending = uidl.getBooleanVariable("sortascending");
+            sortColumn = uidl.getStringVariable("sortcolumn");
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public void resizeSortedColumnForSortIndicator() {
+        // Force recalculation of the captionContainer element inside the header
+        // cell to accomodate for the size of the sort arrow.
+        HeaderCell sortedHeader = tHead.getHeaderCell(sortColumn);
+        if (sortedHeader != null) {
+            // Mark header as sorted now. Any earlier marking would lead to
+            // columns with wrong sizes
+            sortedHeader.setSorted(true);
+            tHead.resizeCaptionContainer(sortedHeader);
+        }
+        // Also recalculate the width of the captionContainer element in the
+        // previously sorted header, since this now has more room.
+        HeaderCell oldSortedHeader = tHead.getHeaderCell(oldSortColumn);
+        if (oldSortedHeader != null) {
+            tHead.resizeCaptionContainer(oldSortedHeader);
+        }
+    }
+
+    /** The lazy scroller is active. */
+    private boolean lazyScrollerIsActive;
+
+    /** Disable lazy scroller.
+	 */
+    private void disableLazyScroller() {
+        lazyScrollerIsActive = false;
+        scrollBodyPanel.getElement().getStyle().clearOverflowX();
+        scrollBodyPanel.getElement().getStyle().clearOverflowY();
+    }
+
+    /** Enable lazy scroller.
+	 */
+    private void enableLazyScroller() {
+        Scheduler.get().scheduleDeferred(lazyScroller);
+        lazyScrollerIsActive = true;
+        // prevent scrolling to jump in IE11
+        scrollBodyPanel.getElement().getStyle().setOverflowX(Overflow.HIDDEN);
+        scrollBodyPanel.getElement().getStyle().setOverflowY(Overflow.HIDDEN);
+    }
+
+    /** Checks if is lazy scroller active.
+	 *
+	 * @return true, if is lazy scroller active
+	 */
+    private boolean isLazyScrollerActive() {
+        return lazyScrollerIsActive;
+    }
+
+    /** The lazy scroller. */
+    private ScheduledCommand lazyScroller = new ScheduledCommand() {
+
+        @Override
+        public void execute() {
+            if (firstvisible >= 0) {
+                firstRowInViewPort = firstvisible;
+                if (firstvisibleOnLastPage > -1) {
+                    scrollBodyPanel
+                            .setScrollPosition(measureRowHeightOffset(firstvisibleOnLastPage));
+                } else {
+                    scrollBodyPanel
+                            .setScrollPosition(measureRowHeightOffset(firstvisible));
+                }
+            }
+            disableLazyScroller();
+        }
+    };
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updateFirstVisibleAndScrollIfNeeded(UIDL uidl) {
+        firstvisible = uidl.hasVariable("firstvisible") ? uidl
+                .getIntVariable("firstvisible") : 0;
+        firstvisibleOnLastPage = uidl.hasVariable("firstvisibleonlastpage") ? uidl
+                .getIntVariable("firstvisibleonlastpage") : -1;
+        if (firstvisible != lastRequestedFirstvisible && scrollBody != null) {
+
+            // Update lastRequestedFirstvisible right away here
+            // (don't rely on update in the timer which could be cancelled).
+            lastRequestedFirstvisible = firstRowInViewPort;
+
+            // Only scroll if the first visible changes from the server side.
+            // Else we might unintentionally scroll even when the scroll
+            // position has not changed.
+            enableLazyScroller();
+        }
+    }
+
+    /** Measure row height offset.
+	 *
+	 * @param rowIx
+	 *            the row ix
+	 * @return the int
+	 */
+    protected int measureRowHeightOffset(int rowIx) {
+        return (int) (rowIx * scrollBody.getRowHeight());
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updatePageLength(UIDL uidl) {
+        int oldPageLength = pageLength;
+        if (uidl.hasAttribute("pagelength")) {
+            pageLength = uidl.getIntAttribute("pagelength");
+        } else {
+            // pagelenght is "0" meaning scrolling is turned off
+            pageLength = totalRows;
+        }
+
+        if (oldPageLength != pageLength && initializedAndAttached) {
+            // page length changed, need to update size
+            sizeNeedsInit = true;
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 * @param state
+	 *            the state
+	 * @param readOnly
+	 *            the read only
+	 */
+    public void updateSelectionProperties(UIDL uidl,
+            AbstractComponentState state, boolean readOnly) {
+        setMultiSelectMode(uidl.hasAttribute("multiselectmode") ? uidl
+                .getIntAttribute("multiselectmode") : MULTISELECT_MODE_DEFAULT);
+
+        nullSelectionAllowed = uidl.hasAttribute("nsa") ? uidl
+                .getBooleanAttribute("nsa") : true;
+
+        if (uidl.hasAttribute("selectmode")) {
+            if (readOnly) {
+                selectMode = SelectMode.NONE;
+            } else if (uidl.getStringAttribute("selectmode").equals("multi")) {
+                selectMode = SelectMode.MULTI;
+            } else if (uidl.getStringAttribute("selectmode").equals("single")) {
+                selectMode = SelectMode.SINGLE;
+            } else {
+                selectMode = SelectMode.NONE;
+            }
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updateDragMode(UIDL uidl) {
+        dragmode = uidl.hasAttribute("dragmode") ? uidl
+                .getIntAttribute("dragmode") : 0;
+        if (BrowserInfo.get().isIE()) {
+            if (dragmode > 0) {
+                getElement().setPropertyJSO("onselectstart",
+                        getPreventTextSelectionIEHack());
+            } else {
+                getElement().setPropertyJSO("onselectstart", null);
+            }
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the uidl
+	 */
+    public void updateTotalRows(UIDL uidl) {
+        int newTotalRows = uidl.getIntAttribute("totalrows");
+        if (newTotalRows != getTotalRows()) {
+            if (scrollBody != null) {
+                if (getTotalRows() == 0) {
+                    tHead.clear();
+                    tFoot.clear();
+                }
+                initializedAndAttached = false;
+                initialContentReceived = false;
+                isNewBody = true;
+            }
+            setTotalRows(newTotalRows);
+        }
+    }
+
+    /** Sets the for internal use only.
+	 *
+	 * @param newTotalRows
+	 *            the new for internal use only
+	 */
+    protected void setTotalRows(int newTotalRows) {
+        totalRows = newTotalRows;
+    }
+
+    /** Gets the for internal use only.
+	 *
+	 * @return the for internal use only
+	 */
+    public int getTotalRows() {
+        return totalRows;
+    }
+
+    /**
+     * Returns the extra space that is given to the header column when column
+     * width is determined by header text.
+     * 
+     * @return extra space in pixels
+     */
+    private int getHeaderPadding() {
+        return scrollBody.getCellExtraWidth();
+    }
+
+    /**
+     * This method exists for the needs of {@link VTreeTable} only. Not part of
+     * the official API, <b>extend at your own risk</b>. May be removed or
+     * replaced in the future.
+     * 
+     * @return index of TreeTable's hierarchy column, or -1 if not applicable
+     */
+    protected int getHierarchyColumnIndex() {
+        return -1;
+    }
+
+    /**
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public void updateMaxIndent() {
+        int oldIndent = scrollBody.getMaxIndent();
+        scrollBody.calculateMaxIndent();
+        if (oldIndent != scrollBody.getMaxIndent()) {
+            // indent updated, headers might need adjusting
+            triggerLazyColumnAdjustment(true);
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public void focusRowFromBody() {
+        if (selectedRowKeys.size() == 1) {
+            // try to focus a row currently selected and in viewport
+            String selectedRowKey = selectedRowKeys.iterator().next();
+            if (selectedRowKey != null) {
+                VScrollTableRow renderedRow = getRenderedRowByKey(selectedRowKey);
+                if (renderedRow == null || !renderedRow.isInViewPort()) {
+                    setRowFocus(scrollBody.getRowByRowIndex(firstRowInViewPort));
+                } else {
+                    setRowFocus(renderedRow);
+                }
+            }
+        } else {
+            // multiselect mode
+            setRowFocus(scrollBody.getRowByRowIndex(firstRowInViewPort));
+        }
+    }
+
+    /** Creates the scroll body.
+	 *
+	 * @return the v scroll table body
+	 */
+    protected VScrollTableBody createScrollBody() {
+        return new VScrollTableBody();
+    }
+
+    /**
+     * Selects the last row visible in the table
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param focusOnly
+     *            Should the focus only be moved to the last row
+     */
+    public void selectLastRenderedRowInViewPort(boolean focusOnly) {
+        int index = firstRowInViewPort + getFullyVisibleRowCount();
+        VScrollTableRow lastRowInViewport = scrollBody.getRowByRowIndex(index);
+        if (lastRowInViewport == null) {
+            // this should not happen in normal situations (white space at the
+            // end of viewport). Select the last rendered as a fallback.
+            lastRowInViewport = scrollBody.getRowByRowIndex(scrollBody
+                    .getLastRendered());
+            if (lastRowInViewport == null) {
+                return; // empty table
+            }
+        }
+        setRowFocus(lastRowInViewport);
+        if (!focusOnly) {
+            selectFocusedRow(false, multiselectPending);
+            sendSelectedRows();
+        }
+    }
+
+    /**
+     * Selects the first row visible in the table
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param focusOnly
+     *            Should the focus only be moved to the first row
+     */
+    public void selectFirstRenderedRowInViewPort(boolean focusOnly) {
+        int index = firstRowInViewPort;
+        VScrollTableRow firstInViewport = scrollBody.getRowByRowIndex(index);
+        if (firstInViewport == null) {
+            // this should not happen in normal situations
+            return;
+        }
+        setRowFocus(firstInViewport);
+        if (!focusOnly) {
+            selectFocusedRow(false, multiselectPending);
+            sendSelectedRows();
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param uidl
+	 *            the new cache rate from uidl
+	 */
+    public void setCacheRateFromUIDL(UIDL uidl) {
+        setCacheRate(uidl.hasAttribute("cr") ? uidl.getDoubleAttribute("cr")
+                : CACHE_RATE_DEFAULT);
+    }
+
+    /** Sets the cache rate.
+	 *
+	 * @param d
+	 *            the new cache rate
+	 */
+    private void setCacheRate(double d) {
+        if (cache_rate != d) {
+            cache_rate = d;
+            cache_react_rate = 0.75 * d;
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param mainUidl
+	 *            the main uidl
+	 */
+    public void updateActionMap(UIDL mainUidl) {
+        UIDL actionsUidl = mainUidl.getChildByTagName("actions");
+        if (actionsUidl == null) {
+            return;
+        }
+
+        final Iterator<?> it = actionsUidl.getChildIterator();
+        while (it.hasNext()) {
+            final UIDL action = (UIDL) it.next();
+            final String key = action.getStringAttribute("key");
+            final String caption = action.getStringAttribute("caption");
+            actionMap.put(key + "_c", caption);
+            if (action.hasAttribute("icon")) {
+                // TODO need some uri handling ??
+                actionMap.put(key + "_i", client.translateVaadinUri(action
+                        .getStringAttribute("icon")));
+            } else {
+                actionMap.remove(key + "_i");
+            }
+        }
+
+    }
+
+    /** Gets the action caption.
+	 *
+	 * @param actionKey
+	 *            the action key
+	 * @return the action caption
+	 */
+    public String getActionCaption(String actionKey) {
+        return actionMap.get(actionKey + "_c");
+    }
+
+    /** Gets the action icon.
+	 *
+	 * @param actionKey
+	 *            the action key
+	 * @return the action icon
+	 */
+    public String getActionIcon(String actionKey) {
+        return actionMap.get(actionKey + "_i");
+    }
+
+    /** Update header.
+	 *
+	 * @param strings
+	 *            the strings
+	 */
+    private void updateHeader(String[] strings) {
+        if (strings == null) {
+            return;
+        }
+
+        int visibleCols = strings.length;
+        int colIndex = 0;
+        if (showRowHeaders) {
+            tHead.enableColumn(ROW_HEADER_COLUMN_KEY, colIndex);
+            visibleCols++;
+            visibleColOrder = new String[visibleCols];
+            visibleColOrder[colIndex] = ROW_HEADER_COLUMN_KEY;
+            colIndex++;
+        } else {
+            visibleColOrder = new String[visibleCols];
+            tHead.removeCell(ROW_HEADER_COLUMN_KEY);
+        }
+
+        int i;
+        for (i = 0; i < strings.length; i++) {
+            final String cid = strings[i];
+            visibleColOrder[colIndex] = cid;
+            tHead.enableColumn(cid, colIndex);
+            colIndex++;
+        }
+
+        tHead.setVisible(showColHeaders);
+        setContainerHeight();
+
+    }
+
+    /** Updates footers.
+	 * <p>
+	 * Update headers whould be called before this method is called!
+	 * </p>
+	 *
+	 * @param strings
+	 *            the strings
+	 */
+    private void updateFooter(String[] strings) {
+        if (strings == null) {
+            return;
+        }
+
+        // Add dummy column if row headers are present
+        int colIndex = 0;
+        if (showRowHeaders) {
+            tFoot.enableColumn(ROW_HEADER_COLUMN_KEY, colIndex);
+            colIndex++;
+        } else {
+            tFoot.removeCell(ROW_HEADER_COLUMN_KEY);
+        }
+
+        int i;
+        for (i = 0; i < strings.length; i++) {
+            final String cid = strings[i];
+            tFoot.enableColumn(cid, colIndex);
+            colIndex++;
+        }
+
+        tFoot.setVisible(showColFooters);
+    }
+
+    /**
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param uidl
+     *            which contains row data
+     * @param firstRow
+     *            first row in data set
+     * @param reqRows
+     *            amount of rows in data set
+     */
+    public void updateBody(UIDL uidl, int firstRow, int reqRows) {
+        int oldIndent = scrollBody.getMaxIndent();
+        if (uidl == null || reqRows < 1) {
+            // container is empty, remove possibly existing rows
+            if (firstRow <= 0) {
+                postponeSanityCheckForLastRendered = true;
+                while (scrollBody.getLastRendered() > scrollBody
+                        .getFirstRendered()) {
+                    scrollBody.unlinkRow(false);
+                }
+                postponeSanityCheckForLastRendered = false;
+                scrollBody.unlinkRow(false);
+            }
+            return;
+        }
+
+        scrollBody.renderRows(uidl, firstRow, reqRows);
+
+        discardRowsOutsideCacheWindow();
+        scrollBody.calculateMaxIndent();
+        if (oldIndent != scrollBody.getMaxIndent()) {
+            // indent updated, headers might need adjusting
+            headerChangedDuringUpdate = true;
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @param partialRowUpdates
+	 *            the partial row updates
+	 */
+    public void updateRowsInBody(UIDL partialRowUpdates) {
+        if (partialRowUpdates == null) {
+            return;
+        }
+        int firstRowIx = partialRowUpdates.getIntAttribute("firsturowix");
+        int count = partialRowUpdates.getIntAttribute("numurows");
+        scrollBody.unlinkRows(firstRowIx, count);
+        scrollBody.insertRows(partialRowUpdates, firstRowIx, count);
+    }
+
+    /**
+     * Updates the internal cache by unlinking rows that fall outside of the
+     * caching window.
+     */
+    protected void discardRowsOutsideCacheWindow() {
+        int firstRowToKeep = (int) (firstRowInViewPort - pageLength
+                * cache_rate);
+        int lastRowToKeep = (int) (firstRowInViewPort + pageLength + pageLength
+                * cache_rate);
+        // sanity checks:
+        if (firstRowToKeep < 0) {
+            firstRowToKeep = 0;
+        }
+        if (lastRowToKeep > totalRows) {
+            lastRowToKeep = totalRows - 1;
+        }
+        debug("Client side calculated cache rows to keep: " + firstRowToKeep
+                + "-" + lastRowToKeep);
+
+        if (serverCacheFirst != -1) {
+            firstRowToKeep = serverCacheFirst;
+            lastRowToKeep = serverCacheLast;
+            debug("Server cache rows that override: " + serverCacheFirst + "-"
+                    + serverCacheLast);
+            if (firstRowToKeep < scrollBody.getFirstRendered()
+                    || lastRowToKeep > scrollBody.getLastRendered()) {
+                debug("*** Server wants us to keep " + serverCacheFirst + "-"
+                        + serverCacheLast + " but we only have rows "
+                        + scrollBody.getFirstRendered() + "-"
+                        + scrollBody.getLastRendered() + " rendered!");
+            }
+        }
+        discardRowsOutsideOf(firstRowToKeep, lastRowToKeep);
+
+        scrollBody.fixSpacers();
+
+        scrollBody.restoreRowVisibility();
+    }
+
+    /** Discard rows outside of.
+	 *
+	 * @param optimalFirstRow
+	 *            the optimal first row
+	 * @param optimalLastRow
+	 *            the optimal last row
+	 */
+    private void discardRowsOutsideOf(int optimalFirstRow, int optimalLastRow) {
+        /*
+         * firstDiscarded and lastDiscarded are only calculated for debug
+         * purposes
+         */
+        int firstDiscarded = -1, lastDiscarded = -1;
+        boolean cont = true;
+        while (cont && scrollBody.getLastRendered() > optimalFirstRow
+                && scrollBody.getFirstRendered() < optimalFirstRow) {
+            if (firstDiscarded == -1) {
+                firstDiscarded = scrollBody.getFirstRendered();
+            }
+
+            // removing row from start
+            cont = scrollBody.unlinkRow(true);
+        }
+        if (firstDiscarded != -1) {
+            lastDiscarded = scrollBody.getFirstRendered() - 1;
+            debug("Discarded rows " + firstDiscarded + "-" + lastDiscarded);
+        }
+        firstDiscarded = lastDiscarded = -1;
+
+        cont = true;
+        while (cont && scrollBody.getLastRendered() > optimalLastRow) {
+            if (lastDiscarded == -1) {
+                lastDiscarded = scrollBody.getLastRendered();
+            }
+
+            // removing row from the end
+            cont = scrollBody.unlinkRow(false);
+        }
+        if (lastDiscarded != -1) {
+            firstDiscarded = scrollBody.getLastRendered() + 1;
+            debug("Discarded rows " + firstDiscarded + "-" + lastDiscarded);
+        }
+
+        debug("Now in cache: " + scrollBody.getFirstRendered() + "-"
+                + scrollBody.getLastRendered());
+    }
+
+    /**
+     * Inserts rows in the table body or removes them from the table body based
+     * on the commands in the UIDL.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     * 
+     * @param partialRowAdditions
+     *            the UIDL containing row updates.
+     */
+    public void addAndRemoveRows(UIDL partialRowAdditions) {
+        if (partialRowAdditions == null) {
+            return;
+        }
+        if (partialRowAdditions.hasAttribute("hide")) {
+            scrollBody.unlinkAndReindexRows(
+                    partialRowAdditions.getIntAttribute("firstprowix"),
+                    partialRowAdditions.getIntAttribute("numprows"));
+            scrollBody.ensureCacheFilled();
+        } else {
+            if (partialRowAdditions.hasAttribute("delbelow")) {
+                scrollBody.insertRowsDeleteBelow(partialRowAdditions,
+                        partialRowAdditions.getIntAttribute("firstprowix"),
+                        partialRowAdditions.getIntAttribute("numprows"));
+            } else {
+                scrollBody.insertAndReindexRows(partialRowAdditions,
+                        partialRowAdditions.getIntAttribute("firstprowix"),
+                        partialRowAdditions.getIntAttribute("numprows"));
+            }
+        }
+
+        discardRowsOutsideCacheWindow();
+    }
+
+    /** Gives correct column index for given column key ("cid" in UIDL).
+	 *
+	 * @param colKey
+	 *            the col key
+	 * @return column index of visible columns, -1 if column not visible
+	 */
+    private int getColIndexByKey(String colKey) {
+        // return 0 if asked for rowHeaders
+        if (ROW_HEADER_COLUMN_KEY.equals(colKey)) {
+            return 0;
+        }
+        for (int i = 0; i < visibleColOrder.length; i++) {
+            if (visibleColOrder[i].equals(colKey)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /** Checks if is multi select mode simple.
+	 *
+	 * @return true, if is multi select mode simple
+	 */
+    private boolean isMultiSelectModeSimple() {
+        return selectMode == SelectMode.MULTI
+                && multiselectmode == MULTISELECT_MODE_SIMPLE;
+    }
+
+    /** Checks if is single select mode.
+	 *
+	 * @return true, if is single select mode
+	 */
+    private boolean isSingleSelectMode() {
+        return selectMode == SelectMode.SINGLE;
+    }
+
+    /** Checks if is multi select mode any.
+	 *
+	 * @return true, if is multi select mode any
+	 */
+    private boolean isMultiSelectModeAny() {
+        return selectMode == SelectMode.MULTI;
+    }
+
+    /** Checks if is multi select mode default.
+	 *
+	 * @return true, if is multi select mode default
+	 */
+    private boolean isMultiSelectModeDefault() {
+        return selectMode == SelectMode.MULTI
+                && multiselectmode == MULTISELECT_MODE_DEFAULT;
+    }
+
+    /** Sets the multi select mode.
+	 *
+	 * @param multiselectmode
+	 *            the new multi select mode
+	 */
+    private void setMultiSelectMode(int multiselectmode) {
+        if (BrowserInfo.get().isTouchDevice()) {
+            // Always use the simple mode for touch devices that do not have
+            // shift/ctrl keys
+            this.multiselectmode = MULTISELECT_MODE_SIMPLE;
+        } else {
+            this.multiselectmode = multiselectmode;
+        }
+
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @return true, if is selectable
+	 */
+    public boolean isSelectable() {
+        return selectMode.getId() > SelectMode.NONE.getId();
+    }
+
+    /** Checks if is collapsed column.
+	 *
+	 * @param colKey
+	 *            the col key
+	 * @return true, if is collapsed column
+	 */
+    private boolean isCollapsedColumn(String colKey) {
+        if (collapsedColumns == null) {
+            return false;
+        }
+        if (collapsedColumns.contains(colKey)) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Gets the col key by index.
+	 *
+	 * @param index
+	 *            the index
+	 * @return the col key by index
+	 */
+    private String getColKeyByIndex(int index) {
+        return tHead.getHeaderCell(index).getColKey();
+    }
+
+    /**
+     * Note: not part of the official API, extend at your own risk. May be
+     * removed or replaced in the future.
+     * 
+     * Sets the indicated column's width for headers and scrollBody alike.
+     * 
+     * @param colIndex
+     *            index of the modified column
+     * @param w
+     *            new width (may be subject to modifications if doesn't meet
+     *            minimum requirements)
+     * @param isDefinedWidth
+     *            disables expand ratio if set true
+     */
+    protected void setColWidth(int colIndex, int w, boolean isDefinedWidth) {
+        final HeaderCell hcell = tHead.getHeaderCell(colIndex);
+
+        // Make sure that the column grows to accommodate the sort indicator if
+        // necessary.
+        // get min width with no indent or padding
+        int minWidth = hcell.getMinWidth(false, false);
+        if (w < minWidth) {
+            w = minWidth;
+        }
+
+        // Set header column width WITHOUT INDENT
+        hcell.setWidth(w, isDefinedWidth);
+
+        // Set footer column width likewise
+        FooterCell fcell = tFoot.getFooterCell(colIndex);
+        fcell.setWidth(w, isDefinedWidth);
+
+        // Ensure indicators have been taken into account
+        tHead.resizeCaptionContainer(hcell);
+
+        // Make sure that the body column grows to accommodate the indent if
+        // necessary.
+        // get min width with indent, no padding
+        minWidth = hcell.getMinWidth(true, false);
+        if (w < minWidth) {
+            w = minWidth;
+        }
+
+        // Set body column width
+        scrollBody.setColWidth(colIndex, w);
+    }
+
+    /** Gets the col width.
+	 *
+	 * @param colKey
+	 *            the col key
+	 * @return the col width
+	 */
+    private int getColWidth(String colKey) {
+        return tHead.getHeaderCell(colKey).getWidthWithIndent();
+    }
+
+    /** Get a rendered row by its key.
+	 *
+	 * @param key
+	 *            The key to search with
+	 * @return the rendered row by key
+	 */
+    public VScrollTableRow getRenderedRowByKey(String key) {
+        if (scrollBody != null) {
+            final Iterator<Widget> it = scrollBody.iterator();
+            VScrollTableRow r = null;
+            while (it.hasNext()) {
+                r = (VScrollTableRow) it.next();
+                if (r.getKey().equals(key)) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
+    /** Returns the next row to the given row.
+	 *
+	 * @param row
+	 *            The row to calculate from
+	 * @param offset
+	 *            the offset
+	 * @return The next row or null if no row exists
+	 */
+    private VScrollTableRow getNextRow(VScrollTableRow row, int offset) {
+        final Iterator<Widget> it = scrollBody.iterator();
+        VScrollTableRow r = null;
+        while (it.hasNext()) {
+            r = (VScrollTableRow) it.next();
+            if (r == row) {
+                r = null;
+                while (offset >= 0 && it.hasNext()) {
+                    r = (VScrollTableRow) it.next();
+                    offset--;
+                }
+                return r;
+            }
+        }
+
+        return null;
+    }
+
+    /** Returns the previous row from the given row.
+	 *
+	 * @param row
+	 *            The row to calculate from
+	 * @param offset
+	 *            the offset
+	 * @return The previous row or null if no row exists
+	 */
+    private VScrollTableRow getPreviousRow(VScrollTableRow row, int offset) {
+        final Iterator<Widget> it = scrollBody.iterator();
+        final Iterator<Widget> offsetIt = scrollBody.iterator();
+        VScrollTableRow r = null;
+        VScrollTableRow prev = null;
+        while (it.hasNext()) {
+            r = (VScrollTableRow) it.next();
+            if (offset < 0) {
+                prev = (VScrollTableRow) offsetIt.next();
+            }
+            if (r == row) {
+                return prev;
+            }
+            offset--;
+        }
+
+        return null;
+    }
+
+    /** Re order column.
+	 *
+	 * @param columnKey
+	 *            the column key
+	 * @param newIndex
+	 *            the new index
+	 */
+    protected void reOrderColumn(String columnKey, int newIndex) {
+
+        final int oldIndex = getColIndexByKey(columnKey);
+
+        // Change header order
+        tHead.moveCell(oldIndex, newIndex);
+
+        // Change body order
+        scrollBody.moveCol(oldIndex, newIndex);
+
+        // Change footer order
+        tFoot.moveCell(oldIndex, newIndex);
+
+        /*
+         * Build new columnOrder and update it to server Note that columnOrder
+         * also contains collapsed columns so we cannot directly build it from
+         * cells vector Loop the old columnOrder and append in order to new
+         * array unless on moved columnKey. On new index also put the moved key
+         * i == index on columnOrder, j == index on newOrder
+         */
+        final String oldKeyOnNewIndex = visibleColOrder[newIndex];
+        if (showRowHeaders) {
+            newIndex--; // columnOrder don't have rowHeader
+        }
+        // add back hidden rows,
+        for (int i = 0; i < columnOrder.length; i++) {
+            if (columnOrder[i].equals(oldKeyOnNewIndex)) {
+                break; // break loop at target
+            }
+            if (isCollapsedColumn(columnOrder[i])) {
+                newIndex++;
+            }
+        }
+        // finally we can build the new columnOrder for server
+        final String[] newOrder = new String[columnOrder.length];
+        for (int i = 0, j = 0; j < newOrder.length; i++) {
+            if (j == newIndex) {
+                newOrder[j] = columnKey;
+                j++;
+            }
+            if (i == columnOrder.length) {
+                break;
+            }
+            if (columnOrder[i].equals(columnKey)) {
+                continue;
+            }
+            newOrder[j] = columnOrder[i];
+            j++;
+        }
+        columnOrder = newOrder;
+        // also update visibleColumnOrder
+        int i = showRowHeaders ? 1 : 0;
+        for (int j = 0; j < newOrder.length; j++) {
+            final String cid = newOrder[j];
+            if (!isCollapsedColumn(cid)) {
+                visibleColOrder[i++] = cid;
+            }
+        }
+        client.updateVariable(paintableId, "columnorder", columnOrder, false);
+        if (client.hasEventListeners(this,
+                TableConstants.COLUMN_REORDER_EVENT_ID)) {
+            client.sendPendingVariableChanges();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.Widget#onDetach()
+     */
+    @Override
+    protected void onDetach() {
+        detachedScrollPosition = scrollBodyPanel.getScrollPosition();
+        rowRequestHandler.cancel();
+        super.onDetach();
+        // ensure that scrollPosElement will be detached
+        if (scrollPositionElement != null) {
+            final Element parent = DOM.getParent(scrollPositionElement);
+            if (parent != null) {
+                DOM.removeChild(parent, scrollPositionElement);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.Widget#onAttach()
+     */
+    @Override
+    public void onAttach() {
+        super.onAttach();
+        scrollBodyPanel.setScrollPosition(detachedScrollPosition);
+    }
+
+    /**
+     * Run only once when component is attached and received its initial
+     * content. This function:
+     * 
+     * * Syncs headers and bodys "natural widths and saves the values.
+     * 
+     * * Sets proper width and height
+     * 
+     * * Makes deferred request to get some cache rows
+     * 
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public void sizeInit() {
+        sizeNeedsInit = false;
+
+        scrollBody.setContainerHeight();
+
+        /*
+         * We will use browsers table rendering algorithm to find proper column
+         * widths. If content and header take less space than available, we will
+         * divide extra space relatively to each column which has not width set.
+         * 
+         * Overflow pixels are added to last column.
+         */
+
+        Iterator<Widget> headCells = tHead.iterator();
+        Iterator<Widget> footCells = tFoot.iterator();
+        int i = 0;
+        int totalExplicitColumnsWidths = 0;
+        int total = 0;
+        float expandRatioDivider = 0;
+
+        final int[] widths = new int[tHead.visibleCells.size()];
+
+        tHead.enableBrowserIntelligence();
+        tFoot.enableBrowserIntelligence();
+
+        int hierarchyColumnIndent = scrollBody != null ? scrollBody
+                .getMaxIndent() : 0;
+        HeaderCell hierarchyHeaderWithExpandRatio = null;
+
+        // first loop: collect natural widths
+        while (headCells.hasNext()) {
+            final HeaderCell hCell = (HeaderCell) headCells.next();
+            final FooterCell fCell = (FooterCell) footCells.next();
+            boolean needsIndent = hierarchyColumnIndent > 0
+                    && hCell.isHierarchyColumn();
+            int w = hCell.getWidth();
+            if (hCell.isDefinedWidth()) {
+                // server has defined column width explicitly
+                if (needsIndent && w < hierarchyColumnIndent) {
+                    // hierarchy indent overrides explicitly set width
+                    w = hierarchyColumnIndent;
+                }
+                totalExplicitColumnsWidths += w;
+            } else {
+                if (hCell.getExpandRatio() > 0) {
+                    expandRatioDivider += hCell.getExpandRatio();
+                    w = 0;
+                    if (needsIndent && w < hierarchyColumnIndent) {
+                        hierarchyHeaderWithExpandRatio = hCell;
+                        // don't add to widths here, because will be included in
+                        // the expand ratio space if there's enough of it
+                    }
+                } else {
+                    // get and store greater of header width and column width,
+                    // and store it as a minimum natural column width (these
+                    // already contain the indent if any)
+                    int headerWidth = hCell.getNaturalColumnWidth(i);
+                    int footerWidth = fCell.getNaturalColumnWidth(i);
+                    w = headerWidth > footerWidth ? headerWidth : footerWidth;
+                }
+                hCell.setNaturalMinimumColumnWidth(w);
+                fCell.setNaturalMinimumColumnWidth(w);
+            }
+            widths[i] = w;
+            total += w;
+            i++;
+        }
+        if (hierarchyHeaderWithExpandRatio != null) {
+            total += hierarchyColumnIndent;
+        }
+
+        tHead.disableBrowserIntelligence();
+        tFoot.disableBrowserIntelligence();
+
+        boolean willHaveScrollbarz = willHaveScrollbars();
+
+        // fix "natural" width if width not set
+        if (isDynamicWidth()) {
+            int w = total;
+            w += scrollBody.getCellExtraWidth() * visibleColOrder.length;
+            if (willHaveScrollbarz) {
+                w += Util.getNativeScrollbarSize();
+            }
+            setContentWidth(w);
+        }
+
+        int availW = scrollBody.getAvailableWidth();
+        if (BrowserInfo.get().isIE()) {
+            // Hey IE, are you really sure about this?
+            availW = scrollBody.getAvailableWidth();
+        }
+        availW -= scrollBody.getCellExtraWidth() * visibleColOrder.length;
+
+        if (willHaveScrollbarz) {
+            availW -= Util.getNativeScrollbarSize();
+        }
+
+        // TODO refactor this code to be the same as in resize timer
+
+        if (availW > total) {
+            // natural size is smaller than available space
+            int extraSpace = availW - total;
+            if (hierarchyHeaderWithExpandRatio != null) {
+                /*
+                 * add the indent's space back to ensure each column gets an
+                 * even share according to the expand ratios (note: if the
+                 * allocated space isn't enough for the hierarchy column it
+                 * shall be treated like a defined width column and the indent
+                 * space gets removed from the extra space again)
+                 */
+                extraSpace += hierarchyColumnIndent;
+            }
+            final int totalWidthR = total - totalExplicitColumnsWidths;
+            int checksum = 0;
+
+            if (extraSpace == 1) {
+                // We cannot divide one single pixel so we give it the first
+                // undefined column
+                // no need to worry about indent here
+                headCells = tHead.iterator();
+                i = 0;
+                checksum = availW;
+                while (headCells.hasNext()) {
+                    HeaderCell hc = (HeaderCell) headCells.next();
+                    if (!hc.isDefinedWidth()) {
+                        widths[i]++;
+                        break;
+                    }
+                    i++;
+                }
+
+            } else if (expandRatioDivider > 0) {
+                boolean setIndentToHierarchyHeader = false;
+                if (hierarchyHeaderWithExpandRatio != null) {
+                    // ensure first that the hierarchyColumn gets at least the
+                    // space allocated for indent
+                    final int newSpace = Math
+                            .round((extraSpace * (hierarchyHeaderWithExpandRatio
+                                    .getExpandRatio() / expandRatioDivider)));
+                    if (newSpace < hierarchyColumnIndent) {
+                        // not enough space for indent, remove indent from the
+                        // extraSpace again and handle hierarchy column's header
+                        // separately
+                        setIndentToHierarchyHeader = true;
+                        extraSpace -= hierarchyColumnIndent;
+                    }
+                }
+
+                // visible columns have some active expand ratios, excess
+                // space is divided according to them
+                headCells = tHead.iterator();
+                i = 0;
+                while (headCells.hasNext()) {
+                    HeaderCell hCell = (HeaderCell) headCells.next();
+                    if (hCell.getExpandRatio() > 0) {
+                        int w = widths[i];
+                        if (setIndentToHierarchyHeader
+                                && hierarchyHeaderWithExpandRatio.equals(hCell)) {
+                            // hierarchy column's header is no longer part of
+                            // the expansion divide and only gets indent
+                            w += hierarchyColumnIndent;
+                        } else {
+                            final int newSpace = Math
+                                    .round((extraSpace * (hCell
+                                            .getExpandRatio() / expandRatioDivider)));
+                            w += newSpace;
+                        }
+                        widths[i] = w;
+                    }
+                    checksum += widths[i];
+                    i++;
+                }
+            } else if (totalWidthR > 0) {
+                // no expand ratios defined, we will share extra space
+                // relatively to "natural widths" among those without
+                // explicit width
+                // no need to worry about indent here, it's already included
+                headCells = tHead.iterator();
+                i = 0;
+                while (headCells.hasNext()) {
+                    HeaderCell hCell = (HeaderCell) headCells.next();
+                    if (!hCell.isDefinedWidth()) {
+                        int w = widths[i];
+                        final int newSpace = Math.round((float) extraSpace
+                                * (float) w / totalWidthR);
+                        w += newSpace;
+                        widths[i] = w;
+                    }
+                    checksum += widths[i];
+                    i++;
+                }
+            }
+
+            if (extraSpace > 0 && checksum != availW) {
+                /*
+                 * There might be in some cases a rounding error of 1px when
+                 * extra space is divided so if there is one then we give the
+                 * first undefined column 1 more pixel
+                 */
+                headCells = tHead.iterator();
+                i = 0;
+                while (headCells.hasNext()) {
+                    HeaderCell hc = (HeaderCell) headCells.next();
+                    if (!hc.isDefinedWidth()) {
+                        widths[i] += availW - checksum;
+                        break;
+                    }
+                    i++;
+                }
+            }
+
+        } else {
+            // body's size will be more than available and scrollbar will appear
+        }
+
+        // last loop: set possibly modified values or reset if new tBody
+        i = 0;
+        headCells = tHead.iterator();
+        while (headCells.hasNext()) {
+            final HeaderCell hCell = (HeaderCell) headCells.next();
+            if (isNewBody || hCell.getWidth() == -1) {
+                final int w = widths[i];
+                setColWidth(i, w, false);
+            }
+            i++;
+        }
+
+        initializedAndAttached = true;
+
+        updatePageLength();
+
+        /*
+         * Fix "natural" height if height is not set. This must be after width
+         * fixing so the components' widths have been adjusted.
+         */
+        if (isDynamicHeight()) {
+            /*
+             * We must force an update of the row height as this point as it
+             * might have been (incorrectly) calculated earlier
+             */
+
+            /*
+             * TreeTable updates stuff in a funky order, so we must set the
+             * height as zero here before doing the real update to make it
+             * realize that there is no content,
+             */
+            if (pageLength == totalRows && pageLength == 0) {
+                scrollBody.setHeight("0px");
+            }
+
+            int bodyHeight;
+            if (pageLength == totalRows) {
+                /*
+                 * A hack to support variable height rows when paging is off.
+                 * Generally this is not supported by scrolltable. We want to
+                 * show all rows so the bodyHeight should be equal to the table
+                 * height.
+                 */
+                // int bodyHeight = scrollBody.getOffsetHeight();
+                bodyHeight = scrollBody.getRequiredHeight();
+            } else {
+                bodyHeight = (int) Math.round(scrollBody.getRowHeight(true)
+                        * pageLength);
+            }
+            boolean needsSpaceForHorizontalSrollbar = (total > availW);
+            if (needsSpaceForHorizontalSrollbar) {
+                bodyHeight += Util.getNativeScrollbarSize();
+            }
+            scrollBodyPanel.setHeight(bodyHeight + "px");
+            Util.runWebkitOverflowAutoFix(scrollBodyPanel.getElement());
+        }
+
+        isNewBody = false;
+
+        if (firstvisible > 0) {
+            enableLazyScroller();
+        }
+
+        if (enabled) {
+            // Do we need cache rows
+            if (scrollBody.getLastRendered() + 1 < firstRowInViewPort
+                    + pageLength + (int) cache_react_rate * pageLength) {
+                if (totalRows - 1 > scrollBody.getLastRendered()) {
+                    // fetch cache rows
+                    int firstInNewSet = scrollBody.getLastRendered() + 1;
+                    int lastInNewSet = (int) (firstRowInViewPort + pageLength + cache_rate
+                            * pageLength);
+                    if (lastInNewSet > totalRows - 1) {
+                        lastInNewSet = totalRows - 1;
+                    }
+                    rowRequestHandler.triggerRowFetch(firstInNewSet,
+                            lastInNewSet - firstInNewSet + 1, 1);
+                }
+            }
+        }
+
+        /*
+         * Ensures the column alignments are correct at initial loading. <br>
+         * (child components widths are correct)
+         */
+        Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement());
+
+        hadScrollBars = willHaveScrollbarz;
+    }
+
+    /**
+     * Note: this method is not part of official API although declared as
+     * protected. Extend at your own risk.
+     * 
+     * @return true if content area will have scrollbars visible.
+     */
+    protected boolean willHaveScrollbars() {
+        if (isDynamicHeight()) {
+            if (pageLength < totalRows) {
+                return true;
+            }
+        } else {
+            int fakeheight = (int) Math.round(scrollBody.getRowHeight()
+                    * totalRows);
+            int availableHeight = scrollBodyPanel.getElement().getPropertyInt(
+                    "clientHeight");
+            if (fakeheight > availableHeight) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Announce scroll position.
+	 */
+    private void announceScrollPosition() {
+        if (scrollPositionElement == null) {
+            scrollPositionElement = DOM.createDiv();
+            scrollPositionElement.setClassName(getStylePrimaryName()
+                    + "-scrollposition");
+            scrollPositionElement.getStyle().setPosition(Position.ABSOLUTE);
+            scrollPositionElement.getStyle().setDisplay(Display.NONE);
+            getElement().appendChild(scrollPositionElement);
+        }
+
+        Style style = scrollPositionElement.getStyle();
+        style.setMarginLeft(getElement().getOffsetWidth() / 2 - 80, Unit.PX);
+        style.setMarginTop(-scrollBodyPanel.getOffsetHeight(), Unit.PX);
+
+        // indexes go from 1-totalRows, as rowheaders in index-mode indicate
+        int last = (firstRowInViewPort + pageLength);
+        if (last > totalRows) {
+            last = totalRows;
+        }
+        scrollPositionElement.setInnerHTML("<span>" + (firstRowInViewPort + 1)
+                + " &ndash; " + (last) + "..." + "</span>");
+        style.setDisplay(Display.BLOCK);
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public void hideScrollPositionAnnotation() {
+        if (scrollPositionElement != null) {
+            scrollPositionElement.getStyle().setDisplay(Display.NONE);
+        }
+    }
+
+    /** For internal use only. May be removed or replaced in the future.
+	 *
+	 * @return true, if is scroll position visible
+	 */
+    public boolean isScrollPositionVisible() {
+        return scrollPositionElement != null
+                && !scrollPositionElement.getStyle().getDisplay()
+                        .equals(Display.NONE.toString());
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public class RowRequestHandler extends Timer {
+
+        /** The req first row. */
+        private int reqFirstRow = 0;
+        
+        /** The req rows. */
+        private int reqRows = 0;
+        
+        /** The is request handler running. */
+        private boolean isRequestHandlerRunning = false;
+
+        /** Trigger row fetch.
+		 *
+		 * @param first
+		 *            the first
+		 * @param rows
+		 *            the rows
+		 */
+        public void triggerRowFetch(int first, int rows) {
+            setReqFirstRow(first);
+            setReqRows(rows);
+            deferRowFetch();
+        }
+
+        /** Trigger row fetch.
+		 *
+		 * @param first
+		 *            the first
+		 * @param rows
+		 *            the rows
+		 * @param delay
+		 *            the delay
+		 */
+        public void triggerRowFetch(int first, int rows, int delay) {
+            setReqFirstRow(first);
+            setReqRows(rows);
+            deferRowFetch(delay);
+        }
+
+        /** Defer row fetch.
+		 */
+        public void deferRowFetch() {
+            deferRowFetch(250);
+        }
+
+        /** Checks if is request handler running.
+		 *
+		 * @return true, if is request handler running
+		 */
+        public boolean isRequestHandlerRunning() {
+            return isRequestHandlerRunning;
+        }
+
+        /** Defer row fetch.
+		 *
+		 * @param msec
+		 *            the msec
+		 */
+        public void deferRowFetch(int msec) {
+            isRequestHandlerRunning = true;
+            if (reqRows > 0 && reqFirstRow < totalRows) {
+                schedule(msec);
+
+                // tell scroll position to user if currently "visible" rows are
+                // not rendered
+                if (totalRows > pageLength
+                        && ((firstRowInViewPort + pageLength > scrollBody
+                                .getLastRendered()) || (firstRowInViewPort < scrollBody
+                                .getFirstRendered()))) {
+                    announceScrollPosition();
+                } else {
+                    hideScrollPositionAnnotation();
+                }
+            }
+        }
+
+        /** Gets the req first row.
+		 *
+		 * @return the req first row
+		 */
+        public int getReqFirstRow() {
+            return reqFirstRow;
+        }
+
+        /** Sets the req first row.
+		 *
+		 * @param reqFirstRow
+		 *            the new req first row
+		 */
+        public void setReqFirstRow(int reqFirstRow) {
+            if (reqFirstRow < 0) {
+                this.reqFirstRow = 0;
+            } else if (reqFirstRow >= totalRows) {
+                this.reqFirstRow = totalRows - 1;
+            } else {
+                this.reqFirstRow = reqFirstRow;
+            }
+        }
+
+        /** Sets the req rows.
+		 *
+		 * @param reqRows
+		 *            the new req rows
+		 */
+        public void setReqRows(int reqRows) {
+            if (reqRows < 0) {
+                this.reqRows = 0;
+            } else if (reqFirstRow + reqRows > totalRows) {
+                this.reqRows = totalRows - reqFirstRow;
+            } else {
+                this.reqRows = reqRows;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.Timer#run()
+         */
+        @Override
+        public void run() {
+            if (client.getMessageSender().hasActiveRequest() || navKeyDown) {
+                // if client connection is busy, don't bother loading it more
+                VConsole.log("Postponed rowfetch");
+                schedule(250);
+            } else if (!updatedReqRows && allRenderedRowsAreNew()) {
+
+                /*
+                 * If all rows are new, there might have been a server-side call
+                 * to Table.setCurrentPageFirstItemIndex(int) In this case,
+                 * scrolling event takes way too late, and all the rows from
+                 * previous viewport to this one were requested.
+                 * 
+                 * This should prevent requesting unneeded rows by updating
+                 * reqFirstRow and reqRows before needing them. See (#14135)
+                 */
+
+                setReqFirstRow((firstRowInViewPort - (int) (pageLength * cache_rate)));
+                int last = firstRowInViewPort + (int) (cache_rate * pageLength)
+                        + pageLength - 1;
+                if (last >= totalRows) {
+                    last = totalRows - 1;
+                }
+                setReqRows(last - getReqFirstRow() + 1);
+                updatedReqRows = true;
+                schedule(250);
+            } else {
+
+                int firstRendered = scrollBody.getFirstRendered();
+                int lastRendered = scrollBody.getLastRendered();
+                if (lastRendered > totalRows) {
+                    lastRendered = totalRows - 1;
+                }
+                boolean rendered = firstRendered >= 0 && lastRendered >= 0;
+
+                int firstToBeRendered = firstRendered;
+
+                if (reqFirstRow < firstToBeRendered) {
+                    firstToBeRendered = reqFirstRow;
+                } else if (firstRowInViewPort - (int) (cache_rate * pageLength) > firstToBeRendered) {
+                    firstToBeRendered = firstRowInViewPort
+                            - (int) (cache_rate * pageLength);
+                    if (firstToBeRendered < 0) {
+                        firstToBeRendered = 0;
+                    }
+                } else if (rendered && firstRendered + 1 < reqFirstRow
+                        && lastRendered + 1 < reqFirstRow) {
+                    // requested rows must fall within the requested rendering
+                    // area
+                    firstToBeRendered = reqFirstRow;
+                }
+                if (firstToBeRendered + reqRows < firstRendered) {
+                    // must increase the required row count accordingly,
+                    // otherwise may leave a gap and the rows beyond will get
+                    // removed
+                    setReqRows(firstRendered - firstToBeRendered);
+                }
+
+                int lastToBeRendered = lastRendered;
+                int lastReqRow = reqFirstRow + reqRows - 1;
+
+                if (lastReqRow > lastToBeRendered) {
+                    lastToBeRendered = lastReqRow;
+                } else if (firstRowInViewPort + pageLength + pageLength
+                        * cache_rate < lastToBeRendered) {
+                    lastToBeRendered = (firstRowInViewPort + pageLength + (int) (pageLength * cache_rate));
+                    if (lastToBeRendered >= totalRows) {
+                        lastToBeRendered = totalRows - 1;
+                    }
+                    // due Safari 3.1 bug (see #2607), verify reqrows, original
+                    // problem unknown, but this should catch the issue
+                    if (lastReqRow > lastToBeRendered) {
+                        setReqRows(lastToBeRendered - reqFirstRow);
+                    }
+                } else if (rendered && lastRendered - 1 > lastReqRow
+                        && firstRendered - 1 > lastReqRow) {
+                    // requested rows must fall within the requested rendering
+                    // area
+                    lastToBeRendered = lastReqRow;
+                }
+
+                if (lastToBeRendered > totalRows) {
+                    lastToBeRendered = totalRows - 1;
+                }
+                if (reqFirstRow < firstToBeRendered
+                        || (reqFirstRow > firstToBeRendered && (reqFirstRow < firstRendered || reqFirstRow > lastRendered + 1))) {
+                    setReqFirstRow(firstToBeRendered);
+                }
+                if (lastRendered < lastToBeRendered
+                        && lastRendered + reqRows < lastToBeRendered) {
+                    // must increase the required row count accordingly,
+                    // otherwise may leave a gap and the rows after will get
+                    // removed
+                    setReqRows(lastToBeRendered - lastRendered);
+                } else if (lastToBeRendered >= firstRendered
+                        && reqFirstRow + reqRows < firstRendered) {
+                    setReqRows(lastToBeRendered - lastRendered);
+                }
+
+                client.updateVariable(paintableId, "firstToBeRendered",
+                        firstToBeRendered, false);
+                client.updateVariable(paintableId, "lastToBeRendered",
+                        lastToBeRendered, false);
+
+                // don't request server to update page first index in case it
+                // has not been changed
+                if (firstRowInViewPort != firstvisible) {
+                    // remember which firstvisible we requested, in case the
+                    // server has a differing opinion
+                    lastRequestedFirstvisible = firstRowInViewPort;
+                    client.updateVariable(paintableId, "firstvisible",
+                            firstRowInViewPort, false);
+                }
+                client.updateVariable(paintableId, "reqfirstrow", reqFirstRow,
+                        false);
+                client.updateVariable(paintableId, "reqrows", reqRows, true);
+
+                if (selectionChanged) {
+                    unSyncedselectionsBeforeRowFetch = new HashSet<Object>(
+                            selectedRowKeys);
+                }
+                isRequestHandlerRunning = false;
+            }
+        }
+
+        /**
+         * Sends request to refresh content at this position.
+         */
+        public void refreshContent() {
+            isRequestHandlerRunning = true;
+            int first = (int) (firstRowInViewPort - pageLength * cache_rate);
+            int reqRows = (int) (2 * pageLength * cache_rate + pageLength);
+            if (first < 0) {
+                reqRows = reqRows + first;
+                first = 0;
+            }
+            setReqFirstRow(first);
+            setReqRows(reqRows);
+            run();
+        }
+    }
+
+    /** The Class HeaderCell.
+	 */
+    public class HeaderCell extends Widget {
+
+        /** The td. */
+        Element td = DOM.createTD();
+
+        /** The caption container. */
+        Element captionContainer = DOM.createDiv();
+
+        /** The sort indicator. */
+        Element sortIndicator = DOM.createDiv();
+
+        /** The col resize widget. */
+        Element colResizeWidget = DOM.createDiv();
+
+        /** The floating copy of header cell. */
+        Element floatingCopyOfHeaderCell;
+
+        /** The sortable. */
+        private boolean sortable = false;
+        
+        /** The cid. */
+        private final String cid;
+        
+        /** The dragging. */
+        private boolean dragging;
+
+        /** The drag start x. */
+        private int dragStartX;
+        
+        /** The col index. */
+        private int colIndex;
+        
+        /** The original width. */
+        private int originalWidth;
+
+        /** The is resizing. */
+        private boolean isResizing;
+
+        /** The header x. */
+        private int headerX;
+
+        /** The moved. */
+        private boolean moved;
+
+        /** The closest slot. */
+        private int closestSlot;
+
+        /** The width. */
+        private int width = -1;
+
+        /** The natural width. */
+        private int naturalWidth = -1;
+
+        /** The align. */
+        private char align = ALIGN_LEFT;
+
+        /** The defined width. */
+        boolean definedWidth = false;
+
+        /** The expand ratio. */
+        private float expandRatio = 0;
+
+        /** The sorted. */
+        private boolean sorted;
+
+        /** Sets the sortable.
+		 *
+		 * @param b
+		 *            the new sortable
+		 */
+        public void setSortable(boolean b) {
+            sortable = b;
+        }
+
+        /** Makes room for the sorting indicator in case the column that the
+		 * header cell belongs to is sorted. This is done by resizing the width
+		 * of the caption container element by the correct amount
+		 *
+		 * @param rightSpacing
+		 *            the right spacing
+		 */
+        public void resizeCaptionContainer(int rightSpacing) {
+            int captionContainerWidth = width
+                    - colResizeWidget.getOffsetWidth() - rightSpacing;
+
+            if (td.getClassName().contains("-asc")
+                    || td.getClassName().contains("-desc")) {
+                // Leave room for the sort indicator
+                captionContainerWidth -= sortIndicator.getOffsetWidth();
+            }
+
+            if (captionContainerWidth < 0) {
+                rightSpacing += captionContainerWidth;
+                captionContainerWidth = 0;
+            }
+
+            captionContainer.getStyle().setPropertyPx("width",
+                    captionContainerWidth);
+
+            // Apply/Remove spacing if defined
+            if (rightSpacing > 0) {
+                colResizeWidget.getStyle().setMarginLeft(rightSpacing, Unit.PX);
+            } else {
+                colResizeWidget.getStyle().clearMarginLeft();
+            }
+        }
+
+        /** Sets the natural minimum column width.
+		 *
+		 * @param w
+		 *            the new natural minimum column width
+		 */
+        public void setNaturalMinimumColumnWidth(int w) {
+            naturalWidth = w;
+        }
+
+        /** Instantiates a new header cell.
+		 *
+		 * @param colId
+		 *            the col id
+		 * @param headerText
+		 *            the header text
+		 */
+        public HeaderCell(String colId, String headerText) {
+            cid = colId;
+
+            setText(headerText);
+
+            td.appendChild(colResizeWidget);
+
+            // ensure no clipping initially (problem on column additions)
+            captionContainer.getStyle().setOverflow(Overflow.VISIBLE);
+
+            td.appendChild(sortIndicator);
+            td.appendChild(captionContainer);
+
+            DOM.sinkEvents(td, Event.MOUSEEVENTS | Event.ONDBLCLICK
+                    | Event.ONCONTEXTMENU | Event.TOUCHEVENTS);
+
+            setElement(td);
+
+            setAlign(ALIGN_LEFT);
+        }
+
+        /** Update style names.
+		 *
+		 * @param primaryStyleName
+		 *            the primary style name
+		 */
+        protected void updateStyleNames(String primaryStyleName) {
+            colResizeWidget.setClassName(primaryStyleName + "-resizer");
+            sortIndicator.setClassName(primaryStyleName + "-sort-indicator");
+            captionContainer.setClassName(primaryStyleName
+                    + "-caption-container");
+            if (sorted) {
+                if (sortAscending) {
+                    setStyleName(primaryStyleName + "-header-cell-asc");
+                } else {
+                    setStyleName(primaryStyleName + "-header-cell-desc");
+                }
+            } else {
+                setStyleName(primaryStyleName + "-header-cell");
+            }
+
+            final String ALIGN_PREFIX = primaryStyleName
+                    + "-caption-container-align-";
+
+            switch (align) {
+            case ALIGN_CENTER:
+                captionContainer.addClassName(ALIGN_PREFIX + "center");
+                break;
+            case ALIGN_RIGHT:
+                captionContainer.addClassName(ALIGN_PREFIX + "right");
+                break;
+            default:
+                captionContainer.addClassName(ALIGN_PREFIX + "left");
+                break;
+            }
+
+        }
+
+        /** Disable auto width calculation.
+		 */
+        public void disableAutoWidthCalculation() {
+            definedWidth = true;
+            expandRatio = 0;
+        }
+
+        /**
+         * Sets width to the header cell. This width should not include any
+         * possible indent modifications that are present in
+         * {@link VScrollTableBody#getMaxIndent()}.
+         * 
+         * @param w
+         *            required width of the cell sans indentations
+         * @param ensureDefinedWidth
+         *            disables expand ratio if required
+         */
+        public void setWidth(int w, boolean ensureDefinedWidth) {
+            if (ensureDefinedWidth) {
+                definedWidth = true;
+                // on column resize expand ratio becomes zero
+                expandRatio = 0;
+            }
+            if (width == -1) {
+                // go to default mode, clip content if necessary
+                captionContainer.getStyle().clearOverflow();
+            }
+            width = w;
+            if (w == -1) {
+                captionContainer.getStyle().clearWidth();
+                setWidth("");
+            } else {
+                tHead.resizeCaptionContainer(this);
+
+                /*
+                 * if we already have tBody, set the header width properly, if
+                 * not defer it. IE will fail with complex float in table header
+                 * unless TD width is not explicitly set.
+                 */
+                if (scrollBody != null) {
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (w < maxIndent && isHierarchyColumn()) {
+                        w = maxIndent;
+                    }
+                    int tdWidth = w + scrollBody.getCellExtraWidth();
+                    setWidth(tdWidth + "px");
+                } else {
+                    Scheduler.get().scheduleDeferred(new Command() {
+
+                        @Override
+                        public void execute() {
+                            int maxIndent = scrollBody.getMaxIndent();
+                            int tdWidth = width;
+                            if (tdWidth < maxIndent && isHierarchyColumn()) {
+                                tdWidth = maxIndent;
+                            }
+                            tdWidth += scrollBody.getCellExtraWidth();
+                            setWidth(tdWidth + "px");
+                        }
+                    });
+                }
+            }
+        }
+
+        /** Sets the undefined width.
+		 */
+        public void setUndefinedWidth() {
+            definedWidth = false;
+            if (!isResizing) {
+                setWidth(-1, false);
+            }
+        }
+
+        /**
+         * Detects if width is fixed by developer on server side or resized to
+         * current width by user.
+         * 
+         * @return true if defined, false if "natural" width
+         */
+        public boolean isDefinedWidth() {
+            return definedWidth && width >= 0;
+        }
+
+        /**
+         * This method exists for the needs of {@link VTreeTable} only.
+         * 
+         * Returns the pixels width of the header cell. This includes the
+         * indent, if applicable.
+         * 
+         * @return The width in pixels
+         */
+        public int getWidthWithIndent() {
+            if (scrollBody != null && isHierarchyColumn()) {
+                int maxIndent = scrollBody.getMaxIndent();
+                if (maxIndent > width) {
+                    return maxIndent;
+                }
+            }
+            return width;
+        }
+
+        /**
+         * Returns the pixels width of the header cell.
+         * 
+         * @return The width in pixels
+         */
+        public int getWidth() {
+            return width;
+        }
+
+        /**
+         * This method exists for the needs of {@link VTreeTable} only.
+         * 
+         * @return <code>true</code> if this is hierarcyColumn's header cell,
+         *         <code>false</code> otherwise
+         */
+        private boolean isHierarchyColumn() {
+            int hierarchyColumnIndex = getHierarchyColumnIndex();
+            return hierarchyColumnIndex >= 0
+                    && tHead.visibleCells.indexOf(this) == hierarchyColumnIndex;
+        }
+
+        /** Sets the text.
+		 *
+		 * @param headerText
+		 *            the new text
+		 */
+        public void setText(String headerText) {
+            DOM.setInnerHTML(captionContainer, headerText);
+        }
+
+        /** Gets the col key.
+		 *
+		 * @return the col key
+		 */
+        public String getColKey() {
+            return cid;
+        }
+
+        /** Sets the sorted.
+		 *
+		 * @param sorted
+		 *            the new sorted
+		 */
+        private void setSorted(boolean sorted) {
+            this.sorted = sorted;
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /** Handle column reordering.
+		 *
+		 * @param event
+		 *            the event
+		 */
+
+        @Override
+        public void onBrowserEvent(Event event) {
+            if (enabled && event != null) {
+                if (isResizing
+                        || event.getEventTarget().cast() == colResizeWidget) {
+                    if (dragging
+                            && (event.getTypeInt() == Event.ONMOUSEUP || event
+                                    .getTypeInt() == Event.ONTOUCHEND)) {
+                        // Handle releasing column header on spacer #5318
+                        handleCaptionEvent(event);
+                    } else {
+                        onResizeEvent(event);
+                    }
+                } else {
+                    /*
+                     * Ensure focus before handling caption event. Otherwise
+                     * variables changed from caption event may be before
+                     * variables from other components that fire variables when
+                     * they lose focus.
+                     */
+                    if (event.getTypeInt() == Event.ONMOUSEDOWN
+                            || event.getTypeInt() == Event.ONTOUCHSTART) {
+                        scrollBodyPanel.setFocus(true);
+                    }
+                    handleCaptionEvent(event);
+                    boolean stopPropagation = true;
+                    if (event.getTypeInt() == Event.ONCONTEXTMENU
+                            && !client.hasEventListeners(
+                                    VCustomScrollTable.this,
+                                    TableConstants.HEADER_CLICK_EVENT_ID)) {
+                        // Prevent showing the browser's context menu only when
+                        // there is a header click listener.
+                        stopPropagation = false;
+                    }
+                    if (stopPropagation) {
+                        event.stopPropagation();
+                        event.preventDefault();
+                    }
+                }
+            }
+        }
+
+        /** Creates the floating copy.
+		 */
+        private void createFloatingCopy() {
+            floatingCopyOfHeaderCell = DOM.createDiv();
+            DOM.setInnerHTML(floatingCopyOfHeaderCell, DOM.getInnerHTML(td));
+            floatingCopyOfHeaderCell = DOM
+                    .getChild(floatingCopyOfHeaderCell, 2);
+            // #12714 the shown "ghost element" should be inside
+            // v-overlay-container, and it should contain the same styles as the
+            // table to enable theming (except v-table & v-widget).
+            String stylePrimaryName = VCustomScrollTable.this
+                    .getStylePrimaryName();
+            StringBuilder sb = new StringBuilder();
+            for (String s : VCustomScrollTable.this.getStyleName().split(" ")) {
+                if (!s.equals(StyleConstants.UI_WIDGET)) {
+                    sb.append(s);
+                    if (s.equals(stylePrimaryName)) {
+                        sb.append("-header-drag ");
+                    } else {
+                        sb.append(" ");
+                    }
+                }
+            }
+            floatingCopyOfHeaderCell.setClassName(sb.toString().trim());
+            // otherwise might wrap or be cut if narrow column
+            floatingCopyOfHeaderCell.getStyle().setProperty("width", "auto");
+            updateFloatingCopysPosition(DOM.getAbsoluteLeft(td),
+                    DOM.getAbsoluteTop(td));
+            DOM.appendChild(VOverlay.getOverlayContainer(client),
+                    floatingCopyOfHeaderCell);
+        }
+
+        /** Update floating copys position.
+		 *
+		 * @param x
+		 *            the x
+		 * @param y
+		 *            the y
+		 */
+        private void updateFloatingCopysPosition(int x, int y) {
+            x -= DOM.getElementPropertyInt(floatingCopyOfHeaderCell,
+                    "offsetWidth") / 2;
+            floatingCopyOfHeaderCell.getStyle().setLeft(x, Unit.PX);
+            if (y > 0) {
+                floatingCopyOfHeaderCell.getStyle().setTop(y + 7, Unit.PX);
+            }
+        }
+
+        /** Hide floating copy.
+		 */
+        private void hideFloatingCopy() {
+            floatingCopyOfHeaderCell.removeFromParent();
+            floatingCopyOfHeaderCell = null;
+        }
+
+        /** Fires a header click event after the user has clicked a column
+		 * header cell.
+		 *
+		 * @param event
+		 *            The click event
+		 */
+        private void fireHeaderClickedEvent(Event event) {
+            if (client.hasEventListeners(VCustomScrollTable.this,
+                    TableConstants.HEADER_CLICK_EVENT_ID)) {
+                MouseEventDetails details = MouseEventDetailsBuilder
+                        .buildMouseEventDetails(event);
+                client.updateVariable(paintableId, "headerClickEvent",
+                        details.toString(), false);
+                client.updateVariable(paintableId, "headerClickCID", cid, true);
+            }
+        }
+
+        /** Handle caption event.
+		 *
+		 * @param event
+		 *            the event
+		 */
+        protected void handleCaptionEvent(Event event) {
+            switch (DOM.eventGetType(event)) {
+            case Event.ONTOUCHSTART:
+            case Event.ONMOUSEDOWN:
+                if (columnReordering
+                        && Util.isTouchEventOrLeftMouseButton(event)) {
+                    if (event.getTypeInt() == Event.ONTOUCHSTART) {
+                        /*
+                         * prevent using this event in e.g. scrolling
+                         */
+                        event.stopPropagation();
+                    }
+                    dragging = true;
+                    moved = false;
+                    colIndex = getColIndexByKey(cid);
+                    DOM.setCapture(getElement());
+                    headerX = tHead.getAbsoluteLeft();
+                    event.preventDefault(); // prevent selecting text &&
+                                            // generated touch events
+                }
+                break;
+            case Event.ONMOUSEUP:
+            case Event.ONTOUCHEND:
+            case Event.ONTOUCHCANCEL:
+                if (columnReordering
+                        && Util.isTouchEventOrLeftMouseButton(event)) {
+                    dragging = false;
+                    DOM.releaseCapture(getElement());
+                    if (moved) {
+                        hideFloatingCopy();
+                        tHead.removeSlotFocus();
+                        if (closestSlot != colIndex
+                                && closestSlot != (colIndex + 1)) {
+                            if (closestSlot > colIndex) {
+                                reOrderColumn(cid, closestSlot - 1);
+                            } else {
+                                reOrderColumn(cid, closestSlot);
+                            }
+                        }
+                    }
+                    if (Util.isTouchEvent(event)) {
+                        /*
+                         * Prevent using in e.g. scrolling and prevent generated
+                         * events.
+                         */
+                        event.preventDefault();
+                        event.stopPropagation();
+                    }
+                }
+
+                if (!moved) {
+                    // mouse event was a click to header -> sort column
+                    if (sortable && Util.isTouchEventOrLeftMouseButton(event)) {
+                        if (sortColumn.equals(cid)) {
+                            // just toggle order
+                            client.updateVariable(paintableId, "sortascending",
+                                    !sortAscending, false);
+                        } else {
+                            // set table sorted by this column
+                            client.updateVariable(paintableId, "sortcolumn",
+                                    cid, false);
+                        }
+                        // get also cache columns at the same request
+                        scrollBodyPanel.setScrollPosition(0);
+                        firstvisible = 0;
+                        rowRequestHandler.setReqFirstRow(0);
+                        rowRequestHandler.setReqRows((int) (2 * pageLength
+                                * cache_rate + pageLength));
+                        rowRequestHandler.deferRowFetch(); // some validation +
+                                                           // defer 250ms
+                        rowRequestHandler.cancel(); // instead of waiting
+                        rowRequestHandler.run(); // run immediately
+                    }
+                    fireHeaderClickedEvent(event);
+                    if (Util.isTouchEvent(event)) {
+                        /*
+                         * Prevent using in e.g. scrolling and prevent generated
+                         * events.
+                         */
+                        event.preventDefault();
+                        event.stopPropagation();
+                    }
+                    break;
+                }
+                break;
+            case Event.ONDBLCLICK:
+                fireHeaderClickedEvent(event);
+                break;
+            case Event.ONTOUCHMOVE:
+            case Event.ONMOUSEMOVE:
+                if (dragging && Util.isTouchEventOrLeftMouseButton(event)) {
+                    if (event.getTypeInt() == Event.ONTOUCHMOVE) {
+                        /*
+                         * prevent using this event in e.g. scrolling
+                         */
+                        event.stopPropagation();
+                    }
+                    if (!moved) {
+                        createFloatingCopy();
+                        moved = true;
+                    }
+
+                    final int clientX = Util.getTouchOrMouseClientX(event);
+                    final int x = clientX + tHead.hTableWrapper.getScrollLeft();
+                    int slotX = headerX;
+                    closestSlot = colIndex;
+                    int closestDistance = -1;
+                    int start = 0;
+                    if (showRowHeaders) {
+                        start++;
+                    }
+                    final int visibleCellCount = tHead.getVisibleCellCount();
+                    for (int i = start; i <= visibleCellCount; i++) {
+                        if (i > 0) {
+                            final String colKey = getColKeyByIndex(i - 1);
+                            // getColWidth only returns the internal width
+                            // without padding, not the offset width of the
+                            // whole td (#10890)
+                            slotX += getColWidth(colKey)
+                                    + scrollBody.getCellExtraWidth();
+                        }
+                        final int dist = Math.abs(x - slotX);
+                        if (closestDistance == -1 || dist < closestDistance) {
+                            closestDistance = dist;
+                            closestSlot = i;
+                        }
+                    }
+                    tHead.focusSlot(closestSlot);
+
+                    updateFloatingCopysPosition(clientX, -1);
+                }
+                break;
+            default:
+                break;
+            }
+        }
+
+        /** On resize event.
+		 *
+		 * @param event
+		 *            the event
+		 */
+        private void onResizeEvent(Event event) {
+            switch (DOM.eventGetType(event)) {
+            case Event.ONMOUSEDOWN:
+                if (!Util.isTouchEventOrLeftMouseButton(event)) {
+                    return;
+                }
+                isResizing = true;
+                DOM.setCapture(getElement());
+                dragStartX = DOM.eventGetClientX(event);
+                colIndex = getColIndexByKey(cid);
+                originalWidth = getWidthWithIndent();
+                DOM.eventPreventDefault(event);
+                break;
+            case Event.ONMOUSEUP:
+                if (!Util.isTouchEventOrLeftMouseButton(event)) {
+                    return;
+                }
+                isResizing = false;
+                DOM.releaseCapture(getElement());
+                tHead.disableAutoColumnWidthCalculation(this);
+
+                // Ensure last header cell is taking into account possible
+                // column selector
+                HeaderCell lastCell = tHead.getHeaderCell(tHead
+                        .getVisibleCellCount() - 1);
+                tHead.resizeCaptionContainer(lastCell);
+                triggerLazyColumnAdjustment(true);
+
+                fireColumnResizeEvent(cid, originalWidth, getColWidth(cid));
+                break;
+            case Event.ONMOUSEMOVE:
+                if (!Util.isTouchEventOrLeftMouseButton(event)) {
+                    return;
+                }
+                if (isResizing) {
+                    final int deltaX = DOM.eventGetClientX(event) - dragStartX;
+                    if (deltaX == 0) {
+                        return;
+                    }
+                    tHead.disableAutoColumnWidthCalculation(this);
+
+                    int newWidth = originalWidth + deltaX;
+                    // get min width with indent, no padding
+                    int minWidth = getMinWidth(true, false);
+                    if (newWidth < minWidth) {
+                        // already includes indent if any
+                        newWidth = minWidth;
+                    }
+                    setColWidth(colIndex, newWidth, true);
+                    triggerLazyColumnAdjustment(false);
+                    forceRealignColumnHeaders();
+                }
+                break;
+            default:
+                break;
+            }
+        }
+
+        /** Returns the smallest possible cell width in pixels.
+		 *
+		 * @param includeIndent
+		 *            - width should include hierarchy column indent if
+		 *            applicable (VTreeTable only)
+		 * @param includeCellExtraWidth
+		 *            - width should include paddings etc.
+		 * @return the min width
+		 */
+        private int getMinWidth(boolean includeIndent,
+                boolean includeCellExtraWidth) {
+            int minWidth = sortIndicator.getOffsetWidth();
+            if (scrollBody != null) {
+                // check the need for indent before adding paddings etc.
+                if (includeIndent && isHierarchyColumn()) {
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (minWidth < maxIndent) {
+                        minWidth = maxIndent;
+                    }
+                }
+                if (includeCellExtraWidth) {
+                    minWidth += scrollBody.getCellExtraWidth();
+                }
+            }
+            return minWidth;
+        }
+
+        /** Gets the min width.
+		 *
+		 * @return the min width
+		 */
+        public int getMinWidth() {
+            // get min width with padding, no indent
+            return getMinWidth(false, true);
+        }
+
+        /** Gets the caption.
+		 *
+		 * @return the caption
+		 */
+        public String getCaption() {
+            return DOM.getInnerText(captionContainer);
+        }
+
+        /** Checks if is enabled.
+		 *
+		 * @return true, if is enabled
+		 */
+        public boolean isEnabled() {
+            return getParent() != null;
+        }
+
+        /** Sets the align.
+		 *
+		 * @param c
+		 *            the new align
+		 */
+        public void setAlign(char c) {
+            align = c;
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /** Gets the align.
+		 *
+		 * @return the align
+		 */
+        public char getAlign() {
+            return align;
+        }
+
+        /** Detects the natural minimum width for the column of this header
+		 * cell. If column is resized by user or the width is defined by server
+		 * the actual width is returned. Else the natural min width is returned.
+		 *
+		 * @param columnIndex
+		 *            column index hint, if -1 (unknown) it will be detected
+		 * @return the natural column width
+		 */
+        public int getNaturalColumnWidth(int columnIndex) {
+            final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody
+                    .getMaxIndent() : 0;
+            if (isDefinedWidth()) {
+                if (iw > width) {
+                    return iw;
+                }
+                return width;
+            } else {
+                if (naturalWidth < 0) {
+                    // This is recently revealed column. Try to detect a proper
+                    // value (greater of header and data columns)
+
+                    int hw = captionContainer.getOffsetWidth()
+                            + getHeaderPadding();
+                    if (BrowserInfo.get().isGecko()) {
+                        hw += sortIndicator.getOffsetWidth();
+                    }
+                    if (columnIndex < 0) {
+                        columnIndex = 0;
+                        for (Iterator<Widget> it = tHead.iterator(); it
+                                .hasNext(); columnIndex++) {
+                            if (it.next() == this) {
+                                break;
+                            }
+                        }
+                    }
+                    final int cw = scrollBody.getColWidth(columnIndex);
+                    naturalWidth = (hw > cw ? hw : cw);
+                }
+                if (iw > naturalWidth) {
+                    // indent is temporary value, naturalWidth shouldn't be
+                    // updated
+                    return iw;
+                } else {
+                    return naturalWidth;
+                }
+            }
+        }
+
+        /** Sets the expand ratio.
+		 *
+		 * @param floatAttribute
+		 *            the new expand ratio
+		 */
+        public void setExpandRatio(float floatAttribute) {
+            if (floatAttribute != expandRatio) {
+                triggerLazyColumnAdjustment(false);
+            }
+            expandRatio = floatAttribute;
+        }
+
+        /** Gets the expand ratio.
+		 *
+		 * @return the expand ratio
+		 */
+        public float getExpandRatio() {
+            return expandRatio;
+        }
+
+        /** Checks if is sorted.
+		 *
+		 * @return true, if is sorted
+		 */
+        public boolean isSorted() {
+            return sorted;
+        }
+    }
+
+    /**
+     * HeaderCell that is header cell for row headers.
+     * 
+     * Reordering disabled and clicking on it resets sorting.
+     */
+    public class RowHeadersHeaderCell extends HeaderCell {
+
+        /** Instantiates a new row headers header cell.
+		 */
+        RowHeadersHeaderCell() {
+            super(ROW_HEADER_COLUMN_KEY, "");
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.VCustomScrollTable.HeaderCell#updateStyleNames(java.lang.String)
+         */
+        @Override
+        protected void updateStyleNames(String primaryStyleName) {
+            super.updateStyleNames(primaryStyleName);
+            setStyleName(primaryStyleName + "-header-cell-rowheader");
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.VCustomScrollTable.HeaderCell#handleCaptionEvent(com.google.gwt.user.client.Event)
+         */
+        @Override
+        protected void handleCaptionEvent(Event event) {
+            // NOP: RowHeaders cannot be reordered
+            // TODO It'd be nice to reset sorting here
+        }
+    }
+
+    /** The Class TableHead.
+	 */
+    public class TableHead extends Panel implements ActionOwner {
+
+        /** The Constant WRAPPER_WIDTH. */
+        private static final int WRAPPER_WIDTH = 900000;
+
+        /** The visible cells. */
+        ArrayList<Widget> visibleCells = new ArrayList<Widget>();
+
+        /** The available cells. */
+        HashMap<String, HeaderCell> availableCells = new HashMap<String, HeaderCell>();
+
+        /** The div. */
+        Element div = DOM.createDiv();
+        
+        /** The h table wrapper. */
+        Element hTableWrapper = DOM.createDiv();
+        
+        /** The h table container. */
+        Element hTableContainer = DOM.createDiv();
+        
+        /** The table. */
+        Element table = DOM.createTable();
+        
+        /** The header table body. */
+        Element headerTableBody = DOM.createTBody();
+        
+        /** The tr. */
+        Element tr = DOM.createTR();
+
+        /** The column selector. */
+        private final Element columnSelector = DOM.createDiv();
+
+        /** The focused slot. */
+        private int focusedSlot = -1;
+
+        /** Instantiates a new table head.
+		 */
+        public TableHead() {
+            if (BrowserInfo.get().isIE()) {
+                table.setPropertyInt("cellSpacing", 0);
+            }
+
+            hTableWrapper.getStyle().setOverflow(Overflow.HIDDEN);
+            columnSelector.getStyle().setDisplay(Display.NONE);
+
+            DOM.appendChild(table, headerTableBody);
+            DOM.appendChild(headerTableBody, tr);
+            DOM.appendChild(hTableContainer, table);
+            DOM.appendChild(hTableWrapper, hTableContainer);
+            DOM.appendChild(div, hTableWrapper);
+            DOM.appendChild(div, columnSelector);
+            setElement(div);
+
+            DOM.sinkEvents(columnSelector, Event.ONCLICK);
+
+            availableCells.put(ROW_HEADER_COLUMN_KEY,
+                    new RowHeadersHeaderCell());
+        }
+
+        /** Update style names.
+		 *
+		 * @param primaryStyleName
+		 *            the primary style name
+		 */
+        protected void updateStyleNames(String primaryStyleName) {
+            hTableWrapper.setClassName(primaryStyleName + "-header");
+            columnSelector.setClassName(primaryStyleName + "-column-selector");
+            setStyleName(primaryStyleName + "-header-wrap");
+            for (HeaderCell c : availableCells.values()) {
+                c.updateStyleNames(primaryStyleName);
+            }
+        }
+
+        /** Resize caption container.
+		 *
+		 * @param cell
+		 *            the cell
+		 */
+        public void resizeCaptionContainer(HeaderCell cell) {
+            HeaderCell lastcell = getHeaderCell(visibleCells.size() - 1);
+            int columnSelectorOffset = columnSelector.getOffsetWidth();
+
+            if (cell == lastcell && columnSelectorOffset > 0
+                    && !hasVerticalScrollbar()) {
+
+                // Measure column widths
+                int columnTotalWidth = 0;
+                for (Widget w : visibleCells) {
+                    int cellExtraWidth = w.getOffsetWidth();
+                    if (scrollBody != null
+                            && visibleCells.indexOf(w) == getHierarchyColumnIndex()
+                            && cellExtraWidth < scrollBody.getMaxIndent()) {
+                        // indent must be taken into consideration even if it
+                        // hasn't been applied yet
+                        columnTotalWidth += scrollBody.getMaxIndent();
+                    } else {
+                        columnTotalWidth += cellExtraWidth;
+                    }
+                }
+
+                int divOffset = div.getOffsetWidth();
+                if (columnTotalWidth >= divOffset - columnSelectorOffset) {
+                    /*
+                     * Ensure column caption is visible when placed under the
+                     * column selector widget by shifting and resizing the
+                     * caption.
+                     */
+                    int offset = 0;
+                    int diff = divOffset - columnTotalWidth;
+                    if (diff < columnSelectorOffset && diff > 0) {
+                        /*
+                         * If the difference is less than the column selectors
+                         * width then just offset by the difference
+                         */
+                        offset = columnSelectorOffset - diff;
+                    } else {
+                        // Else offset by the whole column selector
+                        offset = columnSelectorOffset;
+                    }
+                    lastcell.resizeCaptionContainer(offset);
+                } else {
+                    cell.resizeCaptionContainer(0);
+                }
+            } else {
+                cell.resizeCaptionContainer(0);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Panel#clear()
+         */
+        @Override
+        public void clear() {
+            for (String cid : availableCells.keySet()) {
+                removeCell(cid);
+            }
+            availableCells.clear();
+            availableCells.put(ROW_HEADER_COLUMN_KEY,
+                    new RowHeadersHeaderCell());
+        }
+
+        /** Update cells from uidl.
+		 *
+		 * @param uidl
+		 *            the uidl
+		 */
+        public void updateCellsFromUIDL(UIDL uidl) {
+            Iterator<?> it = uidl.getChildIterator();
+            HashSet<String> updated = new HashSet<String>();
+            boolean refreshContentWidths = initializedAndAttached
+                    && hadScrollBars != willHaveScrollbars();
+            while (it.hasNext()) {
+                final UIDL col = (UIDL) it.next();
+                final String cid = col.getStringAttribute("cid");
+                updated.add(cid);
+
+                String caption = buildCaptionHtmlSnippet(col);
+                HeaderCell c = getHeaderCell(cid);
+                if (c == null) {
+                    c = new HeaderCell(cid, caption);
+                    availableCells.put(cid, c);
+                    if (initializedAndAttached) {
+                        // we will need a column width recalculation
+                        initializedAndAttached = false;
+                        initialContentReceived = false;
+                        isNewBody = true;
+                    }
+                } else {
+                    c.setText(caption);
+                }
+
+                if (col.hasAttribute("sortable")) {
+                    c.setSortable(true);
+                    c.setSorted(false);
+                } else {
+                    c.setSortable(false);
+                }
+
+                if (col.hasAttribute("align")) {
+                    c.setAlign(col.getStringAttribute("align").charAt(0));
+                } else {
+                    c.setAlign(ALIGN_LEFT);
+
+                }
+                if (col.hasAttribute("width") && !c.isResizing) {
+                    // Make sure to accomodate for the sort indicator if
+                    // necessary.
+                    int width = col.getIntAttribute("width");
+                    int widthWithoutAddedIndent = width;
+
+                    // get min width with indent, no padding
+                    int minWidth = c.getMinWidth(true, false);
+                    if (width < minWidth) {
+                        width = minWidth;
+                    }
+                    if (scrollBody != null && width != c.getWidthWithIndent()) {
+                        // Do a more thorough update if a column is resized from
+                        // the server *after* the header has been properly
+                        // initialized
+                        final int colIx = getColIndexByKey(c.cid);
+                        final int newWidth = width;
+                        Scheduler.get().scheduleDeferred(
+                                new ScheduledCommand() {
+
+                                    @Override
+                                    public void execute() {
+                                        setColWidth(colIx, newWidth, true);
+                                    }
+                                });
+                        refreshContentWidths = true;
+                    } else {
+                        // get min width with no indent or padding
+                        minWidth = c.getMinWidth(false, false);
+                        if (widthWithoutAddedIndent < minWidth) {
+                            widthWithoutAddedIndent = minWidth;
+                        }
+                        // save min width without indent
+                        c.setWidth(widthWithoutAddedIndent, true);
+                    }
+                } else if (col.hasAttribute("er")) {
+                    c.setExpandRatio(col.getFloatAttribute("er"));
+
+                } else if (recalcWidths) {
+                    c.setUndefinedWidth();
+
+                } else {
+                    boolean hadExpandRatio = c.getExpandRatio() > 0;
+                    boolean hadDefinedWidth = c.isDefinedWidth();
+                    if (hadExpandRatio || hadDefinedWidth) {
+                        // Someone has removed a expand width or the defined
+                        // width on the server side (setting it to -1), make the
+                        // column undefined again and measure columns again.
+                        c.setUndefinedWidth();
+                        c.setExpandRatio(0);
+                        refreshContentWidths = true;
+                    }
+                }
+
+                if (col.hasAttribute("collapsed")) {
+                    // ensure header is properly removed from parent (case when
+                    // collapsing happens via servers side api)
+                    if (c.isAttached()) {
+                        c.removeFromParent();
+                        headerChangedDuringUpdate = true;
+                    }
+                }
+            }
+
+            if (refreshContentWidths) {
+                // Recalculate the column sizings if any column has changed
+                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+
+                    @Override
+                    public void execute() {
+                        triggerLazyColumnAdjustment(true);
+                    }
+                });
+            }
+
+            // check for orphaned header cells
+            for (Iterator<String> cit = availableCells.keySet().iterator(); cit
+                    .hasNext();) {
+                String cid = cit.next();
+                if (!updated.contains(cid)) {
+                    removeCell(cid);
+                    cit.remove();
+                    // we will need a column width recalculation, since columns
+                    // with expand ratios should expand to fill the void.
+                    initializedAndAttached = false;
+                    initialContentReceived = false;
+                    isNewBody = true;
+                }
+            }
+        }
+
+        /** Enable column.
+		 *
+		 * @param cid
+		 *            the cid
+		 * @param index
+		 *            the index
+		 */
+        public void enableColumn(String cid, int index) {
+            final HeaderCell c = getHeaderCell(cid);
+            if (!c.isEnabled() || getHeaderCell(index) != c) {
+                setHeaderCell(index, c);
+                if (initializedAndAttached) {
+                    headerChangedDuringUpdate = true;
+                }
+            }
+        }
+
+        /** Gets the visible cell count.
+		 *
+		 * @return the visible cell count
+		 */
+        public int getVisibleCellCount() {
+            return visibleCells.size();
+        }
+
+        /** Sets the horizontal scroll position.
+		 *
+		 * @param scrollLeft
+		 *            the new horizontal scroll position
+		 */
+        public void setHorizontalScrollPosition(int scrollLeft) {
+            hTableWrapper.setScrollLeft(scrollLeft);
+        }
+
+        /** Sets the column collapsing allowed.
+		 *
+		 * @param cc
+		 *            the new column collapsing allowed
+		 */
+        public void setColumnCollapsingAllowed(boolean cc) {
+            if (cc) {
+                columnSelector.getStyle().setDisplay(Display.BLOCK);
+            } else {
+                columnSelector.getStyle().setDisplay(Display.NONE);
+            }
+        }
+
+        /** Disable browser intelligence.
+		 */
+        public void disableBrowserIntelligence() {
+            hTableContainer.getStyle().setWidth(WRAPPER_WIDTH, Unit.PX);
+        }
+
+        /** Enable browser intelligence.
+		 */
+        public void enableBrowserIntelligence() {
+            hTableContainer.getStyle().clearWidth();
+        }
+
+        /** Sets the header cell.
+		 *
+		 * @param index
+		 *            the index
+		 * @param cell
+		 *            the cell
+		 */
+        public void setHeaderCell(int index, HeaderCell cell) {
+            if (cell.isEnabled()) {
+                // we're moving the cell
+                DOM.removeChild(tr, cell.getElement());
+                orphan(cell);
+                visibleCells.remove(cell);
+            }
+            if (index < visibleCells.size()) {
+                // insert to right slot
+                DOM.insertChild(tr, cell.getElement(), index);
+                adopt(cell);
+                visibleCells.add(index, cell);
+            } else if (index == visibleCells.size()) {
+                // simply append
+                DOM.appendChild(tr, cell.getElement());
+                adopt(cell);
+                visibleCells.add(cell);
+            } else {
+                throw new RuntimeException(
+                        "Header cells must be appended in order");
+            }
+        }
+
+        /** Gets the header cell.
+		 *
+		 * @param index
+		 *            the index
+		 * @return the header cell
+		 */
+        public HeaderCell getHeaderCell(int index) {
+            if (index >= 0 && index < visibleCells.size()) {
+                return (HeaderCell) visibleCells.get(index);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Get's HeaderCell by it's column Key.
+         * 
+         * Note that this returns HeaderCell even if it is currently collapsed.
+         * 
+         * @param cid
+         *            Column key of accessed HeaderCell
+         * @return HeaderCell
+         */
+        public HeaderCell getHeaderCell(String cid) {
+            return availableCells.get(cid);
+        }
+
+        /** Move cell.
+		 *
+		 * @param oldIndex
+		 *            the old index
+		 * @param newIndex
+		 *            the new index
+		 */
+        public void moveCell(int oldIndex, int newIndex) {
+            final HeaderCell hCell = getHeaderCell(oldIndex);
+            final Element cell = hCell.getElement();
+
+            visibleCells.remove(oldIndex);
+            DOM.removeChild(tr, cell);
+
+            DOM.insertChild(tr, cell, newIndex);
+            visibleCells.add(newIndex, hCell);
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.HasWidgets#iterator()
+         */
+        @Override
+        public Iterator<Widget> iterator() {
+            return visibleCells.iterator();
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Panel#remove(com.google.gwt.user.client.ui.Widget)
+         */
+        @Override
+        public boolean remove(Widget w) {
+            if (visibleCells.contains(w)) {
+                visibleCells.remove(w);
+                orphan(w);
+                DOM.removeChild(DOM.getParent(w.getElement()), w.getElement());
+                return true;
+            }
+            return false;
+        }
+
+        /** Removes the cell.
+		 *
+		 * @param colKey
+		 *            the col key
+		 */
+        public void removeCell(String colKey) {
+            final HeaderCell c = getHeaderCell(colKey);
+            remove(c);
+        }
+
+        /** Focus slot.
+		 *
+		 * @param index
+		 *            the index
+		 */
+        private void focusSlot(int index) {
+            removeSlotFocus();
+            if (index > 0) {
+                Element child = tr.getChild(index - 1).getFirstChild().cast();
+                child.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-resizer");
+                child.addClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-focus-slot-right");
+            } else {
+                Element child = tr.getChild(index).getFirstChild().cast();
+                child.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-resizer");
+                child.addClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-focus-slot-left");
+            }
+            focusedSlot = index;
+        }
+
+        /** Removes the slot focus.
+		 */
+        private void removeSlotFocus() {
+            if (focusedSlot < 0) {
+                return;
+            }
+            if (focusedSlot == 0) {
+                Element child = tr.getChild(focusedSlot).getFirstChild().cast();
+                child.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-resizer");
+            } else if (focusedSlot > 0) {
+                Element child = tr.getChild(focusedSlot - 1).getFirstChild()
+                        .cast();
+                child.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-resizer");
+            }
+            focusedSlot = -1;
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user.client.Event)
+         */
+        @Override
+        public void onBrowserEvent(Event event) {
+            if (enabled) {
+                if (event.getEventTarget().cast() == columnSelector) {
+                    final int left = DOM.getAbsoluteLeft(columnSelector);
+                    final int top = DOM.getAbsoluteTop(columnSelector)
+                            + DOM.getElementPropertyInt(columnSelector,
+                                    "offsetHeight");
+                    client.getContextMenu().showAt(this, left, top);
+                }
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Widget#onDetach()
+         */
+        @Override
+        protected void onDetach() {
+            super.onDetach();
+            if (client != null) {
+                client.getContextMenu().ensureHidden(this);
+            }
+        }
+
+        /** The Class VisibleColumnAction.
+		 */
+        class VisibleColumnAction extends Action {
+
+            /** The col key. */
+            String colKey;
+            
+            /** The collapsed. */
+            private boolean collapsed;
+            
+            /** The noncollapsible. */
+            private boolean noncollapsible = false;
+            
+            /** The currently focused row. */
+            private VScrollTableRow currentlyFocusedRow;
+
+            /** Instantiates a new visible column action.
+			 *
+			 * @param colKey
+			 *            the col key
+			 */
+            public VisibleColumnAction(String colKey) {
+                super(VCustomScrollTable.TableHead.this);
+                this.colKey = colKey;
+                caption = tHead.getHeaderCell(colKey).getCaption();
+                currentlyFocusedRow = focusedRow;
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.Action#execute()
+             */
+            @Override
+            public void execute() {
+                if (noncollapsible) {
+                    return;
+                }
+                client.getContextMenu().hide();
+                // toggle selected column
+                if (collapsedColumns.contains(colKey)) {
+                    collapsedColumns.remove(colKey);
+                } else {
+                    tHead.removeCell(colKey);
+                    collapsedColumns.add(colKey);
+                    triggerLazyColumnAdjustment(true);
+                }
+
+                // update variable to server
+                client.updateVariable(paintableId, "collapsedcolumns",
+                        collapsedColumns.toArray(new String[collapsedColumns
+                                .size()]), false);
+                // let rowRequestHandler determine proper rows
+                rowRequestHandler.refreshContent();
+                lazyRevertFocusToRow(currentlyFocusedRow);
+            }
+
+            /** Sets the collapsed.
+			 *
+			 * @param b
+			 *            the new collapsed
+			 */
+            public void setCollapsed(boolean b) {
+                collapsed = b;
+            }
+
+            /** Sets the noncollapsible.
+			 *
+			 * @param b
+			 *            the new noncollapsible
+			 */
+            public void setNoncollapsible(boolean b) {
+                noncollapsible = b;
+            }
+
+            /** Override default method to distinguish on/off columns.
+			 *
+			 * @return the html
+			 */
+
+            @Override
+            public String getHTML() {
+                final StringBuffer buf = new StringBuffer();
+                buf.append("<span class=\"");
+                if (collapsed) {
+                    buf.append("v-off");
+                } else {
+                    buf.append("v-on");
+                }
+                if (noncollapsible) {
+                    buf.append(" v-disabled");
+                }
+                buf.append("\">");
+
+                buf.append(super.getHTML());
+                buf.append("</span>");
+
+                return buf.toString();
+            }
+
+        }
+
+        /*
+         * Returns columns as Action array for column select popup
+         */
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.ActionOwner#getActions()
+         */
+        @Override
+        public Action[] getActions() {
+            Object[] cols;
+            if (columnReordering && columnOrder != null) {
+                cols = columnOrder;
+            } else {
+                // if columnReordering is disabled, we need different way to get
+                // all available columns
+                cols = visibleColOrder;
+                cols = new Object[visibleColOrder.length
+                        + collapsedColumns.size()];
+                int i;
+                for (i = 0; i < visibleColOrder.length; i++) {
+                    cols[i] = visibleColOrder[i];
+                }
+                for (final Iterator<String> it = collapsedColumns.iterator(); it
+                        .hasNext();) {
+                    cols[i++] = it.next();
+                }
+            }
+            final Action[] actions = new Action[cols.length];
+
+            for (int i = 0; i < cols.length; i++) {
+                final String cid = (String) cols[i];
+                final HeaderCell c = getHeaderCell(cid);
+                final VisibleColumnAction a = new VisibleColumnAction(
+                        c.getColKey());
+                a.setCaption(c.getCaption());
+                if (!c.isEnabled()) {
+                    a.setCollapsed(true);
+                }
+                if (noncollapsibleColumns.contains(cid)) {
+                    a.setNoncollapsible(true);
+                }
+                actions[i] = a;
+            }
+            return actions;
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.ActionOwner#getClient()
+         */
+        @Override
+        public ApplicationConnection getClient() {
+            return client;
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.ActionOwner#getPaintableId()
+         */
+        @Override
+        public String getPaintableId() {
+            return paintableId;
+        }
+
+        /** Returns column alignments for visible columns.
+		 *
+		 * @return the column alignments
+		 */
+        public char[] getColumnAlignments() {
+            final Iterator<Widget> it = visibleCells.iterator();
+            final char[] aligns = new char[visibleCells.size()];
+            int colIndex = 0;
+            while (it.hasNext()) {
+                aligns[colIndex++] = ((HeaderCell) it.next()).getAlign();
+            }
+            return aligns;
+        }
+
+        /** Disables the automatic calculation of all column widths by
+		 * forcing the widths to be "defined" thus turning off expand ratios and
+		 * such.
+		 *
+		 * @param source
+		 *            the source
+		 */
+        public void disableAutoColumnWidthCalculation(HeaderCell source) {
+            for (HeaderCell cell : availableCells.values()) {
+                cell.disableAutoWidthCalculation();
+            }
+            // fire column resize events for all columns but the source of the
+            // resize action, since an event will fire separately for this.
+            ArrayList<HeaderCell> columns = new ArrayList<HeaderCell>(
+                    availableCells.values());
+            columns.remove(source);
+            sendColumnWidthUpdates(columns);
+            forceRealignColumnHeaders();
+        }
+    }
+
+    /** A cell in the footer.
+	 */
+    public class FooterCell extends Widget {
+        
+        /** The td. */
+        private final Element td = DOM.createTD();
+        
+        /** The caption container. */
+        private final Element captionContainer = DOM.createDiv();
+        
+        /** The align. */
+        private char align = ALIGN_LEFT;
+        
+        /** The width. */
+        private int width = -1;
+        
+        /** The expand ratio. */
+        private float expandRatio = 0;
+        
+        /** The cid. */
+        private final String cid;
+        
+        /** The defined width. */
+        boolean definedWidth = false;
+        
+        /** The natural width. */
+        private int naturalWidth = -1;
+
+        /** Instantiates a new footer cell.
+		 *
+		 * @param colId
+		 *            the col id
+		 * @param headerText
+		 *            the header text
+		 */
+        public FooterCell(String colId, String headerText) {
+            cid = colId;
+
+            setText(headerText);
+
+            // ensure no clipping initially (problem on column additions)
+            captionContainer.getStyle().setOverflow(Overflow.VISIBLE);
+
+            DOM.sinkEvents(captionContainer, Event.MOUSEEVENTS);
+
+            DOM.appendChild(td, captionContainer);
+
+            DOM.sinkEvents(td, Event.MOUSEEVENTS | Event.ONDBLCLICK
+                    | Event.ONCONTEXTMENU);
+
+            setElement(td);
+
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /** Update style names.
+		 *
+		 * @param primaryStyleName
+		 *            the primary style name
+		 */
+        protected void updateStyleNames(String primaryStyleName) {
+            captionContainer.setClassName(primaryStyleName
+                    + "-footer-container");
+        }
+
+        /** Sets the text of the footer.
+		 *
+		 * @param footerText
+		 *            The text in the footer
+		 */
+        public void setText(String footerText) {
+            if (footerText == null || footerText.equals("")) {
+                footerText = "&nbsp;";
+            }
+
+            DOM.setInnerHTML(captionContainer, footerText);
+        }
+
+        /** Set alignment of the text in the cell.
+		 *
+		 * @param c
+		 *            The alignment which can be ALIGN_CENTER, ALIGN_LEFT,
+		 *            ALIGN_RIGHT
+		 */
+        public void setAlign(char c) {
+            if (align != c) {
+                switch (c) {
+                case ALIGN_CENTER:
+                    captionContainer.getStyle().setTextAlign(TextAlign.CENTER);
+                    break;
+                case ALIGN_RIGHT:
+                    captionContainer.getStyle().setTextAlign(TextAlign.RIGHT);
+                    break;
+                default:
+                    captionContainer.getStyle().setTextAlign(TextAlign.LEFT);
+                    break;
+                }
+            }
+            align = c;
+        }
+
+        /** Get the alignment of the text int the cell.
+		 *
+		 * @return Returns either ALIGN_CENTER, ALIGN_LEFT or ALIGN_RIGHT
+		 */
+        public char getAlign() {
+            return align;
+        }
+
+        /**
+         * Sets the width of the cell. This width should not include any
+         * possible indent modifications that are present in
+         * {@link VScrollTableBody#getMaxIndent()}.
+         * 
+         * @param w
+         *            The width of the cell
+         * @param ensureDefinedWidth
+         *            Ensures that the given width is not recalculated
+         */
+        public void setWidth(int w, boolean ensureDefinedWidth) {
+
+            if (ensureDefinedWidth) {
+                definedWidth = true;
+                // on column resize expand ratio becomes zero
+                expandRatio = 0;
+            }
+            if (width == w) {
+                return;
+            }
+            if (width == -1) {
+                // go to default mode, clip content if necessary
+                captionContainer.getStyle().clearOverflow();
+            }
+            width = w;
+            if (w == -1) {
+                captionContainer.getStyle().clearWidth();
+                setWidth("");
+            } else {
+                /*
+                 * Reduce width with one pixel for the right border since the
+                 * footers does not have any spacers between them.
+                 */
+                final int borderWidths = 1;
+
+                // Set the container width (check for negative value)
+                captionContainer.getStyle().setPropertyPx("width",
+                        Math.max(w - borderWidths, 0));
+
+                /*
+                 * if we already have tBody, set the header width properly, if
+                 * not defer it. IE will fail with complex float in table header
+                 * unless TD width is not explicitly set.
+                 */
+                if (scrollBody != null) {
+                    int maxIndent = scrollBody.getMaxIndent();
+                    if (w < maxIndent
+                            && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) {
+                        // ensure there's room for the indent
+                        w = maxIndent;
+                    }
+                    int tdWidth = w + scrollBody.getCellExtraWidth()
+                            - borderWidths;
+                    setWidth(Math.max(tdWidth, 0) + "px");
+                } else {
+                    Scheduler.get().scheduleDeferred(new Command() {
+
+                        @Override
+                        public void execute() {
+                            int tdWidth = width;
+                            int maxIndent = scrollBody.getMaxIndent();
+                            if (tdWidth < maxIndent
+                                    && tFoot.visibleCells.indexOf(this) == getHierarchyColumnIndex()) {
+                                // ensure there's room for the indent
+                                tdWidth = maxIndent;
+                            }
+                            tdWidth += scrollBody.getCellExtraWidth()
+                                    - borderWidths;
+                            setWidth(Math.max(tdWidth, 0) + "px");
+                        }
+                    });
+                }
+            }
+        }
+
+        /** Sets the width to undefined.
+		 */
+        public void setUndefinedWidth() {
+            definedWidth = false;
+            setWidth(-1, false);
+        }
+
+        /**
+         * Detects if width is fixed by developer on server side or resized to
+         * current width by user.
+         * 
+         * @return true if defined, false if "natural" width
+         */
+        public boolean isDefinedWidth() {
+            return definedWidth && width >= 0;
+        }
+
+        /**
+         * Returns the pixels width of the footer cell.
+         * 
+         * @return The width in pixels
+         */
+        public int getWidth() {
+            return width;
+        }
+
+        /** Sets the expand ratio of the cell.
+		 *
+		 * @param floatAttribute
+		 *            The expand ratio
+		 */
+        public void setExpandRatio(float floatAttribute) {
+            expandRatio = floatAttribute;
+        }
+
+        /** Returns the expand ration of the cell.
+		 *
+		 * @return The expand ratio
+		 */
+        public float getExpandRatio() {
+            return expandRatio;
+        }
+
+        /** Is the cell enabled?.
+		 *
+		 * @return True if enabled else False
+		 */
+        public boolean isEnabled() {
+            return getParent() != null;
+        }
+
+        /** Handle column clicking.
+		 *
+		 * @param event
+		 *            the event
+		 */
+
+        @Override
+        public void onBrowserEvent(Event event) {
+            if (enabled && event != null) {
+                handleCaptionEvent(event);
+
+                if (DOM.eventGetType(event) == Event.ONMOUSEUP) {
+                    scrollBodyPanel.setFocus(true);
+                }
+                boolean stopPropagation = true;
+                if (event.getTypeInt() == Event.ONCONTEXTMENU
+                        && !client.hasEventListeners(VCustomScrollTable.this,
+                                TableConstants.FOOTER_CLICK_EVENT_ID)) {
+                    // Show browser context menu if a footer click listener is
+                    // not present
+                    stopPropagation = false;
+                }
+                if (stopPropagation) {
+                    event.stopPropagation();
+                    event.preventDefault();
+                }
+            }
+        }
+
+        /** Handles a event on the captions.
+		 *
+		 * @param event
+		 *            The event to handle
+		 */
+        protected void handleCaptionEvent(Event event) {
+            if (event.getTypeInt() == Event.ONMOUSEUP
+                    || event.getTypeInt() == Event.ONDBLCLICK) {
+                fireFooterClickedEvent(event);
+            }
+        }
+
+        /** Fires a footer click event after the user has clicked a column
+		 * footer cell.
+		 *
+		 * @param event
+		 *            The click event
+		 */
+        private void fireFooterClickedEvent(Event event) {
+            if (client.hasEventListeners(VCustomScrollTable.this,
+                    TableConstants.FOOTER_CLICK_EVENT_ID)) {
+                MouseEventDetails details = MouseEventDetailsBuilder
+                        .buildMouseEventDetails(event);
+                client.updateVariable(paintableId, "footerClickEvent",
+                        details.toString(), false);
+                client.updateVariable(paintableId, "footerClickCID", cid, true);
+            }
+        }
+
+        /** Returns the column key of the column.
+		 *
+		 * @return The column key
+		 */
+        public String getColKey() {
+            return cid;
+        }
+
+        /** Detects the natural minimum width for the column of this header
+		 * cell. If column is resized by user or the width is defined by server
+		 * the actual width is returned. Else the natural min width is returned.
+		 *
+		 * @param columnIndex
+		 *            column index hint, if -1 (unknown) it will be detected
+		 * @return the natural column width
+		 */
+        public int getNaturalColumnWidth(int columnIndex) {
+            final int iw = columnIndex == getHierarchyColumnIndex() ? scrollBody
+                    .getMaxIndent() : 0;
+            if (isDefinedWidth()) {
+                if (iw > width) {
+                    return iw;
+                }
+                return width;
+            } else {
+                if (naturalWidth < 0) {
+                    // This is recently revealed column. Try to detect a proper
+                    // value (greater of header and data
+                    // cols)
+
+                    final int hw = ((Element) getElement().getLastChild())
+                            .getOffsetWidth() + getHeaderPadding();
+                    if (columnIndex < 0) {
+                        columnIndex = 0;
+                        for (Iterator<Widget> it = tHead.iterator(); it
+                                .hasNext(); columnIndex++) {
+                            if (it.next() == this) {
+                                break;
+                            }
+                        }
+                    }
+                    final int cw = scrollBody.getColWidth(columnIndex);
+                    naturalWidth = (hw > cw ? hw : cw);
+                }
+                if (iw > naturalWidth) {
+                    return iw;
+                } else {
+                    return naturalWidth;
+                }
+            }
+        }
+
+        /** Sets the natural minimum column width.
+		 *
+		 * @param w
+		 *            the new natural minimum column width
+		 */
+        public void setNaturalMinimumColumnWidth(int w) {
+            naturalWidth = w;
+        }
+    }
+
+    /**
+     * HeaderCell that is header cell for row headers.
+     * 
+     * Reordering disabled and clicking on it resets sorting.
+     */
+    public class RowHeadersFooterCell extends FooterCell {
+
+        /** Instantiates a new row headers footer cell.
+		 */
+        RowHeadersFooterCell() {
+            super(ROW_HEADER_COLUMN_KEY, "");
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.VCustomScrollTable.FooterCell#handleCaptionEvent(com.google.gwt.user.client.Event)
+         */
+        @Override
+        protected void handleCaptionEvent(Event event) {
+            // NOP: RowHeaders cannot be reordered
+            // TODO It'd be nice to reset sorting here
+        }
+    }
+
+    /**
+     * The footer of the table which can be seen in the bottom of the Table.
+     */
+    public class TableFooter extends Panel {
+
+        /** The Constant WRAPPER_WIDTH. */
+        private static final int WRAPPER_WIDTH = 900000;
+
+        /** The visible cells. */
+        ArrayList<Widget> visibleCells = new ArrayList<Widget>();
+        
+        /** The available cells. */
+        HashMap<String, FooterCell> availableCells = new HashMap<String, FooterCell>();
+
+        /** The div. */
+        Element div = DOM.createDiv();
+        
+        /** The h table wrapper. */
+        Element hTableWrapper = DOM.createDiv();
+        
+        /** The h table container. */
+        Element hTableContainer = DOM.createDiv();
+        
+        /** The table. */
+        Element table = DOM.createTable();
+        
+        /** The header table body. */
+        Element headerTableBody = DOM.createTBody();
+        
+        /** The tr. */
+        Element tr = DOM.createTR();
+
+        /** Instantiates a new table footer.
+		 */
+        public TableFooter() {
+
+            hTableWrapper.getStyle().setOverflow(Overflow.HIDDEN);
+
+            DOM.appendChild(table, headerTableBody);
+            DOM.appendChild(headerTableBody, tr);
+            DOM.appendChild(hTableContainer, table);
+            DOM.appendChild(hTableWrapper, hTableContainer);
+            DOM.appendChild(div, hTableWrapper);
+            setElement(div);
+
+            availableCells.put(ROW_HEADER_COLUMN_KEY,
+                    new RowHeadersFooterCell());
+
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /** Update style names.
+		 *
+		 * @param primaryStyleName
+		 *            the primary style name
+		 */
+        protected void updateStyleNames(String primaryStyleName) {
+            hTableWrapper.setClassName(primaryStyleName + "-footer");
+            setStyleName(primaryStyleName + "-footer-wrap");
+            for (FooterCell c : availableCells.values()) {
+                c.updateStyleNames(primaryStyleName);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Panel#clear()
+         */
+        @Override
+        public void clear() {
+            for (String cid : availableCells.keySet()) {
+                removeCell(cid);
+            }
+            availableCells.clear();
+            availableCells.put(ROW_HEADER_COLUMN_KEY,
+                    new RowHeadersFooterCell());
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * com.google.gwt.user.client.ui.Panel#remove(com.google.gwt.user.client
+         * .ui.Widget)
+         */
+
+        @Override
+        public boolean remove(Widget w) {
+            if (visibleCells.contains(w)) {
+                visibleCells.remove(w);
+                orphan(w);
+                DOM.removeChild(DOM.getParent(w.getElement()), w.getElement());
+                return true;
+            }
+            return false;
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see com.google.gwt.user.client.ui.HasWidgets#iterator()
+         */
+
+        @Override
+        public Iterator<Widget> iterator() {
+            return visibleCells.iterator();
+        }
+
+        /** Gets a footer cell which represents the given columnId.
+		 *
+		 * @param cid
+		 *            The columnId
+		 * @return The cell
+		 */
+        public FooterCell getFooterCell(String cid) {
+            return availableCells.get(cid);
+        }
+
+        /** Gets a footer cell by using a column index.
+		 *
+		 * @param index
+		 *            The index of the column
+		 * @return The Cell
+		 */
+        public FooterCell getFooterCell(int index) {
+            if (index < visibleCells.size()) {
+                return (FooterCell) visibleCells.get(index);
+            } else {
+                return null;
+            }
+        }
+
+        /** Updates the cells contents when updateUIDL request is received.
+		 *
+		 * @param uidl
+		 *            The UIDL
+		 */
+        public void updateCellsFromUIDL(UIDL uidl) {
+            Iterator<?> columnIterator = uidl.getChildIterator();
+            HashSet<String> updated = new HashSet<String>();
+            while (columnIterator.hasNext()) {
+                final UIDL col = (UIDL) columnIterator.next();
+                final String cid = col.getStringAttribute("cid");
+                updated.add(cid);
+
+                String caption = col.hasAttribute("fcaption") ? col
+                        .getStringAttribute("fcaption") : "";
+                FooterCell c = getFooterCell(cid);
+                if (c == null) {
+                    c = new FooterCell(cid, caption);
+                    availableCells.put(cid, c);
+                    if (initializedAndAttached) {
+                        // we will need a column width recalculation
+                        initializedAndAttached = false;
+                        initialContentReceived = false;
+                        isNewBody = true;
+                    }
+                } else {
+                    c.setText(caption);
+                }
+
+                if (col.hasAttribute("align")) {
+                    c.setAlign(col.getStringAttribute("align").charAt(0));
+                } else {
+                    c.setAlign(ALIGN_LEFT);
+
+                }
+                if (col.hasAttribute("width")) {
+                    if (scrollBody == null || isNewBody) {
+                        // Already updated by setColWidth called from
+                        // TableHeads.updateCellsFromUIDL in case of a server
+                        // side resize
+                        final int width = col.getIntAttribute("width");
+                        c.setWidth(width, true);
+                    }
+                } else if (recalcWidths) {
+                    c.setUndefinedWidth();
+                }
+                if (col.hasAttribute("er")) {
+                    c.setExpandRatio(col.getFloatAttribute("er"));
+                }
+                if (col.hasAttribute("collapsed")) {
+                    // ensure header is properly removed from parent (case when
+                    // collapsing happens via servers side api)
+                    if (c.isAttached()) {
+                        c.removeFromParent();
+                        headerChangedDuringUpdate = true;
+                    }
+                }
+            }
+
+            // check for orphaned header cells
+            for (Iterator<String> cit = availableCells.keySet().iterator(); cit
+                    .hasNext();) {
+                String cid = cit.next();
+                if (!updated.contains(cid)) {
+                    removeCell(cid);
+                    cit.remove();
+                }
+            }
+        }
+
+        /** Set a footer cell for a specified column index.
+		 *
+		 * @param index
+		 *            The index
+		 * @param cell
+		 *            The footer cell
+		 */
+        public void setFooterCell(int index, FooterCell cell) {
+            if (cell.isEnabled()) {
+                // we're moving the cell
+                DOM.removeChild(tr, cell.getElement());
+                orphan(cell);
+                visibleCells.remove(cell);
+            }
+            if (index < visibleCells.size()) {
+                // insert to right slot
+                DOM.insertChild(tr, cell.getElement(), index);
+                adopt(cell);
+                visibleCells.add(index, cell);
+            } else if (index == visibleCells.size()) {
+                // simply append
+                DOM.appendChild(tr, cell.getElement());
+                adopt(cell);
+                visibleCells.add(cell);
+            } else {
+                throw new RuntimeException(
+                        "Header cells must be appended in order");
+            }
+        }
+
+        /** Remove a cell by using the columnId.
+		 *
+		 * @param colKey
+		 *            The columnId to remove
+		 */
+        public void removeCell(String colKey) {
+            final FooterCell c = getFooterCell(colKey);
+            remove(c);
+        }
+
+        /** Enable a column (Sets the footer cell).
+		 *
+		 * @param cid
+		 *            The columnId
+		 * @param index
+		 *            The index of the column
+		 */
+        public void enableColumn(String cid, int index) {
+            final FooterCell c = getFooterCell(cid);
+            if (!c.isEnabled() || getFooterCell(index) != c) {
+                setFooterCell(index, c);
+                if (initializedAndAttached) {
+                    headerChangedDuringUpdate = true;
+                }
+            }
+        }
+
+        /** Disable browser measurement of the table width.
+		 */
+        public void disableBrowserIntelligence() {
+            hTableContainer.getStyle().setWidth(WRAPPER_WIDTH, Unit.PX);
+        }
+
+        /** Enable browser measurement of the table width.
+		 */
+        public void enableBrowserIntelligence() {
+            hTableContainer.getStyle().clearWidth();
+        }
+
+        /**
+         * Set the horizontal position in the cell in the footer. This is done
+         * when a horizontal scrollbar is present.
+         * 
+         * @param scrollLeft
+         *            The value of the leftScroll
+         */
+        public void setHorizontalScrollPosition(int scrollLeft) {
+            hTableWrapper.setScrollLeft(scrollLeft);
+        }
+
+        /** Swap cells when the column are dragged.
+		 *
+		 * @param oldIndex
+		 *            The old index of the cell
+		 * @param newIndex
+		 *            The new index of the cell
+		 */
+        public void moveCell(int oldIndex, int newIndex) {
+            final FooterCell hCell = getFooterCell(oldIndex);
+            final Element cell = hCell.getElement();
+
+            visibleCells.remove(oldIndex);
+            DOM.removeChild(tr, cell);
+
+            DOM.insertChild(tr, cell, newIndex);
+            visibleCells.add(newIndex, hCell);
+        }
+    }
+
+    /**
+     * This Panel can only contain VScrollTableRow type of widgets. This
+     * "simulates" very large table, keeping spacers which take room of
+     * unrendered rows.
+     * 
+     */
+    public class VScrollTableBody extends Panel {
+
+        /** The Constant DEFAULT_ROW_HEIGHT. */
+        public static final int DEFAULT_ROW_HEIGHT = 24;
+
+        /** The row height. */
+        private double rowHeight = -1;
+
+        /** The rendered rows. */
+        private final LinkedList<Widget> renderedRows = new LinkedList<Widget>();
+
+        /**
+         * Due some optimizations row height measuring is deferred and initial
+         * set of rows is rendered detached. Flag set on when table body has
+         * been attached in dom and rowheight has been measured.
+         */
+        private boolean tBodyMeasurementsDone = false;
+
+        /** The pre spacer. */
+        Element preSpacer = DOM.createDiv();
+        
+        /** The post spacer. */
+        Element postSpacer = DOM.createDiv();
+
+        /** The container. */
+        Element container = DOM.createDiv();
+
+        /** The t body element. */
+        TableSectionElement tBodyElement = Document.get().createTBodyElement();
+        
+        /** The table. */
+        Element table = DOM.createTable();
+
+        /** The first rendered. */
+        private int firstRendered;
+        
+        /** The last rendered. */
+        private int lastRendered;
+
+        /** The aligns. */
+        private char[] aligns;
+
+        /** Instantiates a new v scroll table body.
+		 */
+        protected VScrollTableBody() {
+            constructDOM();
+            setElement(container);
+        }
+
+        /** Sets the last rendered.
+		 *
+		 * @param lastRendered
+		 *            the new last rendered
+		 */
+        public void setLastRendered(int lastRendered) {
+            if (totalRows >= 0 && lastRendered > totalRows) {
+                VConsole.log("setLastRendered: " + this.lastRendered + " -> "
+                        + lastRendered);
+                this.lastRendered = totalRows - 1;
+            } else {
+                this.lastRendered = lastRendered;
+            }
+        }
+
+        /** Gets the last rendered.
+		 *
+		 * @return the last rendered
+		 */
+        public int getLastRendered() {
+            return lastRendered;
+        }
+
+        /** Gets the first rendered.
+		 *
+		 * @return the first rendered
+		 */
+        public int getFirstRendered() {
+            return firstRendered;
+        }
+
+        /** Gets the row by row index.
+		 *
+		 * @param indexInTable
+		 *            the index in table
+		 * @return the row by row index
+		 */
+        public VScrollTableRow getRowByRowIndex(int indexInTable) {
+            int internalIndex = indexInTable - firstRendered;
+            if (internalIndex >= 0 && internalIndex < renderedRows.size()) {
+                return (VScrollTableRow) renderedRows.get(internalIndex);
+            } else {
+                return null;
+            }
+        }
+
+        /** Gets the required height.
+		 *
+		 * @return the height of scrollable body, subpixels ceiled.
+		 */
+        public int getRequiredHeight() {
+            return preSpacer.getOffsetHeight() + postSpacer.getOffsetHeight()
+                    + Util.getRequiredHeight(table);
+        }
+
+        /** Construct dom.
+		 */
+        private void constructDOM() {
+            if (BrowserInfo.get().isIE()) {
+                table.setPropertyInt("cellSpacing", 0);
+            }
+
+            table.appendChild(tBodyElement);
+            DOM.appendChild(container, preSpacer);
+            DOM.appendChild(container, table);
+            DOM.appendChild(container, postSpacer);
+            if (BrowserInfo.get().requiresTouchScrollDelegate()) {
+                NodeList<Node> childNodes = container.getChildNodes();
+                for (int i = 0; i < childNodes.getLength(); i++) {
+                    Element item = (Element) childNodes.getItem(i);
+                    item.getStyle().setProperty("webkitTransform",
+                            "translate3d(0,0,0)");
+                }
+            }
+            updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+        }
+
+        /** Update style names.
+		 *
+		 * @param primaryStyleName
+		 *            the primary style name
+		 */
+        protected void updateStyleNames(String primaryStyleName) {
+            table.setClassName(primaryStyleName + "-table");
+            preSpacer.setClassName(primaryStyleName + "-row-spacer");
+            postSpacer.setClassName(primaryStyleName + "-row-spacer");
+            for (Widget w : renderedRows) {
+                VScrollTableRow row = (VScrollTableRow) w;
+                row.updateStyleNames(primaryStyleName);
+            }
+        }
+
+        /** Gets the available width.
+		 *
+		 * @return the available width
+		 */
+        public int getAvailableWidth() {
+            int availW = scrollBodyPanel.getOffsetWidth() - getBorderWidth();
+            return availW;
+        }
+
+        /** Render initial rows.
+		 *
+		 * @param rowData
+		 *            the row data
+		 * @param firstIndex
+		 *            the first index
+		 * @param rows
+		 *            the rows
+		 */
+        public void renderInitialRows(UIDL rowData, int firstIndex, int rows) {
+            firstRendered = firstIndex;
+            setLastRendered(firstIndex + rows - 1);
+            final Iterator<?> it = rowData.getChildIterator();
+            aligns = tHead.getColumnAlignments();
+            while (it.hasNext()) {
+                final VScrollTableRow row = createRow((UIDL) it.next(), aligns);
+                addRow(row);
+            }
+            if (isAttached()) {
+                fixSpacers();
+            }
+        }
+
+        /** Render rows.
+		 *
+		 * @param rowData
+		 *            the row data
+		 * @param firstIndex
+		 *            the first index
+		 * @param rows
+		 *            the rows
+		 */
+        public void renderRows(UIDL rowData, int firstIndex, int rows) {
+            // FIXME REVIEW
+            aligns = tHead.getColumnAlignments();
+            final Iterator<?> it = rowData.getChildIterator();
+            if (firstIndex == lastRendered + 1) {
+                while (it.hasNext()) {
+                    final VScrollTableRow row = prepareRow((UIDL) it.next());
+                    addRow(row);
+                    setLastRendered(lastRendered + 1);
+                }
+                fixSpacers();
+            } else if (firstIndex + rows == firstRendered) {
+                final VScrollTableRow[] rowArray = new VScrollTableRow[rows];
+                int i = rows;
+                while (it.hasNext()) {
+                    i--;
+                    rowArray[i] = prepareRow((UIDL) it.next());
+                }
+                for (i = 0; i < rows; i++) {
+                    addRowBeforeFirstRendered(rowArray[i]);
+                    firstRendered--;
+                }
+            } else {
+                // completely new set of rows
+
+                // there can't be sanity checks for last rendered within this
+                // while loop regardless of what has been set previously, so
+                // change it temporarily to true and then return the original
+                // value
+                boolean temp = postponeSanityCheckForLastRendered;
+                postponeSanityCheckForLastRendered = true;
+                while (lastRendered + 1 > firstRendered) {
+                    unlinkRow(false);
+                }
+                postponeSanityCheckForLastRendered = temp;
+                VScrollTableRow row = prepareRow((UIDL) it.next());
+                firstRendered = firstIndex;
+                setLastRendered(firstIndex - 1);
+                addRow(row);
+                setLastRendered(lastRendered + 1);
+                setContainerHeight();
+                fixSpacers();
+                while (it.hasNext()) {
+                    addRow(prepareRow((UIDL) it.next()));
+                    setLastRendered(lastRendered + 1);
+                }
+                fixSpacers();
+            }
+
+            // this may be a new set of rows due content change,
+            // ensure we have proper cache rows
+            ensureCacheFilled();
+        }
+
+        /**
+         * Ensure we have the correct set of rows on client side, e.g. if the
+         * content on the server side has changed, or the client scroll position
+         * has changed since the last request.
+         */
+        protected void ensureCacheFilled() {
+            int reactFirstRow = (int) (firstRowInViewPort - pageLength
+                    * cache_react_rate);
+            int reactLastRow = (int) (firstRowInViewPort + pageLength + pageLength
+                    * cache_react_rate);
+            if (reactFirstRow < 0) {
+                reactFirstRow = 0;
+            }
+            if (reactLastRow >= totalRows) {
+                reactLastRow = totalRows - 1;
+            }
+            if (lastRendered < reactFirstRow || firstRendered > reactLastRow) {
+                /*
+                 * #8040 - scroll position is completely changed since the
+                 * latest request, so request a new set of rows.
+                 * 
+                 * TODO: We should probably check whether the fetched rows match
+                 * the current scroll position right when they arrive, so as to
+                 * not waste time rendering a set of rows that will never be
+                 * visible...
+                 */
+                rowRequestHandler.triggerRowFetch(reactFirstRow, reactLastRow
+                        - reactFirstRow + 1, 1);
+            } else if (lastRendered < reactLastRow) {
+                // get some cache rows below visible area
+                rowRequestHandler.triggerRowFetch(lastRendered + 1,
+                        reactLastRow - lastRendered, 1);
+            } else if (firstRendered > reactFirstRow) {
+                /*
+                 * Branch for fetching cache above visible area.
+                 * 
+                 * If cache needed for both before and after visible area, this
+                 * will be rendered after-cache is received and rendered. So in
+                 * some rare situations the table may make two cache visits to
+                 * server.
+                 */
+                rowRequestHandler.triggerRowFetch(reactFirstRow, firstRendered
+                        - reactFirstRow, 1);
+            }
+        }
+
+        /** Inserts rows as provided in the rowData starting at firstIndex.
+		 *
+		 * @param rowData
+		 *            the row data
+		 * @param firstIndex
+		 *            the first index
+		 * @param rows
+		 *            the number of rows
+		 * @return a list of the rows added.
+		 */
+        protected List<VScrollTableRow> insertRows(UIDL rowData,
+                int firstIndex, int rows) {
+            aligns = tHead.getColumnAlignments();
+            final Iterator<?> it = rowData.getChildIterator();
+            List<VScrollTableRow> insertedRows = new ArrayList<VScrollTableRow>();
+
+            if (firstIndex == lastRendered + 1) {
+                while (it.hasNext()) {
+                    final VScrollTableRow row = prepareRow((UIDL) it.next());
+                    addRow(row);
+                    insertedRows.add(row);
+                    if (postponeSanityCheckForLastRendered) {
+                        lastRendered++;
+                    } else {
+                        setLastRendered(lastRendered + 1);
+                    }
+                }
+                fixSpacers();
+            } else if (firstIndex + rows == firstRendered) {
+                final VScrollTableRow[] rowArray = new VScrollTableRow[rows];
+                int i = rows;
+                while (it.hasNext()) {
+                    i--;
+                    rowArray[i] = prepareRow((UIDL) it.next());
+                }
+                for (i = 0; i < rows; i++) {
+                    addRowBeforeFirstRendered(rowArray[i]);
+                    insertedRows.add(rowArray[i]);
+                    firstRendered--;
+                }
+            } else {
+                // insert in the middle
+                int ix = firstIndex;
+                while (it.hasNext()) {
+                    VScrollTableRow row = prepareRow((UIDL) it.next());
+                    insertRowAt(row, ix);
+                    insertedRows.add(row);
+                    if (postponeSanityCheckForLastRendered) {
+                        lastRendered++;
+                    } else {
+                        setLastRendered(lastRendered + 1);
+                    }
+                    ix++;
+                }
+                fixSpacers();
+            }
+            return insertedRows;
+        }
+
+        /** Insert and reindex rows.
+		 *
+		 * @param rowData
+		 *            the row data
+		 * @param firstIndex
+		 *            the first index
+		 * @param rows
+		 *            the rows
+		 * @return the list
+		 */
+        protected List<VScrollTableRow> insertAndReindexRows(UIDL rowData,
+                int firstIndex, int rows) {
+            List<VScrollTableRow> inserted = insertRows(rowData, firstIndex,
+                    rows);
+            int actualIxOfFirstRowAfterInserted = firstIndex + rows
+                    - firstRendered;
+            for (int ix = actualIxOfFirstRowAfterInserted; ix < renderedRows
+                    .size(); ix++) {
+                VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
+                r.setIndex(r.getIndex() + rows);
+            }
+            setContainerHeight();
+            return inserted;
+        }
+
+        /** Insert rows delete below.
+		 *
+		 * @param rowData
+		 *            the row data
+		 * @param firstIndex
+		 *            the first index
+		 * @param rows
+		 *            the rows
+		 */
+        protected void insertRowsDeleteBelow(UIDL rowData, int firstIndex,
+                int rows) {
+            unlinkAllRowsStartingAt(firstIndex);
+            insertRows(rowData, firstIndex, rows);
+            setContainerHeight();
+        }
+
+        /** This method is used to instantiate new rows for this table. It
+		 * automatically sets correct widths to rows cells and assigns correct
+		 * client reference for child widgets.
+		 * 
+		 * This method can be called only after table has been initialized
+		 *
+		 * @param uidl
+		 *            the uidl
+		 * @return the v scroll table row
+		 */
+        private VScrollTableRow prepareRow(UIDL uidl) {
+            final VScrollTableRow row = createRow(uidl, aligns);
+            row.initCellWidths();
+            return row;
+        }
+
+        /** Creates the row.
+		 *
+		 * @param uidl
+		 *            the uidl
+		 * @param aligns2
+		 *            the aligns2
+		 * @return the v scroll table row
+		 */
+        protected VScrollTableRow createRow(UIDL uidl, char[] aligns2) {
+            if (uidl.hasAttribute("gen_html")) {
+                // This is a generated row.
+                return new VScrollTableGeneratedRow(uidl, aligns2);
+            }
+            return new VScrollTableRow(uidl, aligns2);
+        }
+
+        /** Adds the row before first rendered.
+		 *
+		 * @param row
+		 *            the row
+		 */
+        private void addRowBeforeFirstRendered(VScrollTableRow row) {
+            row.setIndex(firstRendered - 1);
+            if (row.isSelected()) {
+                row.addStyleName("v-selected");
+            }
+            tBodyElement.insertBefore(row.getElement(),
+                    tBodyElement.getFirstChild());
+            adopt(row);
+            renderedRows.add(0, row);
+        }
+
+        /** Adds the row.
+		 *
+		 * @param row
+		 *            the row
+		 */
+        private void addRow(VScrollTableRow row) {
+            row.setIndex(firstRendered + renderedRows.size());
+            if (row.isSelected()) {
+                row.addStyleName("v-selected");
+            }
+            tBodyElement.appendChild(row.getElement());
+            // Add to renderedRows before adopt so iterator() will return also
+            // this row if called in an attach handler (#9264)
+            renderedRows.add(row);
+            adopt(row);
+        }
+
+        /** Insert row at.
+		 *
+		 * @param row
+		 *            the row
+		 * @param index
+		 *            the index
+		 */
+        private void insertRowAt(VScrollTableRow row, int index) {
+            row.setIndex(index);
+            if (row.isSelected()) {
+                row.addStyleName("v-selected");
+            }
+            if (index > 0) {
+                VScrollTableRow sibling = getRowByRowIndex(index - 1);
+                tBodyElement
+                        .insertAfter(row.getElement(), sibling.getElement());
+            } else {
+                VScrollTableRow sibling = getRowByRowIndex(index);
+                tBodyElement.insertBefore(row.getElement(),
+                        sibling.getElement());
+            }
+            adopt(row);
+            int actualIx = index - firstRendered;
+            renderedRows.add(actualIx, row);
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.HasWidgets#iterator()
+         */
+        @Override
+        public Iterator<Widget> iterator() {
+            return renderedRows.iterator();
+        }
+
+        /** Unlink row.
+		 *
+		 * @param fromBeginning
+		 *            the from beginning
+		 * @return false if couldn't remove row
+		 */
+        protected boolean unlinkRow(boolean fromBeginning) {
+            if (lastRendered - firstRendered < 0) {
+                return false;
+            }
+            int actualIx;
+            if (fromBeginning) {
+                actualIx = 0;
+                firstRendered++;
+            } else {
+                actualIx = renderedRows.size() - 1;
+                if (postponeSanityCheckForLastRendered) {
+                    --lastRendered;
+                } else {
+                    setLastRendered(lastRendered - 1);
+                }
+            }
+            if (actualIx >= 0) {
+                unlinkRowAtActualIndex(actualIx);
+                fixSpacers();
+                return true;
+            }
+            return false;
+        }
+
+        /** Unlink rows.
+		 *
+		 * @param firstIndex
+		 *            the first index
+		 * @param count
+		 *            the count
+		 */
+        protected void unlinkRows(int firstIndex, int count) {
+            if (count < 1) {
+                return;
+            }
+            if (firstRendered > firstIndex
+                    && firstRendered < firstIndex + count) {
+                count = count - (firstRendered - firstIndex);
+                firstIndex = firstRendered;
+            }
+            int lastIndex = firstIndex + count - 1;
+            if (lastRendered < lastIndex) {
+                lastIndex = lastRendered;
+            }
+            for (int ix = lastIndex; ix >= firstIndex; ix--) {
+                unlinkRowAtActualIndex(actualIndex(ix));
+                if (postponeSanityCheckForLastRendered) {
+                    // partialUpdate handles sanity check later
+                    lastRendered--;
+                } else {
+                    setLastRendered(lastRendered - 1);
+                }
+            }
+            fixSpacers();
+        }
+
+        /** Unlink and reindex rows.
+		 *
+		 * @param firstIndex
+		 *            the first index
+		 * @param count
+		 *            the count
+		 */
+        protected void unlinkAndReindexRows(int firstIndex, int count) {
+            unlinkRows(firstIndex, count);
+            int actualFirstIx = firstIndex - firstRendered;
+            for (int ix = actualFirstIx; ix < renderedRows.size(); ix++) {
+                VScrollTableRow r = (VScrollTableRow) renderedRows.get(ix);
+                r.setIndex(r.getIndex() - count);
+            }
+            setContainerHeight();
+        }
+
+        /** Unlink all rows starting at.
+		 *
+		 * @param index
+		 *            the index
+		 */
+        protected void unlinkAllRowsStartingAt(int index) {
+            if (firstRendered > index) {
+                index = firstRendered;
+            }
+            for (int ix = renderedRows.size() - 1; ix >= index; ix--) {
+                unlinkRowAtActualIndex(actualIndex(ix));
+                setLastRendered(lastRendered - 1);
+            }
+            fixSpacers();
+        }
+
+        /** Actual index.
+		 *
+		 * @param index
+		 *            the index
+		 * @return the int
+		 */
+        private int actualIndex(int index) {
+            return index - firstRendered;
+        }
+
+        /** Unlink row at actual index.
+		 *
+		 * @param index
+		 *            the index
+		 */
+        private void unlinkRowAtActualIndex(int index) {
+            final VScrollTableRow toBeRemoved = (VScrollTableRow) renderedRows
+                    .get(index);
+            tBodyElement.removeChild(toBeRemoved.getElement());
+            orphan(toBeRemoved);
+            renderedRows.remove(index);
+        }
+
+        /* (non-Javadoc)
+         * @see com.google.gwt.user.client.ui.Panel#remove(com.google.gwt.user.client.ui.Widget)
+         */
+        @Override
+        public boolean remove(Widget w) {
+            throw new UnsupportedOperationException();
+        }
+
+        /** Fix container blocks height according to totalRows to avoid
+		 * "bouncing" when scrolling.
+		 */
+        private void setContainerHeight() {
+            fixSpacers();
+            container.getStyle().setHeight(measureRowHeightOffset(totalRows),
+                    Unit.PX);
+        }
+
+        /** Fix spacers.
+		 */
+        private void fixSpacers() {
+            int prepx = measureRowHeightOffset(firstRendered);
+            if (prepx < 0) {
+                prepx = 0;
+            }
+            preSpacer.getStyle().setPropertyPx("height", prepx);
+            int postpx;
+            if (pageLength == 0 && totalRows == pageLength) {
+                /*
+                 * TreeTable depends on having lastRendered out of sync in some
+                 * situations, which makes this method miss the special
+                 * situation in which one row worth of post spacer to be added
+                 * if there are no rows in the table. #9203
+                 */
+                postpx = measureRowHeightOffset(1);
+            } else {
+                postpx = measureRowHeightOffset(totalRows - 1)
+                        - measureRowHeightOffset(lastRendered);
+            }
+
+            if (postpx < 0) {
+                postpx = 0;
+            }
+            postSpacer.getStyle().setPropertyPx("height", postpx);
+        }
+
+        /** Gets the row height.
+		 *
+		 * @return the row height
+		 */
+        public double getRowHeight() {
+            return getRowHeight(false);
+        }
+
+        /** Gets the row height.
+		 *
+		 * @param forceUpdate
+		 *            the force update
+		 * @return the row height
+		 */
+        public double getRowHeight(boolean forceUpdate) {
+            if (tBodyMeasurementsDone && !forceUpdate) {
+                return rowHeight;
+            } else {
+                if (tBodyElement.getRows().getLength() > 0) {
+                    int tableHeight = getTableHeight();
+                    int rowCount = tBodyElement.getRows().getLength();
+                    rowHeight = tableHeight / (double) rowCount;
+                } else {
+                    // Special cases if we can't just measure the current rows
+                    if (!Double.isNaN(lastKnownRowHeight)) {
+                        // Use previous value if available
+                        if (BrowserInfo.get().isIE()) {
+                            /*
+                             * IE needs to reflow the table element at this
+                             * point to work correctly (e.g.
+                             * com.vaadin.tests.components.table.
+                             * ContainerSizeChange) - the other code paths
+                             * already trigger reflows, but here it must be done
+                             * explicitly.
+                             */
+                            getTableHeight();
+                        }
+                        rowHeight = lastKnownRowHeight;
+                    } else if (isAttached()) {
+                        // measure row height by adding a dummy row
+                        VScrollTableRow scrollTableRow = new VScrollTableRow();
+                        tBodyElement.appendChild(scrollTableRow.getElement());
+                        getRowHeight(forceUpdate);
+                        tBodyElement.removeChild(scrollTableRow.getElement());
+                    } else {
+                        // TODO investigate if this can never happen anymore
+                        return DEFAULT_ROW_HEIGHT;
+                    }
+                }
+                lastKnownRowHeight = rowHeight;
+                tBodyMeasurementsDone = true;
+                return rowHeight;
+            }
+        }
+
+        /** Gets the table height.
+		 *
+		 * @return the table height
+		 */
+        public int getTableHeight() {
+            return table.getOffsetHeight();
+        }
+
+        /** Returns the width available for column content.
+		 *
+		 * @param columnIndex
+		 *            the column index
+		 * @return the col width
+		 */
+        public int getColWidth(int columnIndex) {
+            if (tBodyMeasurementsDone) {
+                if (renderedRows.isEmpty()) {
+                    // no rows yet rendered
+                    return 0;
+                }
+                for (Widget row : renderedRows) {
+                    if (!(row instanceof VScrollTableGeneratedRow)) {
+                        TableRowElement tr = row.getElement().cast();
+                        Element wrapperdiv = tr.getCells().getItem(columnIndex)
+                                .getFirstChildElement().cast();
+                        return wrapperdiv.getOffsetWidth();
+                    }
+                }
+                return 0;
+            } else {
+                return 0;
+            }
+        }
+
+        /** Sets the content width of a column.
+		 * 
+		 * Due IE limitation, we must set the width to a wrapper elements inside
+		 * table cells (with overflow hidden, which does not work on td
+		 * elements).
+		 * 
+		 * To get this work properly crossplatform, we will also set the width
+		 * of td.
+		 *
+		 * @param colIndex
+		 *            the col index
+		 * @param w
+		 *            the w
+		 */
+        public void setColWidth(int colIndex, int w) {
+            for (Widget row : renderedRows) {
+                ((VScrollTableRow) row).setCellWidth(colIndex, w);
+            }
+        }
+
+        /** The cell extra width. */
+        private int cellExtraWidth = -1;
+
+        /** Method to return the space used for cell paddings + border.
+		 *
+		 * @return the cell extra width
+		 */
+        private int getCellExtraWidth() {
+            if (cellExtraWidth < 0) {
+                detectExtrawidth();
+            }
+            return cellExtraWidth;
+        }
+
+        /** This method exists for the needs of {@link VTreeTable} only. May
+		 * be removed or replaced in the future.<br> <br> Returns the maximum
+		 * indent of the hierarcyColumn, if applicable.
+		 *
+		 * @return maximum indent in pixels
+		 * @see #getHierarchyColumnIndex()
+		 */
+        protected int getMaxIndent() {
+            return 0;
+        }
+
+        /**
+         * This method exists for the needs of {@link VTreeTable} only. May be
+         * removed or replaced in the future.<br> <br> Calculates the maximum
+         * indent of the hierarcyColumn, if applicable.
+         */
+        protected void calculateMaxIndent() {
+            // NOP
+        }
+
+        /** Detect extrawidth.
+		 */
+        private void detectExtrawidth() {
+            NodeList<TableRowElement> rows = tBodyElement.getRows();
+            if (rows.getLength() == 0) {
+                /* need to temporary add empty row and detect */
+                VScrollTableRow scrollTableRow = new VScrollTableRow();
+                scrollTableRow.updateStyleNames(VCustomScrollTable.this
+                        .getStylePrimaryName());
+                tBodyElement.appendChild(scrollTableRow.getElement());
+                detectExtrawidth();
+                tBodyElement.removeChild(scrollTableRow.getElement());
+            } else {
+                boolean noCells = false;
+                TableRowElement item = rows.getItem(0);
+                TableCellElement firstTD = item.getCells().getItem(0);
+                if (firstTD == null) {
+                    // content is currently empty, we need to add a fake cell
+                    // for measuring
+                    noCells = true;
+                    VScrollTableRow next = (VScrollTableRow) iterator().next();
+                    boolean sorted = tHead.getHeaderCell(0) != null ? tHead
+                            .getHeaderCell(0).isSorted() : false;
+                    next.addCell(null, "", ALIGN_LEFT, "", true, sorted);
+                    firstTD = item.getCells().getItem(0);
+                }
+                com.google.gwt.dom.client.Element wrapper = firstTD
+                        .getFirstChildElement();
+                cellExtraWidth = firstTD.getOffsetWidth()
+                        - wrapper.getOffsetWidth();
+                if (noCells) {
+                    firstTD.getParentElement().removeChild(firstTD);
+                }
+            }
+        }
+
+        /** Move col.
+		 *
+		 * @param oldIndex
+		 *            the old index
+		 * @param newIndex
+		 *            the new index
+		 */
+        public void moveCol(int oldIndex, int newIndex) {
+
+            // loop all rows and move given index to its new place
+            final Iterator<?> rows = iterator();
+            while (rows.hasNext()) {
+                final VScrollTableRow row = (VScrollTableRow) rows.next();
+
+                final Element td = DOM.getChild(row.getElement(), oldIndex);
+                if (td != null) {
+                    DOM.removeChild(row.getElement(), td);
+
+                    DOM.insertChild(row.getElement(), td, newIndex);
+                }
+            }
+
+        }
+
+        /**
+         * Restore row visibility which is set to "none" when the row is
+         * rendered (due a performance optimization).
+         */
+        private void restoreRowVisibility() {
+            for (Widget row : renderedRows) {
+                row.getElement().getStyle().setProperty("visibility", "");
+            }
+        }
+
+        /** Index of.
+		 *
+		 * @param row
+		 *            the row
+		 * @return the int
+		 */
+        public int indexOf(Widget row) {
+            int relIx = -1;
+            for (int ix = 0; ix < renderedRows.size(); ix++) {
+                if (renderedRows.get(ix) == row) {
+                    relIx = ix;
+                    break;
+                }
+            }
+            if (relIx >= 0) {
+                return firstRendered + relIx;
+            }
+            return -1;
+        }
+
+        /** The Class VScrollTableRow.
+		 */
+        public class VScrollTableRow extends Panel implements ActionOwner,
+                ContextMenuOwner {
+
+            /** The Constant TOUCHSCROLL_TIMEOUT. */
+            private static final int TOUCHSCROLL_TIMEOUT = 100;
+            
+            /** The Constant DRAGMODE_MULTIROW. */
+            private static final int DRAGMODE_MULTIROW = 2;
+            
+            /** The child widgets. */
+            protected ArrayList<Widget> childWidgets = new ArrayList<Widget>();
+            
+            /** The selected. */
+            private boolean selected = false;
+            
+            /** The row key. */
+            protected final int rowKey;
+
+            /** The action keys. */
+            private String[] actionKeys = null;
+            
+            /** The row element. */
+            private final TableRowElement rowElement;
+            
+            /** The index. */
+            private int index;
+            
+            /** The touch start. */
+            private Event touchStart;
+
+            /** The Constant TOUCH_CONTEXT_MENU_TIMEOUT. */
+            private static final int TOUCH_CONTEXT_MENU_TIMEOUT = 500;
+            
+            /** The context touch timeout. */
+            private Timer contextTouchTimeout;
+            
+            /** The drag touch timeout. */
+            private Timer dragTouchTimeout;
+            
+            /** The touch start y. */
+            private int touchStartY;
+            
+            /** The touch start x. */
+            private int touchStartX;
+
+            /** The touch context provider. */
+            private TouchContextProvider touchContextProvider = new TouchContextProvider(
+                    this);
+
+            /** The tooltip info. */
+            private TooltipInfo tooltipInfo = null;
+            
+            /** The cell tool tips. */
+            private Map<TableCellElement, TooltipInfo> cellToolTips = new HashMap<TableCellElement, TooltipInfo>();
+            
+            /** The is dragging. */
+            private boolean isDragging = false;
+            
+            /** The row style. */
+            private String rowStyle = null;
+
+            /** Instantiates a new v scroll table row.
+			 *
+			 * @param rowKey
+			 *            the row key
+			 */
+            private VScrollTableRow(int rowKey) {
+                this.rowKey = rowKey;
+                rowElement = Document.get().createTRElement();
+                setElement(rowElement);
+                DOM.sinkEvents(getElement(), Event.MOUSEEVENTS
+                        | Event.TOUCHEVENTS | Event.ONDBLCLICK
+                        | Event.ONCONTEXTMENU | VTooltip.TOOLTIP_EVENTS);
+            }
+
+            /** Instantiates a new v scroll table row.
+			 *
+			 * @param uidl
+			 *            the uidl
+			 * @param aligns
+			 *            the aligns
+			 */
+            public VScrollTableRow(UIDL uidl, char[] aligns) {
+                this(uidl.getIntAttribute("key"));
+
+                /*
+                 * Rendering the rows as hidden improves Firefox and Safari
+                 * performance drastically.
+                 */
+                getElement().getStyle().setProperty("visibility", "hidden");
+
+                rowStyle = uidl.getStringAttribute("rowstyle");
+                updateStyleNames(VCustomScrollTable.this.getStylePrimaryName());
+
+                String rowDescription = uidl.getStringAttribute("rowdescr");
+                if (rowDescription != null && !rowDescription.equals("")) {
+                    tooltipInfo = new TooltipInfo(rowDescription, null, this);
+                } else {
+                    tooltipInfo = null;
+                }
+
+                tHead.getColumnAlignments();
+                int col = 0;
+                int visibleColumnIndex = -1;
+
+                // row header
+                if (showRowHeaders) {
+                    boolean sorted = tHead.getHeaderCell(col).isSorted();
+                    addCell(uidl, buildCaptionHtmlSnippet(uidl), aligns[col++],
+                            "rowheader", true, sorted);
+                    visibleColumnIndex++;
+                }
+
+                if (uidl.hasAttribute("al")) {
+                    actionKeys = uidl.getStringArrayAttribute("al");
+                }
+
+                addCellsFromUIDL(uidl, aligns, col, visibleColumnIndex);
+
+                if (uidl.hasAttribute("selected") && !isSelected()) {
+                    toggleSelection();
+                }
+            }
+
+            /** Update style names.
+			 *
+			 * @param primaryStyleName
+			 *            the primary style name
+			 */
+            protected void updateStyleNames(String primaryStyleName) {
+
+                if (getStylePrimaryName().contains("odd")) {
+                    setStyleName(primaryStyleName + "-row-odd");
+                } else {
+                    setStyleName(primaryStyleName + "-row");
+                }
+
+                if (rowStyle != null) {
+                    addStyleName(primaryStyleName + "-row-" + rowStyle);
+                }
+
+                for (int i = 0; i < rowElement.getChildCount(); i++) {
+                    TableCellElement cell = (TableCellElement) rowElement
+                            .getChild(i);
+                    updateCellStyleNames(cell, primaryStyleName);
+                }
+            }
+
+            /** Gets the tooltip info.
+			 *
+			 * @return the tooltip info
+			 */
+            public TooltipInfo getTooltipInfo() {
+                return tooltipInfo;
+            }
+
+            /**
+             * Add a dummy row, used for measurements if Table is empty.
+             */
+            public VScrollTableRow() {
+                this(0);
+                addCell(null, "_", 'b', "", true, false);
+            }
+
+            /** Inits the cell widths.
+			 */
+            protected void initCellWidths() {
+                final int cells = tHead.getVisibleCellCount();
+                for (int i = 0; i < cells; i++) {
+                    int w = VCustomScrollTable.this
+                            .getColWidth(getColKeyByIndex(i));
+                    if (w < 0) {
+                        w = 0;
+                    }
+                    setCellWidth(i, w);
+                }
+            }
+
+            /** Sets the cell width.
+			 *
+			 * @param cellIx
+			 *            the cell ix
+			 * @param width
+			 *            the width
+			 */
+            protected void setCellWidth(int cellIx, int width) {
+                final Element cell = DOM.getChild(getElement(), cellIx);
+                Style wrapperStyle = cell.getFirstChildElement().getStyle();
+                int wrapperWidth = width;
+                if (BrowserInfo.get().isWebkit()
+                        || BrowserInfo.get().isOpera10()) {
+                    /*
+                     * Some versions of Webkit and Opera ignore the width
+                     * definition of zero width table cells. Instead, use 1px
+                     * and compensate with a negative margin.
+                     */
+                    if (width == 0) {
+                        wrapperWidth = 1;
+                        wrapperStyle.setMarginRight(-1, Unit.PX);
+                    } else {
+                        wrapperStyle.clearMarginRight();
+                    }
+                }
+                wrapperStyle.setPropertyPx("width", wrapperWidth);
+                cell.getStyle().setPropertyPx("width", width);
+            }
+
+            /** Adds the cells from uidl.
+			 *
+			 * @param uidl
+			 *            the uidl
+			 * @param aligns
+			 *            the aligns
+			 * @param col
+			 *            the col
+			 * @param visibleColumnIndex
+			 *            the visible column index
+			 */
+            protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
+                    int visibleColumnIndex) {
+                final Iterator<?> cells = uidl.getChildIterator();
+                while (cells.hasNext()) {
+                    final Object cell = cells.next();
+                    visibleColumnIndex++;
+
+                    String columnId = visibleColOrder[visibleColumnIndex];
+
+                    String style = "";
+                    if (uidl.hasAttribute("style-" + columnId)) {
+                        style = uidl.getStringAttribute("style-" + columnId);
+                    }
+
+                    String description = null;
+                    if (uidl.hasAttribute("descr-" + columnId)) {
+                        description = uidl.getStringAttribute("descr-"
+                                + columnId);
+                    }
+
+                    boolean sorted = tHead.getHeaderCell(col).isSorted();
+                    if (cell instanceof String) {
+                        addCell(uidl, cell.toString(), aligns[col++], style,
+                                isRenderHtmlInCells(), sorted, description);
+                    } else {
+                        final ComponentConnector cellContent = client
+                                .getPaintable((UIDL) cell);
+
+                        addCell(uidl, cellContent.getWidget(), aligns[col++],
+                                style, sorted, description);
+                    }
+                }
+            }
+
+            /**
+             * Overriding this and returning true causes all text cells to be
+             * rendered as HTML.
+             * 
+             * @return always returns false in the default implementation
+             */
+            protected boolean isRenderHtmlInCells() {
+                return false;
+            }
+
+            /** Detects whether row is visible in tables viewport.
+			 *
+			 * @return true, if is in view port
+			 */
+            public boolean isInViewPort() {
+                int absoluteTop = getAbsoluteTop();
+                int absoluteBottom = absoluteTop + getOffsetHeight();
+                int viewPortTop = scrollBodyPanel.getAbsoluteTop();
+                int viewPortBottom = viewPortTop
+                        + scrollBodyPanel.getOffsetHeight();
+                return absoluteBottom > viewPortTop
+                        && absoluteTop < viewPortBottom;
+            }
+
+            /** Makes a check based on indexes whether the row is before the
+			 * compared row.
+			 *
+			 * @param row1
+			 *            the row1
+			 * @return true if this rows index is smaller than in the row1
+			 */
+            public boolean isBefore(VScrollTableRow row1) {
+                return getIndex() < row1.getIndex();
+            }
+
+            /** Sets the index of the row in the whole table. Currently used
+			 * just to set even/odd classname
+			 *
+			 * @param indexInWholeTable
+			 *            the new index
+			 */
+            private void setIndex(int indexInWholeTable) {
+                index = indexInWholeTable;
+                boolean isOdd = indexInWholeTable % 2 == 0;
+                // Inverted logic to be backwards compatible with earlier 6.4.
+                // It is very strange because rows 1,3,5 are considered "even"
+                // and 2,4,6 "odd".
+                //
+                // First remove any old styles so that both styles aren't
+                // applied when indexes are updated.
+                String primaryStyleName = getStylePrimaryName();
+                if (primaryStyleName != null && !primaryStyleName.equals("")) {
+                    removeStyleName(getStylePrimaryName());
+                }
+                if (!isOdd) {
+                    addStyleName(VCustomScrollTable.this.getStylePrimaryName()
+                            + "-row-odd");
+                } else {
+                    addStyleName(VCustomScrollTable.this.getStylePrimaryName()
+                            + "-row");
+                }
+            }
+
+            /** Gets the index.
+			 *
+			 * @return the index
+			 */
+            public int getIndex() {
+                return index;
+            }
+
+            /* (non-Javadoc)
+             * @see com.google.gwt.user.client.ui.Widget#onDetach()
+             */
+            @Override
+            protected void onDetach() {
+                super.onDetach();
+                client.getContextMenu().ensureHidden(this);
+            }
+
+            /** Gets the key.
+			 *
+			 * @return the key
+			 */
+            public String getKey() {
+                return String.valueOf(rowKey);
+            }
+
+            /** Adds the cell.
+			 *
+			 * @param rowUidl
+			 *            the row uidl
+			 * @param text
+			 *            the text
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param textIsHTML
+			 *            the text is html
+			 * @param sorted
+			 *            the sorted
+			 */
+            public void addCell(UIDL rowUidl, String text, char align,
+                    String style, boolean textIsHTML, boolean sorted) {
+                addCell(rowUidl, text, align, style, textIsHTML, sorted, null);
+            }
+
+            /** Adds the cell.
+			 *
+			 * @param rowUidl
+			 *            the row uidl
+			 * @param text
+			 *            the text
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param textIsHTML
+			 *            the text is html
+			 * @param sorted
+			 *            the sorted
+			 * @param description
+			 *            the description
+			 */
+            public void addCell(UIDL rowUidl, String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description) {
+                // String only content is optimized by not using Label widget
+                final TableCellElement td = DOM.createTD().cast();
+                initCellWithText(text, align, style, textIsHTML, sorted,
+                        description, td);
+            }
+
+            /** Inits the cell with text.
+			 *
+			 * @param text
+			 *            the text
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param textIsHTML
+			 *            the text is html
+			 * @param sorted
+			 *            the sorted
+			 * @param description
+			 *            the description
+			 * @param td
+			 *            the td
+			 */
+            protected void initCellWithText(String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description, final TableCellElement td) {
+                final Element container = DOM.createDiv();
+                container.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-cell-wrapper");
+
+                td.setClassName(VCustomScrollTable.this.getStylePrimaryName()
+                        + "-cell-content");
+
+                if (style != null && !style.equals("")) {
+                    td.addClassName(VCustomScrollTable.this
+                            .getStylePrimaryName() + "-cell-content-" + style);
+                }
+
+                if (sorted) {
+                    td.addClassName(VCustomScrollTable.this
+                            .getStylePrimaryName() + "-cell-content-sorted");
+                }
+
+                if (textIsHTML) {
+                    container.setInnerHTML(text);
+                } else {
+                    container.setInnerText(text);
+                }
+                setAlign(align, container);
+                setTooltip(td, description);
+
+                td.appendChild(container);
+                getElement().appendChild(td);
+            }
+
+            /** Update cell style names.
+			 *
+			 * @param td
+			 *            the td
+			 * @param primaryStyleName
+			 *            the primary style name
+			 */
+            protected void updateCellStyleNames(TableCellElement td,
+                    String primaryStyleName) {
+                Element container = td.getFirstChild().cast();
+                container.setClassName(primaryStyleName + "-cell-wrapper");
+
+                /*
+                 * Replace old primary style name with new one
+                 */
+                String className = td.getClassName();
+                String oldPrimaryName = className.split("-cell-content")[0];
+                td.setClassName(className.replaceAll(oldPrimaryName,
+                        primaryStyleName));
+            }
+
+            /** Adds the cell.
+			 *
+			 * @param rowUidl
+			 *            the row uidl
+			 * @param w
+			 *            the w
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param sorted
+			 *            the sorted
+			 * @param description
+			 *            the description
+			 */
+            public void addCell(UIDL rowUidl, Widget w, char align,
+                    String style, boolean sorted, String description) {
+                final TableCellElement td = DOM.createTD().cast();
+                initCellWithWidget(w, align, style, sorted, td);
+                setTooltip(td, description);
+            }
+
+            /** Sets the tooltip.
+			 *
+			 * @param td
+			 *            the td
+			 * @param description
+			 *            the description
+			 */
+            private void setTooltip(TableCellElement td, String description) {
+                if (description != null && !description.equals("")) {
+                    TooltipInfo info = new TooltipInfo(description, null, this);
+                    cellToolTips.put(td, info);
+                } else {
+                    cellToolTips.remove(td);
+                }
+
+            }
+
+            /** Sets the align.
+			 *
+			 * @param align
+			 *            the align
+			 * @param container
+			 *            the container
+			 */
+            private void setAlign(char align, final Element container) {
+                switch (align) {
+                case ALIGN_CENTER:
+                    container.getStyle().setProperty("textAlign", "center");
+                    break;
+                case ALIGN_LEFT:
+                    container.getStyle().setProperty("textAlign", "left");
+                    break;
+                case ALIGN_RIGHT:
+                default:
+                    container.getStyle().setProperty("textAlign", "right");
+                    break;
+                }
+            }
+
+            /** Inits the cell with widget.
+			 *
+			 * @param w
+			 *            the w
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param sorted
+			 *            the sorted
+			 * @param td
+			 *            the td
+			 */
+            protected void initCellWithWidget(Widget w, char align,
+                    String style, boolean sorted, final TableCellElement td) {
+                final Element container = DOM.createDiv();
+                String className = VCustomScrollTable.this
+                        .getStylePrimaryName() + "-cell-content";
+                if (style != null && !style.equals("")) {
+                    className += " "
+                            + VCustomScrollTable.this.getStylePrimaryName()
+                            + "-cell-content-" + style;
+                }
+                if (sorted) {
+                    className += " "
+                            + VCustomScrollTable.this.getStylePrimaryName()
+                            + "-cell-content-sorted";
+                }
+                td.setClassName(className);
+                container.setClassName(VCustomScrollTable.this
+                        .getStylePrimaryName() + "-cell-wrapper");
+                setAlign(align, container);
+                td.appendChild(container);
+                getElement().appendChild(td);
+                // ensure widget not attached to another element (possible tBody
+                // change)
+                w.removeFromParent();
+                container.appendChild(w.getElement());
+                adopt(w);
+                childWidgets.add(w);
+            }
+
+            /* (non-Javadoc)
+             * @see com.google.gwt.user.client.ui.HasWidgets#iterator()
+             */
+            @Override
+            public Iterator<Widget> iterator() {
+                return childWidgets.iterator();
+            }
+
+            /* (non-Javadoc)
+             * @see com.google.gwt.user.client.ui.Panel#remove(com.google.gwt.user.client.ui.Widget)
+             */
+            @Override
+            public boolean remove(Widget w) {
+                if (childWidgets.contains(w)) {
+                    orphan(w);
+                    DOM.removeChild(DOM.getParent(w.getElement()),
+                            w.getElement());
+                    childWidgets.remove(w);
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+
+            /** If there are registered click listeners, sends a click event
+			 * and returns true. Otherwise, does nothing and returns false.
+			 *
+			 * @param event
+			 *            the event
+			 * @param targetTdOrTr
+			 *            the target td or tr
+			 * @param immediate
+			 *            Whether the event is sent immediately
+			 * @return Whether a click event was sent
+			 */
+            private boolean handleClickEvent(Event event, Element targetTdOrTr,
+                    boolean immediate) {
+                if (!client.hasEventListeners(VCustomScrollTable.this,
+                        TableConstants.ITEM_CLICK_EVENT_ID)) {
+                    // Don't send an event if nobody is listening
+                    return false;
+                }
+
+                // This row was clicked
+                client.updateVariable(paintableId, "clickedKey", "" + rowKey,
+                        false);
+
+                if (getElement() == targetTdOrTr.getParentElement()) {
+                    // A specific column was clicked
+                    int childIndex = DOM.getChildIndex(getElement(),
+                            targetTdOrTr);
+                    String colKey = null;
+                    colKey = tHead.getHeaderCell(childIndex).getColKey();
+                    client.updateVariable(paintableId, "clickedColKey", colKey,
+                            false);
+                }
+
+                MouseEventDetails details = MouseEventDetailsBuilder
+                        .buildMouseEventDetails(event);
+
+                client.updateVariable(paintableId, "clickEvent",
+                        details.toString(), immediate);
+
+                return true;
+            }
+
+            /** Gets the tooltip.
+			 *
+			 * @param target
+			 *            the target
+			 * @return the tooltip
+			 */
+            public TooltipInfo getTooltip(
+                    com.google.gwt.dom.client.Element target) {
+
+                TooltipInfo info = null;
+                final Element targetTdOrTr = getTdOrTr(target);
+                if (targetTdOrTr != null
+                        && "td".equals(targetTdOrTr.getTagName().toLowerCase())) {
+                    TableCellElement td = (TableCellElement) targetTdOrTr
+                            .cast();
+                    info = cellToolTips.get(td);
+                }
+
+                if (info == null) {
+                    info = tooltipInfo;
+                }
+
+                return info;
+            }
+
+            /** Gets the td or tr.
+			 *
+			 * @param target
+			 *            the target
+			 * @return the td or tr
+			 */
+            private Element getTdOrTr(Element target) {
+                Element thisTrElement = getElement();
+                if (target == thisTrElement) {
+                    // This was a on the TR element
+                    return target;
+                }
+
+                // Iterate upwards until we find the TR element
+                Element element = target;
+                while (element != null
+                        && element.getParentElement() != thisTrElement) {
+                    element = element.getParentElement();
+                }
+                return element;
+            }
+
+            /** Special handler for touch devices that support native
+			 * scrolling.
+			 *
+			 * @param event
+			 *            the event
+			 * @return Whether the event was handled by this method.
+			 */
+            private boolean handleTouchEvent(final Event event) {
+
+                boolean touchEventHandled = false;
+
+                if (enabled && hasNativeTouchScrolling) {
+                    touchContextProvider.handleTouchEvent(event);
+
+                    final Element targetTdOrTr = getEventTargetTdOrTr(event);
+                    final int type = event.getTypeInt();
+
+                    switch (type) {
+                    case Event.ONTOUCHSTART:
+                        touchEventHandled = true;
+                        touchStart = event;
+                        isDragging = false;
+                        Touch touch = event.getChangedTouches().get(0);
+                        // save position to fields, touches in events are same
+                        // instance during the operation.
+                        touchStartX = touch.getClientX();
+                        touchStartY = touch.getClientY();
+
+                        if (dragmode != 0) {
+                            if (dragTouchTimeout == null) {
+                                dragTouchTimeout = new Timer() {
+
+                                    @Override
+                                    public void run() {
+                                        if (touchStart != null) {
+                                            // Start a drag if a finger is held
+                                            // in place long enough, then moved
+                                            isDragging = true;
+                                        }
+                                    }
+                                };
+                            }
+                            dragTouchTimeout.schedule(TOUCHSCROLL_TIMEOUT);
+                        }
+
+                        if (actionKeys != null) {
+                            if (contextTouchTimeout == null) {
+                                contextTouchTimeout = new Timer() {
+
+                                    @Override
+                                    public void run() {
+                                        if (touchStart != null) {
+                                            // Open the context menu if finger
+                                            // is held in place long enough.
+                                            showContextMenu(touchStart);
+                                            event.preventDefault();
+                                            touchStart = null;
+                                        }
+                                    }
+                                };
+                            }
+                            contextTouchTimeout
+                                    .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+                        }
+                        break;
+                    case Event.ONTOUCHMOVE:
+                        touchEventHandled = true;
+                        if (isSignificantMove(event)) {
+                            if (contextTouchTimeout != null) {
+                                // Moved finger before the context menu timer
+                                // expired, so let the browser handle this as a
+                                // scroll.
+                                contextTouchTimeout.cancel();
+                                contextTouchTimeout = null;
+                            }
+                            if (!isDragging && dragTouchTimeout != null) {
+                                // Moved finger before the drag timer expired,
+                                // so let the browser handle this as a scroll.
+                                dragTouchTimeout.cancel();
+                                dragTouchTimeout = null;
+                            }
+
+                            if (dragmode != 0 && touchStart != null
+                                    && isDragging) {
+                                event.preventDefault();
+                                event.stopPropagation();
+                                startRowDrag(touchStart, type, targetTdOrTr);
+                            }
+                            touchStart = null;
+                        }
+                        break;
+                    case Event.ONTOUCHEND:
+                    case Event.ONTOUCHCANCEL:
+                        touchEventHandled = true;
+                        if (contextTouchTimeout != null) {
+                            contextTouchTimeout.cancel();
+                        }
+                        if (dragTouchTimeout != null) {
+                            dragTouchTimeout.cancel();
+                        }
+                        if (touchStart != null) {
+                            if (!BrowserInfo.get().isAndroid()) {
+                                event.preventDefault();
+                                event.stopPropagation();
+                                Util.simulateClickFromTouchEvent(touchStart,
+                                        this);
+                            }
+                            touchStart = null;
+                        }
+                        isDragging = false;
+                        break;
+                    }
+                }
+                return touchEventHandled;
+            }
+
+            /*
+             * React on click that occur on content cells only
+             */
+
+            /* (non-Javadoc)
+             * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user.client.Event)
+             */
+            @Override
+            public void onBrowserEvent(final Event event) {
+
+                final boolean touchEventHandled = handleTouchEvent(event);
+
+                if (enabled && !touchEventHandled) {
+                    final int type = event.getTypeInt();
+                    final Element targetTdOrTr = getEventTargetTdOrTr(event);
+                    if (type == Event.ONCONTEXTMENU) {
+                        showContextMenu(event);
+                        if (enabled
+                                && (actionKeys != null || client
+                                        .hasEventListeners(
+                                                VCustomScrollTable.this,
+                                                TableConstants.ITEM_CLICK_EVENT_ID))) {
+                            /*
+                             * Prevent browser context menu only if there are
+                             * action handlers or item click listeners
+                             * registered
+                             */
+                            event.stopPropagation();
+                            event.preventDefault();
+                        }
+                        return;
+                    }
+
+                    boolean targetCellOrRowFound = targetTdOrTr != null;
+
+                    switch (type) {
+                    case Event.ONDBLCLICK:
+                        if (targetCellOrRowFound) {
+                            handleClickEvent(event, targetTdOrTr, true);
+                        }
+                        break;
+                    case Event.ONMOUSEUP:
+                        /*
+                         * Only fire a click if the mouseup hits the same
+                         * element as the corresponding mousedown. This is first
+                         * checked in the event preview but we can't fire the
+                         * event there as the event might get canceled before it
+                         * gets here.
+                         */
+                        if (mouseUpPreviewMatched
+                                && lastMouseDownTarget != null
+                                && lastMouseDownTarget == getElementTdOrTr(Util
+                                        .getElementUnderMouse(event))) {
+                            // "Click" with left, right or middle button
+
+                            if (targetCellOrRowFound) {
+                                /*
+                                 * Queue here, send at the same time as the
+                                 * corresponding value change event - see #7127
+                                 */
+                                boolean clickEventSent = handleClickEvent(
+                                        event, targetTdOrTr, false);
+
+                                if (event.getButton() == Event.BUTTON_LEFT
+                                        && isSelectable()) {
+
+                                    // Ctrl+Shift click
+                                    if ((event.getCtrlKey() || event
+                                            .getMetaKey())
+                                            && event.getShiftKey()
+                                            && isMultiSelectModeDefault()) {
+                                        toggleShiftSelection(false);
+                                        setRowFocus(this);
+
+                                        // Ctrl click
+                                    } else if ((event.getCtrlKey() || event
+                                            .getMetaKey())
+                                            && isMultiSelectModeDefault()) {
+                                        boolean wasSelected = isSelected();
+                                        toggleSelection();
+                                        setRowFocus(this);
+                                        /*
+                                         * next possible range select must start
+                                         * on this row
+                                         */
+                                        selectionRangeStart = this;
+                                        if (wasSelected) {
+                                            removeRowFromUnsentSelectionRanges(this);
+                                        }
+
+                                    } else if ((event.getCtrlKey() || event
+                                            .getMetaKey())
+                                            && isSingleSelectMode()) {
+                                        // Ctrl (or meta) click (Single
+                                        // selection)
+                                        if (!isSelected()
+                                                || (isSelected() && nullSelectionAllowed)) {
+
+                                            if (!isSelected()) {
+                                                deselectAll();
+                                            }
+
+                                            toggleSelection();
+                                            setRowFocus(this);
+                                        }
+
+                                    } else if (event.getShiftKey()
+                                            && isMultiSelectModeDefault()) {
+                                        // Shift click
+                                        toggleShiftSelection(true);
+
+                                    } else {
+                                        // click
+                                        boolean currentlyJustThisRowSelected = selectedRowKeys
+                                                .size() == 1
+                                                && selectedRowKeys
+                                                        .contains(getKey());
+
+                                        if (!currentlyJustThisRowSelected) {
+                                            if (isSingleSelectMode()
+                                                    || isMultiSelectModeDefault()) {
+                                                /*
+                                                 * For default multi select mode
+                                                 * (ctrl/shift) and for single
+                                                 * select mode we need to clear
+                                                 * the previous selection before
+                                                 * selecting a new one when the
+                                                 * user clicks on a row. Only in
+                                                 * multiselect/simple mode the
+                                                 * old selection should remain
+                                                 * after a normal click.
+                                                 */
+                                                deselectAll();
+                                            }
+                                            toggleSelection();
+                                        } else if ((isSingleSelectMode() || isMultiSelectModeSimple())
+                                                && nullSelectionAllowed) {
+                                            toggleSelection();
+                                        }/*
+                                          * else NOP to avoid excessive server
+                                          * visits (selection is removed with
+                                          * CTRL/META click)
+                                          */
+
+                                        selectionRangeStart = this;
+                                        setRowFocus(this);
+                                    }
+
+                                    // Remove IE text selection hack
+                                    if (BrowserInfo.get().isIE()) {
+                                        ((Element) event.getEventTarget()
+                                                .cast()).setPropertyJSO(
+                                                "onselectstart", null);
+                                    }
+                                    // Queue value change
+                                    sendSelectedRows(false);
+                                }
+                                /*
+                                 * Send queued click and value change events if
+                                 * any If a click event is sent, send value
+                                 * change with it regardless of the immediate
+                                 * flag, see #7127
+                                 */
+                                if (immediate || clickEventSent) {
+                                    client.sendPendingVariableChanges();
+                                }
+                            }
+                        }
+                        mouseUpPreviewMatched = false;
+                        lastMouseDownTarget = null;
+                        break;
+                    case Event.ONTOUCHEND:
+                    case Event.ONTOUCHCANCEL:
+                        if (touchStart != null) {
+                            /*
+                             * Touch has not been handled as neither context or
+                             * drag start, handle it as a click.
+                             */
+                            Util.simulateClickFromTouchEvent(touchStart, this);
+                            touchStart = null;
+                        }
+                        touchContextProvider.cancel();
+                        break;
+                    case Event.ONTOUCHMOVE:
+                        if (isSignificantMove(event)) {
+                            /*
+                             * TODO figure out scroll delegate don't eat events
+                             * if row is selected. Null check for active
+                             * delegate is as a workaround.
+                             */
+                            if (dragmode != 0
+                                    && touchStart != null
+                                    && (TouchScrollDelegate
+                                            .getActiveScrollDelegate() == null)) {
+                                startRowDrag(touchStart, type, targetTdOrTr);
+                            }
+                            touchContextProvider.cancel();
+                            /*
+                             * Avoid clicks and drags by clearing touch start
+                             * flag.
+                             */
+                            touchStart = null;
+                        }
+
+                        break;
+                    case Event.ONTOUCHSTART:
+                        touchStart = event;
+                        Touch touch = event.getChangedTouches().get(0);
+                        // save position to fields, touches in events are same
+                        // isntance during the operation.
+                        touchStartX = touch.getClientX();
+                        touchStartY = touch.getClientY();
+                        /*
+                         * Prevent simulated mouse events.
+                         */
+                        touchStart.preventDefault();
+                        if (dragmode != 0 || actionKeys != null) {
+                            new Timer() {
+
+                                @Override
+                                public void run() {
+                                    TouchScrollDelegate activeScrollDelegate = TouchScrollDelegate
+                                            .getActiveScrollDelegate();
+                                    /*
+                                     * If there's a scroll delegate, check if
+                                     * we're actually scrolling and handle it.
+                                     * If no delegate, do nothing here and let
+                                     * the row handle potential drag'n'drop or
+                                     * context menu.
+                                     */
+                                    if (activeScrollDelegate != null) {
+                                        if (activeScrollDelegate.isMoved()) {
+                                            /*
+                                             * Prevent the row from handling
+                                             * touch move/end events (the
+                                             * delegate handles those) and from
+                                             * doing drag'n'drop or opening a
+                                             * context menu.
+                                             */
+                                            touchStart = null;
+                                        } else {
+                                            /*
+                                             * Scrolling hasn't started, so
+                                             * cancel delegate and let the row
+                                             * handle potential drag'n'drop or
+                                             * context menu.
+                                             */
+                                            activeScrollDelegate
+                                                    .stopScrolling();
+                                        }
+                                    }
+                                }
+                            }.schedule(TOUCHSCROLL_TIMEOUT);
+
+                            if (contextTouchTimeout == null
+                                    && actionKeys != null) {
+                                contextTouchTimeout = new Timer() {
+
+                                    @Override
+                                    public void run() {
+                                        if (touchStart != null) {
+                                            showContextMenu(touchStart);
+                                            touchStart = null;
+                                        }
+                                    }
+                                };
+                            }
+                            if (contextTouchTimeout != null) {
+                                contextTouchTimeout.cancel();
+                                contextTouchTimeout
+                                        .schedule(TOUCH_CONTEXT_MENU_TIMEOUT);
+                            }
+                        }
+                        break;
+                    case Event.ONMOUSEDOWN:
+                        /*
+                         * When getting a mousedown event, we must detect where
+                         * the corresponding mouseup event if it's on a
+                         * different part of the page.
+                         */
+                        lastMouseDownTarget = getElementTdOrTr(Util
+                                .getElementUnderMouse(event));
+                        mouseUpPreviewMatched = false;
+                        mouseUpEventPreviewRegistration = Event
+                                .addNativePreviewHandler(mouseUpPreviewHandler);
+
+                        if (targetCellOrRowFound) {
+                            setRowFocus(this);
+                            ensureFocus();
+                            if (dragmode != 0
+                                    && (event.getButton() == NativeEvent.BUTTON_LEFT)) {
+                                startRowDrag(event, type, targetTdOrTr);
+
+                            } else if (event.getCtrlKey()
+                                    || event.getShiftKey()
+                                    || event.getMetaKey()
+                                    && isMultiSelectModeDefault()) {
+
+                                // Prevent default text selection in Firefox
+                                event.preventDefault();
+
+                                // Prevent default text selection in IE
+                                if (BrowserInfo.get().isIE()) {
+                                    ((Element) event.getEventTarget().cast())
+                                            .setPropertyJSO(
+                                                    "onselectstart",
+                                                    getPreventTextSelectionIEHack());
+                                }
+
+                                event.stopPropagation();
+                            }
+                        }
+                        break;
+                    case Event.ONMOUSEOUT:
+                        break;
+                    default:
+                        break;
+                    }
+                }
+                super.onBrowserEvent(event);
+            }
+
+            /** Checks if is significant move.
+			 *
+			 * @param event
+			 *            the event
+			 * @return true, if is significant move
+			 */
+            private boolean isSignificantMove(Event event) {
+                if (touchStart == null) {
+                    // no touch start
+                    return false;
+                }
+                /*
+                 * TODO calculate based on real distance instead of separate
+                 * axis checks
+                 */
+                Touch touch = event.getChangedTouches().get(0);
+                if (Math.abs(touch.getClientX() - touchStartX) > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
+                    return true;
+                }
+                if (Math.abs(touch.getClientY() - touchStartY) > TouchScrollDelegate.SIGNIFICANT_MOVE_THRESHOLD) {
+                    return true;
+                }
+                return false;
+            }
+
+            /** Checks if the row represented by the row key has been
+			 * selected.
+			 *
+			 * @param rowKey
+			 *            the row key
+			 * @return true, if successful
+			 */
+            private boolean rowKeyIsSelected(int rowKey) {
+                // Check single selections
+                if (selectedRowKeys.contains("" + rowKey)) {
+                    return true;
+                }
+
+                // Check range selections
+                for (SelectionRange r : selectedRowRanges) {
+                    if (r.inRange(getRenderedRowByKey("" + rowKey))) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            /** Start row drag.
+			 *
+			 * @param event
+			 *            the event
+			 * @param type
+			 *            the type
+			 * @param targetTdOrTr
+			 *            the target td or tr
+			 */
+            protected void startRowDrag(Event event, final int type,
+                    Element targetTdOrTr) {
+                VTransferable transferable = new VTransferable();
+                transferable.setDragSource(ConnectorMap.get(client)
+                        .getConnector(VCustomScrollTable.this));
+                transferable.setData("itemId", "" + rowKey);
+                NodeList<TableCellElement> cells = rowElement.getCells();
+                for (int i = 0; i < cells.getLength(); i++) {
+                    if (cells.getItem(i).isOrHasChild(targetTdOrTr)) {
+                        HeaderCell headerCell = tHead.getHeaderCell(i);
+                        transferable.setData("propertyId", headerCell.cid);
+                        break;
+                    }
+                }
+
+                VDragEvent ev = VDragAndDropManager.get().startDrag(
+                        transferable, event, true);
+                if (dragmode == DRAGMODE_MULTIROW && isMultiSelectModeAny()
+                        && rowKeyIsSelected(rowKey)) {
+
+                    // Create a drag image of ALL rows
+                    ev.createDragImage(scrollBody.tBodyElement, true);
+
+                    // Hide rows which are not selected
+                    Element dragImage = ev.getDragImage();
+                    int i = 0;
+                    for (Iterator<Widget> iterator = scrollBody.iterator(); iterator
+                            .hasNext();) {
+                        VScrollTableRow next = (VScrollTableRow) iterator
+                                .next();
+
+                        Element child = (Element) dragImage.getChild(i++);
+
+                        if (!rowKeyIsSelected(next.rowKey)) {
+                            child.getStyle().setVisibility(Visibility.HIDDEN);
+                        }
+                    }
+                } else {
+                    ev.createDragImage(getElement(), true);
+                }
+                if (type == Event.ONMOUSEDOWN) {
+                    event.preventDefault();
+                }
+                event.stopPropagation();
+            }
+
+            /** Finds the TD that the event interacts with. Returns null if
+			 * the target of the event should not be handled. If the event
+			 * target is the row directly this method returns the TR element
+			 * instead of the TD.
+			 *
+			 * @param event
+			 *            the event
+			 * @return TD or TR element that the event targets (the actual event
+			 *         target is this element or a child of it)
+			 */
+            private Element getEventTargetTdOrTr(Event event) {
+                final Element eventTarget = event.getEventTarget().cast();
+                return getElementTdOrTr(eventTarget);
+            }
+
+            /** Gets the element td or tr.
+			 *
+			 * @param element
+			 *            the element
+			 * @return the element td or tr
+			 */
+            private Element getElementTdOrTr(Element element) {
+
+                Widget widget = Util.findWidget(element, null);
+
+                if (widget != this) {
+                    /*
+                     * This is a workaround to make Labels, read only TextFields
+                     * and Embedded in a Table clickable (see #2688). It is
+                     * really not a fix as it does not work with a custom read
+                     * only components (not extending VLabel/VEmbedded).
+                     */
+                    while (widget != null && widget.getParent() != this) {
+                        widget = widget.getParent();
+                    }
+
+                    if (!(widget instanceof VLabel)
+                            && !(widget instanceof VEmbedded)
+                            && !(widget instanceof VTextField && ((VTextField) widget)
+                                    .isReadOnly())) {
+                        return null;
+                    }
+                }
+                return getTdOrTr(element);
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.VCustomScrollTable.ContextMenuOwner#showContextMenu(com.google.gwt.user.client.Event)
+             */
+            @Override
+            public void showContextMenu(Event event) {
+                if (enabled && actionKeys != null) {
+                    // Show context menu if there are registered action handlers
+                    int left = Util.getTouchOrMouseClientX(event)
+                            + Window.getScrollLeft();
+                    int top = Util.getTouchOrMouseClientY(event)
+                            + Window.getScrollTop();
+                    showContextMenu(left, top);
+                }
+            }
+
+            /** Show context menu.
+			 *
+			 * @param left
+			 *            the left
+			 * @param top
+			 *            the top
+			 */
+            public void showContextMenu(int left, int top) {
+                VContextMenu menu = client.getContextMenu();
+                contextMenu = new ContextMenuDetails(menu, getKey(), left, top);
+                menu.showAt(this, left, top);
+            }
+
+            /** Has the row been selected?.
+			 *
+			 * @return Returns true if selected, else false
+			 */
+            public boolean isSelected() {
+                return selected;
+            }
+
+            /** Toggle the selection of the row.
+			 */
+            public void toggleSelection() {
+                selected = !selected;
+                selectionChanged = true;
+                if (selected) {
+                    selectedRowKeys.add(String.valueOf(rowKey));
+                    addStyleName("v-selected");
+                } else {
+                    removeStyleName("v-selected");
+                    selectedRowKeys.remove(String.valueOf(rowKey));
+                }
+            }
+
+            /**
+             * Is called when a user clicks an item when holding SHIFT key down.
+             * This will select a new range from the last focused row
+             * 
+             * @param deselectPrevious
+             *            Should the previous selected range be deselected
+             */
+            private void toggleShiftSelection(boolean deselectPrevious) {
+
+                /*
+                 * Ensures that we are in multiselect mode and that we have a
+                 * previous selection which was not a deselection
+                 */
+                if (isSingleSelectMode()) {
+                    // No previous selection found
+                    deselectAll();
+                    toggleSelection();
+                    return;
+                }
+
+                // Set the selectable range
+                VScrollTableRow endRow = this;
+                VScrollTableRow startRow = selectionRangeStart;
+                if (startRow == null) {
+                    startRow = focusedRow;
+                    selectionRangeStart = focusedRow;
+                    // If start row is null then we have a multipage selection
+                    // from
+                    // above
+                    if (startRow == null) {
+                        startRow = (VScrollTableRow) scrollBody.iterator()
+                                .next();
+                        setRowFocus(endRow);
+                    }
+                } else if (!startRow.isSelected()) {
+                    // The start row is no longer selected (probably removed)
+                    // and so we select from above
+                    startRow = (VScrollTableRow) scrollBody.iterator().next();
+                    setRowFocus(endRow);
+                }
+
+                // Deselect previous items if so desired
+                if (deselectPrevious) {
+                    deselectAll();
+                }
+
+                // we'll ensure GUI state from top down even though selection
+                // was the opposite way
+                if (!startRow.isBefore(endRow)) {
+                    VScrollTableRow tmp = startRow;
+                    startRow = endRow;
+                    endRow = tmp;
+                }
+                SelectionRange range = new SelectionRange(startRow, endRow);
+
+                for (Widget w : scrollBody) {
+                    VScrollTableRow row = (VScrollTableRow) w;
+                    if (range.inRange(row)) {
+                        if (!row.isSelected()) {
+                            row.toggleSelection();
+                        }
+                        selectedRowKeys.add(row.getKey());
+                    }
+                }
+
+                // Add range
+                if (startRow != endRow) {
+                    selectedRowRanges.add(range);
+                }
+            }
+
+            /*
+             * (non-Javadoc)
+             * 
+             * @see com.vaadin.client.ui.IActionOwner#getActions ()
+             */
+
+            @Override
+            public Action[] getActions() {
+                if (actionKeys == null) {
+                    return new Action[] {};
+                }
+                final Action[] actions = new Action[actionKeys.length];
+                for (int i = 0; i < actions.length; i++) {
+                    final String actionKey = actionKeys[i];
+                    final TreeAction a = new TreeAction(this,
+                            String.valueOf(rowKey), actionKey) {
+
+                        @Override
+                        public void execute() {
+                            super.execute();
+                            lazyRevertFocusToRow(VScrollTableRow.this);
+                        }
+                    };
+                    a.setCaption(getActionCaption(actionKey));
+                    a.setIconUrl(getActionIcon(actionKey));
+                    actions[i] = a;
+                }
+                return actions;
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.ActionOwner#getClient()
+             */
+            @Override
+            public ApplicationConnection getClient() {
+                return client;
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.ActionOwner#getPaintableId()
+             */
+            @Override
+            public String getPaintableId() {
+                return paintableId;
+            }
+
+            /** Gets the col index of.
+			 *
+			 * @param child
+			 *            the child
+			 * @return the col index of
+			 */
+            private int getColIndexOf(Widget child) {
+                com.google.gwt.dom.client.Element widgetCell = child
+                        .getElement().getParentElement().getParentElement();
+                NodeList<TableCellElement> cells = rowElement.getCells();
+                for (int i = 0; i < cells.getLength(); i++) {
+                    if (cells.getItem(i) == widgetCell) {
+                        return i;
+                    }
+                }
+                return -1;
+            }
+
+            /** Gets the widget for paintable.
+			 *
+			 * @return the widget for paintable
+			 */
+            public Widget getWidgetForPaintable() {
+                return this;
+            }
+        }
+
+        /** The Class VScrollTableGeneratedRow.
+		 */
+        protected class VScrollTableGeneratedRow extends VScrollTableRow {
+
+            /** The span columns. */
+            private boolean spanColumns;
+            
+            /** The html content allowed. */
+            private boolean htmlContentAllowed;
+
+            /** Instantiates a new v scroll table generated row.
+			 *
+			 * @param uidl
+			 *            the uidl
+			 * @param aligns
+			 *            the aligns
+			 */
+            public VScrollTableGeneratedRow(UIDL uidl, char[] aligns) {
+                super(uidl, aligns);
+                addStyleName("v-table-generated-row");
+            }
+
+            /** Checks if is span columns.
+			 *
+			 * @return true, if is span columns
+			 */
+            public boolean isSpanColumns() {
+                return spanColumns;
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow#initCellWidths()
+             */
+            @Override
+            protected void initCellWidths() {
+                if (spanColumns) {
+                    setSpannedColumnWidthAfterDOMFullyInited();
+                } else {
+                    super.initCellWidths();
+                }
+            }
+
+            /** Sets the spanned column width after dom fully inited.
+			 */
+            private void setSpannedColumnWidthAfterDOMFullyInited() {
+                // Defer setting width on spanned columns to make sure that
+                // they are added to the DOM before trying to calculate
+                // widths.
+                Scheduler.get().scheduleDeferred(new ScheduledCommand() {
+
+                    @Override
+                    public void execute() {
+                        if (showRowHeaders) {
+                            setCellWidth(0, tHead.getHeaderCell(0)
+                                    .getWidthWithIndent());
+                            calcAndSetSpanWidthOnCell(1);
+                        } else {
+                            calcAndSetSpanWidthOnCell(0);
+                        }
+                    }
+                });
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow#isRenderHtmlInCells()
+             */
+            @Override
+            protected boolean isRenderHtmlInCells() {
+                return htmlContentAllowed;
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow#addCellsFromUIDL(com.vaadin.client.UIDL, char[], int, int)
+             */
+            @Override
+            protected void addCellsFromUIDL(UIDL uidl, char[] aligns, int col,
+                    int visibleColumnIndex) {
+                htmlContentAllowed = uidl.getBooleanAttribute("gen_html");
+                spanColumns = uidl.getBooleanAttribute("gen_span");
+
+                final Iterator<?> cells = uidl.getChildIterator();
+                if (spanColumns) {
+                    int colCount = uidl.getChildCount();
+                    if (cells.hasNext()) {
+                        final Object cell = cells.next();
+                        if (cell instanceof String) {
+                            addSpannedCell(uidl, cell.toString(), aligns[0],
+                                    "", htmlContentAllowed, false, null,
+                                    colCount);
+                        } else {
+                            addSpannedCell(uidl, (Widget) cell, aligns[0], "",
+                                    false, colCount);
+                        }
+                    }
+                } else {
+                    super.addCellsFromUIDL(uidl, aligns, col,
+                            visibleColumnIndex);
+                }
+            }
+
+            /** Adds the spanned cell.
+			 *
+			 * @param rowUidl
+			 *            the row uidl
+			 * @param w
+			 *            the w
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param sorted
+			 *            the sorted
+			 * @param colCount
+			 *            the col count
+			 */
+            private void addSpannedCell(UIDL rowUidl, Widget w, char align,
+                    String style, boolean sorted, int colCount) {
+                TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithWidget(w, align, style, sorted, td);
+            }
+
+            /** Adds the spanned cell.
+			 *
+			 * @param rowUidl
+			 *            the row uidl
+			 * @param text
+			 *            the text
+			 * @param align
+			 *            the align
+			 * @param style
+			 *            the style
+			 * @param textIsHTML
+			 *            the text is html
+			 * @param sorted
+			 *            the sorted
+			 * @param description
+			 *            the description
+			 * @param colCount
+			 *            the col count
+			 */
+            private void addSpannedCell(UIDL rowUidl, String text, char align,
+                    String style, boolean textIsHTML, boolean sorted,
+                    String description, int colCount) {
+                // String only content is optimized by not using Label widget
+                final TableCellElement td = DOM.createTD().cast();
+                td.setColSpan(colCount);
+                initCellWithText(text, align, style, textIsHTML, sorted,
+                        description, td);
+            }
+
+            /* (non-Javadoc)
+             * @see com.vaadin.client.ui.VCustomScrollTable.VScrollTableBody.VScrollTableRow#setCellWidth(int, int)
+             */
+            @Override
+            protected void setCellWidth(int cellIx, int width) {
+                if (isSpanColumns()) {
+                    if (showRowHeaders) {
+                        if (cellIx == 0) {
+                            super.setCellWidth(0, width);
+                        } else {
+                            // We need to recalculate the spanning TDs width for
+                            // every cellIx in order to support column resizing.
+                            calcAndSetSpanWidthOnCell(1);
+                        }
+                    } else {
+                        // Same as above.
+                        calcAndSetSpanWidthOnCell(0);
+                    }
+                } else {
+                    super.setCellWidth(cellIx, width);
+                }
+            }
+
+            /** Calc and set span width on cell.
+			 *
+			 * @param cellIx
+			 *            the cell ix
+			 */
+            private void calcAndSetSpanWidthOnCell(final int cellIx) {
+                int spanWidth = 0;
+                for (int ix = (showRowHeaders ? 1 : 0); ix < tHead
+                        .getVisibleCellCount(); ix++) {
+                    spanWidth += tHead.getHeaderCell(ix).getOffsetWidth();
+                }
+                Util.setWidthExcludingPaddingAndBorder((Element) getElement()
+                        .getChild(cellIx), spanWidth, 13, false);
+            }
+        }
+
+        /**
+         * Ensure the component has a focus.
+         * 
+         * TODO the current implementation simply always calls focus for the
+         * component. In case the Table at some point implements focus/blur
+         * listeners, this method needs to be evolved to conditionally call
+         * focus only if not currently focused.
+         */
+        protected void ensureFocus() {
+            if (!hasFocus) {
+                scrollBodyPanel.setFocus(true);
+            }
+
+        }
+
+    }
+
+    /** Deselects all items.
+	 */
+    public void deselectAll() {
+        for (Widget w : scrollBody) {
+            VScrollTableRow row = (VScrollTableRow) w;
+            if (row.isSelected()) {
+                row.toggleSelection();
+            }
+        }
+        // still ensure all selects are removed from (not necessary rendered)
+        selectedRowKeys.clear();
+        selectedRowRanges.clear();
+        // also notify server that it clears all previous selections (the client
+        // side does not know about the invisible ones)
+        instructServerToForgetPreviousSelections();
+    }
+
+    /**
+     * Used in multiselect mode when the client side knows that all selections
+     * are in the next request.
+     */
+    private void instructServerToForgetPreviousSelections() {
+        client.updateVariable(paintableId, "clearSelections", true, false);
+    }
+
+    /**
+     * Determines the pagelength when the table height is fixed.
+     */
+    public void updatePageLength() {
+        // Only update if visible and enabled
+        if (!isVisible() || !enabled) {
+            return;
+        }
+
+        if (scrollBody == null) {
+            return;
+        }
+
+        if (isDynamicHeight()) {
+            return;
+        }
+
+        int rowHeight = (int) Math.round(scrollBody.getRowHeight());
+        int bodyH = scrollBodyPanel.getOffsetHeight();
+        int rowsAtOnce = bodyH / rowHeight;
+        boolean anotherPartlyVisible = ((bodyH % rowHeight) != 0);
+        if (anotherPartlyVisible) {
+            rowsAtOnce++;
+        }
+        if (pageLength != rowsAtOnce) {
+            pageLength = rowsAtOnce;
+            client.updateVariable(paintableId, "pagelength", pageLength, false);
+
+            if (!rendering) {
+                int currentlyVisible = scrollBody.getLastRendered()
+                        - scrollBody.getFirstRendered();
+                if (currentlyVisible < pageLength
+                        && currentlyVisible < totalRows) {
+                    // shake scrollpanel to fill empty space
+                    scrollBodyPanel.setScrollPosition(scrollTop + 1);
+                    scrollBodyPanel.setScrollPosition(scrollTop - 1);
+                }
+
+                sizeNeedsInit = true;
+            }
+        }
+
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public void updateWidth() {
+        if (!isVisible()) {
+            /*
+             * Do not update size when the table is hidden as all column widths
+             * will be set to zero and they won't be recalculated when the table
+             * is set visible again (until the size changes again)
+             */
+            return;
+        }
+
+        if (!isDynamicWidth()) {
+            int innerPixels = getOffsetWidth() - getBorderWidth();
+            if (innerPixels < 0) {
+                innerPixels = 0;
+            }
+            setContentWidth(innerPixels);
+
+            // readjust undefined width columns
+            triggerLazyColumnAdjustment(false);
+
+        } else {
+
+            sizeNeedsInit = true;
+
+            // readjust undefined width columns
+            triggerLazyColumnAdjustment(false);
+        }
+
+        /*
+         * setting width may affect wheter the component has scrollbars -> needs
+         * scrolling or not
+         */
+        setProperTabIndex();
+    }
+
+    /** The Constant LAZY_COLUMN_ADJUST_TIMEOUT. */
+    private static final int LAZY_COLUMN_ADJUST_TIMEOUT = 300;
+
+    /** The lazy adjust column widths. */
+    private final Timer lazyAdjustColumnWidths = new Timer() {
+        /**
+         * Check for column widths, and available width, to see if we can fix
+         * column widths "optimally". Doing this lazily to avoid expensive
+         * calculation when resizing is not yet finished.
+         */
+
+        @Override
+        public void run() {
+            if (scrollBody == null) {
+                // Try again later if we get here before scrollBody has been
+                // initalized
+                triggerLazyColumnAdjustment(false);
+                return;
+            }
+
+            Iterator<Widget> headCells = tHead.iterator();
+            int usedMinimumWidth = 0;
+            int totalExplicitColumnsWidths = 0;
+            float expandRatioDivider = 0;
+            int colIndex = 0;
+
+            int hierarchyColumnIndent = scrollBody.getMaxIndent();
+            int hierarchyColumnIndex = getHierarchyColumnIndex();
+            HeaderCell hierarchyHeaderInNeedOfFurtherHandling = null;
+
+            while (headCells.hasNext()) {
+                final HeaderCell hCell = (HeaderCell) headCells.next();
+                boolean hasIndent = hierarchyColumnIndent > 0
+                        && hCell.isHierarchyColumn();
+                if (hCell.isDefinedWidth()) {
+                    // get width without indent to find out whether adjustments
+                    // are needed (requires special handling further ahead)
+                    int w = hCell.getWidth();
+                    if (hasIndent && w < hierarchyColumnIndent) {
+                        // enforce indent if necessary
+                        w = hierarchyColumnIndent;
+                        hierarchyHeaderInNeedOfFurtherHandling = hCell;
+                    }
+                    totalExplicitColumnsWidths += w;
+                    usedMinimumWidth += w;
+                } else {
+                    // natural width already includes indent if any
+                    int naturalColumnWidth = hCell
+                            .getNaturalColumnWidth(colIndex);
+                    usedMinimumWidth += naturalColumnWidth;
+                    expandRatioDivider += hCell.getExpandRatio();
+                    if (hasIndent) {
+                        hierarchyHeaderInNeedOfFurtherHandling = hCell;
+                    }
+                }
+                colIndex++;
+            }
+
+            int availW = scrollBody.getAvailableWidth();
+            // Hey IE, are you really sure about this?
+            availW = scrollBody.getAvailableWidth();
+            int visibleCellCount = tHead.getVisibleCellCount();
+            int totalExtraWidth = scrollBody.getCellExtraWidth()
+                    * visibleCellCount;
+            if (willHaveScrollbars()) {
+                totalExtraWidth += Util.getNativeScrollbarSize();
+            }
+            availW -= totalExtraWidth;
+            int forceScrollBodyWidth = -1;
+
+            int extraSpace = availW - usedMinimumWidth;
+            if (extraSpace < 0) {
+                if (getTotalRows() == 0) {
+                    /*
+                     * Too wide header combined with no rows in the table.
+                     * 
+                     * No horizontal scrollbars would be displayed because
+                     * there's no rows that grows too wide causing the
+                     * scrollBody container div to overflow. Must explicitely
+                     * force a width to a scrollbar. (see #9187)
+                     */
+                    forceScrollBodyWidth = usedMinimumWidth + totalExtraWidth;
+                }
+                extraSpace = 0;
+            }
+
+            if (forceScrollBodyWidth > 0) {
+                scrollBody.container.getStyle().setWidth(forceScrollBodyWidth,
+                        Unit.PX);
+            } else {
+                // Clear width that might have been set to force horizontal
+                // scrolling if there are no rows
+                scrollBody.container.getStyle().clearWidth();
+            }
+
+            int totalUndefinedNaturalWidths = usedMinimumWidth
+                    - totalExplicitColumnsWidths;
+
+            if (hierarchyHeaderInNeedOfFurtherHandling != null
+                    && !hierarchyHeaderInNeedOfFurtherHandling.isDefinedWidth()) {
+                // ensure the cell gets enough space for the indent
+                int w = hierarchyHeaderInNeedOfFurtherHandling
+                        .getNaturalColumnWidth(hierarchyColumnIndex);
+                int newSpace = Math.round(w + (float) extraSpace * (float) w
+                        / totalUndefinedNaturalWidths);
+                if (newSpace >= hierarchyColumnIndent) {
+                    // no special handling required
+                    hierarchyHeaderInNeedOfFurtherHandling = null;
+                } else {
+                    // treat as a defined width column of indent's width
+                    totalExplicitColumnsWidths += hierarchyColumnIndent;
+                    usedMinimumWidth -= w - hierarchyColumnIndent;
+                    totalUndefinedNaturalWidths = usedMinimumWidth
+                            - totalExplicitColumnsWidths;
+                    expandRatioDivider += hierarchyHeaderInNeedOfFurtherHandling
+                            .getExpandRatio();
+                    extraSpace = Math.max(availW - usedMinimumWidth, 0);
+                }
+            }
+
+            // we have some space that can be divided optimally
+            HeaderCell hCell;
+            colIndex = 0;
+            headCells = tHead.iterator();
+            int checksum = 0;
+            while (headCells.hasNext()) {
+                hCell = (HeaderCell) headCells.next();
+                if (hCell.isResizing) {
+                    continue;
+                }
+                if (!hCell.isDefinedWidth()) {
+                    int w = hCell.getNaturalColumnWidth(colIndex);
+                    int newSpace;
+                    if (expandRatioDivider > 0) {
+                        // divide excess space by expand ratios
+                        newSpace = Math.round((w + extraSpace
+                                * hCell.getExpandRatio() / expandRatioDivider));
+                    } else {
+                        if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
+                            // still exists, so needs exactly the indent's width
+                            newSpace = hierarchyColumnIndent;
+                        } else if (totalUndefinedNaturalWidths != 0) {
+                            // divide relatively to natural column widths
+                            newSpace = Math.round(w + (float) extraSpace
+                                    * (float) w / totalUndefinedNaturalWidths);
+                        } else {
+                            newSpace = w;
+                        }
+                    }
+                    checksum += newSpace;
+                    setColWidth(colIndex, newSpace, false);
+
+                } else {
+                    if (hierarchyHeaderInNeedOfFurtherHandling == hCell) {
+                        // defined with enforced into indent width
+                        checksum += hierarchyColumnIndent;
+                        setColWidth(colIndex, hierarchyColumnIndent, false);
+                    } else {
+                        int cellWidth = hCell.getWidthWithIndent();
+                        checksum += cellWidth;
+                        if (hCell.isHierarchyColumn()) {
+                            // update in case the indent has changed
+                            // (not detectable earlier)
+                            setColWidth(colIndex, cellWidth, true);
+                        }
+                    }
+                }
+                colIndex++;
+            }
+
+            if (extraSpace > 0 && checksum != availW) {
+                /*
+                 * There might be in some cases a rounding error of 1px when
+                 * extra space is divided so if there is one then we give the
+                 * first undefined column 1 more pixel
+                 */
+                headCells = tHead.iterator();
+                colIndex = 0;
+                while (headCells.hasNext()) {
+                    HeaderCell hc = (HeaderCell) headCells.next();
+                    if (!hc.isResizing && !hc.isDefinedWidth()) {
+                        setColWidth(colIndex, hc.getWidthWithIndent() + availW
+                                - checksum, false);
+                        break;
+                    }
+                    colIndex++;
+                }
+            }
+
+            if (isDynamicHeight() && totalRows == pageLength) {
+                // fix body height (may vary if lazy loading is offhorizontal
+                // scrollbar appears/disappears)
+                int bodyHeight = scrollBody.getRequiredHeight();
+                boolean needsSpaceForHorizontalScrollbar = (availW < usedMinimumWidth);
+                if (needsSpaceForHorizontalScrollbar) {
+                    bodyHeight += Util.getNativeScrollbarSize();
+                }
+                int heightBefore = getOffsetHeight();
+                scrollBodyPanel.setHeight(bodyHeight + "px");
+
+                if (heightBefore != getOffsetHeight()) {
+                    Util.notifyParentOfSizeChange(VCustomScrollTable.this,
+                            rendering);
+                }
+            }
+
+            Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement());
+
+            forceRealignColumnHeaders();
+        }
+
+    };
+
+    /** Force realign column headers.
+	 */
+    private void forceRealignColumnHeaders() {
+        if (BrowserInfo.get().isIE()) {
+            /*
+             * IE does not fire onscroll event if scroll position is reverted to
+             * 0 due to the content element size growth. Ensure headers are in
+             * sync with content manually. Safe to use null event as we don't
+             * actually use the event object in listener.
+             */
+            onScroll(null);
+        }
+    }
+
+    /** helper to set pixel size of head and body part.
+	 *
+	 * @param pixels
+	 *            the new content width
+	 */
+    protected void setContentWidth(int pixels) {
+        tHead.setWidth(pixels + "px");
+        scrollBodyPanel.setWidth(pixels + "px");
+        tFoot.setWidth(pixels + "px");
+    }
+
+    /** The border width. */
+    private int borderWidth = -1;
+
+    /** Gets the border width.
+	 *
+	 * @return border left + border right
+	 */
+    private int getBorderWidth() {
+        if (borderWidth < 0) {
+            borderWidth = Util.measureHorizontalPaddingAndBorder(
+                    scrollBodyPanel.getElement(), 2);
+            if (borderWidth < 0) {
+                borderWidth = 0;
+            }
+        }
+        return borderWidth;
+    }
+
+    /**
+     * Ensures scrollable area is properly sized. This method is used when fixed
+     * size is used.
+     */
+    protected int containerHeight;
+
+    /** Sets the container height.
+	 */
+    protected void setContainerHeight() {
+        if (!isDynamicHeight()) {
+
+            /*
+             * Android 2.3 cannot measure the height of the inline-block
+             * properly, and will return the wrong offset height. So for android
+             * 2.3 we set the element to a block element while measuring and
+             * then restore it which yields the correct result. #11331
+             */
+            if (BrowserInfo.get().isAndroid23()) {
+                getElement().getStyle().setDisplay(Display.BLOCK);
+            }
+
+            containerHeight = getOffsetHeight();
+            containerHeight -= showColHeaders ? tHead.getOffsetHeight() : 0;
+            containerHeight -= tFoot.getOffsetHeight();
+            containerHeight -= getContentAreaBorderHeight();
+            if (containerHeight < 0) {
+                containerHeight = 0;
+            }
+
+            scrollBodyPanel.setHeight(containerHeight + "px");
+
+            if (BrowserInfo.get().isAndroid23()) {
+                getElement().getStyle().clearDisplay();
+            }
+        }
+    }
+
+    /** The content area border height. */
+    private int contentAreaBorderHeight = -1;
+    
+    /** The scroll left. */
+    protected int scrollLeft;
+    
+    /** The scroll top. */
+    private int scrollTop;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public VScrollTableDropHandler dropHandler;
+
+    /** The nav key down. */
+    private boolean navKeyDown;
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public boolean multiselectPending;
+
+    /** Gets the content area border height.
+	 *
+	 * @return border top + border bottom of the scrollable area of table
+	 */
+    protected int getContentAreaBorderHeight() {
+        if (contentAreaBorderHeight < 0) {
+
+            scrollBodyPanel.getElement().getStyle()
+                    .setOverflow(Overflow.HIDDEN);
+            int oh = scrollBodyPanel.getOffsetHeight();
+            int ch = scrollBodyPanel.getElement()
+                    .getPropertyInt("clientHeight");
+            contentAreaBorderHeight = oh - ch;
+            scrollBodyPanel.getElement().getStyle().setOverflow(Overflow.AUTO);
+        }
+        return contentAreaBorderHeight;
+    }
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.UIObject#setHeight(java.lang.String)
+     */
+    @Override
+    public void setHeight(String height) {
+        if (height.length() == 0
+                && getElement().getStyle().getHeight().length() != 0) {
+            /*
+             * Changing from defined to undefined size -> should do a size init
+             * to take page length into account again
+             */
+            sizeNeedsInit = true;
+        }
+        super.setHeight(height);
+    }
+
+    /** For internal use only. May be removed or replaced in the future. */
+    public void updateHeight() {
+        setContainerHeight();
+
+        if (initializedAndAttached) {
+            updatePageLength();
+        }
+        if (!rendering) {
+            // Webkit may sometimes get an odd rendering bug (white space
+            // between header and body), see bug #3875. Running
+            // overflow hack here to shake body element a bit.
+            // We must run the fix as a deferred command to prevent it from
+            // overwriting the scroll position with an outdated value, see
+            // #7607.
+            Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement());
+        }
+
+        triggerLazyColumnAdjustment(false);
+
+        /*
+         * setting height may affect wheter the component has scrollbars ->
+         * needs scrolling or not
+         */
+        setProperTabIndex();
+
+    }
+
+    /*
+     * Overridden due Table might not survive of visibility change (scroll pos
+     * lost). Example ITabPanel just set contained components invisible and back
+     * when changing tabs.
+     */
+
+    /* (non-Javadoc)
+     * @see com.google.gwt.user.client.ui.UIObject#setVisible(boolean)
+     */
+    @Override
+    public void setVisible(boolean visible) {
+        if (isVisible() != visible) {
+            super.setVisible(visible);
+            if (initializedAndAttached) {
+                if (visible) {
+                    Scheduler.get().scheduleDeferred(new Command() {
+
+                        @Override
+                        public void execute() {
+                            scrollBodyPanel
+                                    .setScrollPosition(measureRowHeightOffset(firstRowInViewPort));
+                        }
+                    });
+                }
+            }
+        }
+    }
+
+    /** Helper function to build html snippet for column or row headers.
+	 *
+	 * @param uidl
+	 *            possibly with values caption and icon
+	 * @return html snippet containing possibly an icon + caption text
+	 */
+    protected String buildCaptionHtmlSnippet(UIDL uidl) {
+        String s = uidl.hasAttribute("caption") ? uidl
+                .getStringAttribute("caption") : "";
+        if (uidl.hasAttribute("icon")) {
+            Icon icon = client.getIcon(uidl.getStringAttribute("icon"));
+            icon.setAlternateText("icon");
+            s = icon.getElement().getString() + s;
+        }
+        return s;
+    }
+
+    /** This method has logic which rows needs to be requested from server
+	 * when user scrolls.
+	 *
+	 * @param event
+	 *            the event
+	 */
+
+    @Override
+    public void onScroll(ScrollEvent event) {
+        // Do not handle scroll events while there is scroll initiated from
+        // server side which is not yet executed (#11454)
+        if (isLazyScrollerActive()) {
+            return;
+        }
+
+        scrollLeft = scrollBodyPanel.getElement().getScrollLeft();
+        scrollTop = scrollBodyPanel.getScrollPosition();
+        /*
+         * #6970 - IE sometimes fires scroll events for a detached table.
+         * 
+         * FIXME initializedAndAttached should probably be renamed - its name
+         * doesn't seem to reflect its semantics. onDetach() doesn't set it to
+         * false, and changing that might break something else, so we need to
+         * check isAttached() separately.
+         */
+        if (!initializedAndAttached || !isAttached()) {
+            return;
+        }
+        if (!enabled) {
+            scrollBodyPanel
+                    .setScrollPosition(measureRowHeightOffset(firstRowInViewPort));
+            return;
+        }
+
+        rowRequestHandler.cancel();
+
+        if (BrowserInfo.get().isSafari() && event != null && scrollTop == 0) {
+            // due to the webkitoverflowworkaround, top may sometimes report 0
+            // for webkit, although it really is not. Expecting to have the
+            // correct
+            // value available soon.
+            Scheduler.get().scheduleDeferred(new Command() {
+
+                @Override
+                public void execute() {
+                    onScroll(null);
+                }
+            });
+            return;
+        }
+
+        // fix headers horizontal scrolling
+        tHead.setHorizontalScrollPosition(scrollLeft);
+
+        // fix footers horizontal scrolling
+        tFoot.setHorizontalScrollPosition(scrollLeft);
+
+        if (totalRows == 0) {
+            // No rows, no need to fetch new rows
+            return;
+        }
+
+        firstRowInViewPort = calcFirstRowInViewPort();
+        int maxFirstRow = totalRows - pageLength;
+        if (firstRowInViewPort > maxFirstRow && maxFirstRow >= 0) {
+            firstRowInViewPort = maxFirstRow;
+        }
+
+        int postLimit = (int) (firstRowInViewPort + (pageLength - 1) + pageLength
+                * cache_react_rate);
+        if (postLimit > totalRows - 1) {
+            postLimit = totalRows - 1;
+        }
+        int preLimit = (int) (firstRowInViewPort - pageLength
+                * cache_react_rate);
+        if (preLimit < 0) {
+            preLimit = 0;
+        }
+        final int lastRendered = scrollBody.getLastRendered();
+        final int firstRendered = scrollBody.getFirstRendered();
+
+        if (postLimit <= lastRendered && preLimit >= firstRendered) {
+            // we're within no-react area, no need to request more rows
+            // remember which firstvisible we requested, in case the server has
+            // a differing opinion
+            lastRequestedFirstvisible = firstRowInViewPort;
+            client.updateVariable(paintableId, "firstvisible",
+                    firstRowInViewPort, false);
+            return;
+        }
+
+        if (allRenderedRowsAreNew()) {
+            // need a totally new set of rows
+            rowRequestHandler
+                    .setReqFirstRow((firstRowInViewPort - (int) (pageLength * cache_rate)));
+            int last = firstRowInViewPort + (int) (cache_rate * pageLength)
+                    + pageLength - 1;
+            if (last >= totalRows) {
+                last = totalRows - 1;
+            }
+            rowRequestHandler.setReqRows(last
+                    - rowRequestHandler.getReqFirstRow() + 1);
+            updatedReqRows = false;
+            rowRequestHandler.deferRowFetch();
+            return;
+        }
+        if (preLimit < firstRendered) {
+            // need some rows to the beginning of the rendered area
+            rowRequestHandler
+                    .setReqFirstRow((int) (firstRowInViewPort - pageLength
+                            * cache_rate));
+            rowRequestHandler.setReqRows(firstRendered
+                    - rowRequestHandler.getReqFirstRow());
+            rowRequestHandler.deferRowFetch();
+
+            return;
+        }
+        if (postLimit > lastRendered) {
+            // need some rows to the end of the rendered area
+            int reqRows = (int) ((firstRowInViewPort + pageLength + pageLength
+                    * cache_rate) - lastRendered);
+            rowRequestHandler.triggerRowFetch(lastRendered + 1, reqRows);
+        }
+    }
+
+    /** All rendered rows are new.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean allRenderedRowsAreNew() {
+        int firstRowInViewPort = calcFirstRowInViewPort();
+        int firstRendered = scrollBody.getFirstRendered();
+        int lastRendered = scrollBody.getLastRendered();
+        return (firstRowInViewPort - pageLength * cache_rate > lastRendered || firstRowInViewPort
+                + pageLength + pageLength * cache_rate < firstRendered);
+    }
+
+    /** Calc first row in view port.
+	 *
+	 * @return the int
+	 */
+    protected int calcFirstRowInViewPort() {
+        return (int) Math.ceil(scrollTop / scrollBody.getRowHeight());
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.dd.VHasDropHandler#getDropHandler()
+     */
+    @Override
+    public VScrollTableDropHandler getDropHandler() {
+        return dropHandler;
+    }
+
+    /** The Class TableDDDetails.
+	 */
+    private static class TableDDDetails {
+        
+        /** The overkey. */
+        int overkey = -1;
+        
+        /** The drop location. */
+        VerticalDropLocation dropLocation;
+        
+        /** The colkey. */
+        String colkey;
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof TableDDDetails) {
+                TableDDDetails other = (TableDDDetails) obj;
+                return dropLocation == other.dropLocation
+                        && overkey == other.overkey
+                        && ((colkey != null && colkey.equals(other.colkey)) || (colkey == null && other.colkey == null));
+            }
+            return false;
+        }
+
+        //
+        // public int hashCode() {
+        // return overkey;
+        // }
+    }
+
+    /** The Class VScrollTableDropHandler.
+	 */
+    public class VScrollTableDropHandler extends VAbstractDropHandler {
+
+        /** The Constant ROWSTYLEBASE. */
+        private static final String ROWSTYLEBASE = "v-table-row-drag-";
+        
+        /** The drop details. */
+        private TableDDDetails dropDetails;
+        
+        /** The last emphasized. */
+        private TableDDDetails lastEmphasized;
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#dragEnter(com.vaadin.client.ui.dd.VDragEvent)
+         */
+        @Override
+        public void dragEnter(VDragEvent drag) {
+            updateDropDetails(drag);
+            super.dragEnter(drag);
+        }
+
+        /** Update drop details.
+		 *
+		 * @param drag
+		 *            the drag
+		 */
+        private void updateDropDetails(VDragEvent drag) {
+            dropDetails = new TableDDDetails();
+            Element elementOver = drag.getElementOver();
+
+            Class<? extends Widget> clazz = getRowClass();
+            VScrollTableRow row = null;
+            if (clazz != null) {
+                row = Util.findWidget(elementOver, clazz);
+            }
+            if (row != null) {
+                dropDetails.overkey = row.rowKey;
+                Element tr = row.getElement();
+                Element element = elementOver;
+                while (element != null && element.getParentElement() != tr) {
+                    element = element.getParentElement();
+                }
+                int childIndex = DOM.getChildIndex(tr, element);
+                dropDetails.colkey = tHead.getHeaderCell(childIndex)
+                        .getColKey();
+                dropDetails.dropLocation = DDUtil.getVerticalDropLocation(
+                        row.getElement(), drag.getCurrentGwtEvent(), 0.2);
+            }
+
+            drag.getDropDetails().put("itemIdOver", dropDetails.overkey + "");
+            drag.getDropDetails().put(
+                    "detail",
+                    dropDetails.dropLocation != null ? dropDetails.dropLocation
+                            .toString() : null);
+
+        }
+
+        /** Gets the row class.
+		 *
+		 * @return the row class
+		 */
+        private Class<? extends Widget> getRowClass() {
+            // get the row type this way to make dd work in derived
+            // implementations
+            Iterator<Widget> iterator = scrollBody.iterator();
+            if (iterator.hasNext()) {
+                return iterator.next().getClass();
+            } else {
+                return null;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#dragOver(com.vaadin.client.ui.dd.VDragEvent)
+         */
+        @Override
+        public void dragOver(VDragEvent drag) {
+            TableDDDetails oldDetails = dropDetails;
+            updateDropDetails(drag);
+            if (!oldDetails.equals(dropDetails)) {
+                deEmphasis();
+                final TableDDDetails newDetails = dropDetails;
+                VAcceptCallback cb = new VAcceptCallback() {
+
+                    @Override
+                    public void accepted(VDragEvent event) {
+                        if (newDetails.equals(dropDetails)) {
+                            dragAccepted(event);
+                        }
+                        /*
+                         * Else new target slot already defined, ignore
+                         */
+                    }
+                };
+                validate(cb, drag);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#dragLeave(com.vaadin.client.ui.dd.VDragEvent)
+         */
+        @Override
+        public void dragLeave(VDragEvent drag) {
+            deEmphasis();
+            super.dragLeave(drag);
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#drop(com.vaadin.client.ui.dd.VDragEvent)
+         */
+        @Override
+        public boolean drop(VDragEvent drag) {
+            deEmphasis();
+            return super.drop(drag);
+        }
+
+        /** De emphasis.
+		 */
+        private void deEmphasis() {
+            UIObject.setStyleName(getElement(),
+                    getStylePrimaryName() + "-drag", false);
+            if (lastEmphasized == null) {
+                return;
+            }
+            for (Widget w : scrollBody.renderedRows) {
+                VScrollTableRow row = (VScrollTableRow) w;
+                if (lastEmphasized != null
+                        && row.rowKey == lastEmphasized.overkey) {
+                    String stylename = ROWSTYLEBASE
+                            + lastEmphasized.dropLocation.toString()
+                                    .toLowerCase();
+                    VScrollTableRow.setStyleName(row.getElement(), stylename,
+                            false);
+                    lastEmphasized = null;
+                    return;
+                }
+            }
+        }
+
+        /** TODO needs different drop modes ?? (on cells, on rows), now only
+		 * supports rows.
+		 *
+		 * @param details
+		 *            the details
+		 */
+        private void emphasis(TableDDDetails details) {
+            deEmphasis();
+            UIObject.setStyleName(getElement(),
+                    getStylePrimaryName() + "-drag", true);
+            // iterate old and new emphasized row
+            for (Widget w : scrollBody.renderedRows) {
+                VScrollTableRow row = (VScrollTableRow) w;
+                if (details != null && details.overkey == row.rowKey) {
+                    String stylename = ROWSTYLEBASE
+                            + details.dropLocation.toString().toLowerCase();
+                    VScrollTableRow.setStyleName(row.getElement(), stylename,
+                            true);
+                    lastEmphasized = details;
+                    return;
+                }
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#dragAccepted(com.vaadin.client.ui.dd.VDragEvent)
+         */
+        @Override
+        protected void dragAccepted(VDragEvent drag) {
+            emphasis(dropDetails);
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VAbstractDropHandler#getConnector()
+         */
+        @Override
+        public ComponentConnector getConnector() {
+            return ConnectorMap.get(client).getConnector(
+                    VCustomScrollTable.this);
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.client.ui.dd.VDropHandler#getApplicationConnection()
+         */
+        @Override
+        public ApplicationConnection getApplicationConnection() {
+            return client;
+        }
+
+    }
+
+    /** Gets the currently focused row.
+	 *
+	 * @return the currently focused row
+	 */
+    protected VScrollTableRow getFocusedRow() {
+        return focusedRow;
+    }
+
+    /** Moves the selection head to a specific row.
+	 *
+	 * @param row
+	 *            The row to where the selection head should move
+	 * @return Returns true if focus was moved successfully, else false
+	 */
+    public boolean setRowFocus(VScrollTableRow row) {
+
+        if (!isSelectable()) {
+            return false;
+        }
+
+        // Remove previous selection
+        if (focusedRow != null && focusedRow != row) {
+            focusedRow.removeStyleName(getStylePrimaryName() + "-focus");
+        }
+
+        if (row != null) {
+            // Apply focus style to new selection
+            row.addStyleName(getStylePrimaryName() + "-focus");
+
+            /*
+             * Trying to set focus on already focused row
+             */
+            if (row == focusedRow) {
+                return false;
+            }
+
+            // Set new focused row
+            focusedRow = row;
+
+            ensureRowIsVisible(row);
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /** Ensures that the row is visible.
+	 *
+	 * @param row
+	 *            The row to ensure is visible
+	 */
+    private void ensureRowIsVisible(VScrollTableRow row) {
+        if (BrowserInfo.get().isTouchDevice()) {
+            // Skip due to android devices that have broken scrolltop will may
+            // get odd scrolling here.
+            return;
+        }
+        /*
+         * FIXME The next line doesn't always do what expected, because if the
+         * row is not in the DOM it won't scroll to it.
+         */
+        Util.scrollIntoViewVertically(row.getElement());
+    }
+
+    /** Handles the keyboard events handled by the table.
+	 *
+	 * @param keycode
+	 *            the keycode
+	 * @param ctrl
+	 *            the ctrl
+	 * @param shift
+	 *            the shift
+	 * @return true iff the navigation event was handled
+	 */
+    protected boolean handleNavigation(int keycode, boolean ctrl, boolean shift) {
+        if (keycode == KeyCodes.KEY_TAB || keycode == KeyCodes.KEY_SHIFT) {
+            // Do not handle tab key
+            return false;
+        }
+
+        // Down navigation
+        if (!isSelectable() && keycode == getNavigationDownKey()) {
+            scrollBodyPanel.setScrollPosition(scrollBodyPanel
+                    .getScrollPosition() + scrollingVelocity);
+            return true;
+        } else if (keycode == getNavigationDownKey()) {
+            if (isMultiSelectModeAny() && moveFocusDown()) {
+                selectFocusedRow(ctrl, shift);
+
+            } else if (isSingleSelectMode() && !shift && moveFocusDown()) {
+                selectFocusedRow(ctrl, shift);
+            }
+            return true;
+        }
+
+        // Up navigation
+        if (!isSelectable() && keycode == getNavigationUpKey()) {
+            scrollBodyPanel.setScrollPosition(scrollBodyPanel
+                    .getScrollPosition() - scrollingVelocity);
+            return true;
+        } else if (keycode == getNavigationUpKey()) {
+            if (isMultiSelectModeAny() && moveFocusUp()) {
+                selectFocusedRow(ctrl, shift);
+            } else if (isSingleSelectMode() && !shift && moveFocusUp()) {
+                selectFocusedRow(ctrl, shift);
+            }
+            return true;
+        }
+
+        if (keycode == getNavigationLeftKey()) {
+            // Left navigation
+            scrollBodyPanel.setHorizontalScrollPosition(scrollBodyPanel
+                    .getHorizontalScrollPosition() - scrollingVelocity);
+            return true;
+
+        } else if (keycode == getNavigationRightKey()) {
+            // Right navigation
+            scrollBodyPanel.setHorizontalScrollPosition(scrollBodyPanel
+                    .getHorizontalScrollPosition() + scrollingVelocity);
+            return true;
+        }
+
+        // Select navigation
+        if (isSelectable() && keycode == getNavigationSelectKey()) {
+            if (isSingleSelectMode()) {
+                boolean wasSelected = focusedRow.isSelected();
+                deselectAll();
+                if (!wasSelected || !nullSelectionAllowed) {
+                    focusedRow.toggleSelection();
+                }
+            } else {
+                focusedRow.toggleSelection();
+                removeRowFromUnsentSelectionRanges(focusedRow);
+            }
+
+            sendSelectedRows();
+            return true;
+        }
+
+        // Page Down navigation
+        if (keycode == getNavigationPageDownKey()) {
+            if (isSelectable()) {
+                /*
+                 * If selectable we plagiate MSW behaviour: first scroll to the
+                 * end of current view. If at the end, scroll down one page
+                 * length and keep the selected row in the bottom part of
+                 * visible area.
+                 */
+                if (!isFocusAtTheEndOfTable()) {
+                    VScrollTableRow lastVisibleRowInViewPort = scrollBody
+                            .getRowByRowIndex(firstRowInViewPort
+                                    + getFullyVisibleRowCount() - 1);
+                    if (lastVisibleRowInViewPort != null
+                            && lastVisibleRowInViewPort != focusedRow) {
+                        // focused row is not at the end of the table, move
+                        // focus and select the last visible row
+                        setRowFocus(lastVisibleRowInViewPort);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    } else {
+                        int indexOfToBeFocused = focusedRow.getIndex()
+                                + getFullyVisibleRowCount();
+                        if (indexOfToBeFocused >= totalRows) {
+                            indexOfToBeFocused = totalRows - 1;
+                        }
+                        VScrollTableRow toBeFocusedRow = scrollBody
+                                .getRowByRowIndex(indexOfToBeFocused);
+
+                        if (toBeFocusedRow != null) {
+                            /*
+                             * if the next focused row is rendered
+                             */
+                            setRowFocus(toBeFocusedRow);
+                            selectFocusedRow(ctrl, shift);
+                            // TODO needs scrollintoview ?
+                            sendSelectedRows();
+                        } else {
+                            // scroll down by pixels and return, to wait for
+                            // new rows, then select the last item in the
+                            // viewport
+                            selectLastItemInNextRender = true;
+                            multiselectPending = shift;
+                            scrollByPagelenght(1);
+                        }
+                    }
+                }
+            } else {
+                /* No selections, go page down by scrolling */
+                scrollByPagelenght(1);
+            }
+            return true;
+        }
+
+        // Page Up navigation
+        if (keycode == getNavigationPageUpKey()) {
+            if (isSelectable()) {
+                /*
+                 * If selectable we plagiate MSW behaviour: first scroll to the
+                 * end of current view. If at the end, scroll down one page
+                 * length and keep the selected row in the bottom part of
+                 * visible area.
+                 */
+                if (!isFocusAtTheBeginningOfTable()) {
+                    VScrollTableRow firstVisibleRowInViewPort = scrollBody
+                            .getRowByRowIndex(firstRowInViewPort);
+                    if (firstVisibleRowInViewPort != null
+                            && firstVisibleRowInViewPort != focusedRow) {
+                        // focus is not at the beginning of the table, move
+                        // focus and select the first visible row
+                        setRowFocus(firstVisibleRowInViewPort);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    } else {
+                        int indexOfToBeFocused = focusedRow.getIndex()
+                                - getFullyVisibleRowCount();
+                        if (indexOfToBeFocused < 0) {
+                            indexOfToBeFocused = 0;
+                        }
+                        VScrollTableRow toBeFocusedRow = scrollBody
+                                .getRowByRowIndex(indexOfToBeFocused);
+
+                        if (toBeFocusedRow != null) { // if the next focused row
+                                                      // is rendered
+                            setRowFocus(toBeFocusedRow);
+                            selectFocusedRow(ctrl, shift);
+                            // TODO needs scrollintoview ?
+                            sendSelectedRows();
+                        } else {
+                            // unless waiting for the next rowset already
+                            // scroll down by pixels and return, to wait for
+                            // new rows, then select the last item in the
+                            // viewport
+                            selectFirstItemInNextRender = true;
+                            multiselectPending = shift;
+                            scrollByPagelenght(-1);
+                        }
+                    }
+                }
+            } else {
+                /* No selections, go page up by scrolling */
+                scrollByPagelenght(-1);
+            }
+
+            return true;
+        }
+
+        // Goto start navigation
+        if (keycode == getNavigationStartKey()) {
+            scrollBodyPanel.setScrollPosition(0);
+            if (isSelectable()) {
+                if (focusedRow != null && focusedRow.getIndex() == 0) {
+                    return false;
+                } else {
+                    VScrollTableRow rowByRowIndex = (VScrollTableRow) scrollBody
+                            .iterator().next();
+                    if (rowByRowIndex.getIndex() == 0) {
+                        setRowFocus(rowByRowIndex);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    } else {
+                        // first row of table will come in next row fetch
+                        if (ctrl) {
+                            focusFirstItemInNextRender = true;
+                        } else {
+                            selectFirstItemInNextRender = true;
+                            multiselectPending = shift;
+                        }
+                    }
+                }
+            }
+            return true;
+        }
+
+        // Goto end navigation
+        if (keycode == getNavigationEndKey()) {
+            scrollBodyPanel.setScrollPosition(scrollBody.getOffsetHeight());
+            if (isSelectable()) {
+                final int lastRendered = scrollBody.getLastRendered();
+                if (lastRendered + 1 == totalRows) {
+                    VScrollTableRow rowByRowIndex = scrollBody
+                            .getRowByRowIndex(lastRendered);
+                    if (focusedRow != rowByRowIndex) {
+                        setRowFocus(rowByRowIndex);
+                        selectFocusedRow(ctrl, shift);
+                        sendSelectedRows();
+                    }
+                } else {
+                    if (ctrl) {
+                        focusLastItemInNextRender = true;
+                    } else {
+                        selectLastItemInNextRender = true;
+                        multiselectPending = shift;
+                    }
+                }
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    /** Checks if is focus at the beginning of table.
+	 *
+	 * @return true, if is focus at the beginning of table
+	 */
+    private boolean isFocusAtTheBeginningOfTable() {
+        return focusedRow.getIndex() == 0;
+    }
+
+    /** Checks if is focus at the end of table.
+	 *
+	 * @return true, if is focus at the end of table
+	 */
+    private boolean isFocusAtTheEndOfTable() {
+        return focusedRow.getIndex() + 1 >= totalRows;
+    }
+
+    /** Gets the fully visible row count.
+	 *
+	 * @return the fully visible row count
+	 */
+    private int getFullyVisibleRowCount() {
+        return (int) (scrollBodyPanel.getOffsetHeight() / scrollBody
+                .getRowHeight());
+    }
+
+    /** Scroll by pagelenght.
+	 *
+	 * @param i
+	 *            the i
+	 */
+    private void scrollByPagelenght(int i) {
+        int pixels = i * scrollBodyPanel.getOffsetHeight();
+        int newPixels = scrollBodyPanel.getScrollPosition() + pixels;
+        if (newPixels < 0) {
+            newPixels = 0;
+        } // else if too high, NOP (all know browsers accept illegally big
+          // values here)
+        scrollBodyPanel.setScrollPosition(newPixels);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event
+     * .dom.client.FocusEvent)
+     */
+
+    @Override
+    public void onFocus(FocusEvent event) {
+        if (isFocusable()) {
+            hasFocus = true;
+
+            // Focus a row if no row is in focus
+            if (focusedRow == null) {
+                focusRowFromBody();
+            } else {
+                setRowFocus(focusedRow);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event
+     * .dom.client.BlurEvent)
+     */
+
+    @Override
+    public void onBlur(BlurEvent event) {
+        hasFocus = false;
+        navKeyDown = false;
+
+        if (BrowserInfo.get().isIE()) {
+            /*
+             * IE sometimes moves focus to a clicked table cell... (#7965)
+             * ...and sometimes it sends blur events even though the focus
+             * handler is still active. (#10464)
+             */
+            Element focusedElement = Util.getIEFocusedElement();
+            if (Util.getConnectorForElement(client, getParent(), focusedElement) == this
+                    && focusedElement != null
+                    && focusedElement != scrollBodyPanel.getFocusElement()) {
+                /*
+                 * Steal focus back to the focus handler if it was moved to some
+                 * other part of the table. Avoid stealing focus in other cases.
+                 */
+                focus();
+                return;
+            }
+        }
+
+        if (isFocusable()) {
+            // Unfocus any row
+            setRowFocus(null);
+        }
+    }
+
+    /** Removes a key from a range if the key is found in a selected range.
+	 *
+	 * @param row
+	 *            the row
+	 */
+    private void removeRowFromUnsentSelectionRanges(VScrollTableRow row) {
+        Collection<SelectionRange> newRanges = null;
+        for (Iterator<SelectionRange> iterator = selectedRowRanges.iterator(); iterator
+                .hasNext();) {
+            SelectionRange range = iterator.next();
+            if (range.inRange(row)) {
+                // Split the range if given row is in range
+                Collection<SelectionRange> splitranges = range.split(row);
+                if (newRanges == null) {
+                    newRanges = new ArrayList<SelectionRange>();
+                }
+                newRanges.addAll(splitranges);
+                iterator.remove();
+            }
+        }
+        if (newRanges != null) {
+            selectedRowRanges.addAll(newRanges);
+        }
+    }
+
+    /** Can the Table be focused?.
+	 *
+	 * @return True if the table can be focused, else false
+	 */
+    public boolean isFocusable() {
+        if (scrollBody != null && enabled) {
+            return !(!hasHorizontalScrollbar() && !hasVerticalScrollbar() && !isSelectable());
+        }
+        return false;
+    }
+
+    /** Checks for horizontal scrollbar.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean hasHorizontalScrollbar() {
+        return scrollBody.getOffsetWidth() > scrollBodyPanel.getOffsetWidth();
+    }
+
+    /** Checks for vertical scrollbar.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean hasVerticalScrollbar() {
+        return scrollBody.getOffsetHeight() > scrollBodyPanel.getOffsetHeight();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.client.Focusable#focus()
+     */
+
+    @Override
+    public void focus() {
+        if (isFocusable()) {
+            scrollBodyPanel.focus();
+        }
+    }
+
+    /**
+     * Sets the proper tabIndex for scrollBodyPanel (the focusable elemen in the
+     * component).
+     * <p>
+     * If the component has no explicit tabIndex a zero is given (default
+     * tabbing order based on dom hierarchy) or -1 if the component does not
+     * need to gain focus. The component needs no focus if it has no scrollabars
+     * (not scrollable) and not selectable. Note that in the future shortcut
+     * actions may need focus.
+     * <p>
+     * For internal use only. May be removed or replaced in the future.
+     */
+    public void setProperTabIndex() {
+        int storedScrollTop = 0;
+        int storedScrollLeft = 0;
+
+        if (BrowserInfo.get().getOperaVersion() >= 11) {
+            // Workaround for Opera scroll bug when changing tabIndex (#6222)
+            storedScrollTop = scrollBodyPanel.getScrollPosition();
+            storedScrollLeft = scrollBodyPanel.getHorizontalScrollPosition();
+        }
+
+        if (tabIndex == 0 && !isFocusable()) {
+            scrollBodyPanel.setTabIndex(-1);
+        } else {
+            scrollBodyPanel.setTabIndex(tabIndex);
+        }
+
+        if (BrowserInfo.get().getOperaVersion() >= 11) {
+            // Workaround for Opera scroll bug when changing tabIndex (#6222)
+            scrollBodyPanel.setScrollPosition(storedScrollTop);
+            scrollBodyPanel.setHorizontalScrollPosition(storedScrollLeft);
+        }
+    }
+
+    /** Start scrolling velocity timer.
+	 */
+    public void startScrollingVelocityTimer() {
+        if (scrollingVelocityTimer == null) {
+            scrollingVelocityTimer = new Timer() {
+
+                @Override
+                public void run() {
+                    scrollingVelocity++;
+                }
+            };
+            scrollingVelocityTimer.scheduleRepeating(100);
+        }
+    }
+
+    /** Cancel scrolling velocity timer.
+	 */
+    public void cancelScrollingVelocityTimer() {
+        if (scrollingVelocityTimer != null) {
+            // Remove velocityTimer if it exists and the Table is disabled
+            scrollingVelocityTimer.cancel();
+            scrollingVelocityTimer = null;
+            scrollingVelocity = 10;
+        }
+    }
+
+    /** Checks if is navigation key.
+	 *
+	 * @param keyCode
+	 *            the key code
+	 * @return true if the given keyCode is used by the table for navigation
+	 */
+    private boolean isNavigationKey(int keyCode) {
+        return keyCode == getNavigationUpKey()
+                || keyCode == getNavigationLeftKey()
+                || keyCode == getNavigationRightKey()
+                || keyCode == getNavigationDownKey()
+                || keyCode == getNavigationPageUpKey()
+                || keyCode == getNavigationPageDownKey()
+                || keyCode == getNavigationEndKey()
+                || keyCode == getNavigationStartKey();
+    }
+
+    /** Lazy revert focus to row.
+	 *
+	 * @param currentlyFocusedRow
+	 *            the currently focused row
+	 */
+    public void lazyRevertFocusToRow(final VScrollTableRow currentlyFocusedRow) {
+        Scheduler.get().scheduleFinally(new ScheduledCommand() {
+            @Override
+            public void execute() {
+                if (currentlyFocusedRow != null) {
+                    setRowFocus(currentlyFocusedRow);
+                } else {
+                    VConsole.log("no row?");
+                    focusRowFromBody();
+                }
+                scrollBody.ensureFocus();
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.ActionOwner#getActions()
+     */
+    @Override
+    public Action[] getActions() {
+        if (bodyActionKeys == null) {
+            return new Action[] {};
+        }
+        final Action[] actions = new Action[bodyActionKeys.length];
+        for (int i = 0; i < actions.length; i++) {
+            final String actionKey = bodyActionKeys[i];
+            Action bodyAction = new TreeAction(this, null, actionKey);
+            bodyAction.setCaption(getActionCaption(actionKey));
+            bodyAction.setIconUrl(getActionIcon(actionKey));
+            actions[i] = bodyAction;
+        }
+        return actions;
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.ActionOwner#getClient()
+     */
+    @Override
+    public ApplicationConnection getClient() {
+        return client;
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.ActionOwner#getPaintableId()
+     */
+    @Override
+    public String getPaintableId() {
+        return paintableId;
+    }
+
+    /**
+     * Add this to the element mouse down event by using element.setPropertyJSO
+     * ("onselectstart",applyDisableTextSelectionIEHack()); Remove it then again
+     * when the mouse is depressed in the mouse up event.
+     * 
+     * @return Returns the JSO preventing text selection
+     */
+    private static native JavaScriptObject getPreventTextSelectionIEHack()
+    /*-{
+            return function(){ return false; };
+    }-*/;
+
+    /** Trigger lazy column adjustment.
+	 *
+	 * @param now
+	 *            the now
+	 */
+    public void triggerLazyColumnAdjustment(boolean now) {
+        lazyAdjustColumnWidths.cancel();
+        if (now) {
+            lazyAdjustColumnWidths.run();
+        } else {
+            lazyAdjustColumnWidths.schedule(LAZY_COLUMN_ADJUST_TIMEOUT);
+        }
+    }
+
+    /** Checks if is dynamic width.
+	 *
+	 * @return true, if is dynamic width
+	 */
+    private boolean isDynamicWidth() {
+        ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+                this);
+        return paintable.isUndefinedWidth();
+    }
+
+    /** Checks if is dynamic height.
+	 *
+	 * @return true, if is dynamic height
+	 */
+    protected boolean isDynamicHeight() {
+        ComponentConnector paintable = ConnectorMap.get(client).getConnector(
+                this);
+        if (paintable == null) {
+            // This should be refactored. As isDynamicHeight can be called from
+            // a timer it is possible that the connector has been unregistered
+            // when this method is called, causing getConnector to return null.
+            return false;
+        }
+        return paintable.isUndefinedHeight();
+    }
+
+    /** Debug.
+	 *
+	 * @param msg
+	 *            the msg
+	 */
+    private void debug(String msg) {
+        if (enableDebug) {
+            VConsole.error(msg);
+        }
+    }
+
+    /** Gets the widget for paintable.
+	 *
+	 * @return the widget for paintable
+	 */
+    public Widget getWidgetForPaintable() {
+        return this;
+    }
+
+    /** The Constant SUBPART_HEADER. */
+    private static final String SUBPART_HEADER = "header";
+    
+    /** The Constant SUBPART_FOOTER. */
+    private static final String SUBPART_FOOTER = "footer";
+    
+    /** The Constant SUBPART_ROW. */
+    private static final String SUBPART_ROW = "row";
+    
+    /** The Constant SUBPART_COL. */
+    private static final String SUBPART_COL = "col";
+    
+    /** Matches header[ix] - used for extracting the index of the targeted
+	 * header cell.
+	 */
+    private static final RegExp SUBPART_HEADER_REGEXP = RegExp
+            .compile(SUBPART_HEADER + "\\[(\\d+)\\]");
+    
+    /** Matches footer[ix] - used for extracting the index of the targeted
+	 * footer cell.
+	 */
+    private static final RegExp SUBPART_FOOTER_REGEXP = RegExp
+            .compile(SUBPART_FOOTER + "\\[(\\d+)\\]");
+    
+    /** Matches row[ix] - used for extracting the index of the targeted row. */
+    private static final RegExp SUBPART_ROW_REGEXP = RegExp.compile(SUBPART_ROW
+            + "\\[(\\d+)]");
+    
+    /** Matches col[ix] - used for extracting the index of the targeted
+	 * column.
+	 */
+    private static final RegExp SUBPART_ROW_COL_REGEXP = RegExp
+            .compile(SUBPART_ROW + "\\[(\\d+)\\]/" + SUBPART_COL
+                    + "\\[(\\d+)\\]");
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.SubPartAware#getSubPartElement(java.lang.String)
+     */
+    @Override
+    public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
+        if (SUBPART_ROW_COL_REGEXP.test(subPart)) {
+            MatchResult result = SUBPART_ROW_COL_REGEXP.exec(subPart);
+            int rowIx = Integer.valueOf(result.getGroup(1));
+            int colIx = Integer.valueOf(result.getGroup(2));
+            VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
+            if (row != null) {
+                Element rowElement = row.getElement();
+                if (colIx < rowElement.getChildCount()) {
+                    return rowElement.getChild(colIx).getFirstChild().cast();
+                }
+            }
+
+        } else if (SUBPART_ROW_REGEXP.test(subPart)) {
+            MatchResult result = SUBPART_ROW_REGEXP.exec(subPart);
+            int rowIx = Integer.valueOf(result.getGroup(1));
+            VScrollTableRow row = scrollBody.getRowByRowIndex(rowIx);
+            if (row != null) {
+                return row.getElement();
+            }
+
+        } else if (SUBPART_HEADER_REGEXP.test(subPart)) {
+            MatchResult result = SUBPART_HEADER_REGEXP.exec(subPart);
+            int headerIx = Integer.valueOf(result.getGroup(1));
+            HeaderCell headerCell = tHead.getHeaderCell(headerIx);
+            if (headerCell != null) {
+                return headerCell.getElement();
+            }
+
+        } else if (SUBPART_FOOTER_REGEXP.test(subPart)) {
+            MatchResult result = SUBPART_FOOTER_REGEXP.exec(subPart);
+            int footerIx = Integer.valueOf(result.getGroup(1));
+            FooterCell footerCell = tFoot.getFooterCell(footerIx);
+            if (footerCell != null) {
+                return footerCell.getElement();
+            }
+        }
+        // Nothing found.
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.ui.SubPartAware#getSubPartName(com.google.gwt.user.client.Element)
+     */
+    @Override
+    public String getSubPartName(com.google.gwt.user.client.Element subElement) {
+        Widget widget = Util.findWidget(subElement, null);
+        if (widget instanceof HeaderCell) {
+            return SUBPART_HEADER + "[" + tHead.visibleCells.indexOf(widget)
+                    + "]";
+        } else if (widget instanceof FooterCell) {
+            return SUBPART_FOOTER + "[" + tFoot.visibleCells.indexOf(widget)
+                    + "]";
+        } else if (widget instanceof VScrollTableRow) {
+            // a cell in a row
+            VScrollTableRow row = (VScrollTableRow) widget;
+            int rowIx = scrollBody.indexOf(row);
+            if (rowIx >= 0) {
+                int colIx = -1;
+                for (int ix = 0; ix < row.getElement().getChildCount(); ix++) {
+                    if (row.getElement().getChild(ix).isOrHasChild(subElement)) {
+                        colIx = ix;
+                        break;
+                    }
+                }
+                if (colIx >= 0) {
+                    return SUBPART_ROW + "[" + rowIx + "]/" + SUBPART_COL + "["
+                            + colIx + "]";
+                }
+                return SUBPART_ROW + "[" + rowIx + "]";
+            }
+        }
+        // Nothing found.
+        return null;
+    }
+
+    /** On unregister.
+	 *
+	 * @since 7.2.6
+	 */
+    public void onUnregister() {
+        if (addCloseHandler != null) {
+            addCloseHandler.removeHandler();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.client.DeferredWorker#isWorkPending()
+     */
+    /*
+     * Return true if component need to perform some work and false otherwise.
+     */
+    @Override
+    public boolean isWorkPending() {
+        return lazyAdjustColumnWidths.isRunning();
+    }
+
+    /** Gets the logger.
+	 *
+	 * @return the logger
+	 */
+    private static Logger getLogger() {
+        return Logger.getLogger(VCustomScrollTable.class.getName());
+    }
+}
diff --git a/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTable.java b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTable.java
new file mode 100644
index 0000000..5456948
--- /dev/null
+++ b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTable.java
@@ -0,0 +1,6638 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.data.Container;
+import com.vaadin.data.Item;
+import com.vaadin.data.Property;
+import com.vaadin.data.util.ContainerOrderedWrapper;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.data.util.converter.Converter;
+import com.vaadin.data.util.converter.ConverterUtil;
+import com.vaadin.event.Action;
+import com.vaadin.event.Action.Handler;
+import com.vaadin.event.DataBoundTransferable;
+import com.vaadin.event.ItemClickEvent;
+import com.vaadin.event.ItemClickEvent.ItemClickListener;
+import com.vaadin.event.ItemClickEvent.ItemClickNotifier;
+import com.vaadin.event.MouseEvents.ClickEvent;
+import com.vaadin.event.dd.DragAndDropEvent;
+import com.vaadin.event.dd.DragSource;
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.event.dd.DropTarget;
+import com.vaadin.event.dd.acceptcriteria.ServerSideCriterion;
+import com.vaadin.server.KeyMapper;
+import com.vaadin.server.LegacyCommunicationManager;
+import com.vaadin.server.LegacyPaint;
+import com.vaadin.server.PaintException;
+import com.vaadin.server.PaintTarget;
+import com.vaadin.server.Resource;
+import com.vaadin.shared.MouseEventDetails;
+import com.vaadin.shared.ui.MultiSelectMode;
+import com.vaadin.shared.ui.table.TableConstants;
+
+// TODO: Auto-generated Javadoc
+/**
+ * <p>
+ * <code>CustomTable</code> is used for representing data or components in a
+ * pageable and selectable CustomTable.
+ * 
+ * 
+ * <p>
+ * Scalability of the CustomTable is largely dictated by the container. A table
+ * does not have a limit for the number of items and is just as fast with
+ * hundreds of thousands of items as with just a few. The current GWT
+ * implementation with scrolling however limits the number of rows to around
+ * 500000, depending on the browser and the pixel height of rows.
+ * 
+ * 
+ * <p>
+ * Components in a CustomTable will not have their caption nor icon rendered.
+ * 
+ * 
+ * @author Vaadin Ltd.
+ * @since 3.0
+ */
+@SuppressWarnings({ "deprecation" })
+public class CustomTable extends AbstractSelect implements Action.Container,
+        Container.Ordered, Container.Sortable, ItemClickNotifier, DragSource,
+        DropTarget, HasComponents {
+
+    /** The logger. */
+    private transient Logger logger = null;
+
+    /**
+     * Modes that CustomTable support as drag sourse.
+     */
+    public enum TableDragMode {
+        /**
+         * CustomTable does not start drag and drop events. HTM5 style events
+         * started by browser may still happen.
+         */
+        NONE,
+        /**
+         * CustomTable starts drag with a one row only.
+         */
+        ROW,
+        /**
+         * CustomTable drags selected rows, if drag starts on a selected rows.
+         * Else it starts like in ROW mode. Note, that in Transferable there
+         * will still be only the row on which the drag started, other dragged
+         * rows need to be checked from the source CustomTable.
+         */
+        MULTIROW
+    }
+
+    /** The Constant CELL_KEY. */
+    protected static final int CELL_KEY = 0;
+
+    /** The Constant CELL_HEADER. */
+    protected static final int CELL_HEADER = 1;
+
+    /** The Constant CELL_ICON. */
+    protected static final int CELL_ICON = 2;
+
+    /** The Constant CELL_ITEMID. */
+    protected static final int CELL_ITEMID = 3;
+
+    /** The Constant CELL_GENERATED_ROW. */
+    protected static final int CELL_GENERATED_ROW = 4;
+
+    /** The Constant CELL_FIRSTCOL. */
+    protected static final int CELL_FIRSTCOL = 5;
+
+    /** The Enum Align.
+	 */
+    public enum Align {
+        /**
+         * Left column alignment. <b>This is the default behaviour. </b>
+         */
+        LEFT("b"),
+
+        /**
+         * Center column alignment.
+         */
+        CENTER("c"),
+
+        /**
+         * Right column alignment.
+         */
+        RIGHT("e");
+
+        /** The alignment. */
+        private String alignment;
+
+        /** Instantiates a new align.
+		 *
+		 * @param alignment
+		 *            the alignment
+		 */
+        private Align(String alignment) {
+            this.alignment = alignment;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Enum#toString()
+         */
+        @Override
+        public String toString() {
+            return alignment;
+        }
+
+        /** Convert string to align.
+		 *
+		 * @param string
+		 *            the string
+		 * @return the align
+		 */
+        public Align convertStringToAlign(String string) {
+            if (string == null) {
+                return null;
+            }
+            if (string.equals("b")) {
+                return Align.LEFT;
+            } else if (string.equals("c")) {
+                return Align.CENTER;
+            } else if (string.equals("e")) {
+                return Align.RIGHT;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /** The Constant ALIGN_LEFT.
+	 *
+	 * @deprecated As of 7.0, use {@link Align#LEFT} instead
+	 */
+    @Deprecated
+    public static final Align ALIGN_LEFT = Align.LEFT;
+
+    /** The Constant ALIGN_CENTER.
+	 *
+	 * @deprecated As of 7.0, use {@link Align#CENTER} instead
+	 */
+    @Deprecated
+    public static final Align ALIGN_CENTER = Align.CENTER;
+
+    /** The Constant ALIGN_RIGHT.
+	 *
+	 * @deprecated As of 7.0, use {@link Align#RIGHT} instead
+	 */
+    @Deprecated
+    public static final Align ALIGN_RIGHT = Align.RIGHT;
+
+    /** The Enum ColumnHeaderMode.
+	 */
+    public enum ColumnHeaderMode {
+        /**
+         * Column headers are hidden.
+         */
+        HIDDEN,
+        /**
+         * Property ID:s are used as column headers.
+         */
+        ID,
+        /**
+         * Column headers are explicitly specified with
+         * {@link #setColumnHeaders(String[])}.
+         */
+        EXPLICIT,
+        /**
+         * Column headers are explicitly specified with
+         * {@link #setColumnHeaders(String[])}. If a header is not specified for
+         * a given property, its property id is used instead.
+         * <p>
+         * <b>This is the default behavior. </b>
+         */
+        EXPLICIT_DEFAULTS_ID
+    }
+
+    /** The Constant COLUMN_HEADER_MODE_HIDDEN.
+	 *
+	 * @deprecated As of 7.0, use {@link ColumnHeaderMode#HIDDEN} instead
+	 */
+    @Deprecated
+    public static final ColumnHeaderMode COLUMN_HEADER_MODE_HIDDEN = ColumnHeaderMode.HIDDEN;
+
+    /** The Constant COLUMN_HEADER_MODE_ID.
+	 *
+	 * @deprecated As of 7.0, use {@link ColumnHeaderMode#ID} instead
+	 */
+    @Deprecated
+    public static final ColumnHeaderMode COLUMN_HEADER_MODE_ID = ColumnHeaderMode.ID;
+
+    /** The Constant COLUMN_HEADER_MODE_EXPLICIT.
+	 *
+	 * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT} instead
+	 */
+    @Deprecated
+    public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT = ColumnHeaderMode.EXPLICIT;
+
+    /** The Constant COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID.
+	 *
+	 * @deprecated As of 7.0, use {@link ColumnHeaderMode#EXPLICIT_DEFAULTS_ID}
+	 *             instead
+	 */
+    @Deprecated
+    public static final ColumnHeaderMode COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
+
+    /** The Enum RowHeaderMode.
+	 */
+    public enum RowHeaderMode {
+        /**
+         * Row caption mode: The row headers are hidden. <b>This is the default
+         * mode. </b>
+         */
+        HIDDEN(null),
+        /**
+         * Row caption mode: Items Id-objects toString is used as row caption.
+         */
+        ID(ItemCaptionMode.ID),
+        /**
+         * Row caption mode: Item-objects toString is used as row caption.
+         */
+        ITEM(ItemCaptionMode.ITEM),
+        /**
+         * Row caption mode: Index of the item is used as item caption. The
+         * index mode can only be used with the containers implementing the
+         * {@link com.vaadin.data.Container.Indexed} interface.
+         */
+        INDEX(ItemCaptionMode.INDEX),
+        /**
+         * Row caption mode: Item captions are explicitly specified, but if the
+         * caption is missing, the item id objects <code>toString()</code> is
+         * used instead.
+         */
+        EXPLICIT_DEFAULTS_ID(ItemCaptionMode.EXPLICIT_DEFAULTS_ID),
+        /**
+         * Row caption mode: Item captions are explicitly specified.
+         */
+        EXPLICIT(ItemCaptionMode.EXPLICIT),
+        /**
+         * Row caption mode: Only icons are shown, the captions are hidden.
+         */
+        ICON_ONLY(ItemCaptionMode.ICON_ONLY),
+        /**
+         * Row caption mode: Item captions are read from property specified with
+         * {@link #setItemCaptionPropertyId(Object)}.
+         */
+        PROPERTY(ItemCaptionMode.PROPERTY);
+
+        /** The mode. */
+        ItemCaptionMode mode;
+
+        /** Instantiates a new row header mode.
+		 *
+		 * @param mode
+		 *            the mode
+		 */
+        private RowHeaderMode(ItemCaptionMode mode) {
+            this.mode = mode;
+        }
+
+        /** Gets the item caption mode.
+		 *
+		 * @return the item caption mode
+		 */
+        public ItemCaptionMode getItemCaptionMode() {
+            return mode;
+        }
+    }
+
+    /** The Constant ROW_HEADER_MODE_HIDDEN.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#HIDDEN} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_HIDDEN = RowHeaderMode.HIDDEN;
+
+    /** The Constant ROW_HEADER_MODE_ID.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#ID} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_ID = RowHeaderMode.ID;
+
+    /** The Constant ROW_HEADER_MODE_ITEM.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#ITEM} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_ITEM = RowHeaderMode.ITEM;
+
+    /** The Constant ROW_HEADER_MODE_INDEX.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#INDEX} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_INDEX = RowHeaderMode.INDEX;
+
+    /** The Constant ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT_DEFAULTS_ID}
+	 *             instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
+
+    /** The Constant ROW_HEADER_MODE_EXPLICIT.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#EXPLICIT} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_EXPLICIT = RowHeaderMode.EXPLICIT;
+
+    /** The Constant ROW_HEADER_MODE_ICON_ONLY.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#ICON_ONLY} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_ICON_ONLY = RowHeaderMode.ICON_ONLY;
+
+    /** The Constant ROW_HEADER_MODE_PROPERTY.
+	 *
+	 * @deprecated As of 7.0, use {@link RowHeaderMode#PROPERTY} instead
+	 */
+    @Deprecated
+    public static final RowHeaderMode ROW_HEADER_MODE_PROPERTY = RowHeaderMode.PROPERTY;
+
+    /**
+     * The default rate that table caches rows for smooth scrolling.
+     */
+    private static final double CACHE_RATE_DEFAULT = 2;
+
+    /** The Constant ROW_HEADER_COLUMN_KEY. */
+    private static final String ROW_HEADER_COLUMN_KEY = "0";
+    
+    /** The Constant ROW_HEADER_FAKE_PROPERTY_ID. */
+    private static final Object ROW_HEADER_FAKE_PROPERTY_ID = new UniqueSerializable() {
+    };
+
+    /* Private table extensions to Select */
+
+    /**
+     * True if column collapsing is allowed.
+     */
+    private boolean columnCollapsingAllowed = false;
+
+    /**
+     * True if reordering of columns is allowed on the client side.
+     */
+    private boolean columnReorderingAllowed = false;
+
+    /**
+     * Keymapper for column ids.
+     */
+    protected final KeyMapper<Object> columnIdMap = new KeyMapper<Object>();
+
+    /**
+     * Holds visible column propertyIds - in order.
+     */
+    private LinkedList<Object> visibleColumns = new LinkedList<Object>();
+
+    /**
+     * Holds noncollapsible columns.
+     */
+    private HashSet<Object> noncollapsibleColumns = new HashSet<Object>();
+
+    /**
+     * Holds propertyIds of currently collapsed columns.
+     */
+    private final HashSet<Object> collapsedColumns = new HashSet<Object>();
+
+    /**
+     * Holds headers for visible columns (by propertyId).
+     */
+    private final HashMap<Object, String> columnHeaders = new HashMap<Object, String>();
+
+    /**
+     * Holds footers for visible columns (by propertyId).
+     */
+    private final HashMap<Object, String> columnFooters = new HashMap<Object, String>();
+
+    /**
+     * Holds icons for visible columns (by propertyId).
+     */
+    private final HashMap<Object, Resource> columnIcons = new HashMap<Object, Resource>();
+
+    /**
+     * Holds alignments for visible columns (by propertyId).
+     */
+    private HashMap<Object, Align> columnAlignments = new HashMap<Object, Align>();
+
+    /**
+     * Holds column widths in pixels for visible columns (by propertyId).
+     */
+    private final HashMap<Object, Integer> columnWidths = new HashMap<Object, Integer>();
+
+    /**
+     * Holds column expand rations for visible columns (by propertyId).
+     */
+    private final HashMap<Object, Float> columnExpandRatios = new HashMap<Object, Float>();
+
+    /** Holds column generators. */
+    private final HashMap<Object, ColumnGenerator> columnGenerators = new LinkedHashMap<Object, ColumnGenerator>();
+
+    /**
+     * Holds value of property pageLength. 0 disables paging.
+     */
+    private int pageLength = 15;
+
+    /**
+     * Id the first item on the current page.
+     */
+    private Object currentPageFirstItemId = null;
+
+    /**
+     * Index of the first item on the current page.
+     */
+    private int currentPageFirstItemIndex = 0;
+
+    /**
+     * Index of the "first" item on the last page if a user has used
+     * setCurrentPageFirstItemIndex to scroll down. -1 if not set.
+     */
+    private int currentPageFirstItemIndexOnLastPage = -1;
+
+    /**
+     * Holds value of property selectable.
+     */
+    private boolean selectable = false;
+
+    /**
+     * Holds value of property columnHeaderMode.
+     */
+    private ColumnHeaderMode columnHeaderMode = ColumnHeaderMode.EXPLICIT_DEFAULTS_ID;
+
+    /**
+     * Holds value of property rowHeaderMode.
+     */
+    private RowHeaderMode rowHeaderMode = RowHeaderMode.EXPLICIT_DEFAULTS_ID;
+
+    /** Should the CustomTable footer be visible?. */
+    private boolean columnFootersVisible = false;
+
+    /**
+     * Page contents buffer used in buffered mode.
+     */
+    private Object[][] pageBuffer = null;
+
+    /**
+     * Set of properties listened - the list is kept to release the listeners
+     * later.
+     */
+    private HashSet<Property<?>> listenedProperties = null;
+
+    /**
+     * Set of visible components - the is used for needsRepaint calculation.
+     */
+    protected HashSet<Component> visibleComponents = null;
+
+    /**
+     * List of action handlers.
+     */
+    private LinkedList<Handler> actionHandlers = null;
+
+    /**
+     * Action mapper.
+     */
+    private KeyMapper<Action> actionMapper = null;
+
+    /**
+     * CustomTable cell editor factory.
+     */
+    private TableFieldFactory fieldFactory = DefaultFieldFactory.get();
+
+    /**
+     * Is table editable.
+     */
+    private boolean editable = false;
+
+    /**
+     * Current sorting direction.
+     */
+    private boolean sortAscending = true;
+
+    /**
+     * Currently table is sorted on this propertyId.
+     */
+    private Object sortContainerPropertyId = null;
+
+    /**
+     * Is table sorting by the user enabled.
+     */
+    private boolean sortEnabled = true;
+
+    /**
+     * Number of rows explicitly requested by the client to be painted on next
+     * paint. This is -1 if no request by the client is made. Painting the
+     * component will automatically reset this to -1.
+     */
+    private int reqRowsToPaint = -1;
+
+    /**
+     * Index of the first rows explicitly requested by the client to be painted.
+     * This is -1 if no request by the client is made. Painting the component
+     * will automatically reset this to -1.
+     */
+    private int reqFirstRowToPaint = -1;
+
+    /** The first to be rendered in client. */
+    private int firstToBeRenderedInClient = -1;
+
+    /** The last to be rendered in client. */
+    private int lastToBeRenderedInClient = -1;
+
+    /** The is content refreshes enabled. */
+    private boolean isContentRefreshesEnabled = true;
+
+    /** The page buffer first index. */
+    private int pageBufferFirstIndex;
+
+    /** The container change to be rendered. */
+    private boolean containerChangeToBeRendered = false;
+
+    /** CustomTable cell specific style generator. */
+    private CellStyleGenerator cellStyleGenerator = null;
+
+    /** CustomTable cell specific tooltip generator. */
+    private ItemDescriptionGenerator itemDescriptionGenerator;
+
+    /** The always recalculate column widths. */
+    /*
+     * EXPERIMENTAL feature: will tell the client to re-calculate column widths
+     * if set to true. Currently no setter: extend to enable.
+     */
+    protected boolean alwaysRecalculateColumnWidths = false;
+
+    /** The cache rate. */
+    private double cacheRate = CACHE_RATE_DEFAULT;
+
+    /** The drag mode. */
+    private TableDragMode dragMode = TableDragMode.NONE;
+
+    /** The drop handler. */
+    private DropHandler dropHandler;
+
+    /** The multi select mode. */
+    private MultiSelectMode multiSelectMode = MultiSelectMode.DEFAULT;
+
+    /** The row cache invalidated. */
+    private boolean rowCacheInvalidated;
+
+    /** The row generator. */
+    private RowGenerator rowGenerator = null;
+
+    /** The associated properties. */
+    private final Map<Field<?>, Property<?>> associatedProperties = new HashMap<Field<?>, Property<?>>();
+
+    /** The painted. */
+    private boolean painted = false;
+
+    /** The property value converters. */
+    private HashMap<Object, Converter<String, Object>> propertyValueConverters = new HashMap<Object, Converter<String, Object>>();
+
+    /**
+     * Set to true if the client-side should be informed that the key mapper has
+     * been reset so it can avoid sending back references to keys that are no
+     * longer present.
+     */
+    private boolean keyMapperReset;
+
+    /** The exceptions during cache population. */
+    private List<Throwable> exceptionsDuringCachePopulation = new ArrayList<Throwable>();
+
+    /** The is being painted. */
+    private boolean isBeingPainted;
+
+    /* CustomTable constructors */
+
+    /**
+     * Creates a new empty table.
+     */
+    public CustomTable() {
+        setRowHeaderMode(ROW_HEADER_MODE_HIDDEN);
+    }
+
+    /** Creates a new empty table with caption.
+	 *
+	 * @param caption
+	 *            the caption
+	 */
+    public CustomTable(String caption) {
+        this();
+        setCaption(caption);
+    }
+
+    /** Creates a new table with caption and connect it to a Container.
+	 *
+	 * @param caption
+	 *            the caption
+	 * @param dataSource
+	 *            the data source
+	 */
+    public CustomTable(String caption, Container dataSource) {
+        this();
+        setCaption(caption);
+        setContainerDataSource(dataSource);
+    }
+
+    /* CustomTable functionality */
+
+    /**
+     * Gets the array of visible column id:s, including generated columns.
+     * 
+     * <p>
+     * The columns are show in the order of their appearance in this array.
+     * 
+     * 
+     * @return an array of currently visible propertyIds and generated column
+     *         ids.
+     */
+    public Object[] getVisibleColumns() {
+        if (visibleColumns == null) {
+            return null;
+        }
+        return visibleColumns.toArray();
+    }
+
+    /**
+     * Sets the array of visible column property id:s.
+     * 
+     * <p>
+     * The columns are show in the order of their appearance in this array.
+     * 
+     * 
+     * @param visibleColumns
+     *            the Array of shown property id:s.
+     */
+    public void setVisibleColumns(Object... visibleColumns) {
+
+        // Visible columns must exist
+        if (visibleColumns == null) {
+            throw new NullPointerException(
+                    "Can not set visible columns to null value");
+        }
+
+        final LinkedList<Object> newVC = new LinkedList<Object>();
+
+        // Checks that the new visible columns contains no nulls, properties
+        // exist and that there are no duplicates before adding them to newVC.
+        final Collection<?> properties = getContainerPropertyIds();
+        for (int i = 0; i < visibleColumns.length; i++) {
+            if (visibleColumns[i] == null) {
+                throw new NullPointerException("Ids must be non-nulls");
+            } else if (!properties.contains(visibleColumns[i])
+                    && !columnGenerators.containsKey(visibleColumns[i])) {
+                throw new IllegalArgumentException(
+                        "Ids must exist in the Container or as a generated column, missing id: "
+                                + visibleColumns[i]);
+            } else if (newVC.contains(visibleColumns[i])) {
+                throw new IllegalArgumentException(
+                        "Ids must be unique, duplicate id: "
+                                + visibleColumns[i]);
+            } else {
+                newVC.add(visibleColumns[i]);
+            }
+        }
+
+        // Removes alignments, icons and headers from hidden columns
+        if (this.visibleColumns != null) {
+            boolean disabledHere = disableContentRefreshing();
+            try {
+                for (final Iterator<Object> i = this.visibleColumns.iterator(); i
+                        .hasNext();) {
+                    final Object col = i.next();
+                    if (!newVC.contains(col)) {
+                        setColumnHeader(col, null);
+                        setColumnAlignment(col, (Align) null);
+                        setColumnIcon(col, null);
+                    }
+                }
+            } finally {
+                if (disabledHere) {
+                    enableContentRefreshing(false);
+                }
+            }
+        }
+
+        this.visibleColumns = newVC;
+
+        // Assures visual refresh
+        refreshRowCache();
+    }
+
+    /**
+     * Gets the headers of the columns.
+     * 
+     * <p>
+     * The headers match the property id:s given my the set visible column
+     * headers. The table must be set in either
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
+     * headers. In the defaults mode any nulls in the headers array are replaced
+     * with id.toString().
+     * 
+     * 
+     * @return the Array of column headers.
+     */
+    public String[] getColumnHeaders() {
+        if (columnHeaders == null) {
+            return null;
+        }
+        final String[] headers = new String[visibleColumns.size()];
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext(); i++) {
+            headers[i] = getColumnHeader(it.next());
+        }
+        return headers;
+    }
+
+    /**
+     * Sets the headers of the columns.
+     * 
+     * <p>
+     * The headers match the property id:s given my the set visible column
+     * headers. The table must be set in either
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the
+     * headers. In the defaults mode any nulls in the headers array are replaced
+     * with id.toString() outputs when rendering.
+     * 
+     * 
+     * @param columnHeaders
+     *            the Array of column headers that match the
+     *            {@link #getVisibleColumns()} method.
+     */
+    public void setColumnHeaders(String... columnHeaders) {
+
+        if (columnHeaders.length != visibleColumns.size()) {
+            throw new IllegalArgumentException(
+                    "The length of the headers array must match the number of visible columns");
+        }
+
+        this.columnHeaders.clear();
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext() && i < columnHeaders.length; i++) {
+            this.columnHeaders.put(it.next(), columnHeaders[i]);
+        }
+
+        markAsDirty();
+    }
+
+    /**
+     * Gets the icons of the columns.
+     * 
+     * <p>
+     * The icons in headers match the property id:s given my the set visible
+     * column headers. The table must be set in either
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
+     * with icons.
+     * 
+     * 
+     * @return the Array of icons that match the {@link #getVisibleColumns()}.
+     */
+    public Resource[] getColumnIcons() {
+        if (columnIcons == null) {
+            return null;
+        }
+        final Resource[] icons = new Resource[visibleColumns.size()];
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext(); i++) {
+            icons[i] = columnIcons.get(it.next());
+        }
+
+        return icons;
+    }
+
+    /**
+     * Sets the icons of the columns.
+     * 
+     * <p>
+     * The icons in headers match the property id:s given my the set visible
+     * column headers. The table must be set in either
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT} or
+     * {@link #COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID} mode to show the headers
+     * with icons.
+     * 
+     * 
+     * @param columnIcons
+     *            the Array of icons that match the {@link #getVisibleColumns()}
+     *            .
+     */
+    public void setColumnIcons(Resource... columnIcons) {
+
+        if (columnIcons.length != visibleColumns.size()) {
+            throw new IllegalArgumentException(
+                    "The length of the icons array must match the number of visible columns");
+        }
+
+        this.columnIcons.clear();
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext() && i < columnIcons.length; i++) {
+            this.columnIcons.put(it.next(), columnIcons[i]);
+        }
+
+        markAsDirty();
+    }
+
+    /**
+     * Gets the array of column alignments.
+     * 
+     * <p>
+     * The items in the array must match the properties identified by
+     * {@link #getVisibleColumns()}. The possible values for the alignments
+     * include:
+     * <ul>
+     * <li>{@link Align#LEFT}: Left alignment</li>
+     * <li>{@link Align#CENTER}: Centered</li>
+     * <li>{@link Align#RIGHT}: Right alignment</li>
+     * </ul>
+     * The alignments default to {@link Align#LEFT}: any null values are
+     * rendered as align lefts.
+     * 
+     * 
+     * @return the Column alignments array.
+     */
+    public Align[] getColumnAlignments() {
+        if (columnAlignments == null) {
+            return null;
+        }
+        final Align[] alignments = new Align[visibleColumns.size()];
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext(); i++) {
+            alignments[i] = getColumnAlignment(it.next());
+        }
+
+        return alignments;
+    }
+
+    /**
+     * Sets the column alignments.
+     * 
+     * <p>
+     * The amount of items in the array must match the amount of properties
+     * identified by {@link #getVisibleColumns()}. The possible values for the
+     * alignments include:
+     * <ul>
+     * <li>{@link Align#LEFT}: Left alignment</li>
+     * <li>{@link Align#CENTER}: Centered</li>
+     * <li>{@link Align#RIGHT}: Right alignment</li>
+     * </ul>
+     * The alignments default to {@link Align#LEFT}
+     * 
+     * 
+     * @param columnAlignments
+     *            the Column alignments array.
+     */
+    public void setColumnAlignments(Align... columnAlignments) {
+
+        if (columnAlignments.length != visibleColumns.size()) {
+            throw new IllegalArgumentException(
+                    "The length of the alignments array must match the number of visible columns");
+        }
+
+        // Resets the alignments
+        final HashMap<Object, Align> newCA = new HashMap<Object, Align>();
+        int i = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext() && i < columnAlignments.length; i++) {
+            newCA.put(it.next(), columnAlignments[i]);
+        }
+        this.columnAlignments = newCA;
+
+        // Assures the visual refresh. No need to reset the page buffer before
+        // as the content has not changed, only the alignments.
+        refreshRenderedCells();
+    }
+
+    /**
+     * Sets columns width (in pixels). Theme may not necessary respect very
+     * small or very big values. Setting width to -1 (default) means that theme
+     * will make decision of width.
+     * 
+     * <p>
+     * Column can either have a fixed width or expand ratio. The latter one set
+     * is used. See @link {@link #setColumnExpandRatio(Object, float)}.
+     * 
+     * @param propertyId
+     *            colunmns property id
+     * @param width
+     *            width to be reserved for colunmns content
+     * @since 4.0.3
+     */
+    public void setColumnWidth(Object propertyId, int width) {
+        if (propertyId == null) {
+            // Since propertyId is null, this is the row header. Use the magic
+            // id to store the width of the row header.
+            propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
+        }
+
+        // Setting column width should remove any expand ratios as well
+        columnExpandRatios.remove(propertyId);
+
+        if (width < 0) {
+            columnWidths.remove(propertyId);
+        } else {
+            columnWidths.put(propertyId, width);
+        }
+        markAsDirty();
+    }
+
+    /**
+     * Sets the column expand ratio for given column.
+     * <p>
+     * Expand ratios can be defined to customize the way how excess space is
+     * divided among columns. CustomTable can have excess space if it has its
+     * width defined and there is horizontally more space than columns consume
+     * naturally. Excess space is the space that is not used by columns with
+     * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural
+     * width (no width nor expand ratio).
+     * 
+     * <p>
+     * By default (without expand ratios) the excess space is divided
+     * proportionally to columns natural widths.
+     * 
+     * <p>
+     * Only expand ratios of visible columns are used in final calculations.
+     * 
+     * <p>
+     * Column can either have a fixed width or expand ratio. The latter one set
+     * is used.
+     * 
+     * <p>
+     * A column with expand ratio is considered to be minimum width by default
+     * (if no excess space exists). The minimum width is defined by terminal
+     * implementation.
+     * 
+     * <p>
+     * If terminal implementation supports re-sizable columns the column becomes
+     * fixed width column if users resizes the column.
+     * 
+     * @param propertyId
+     *            columns property id
+     * @param expandRatio
+     *            the expandRatio used to divide excess space for this column
+     */
+    public void setColumnExpandRatio(Object propertyId, float expandRatio) {
+        if (propertyId == null) {
+            // Since propertyId is null, this is the row header. Use the magic
+            // id to store the width of the row header.
+            propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
+        }
+
+        // Setting the column expand ratio should remove and defined column
+        // width
+        columnWidths.remove(propertyId);
+
+        if (expandRatio < 0) {
+            columnExpandRatios.remove(propertyId);
+        } else {
+            columnExpandRatios.put(propertyId, expandRatio);
+        }
+
+        requestRepaint();
+    }
+
+    /**
+     * Gets the column expand ratio for a columnd. See
+     * {@link #setColumnExpandRatio(Object, float)}
+     * 
+     * @param propertyId
+     *            columns property id
+     * @return the expandRatio used to divide excess space for this column
+     */
+    public float getColumnExpandRatio(Object propertyId) {
+        final Float width = columnExpandRatios.get(propertyId);
+        if (width == null) {
+            return -1;
+        }
+        return width.floatValue();
+    }
+
+    /** Gets the pixel width of column.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @return width of column or -1 when value not set
+	 */
+    public int getColumnWidth(Object propertyId) {
+        if (propertyId == null) {
+            // Since propertyId is null, this is the row header. Use the magic
+            // id to retrieve the width of the row header.
+            propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
+        }
+        final Integer width = columnWidths.get(propertyId);
+        if (width == null) {
+            return -1;
+        }
+        return width.intValue();
+    }
+
+    /**
+     * Gets the page length.
+     * 
+     * <p>
+     * Setting page length 0 disables paging.
+     * 
+     * 
+     * @return the Length of one page.
+     */
+    public int getPageLength() {
+        return pageLength;
+    }
+
+    /**
+     * Sets the page length.
+     * 
+     * <p>
+     * Setting page length 0 disables paging. The page length defaults to 15.
+     * 
+     * 
+     * <p>
+     * If CustomTable has width set ({@link #setColumnWidth(Object, int)} ) the client
+     * side may update the page length automatically the correct value.
+     * 
+     * 
+     * @param pageLength
+     *            the length of one page.
+     */
+    public void setPageLength(int pageLength) {
+        if (pageLength >= 0 && this.pageLength != pageLength) {
+            this.pageLength = pageLength;
+            // Assures the visual refresh
+            refreshRowCache();
+        }
+    }
+
+    /**
+     * This method adjusts a possible caching mechanism of table implementation.
+     * 
+     * <p>
+     * CustomTable component may fetch and render some rows outside visible
+     * area. With complex tables (for example containing layouts and
+     * components), the client side may become unresponsive. Setting the value
+     * lower, UI will become more responsive. With higher values scrolling in
+     * client will hit server less frequently.
+     * 
+     * <p>
+     * The amount of cached rows will be cacheRate multiplied with pageLength (
+     * {@link #setPageLength(int)} both below and above visible area..
+     * 
+     * @param cacheRate
+     *            a value over 0 (fastest rendering time). Higher value will
+     *            cache more rows on server (smoother scrolling). Default value
+     *            is 2.
+     */
+    public void setCacheRate(double cacheRate) {
+        if (cacheRate < 0) {
+            throw new IllegalArgumentException(
+                    "cacheRate cannot be less than zero");
+        }
+        if (this.cacheRate != cacheRate) {
+            this.cacheRate = cacheRate;
+            markAsDirty();
+        }
+    }
+
+    /** Gets the cache rate.
+	 *
+	 * @return the current cache rate value
+	 * @see #setCacheRate(double)
+	 */
+    public double getCacheRate() {
+        return cacheRate;
+    }
+
+    /**
+     * Getter for property currentPageFirstItem.
+     * 
+     * @return the Value of property currentPageFirstItem.
+     */
+    public Object getCurrentPageFirstItemId() {
+
+        // Priorise index over id if indexes are supported
+        if (items instanceof Container.Indexed) {
+            final int index = getCurrentPageFirstItemIndex();
+            Object id = null;
+            if (index >= 0 && index < size()) {
+                id = getIdByIndex(index);
+            }
+            if (id != null && !id.equals(currentPageFirstItemId)) {
+                currentPageFirstItemId = id;
+            }
+        }
+
+        // If there is no item id at all, use the first one
+        if (currentPageFirstItemId == null) {
+            currentPageFirstItemId = firstItemId();
+        }
+
+        return currentPageFirstItemId;
+    }
+
+    /**
+     * Returns the item ID for the item represented by the index given. Assumes
+     * that the current container implements {@link Container.Indexed}.
+     * 
+     * See {@link Container.Indexed#getIdByIndex(int)} for more information
+     * about the exceptions that can be thrown.
+     * 
+     * @param index
+     *            the index for which the item ID should be fetched
+     * @return the item ID for the given index
+     * 
+     * @throws ClassCastException
+     *             if container does not implement {@link Container.Indexed}
+     * @throws IndexOutOfBoundsException
+     *             thrown by {@link Container.Indexed#getIdByIndex(int)} if the
+     *             index is invalid
+     */
+    protected Object getIdByIndex(int index) {
+        return ((Container.Indexed) items).getIdByIndex(index);
+    }
+
+    /**
+     * Setter for property currentPageFirstItemId.
+     * 
+     * @param currentPageFirstItemId
+     *            the New value of property currentPageFirstItemId.
+     */
+    public void setCurrentPageFirstItemId(Object currentPageFirstItemId) {
+
+        // Gets the corresponding index
+        int index = -1;
+        if (items instanceof Container.Indexed) {
+            index = indexOfId(currentPageFirstItemId);
+        } else {
+            // If the table item container does not have index, we have to
+            // calculates the index by hand
+            Object id = firstItemId();
+            while (id != null && !id.equals(currentPageFirstItemId)) {
+                index++;
+                id = nextItemId(id);
+            }
+            if (id == null) {
+                index = -1;
+            }
+        }
+
+        // If the search for item index was successful
+        if (index >= 0) {
+            /*
+             * The table is not capable of displaying an item in the container
+             * as the first if there are not enough items following the selected
+             * item so the whole table (pagelength) is filled.
+             */
+            int maxIndex = size() - pageLength;
+            if (maxIndex < 0) {
+                maxIndex = 0;
+            }
+
+            if (index > maxIndex) {
+                // Note that we pass index, not maxIndex, letting
+                // setCurrentPageFirstItemIndex handle the situation.
+                setCurrentPageFirstItemIndex(index);
+                return;
+            }
+
+            this.currentPageFirstItemId = currentPageFirstItemId;
+            currentPageFirstItemIndex = index;
+        }
+
+        // Assures the visual refresh
+        refreshRowCache();
+
+    }
+
+    /** Index of id.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return the int
+	 */
+    protected int indexOfId(Object itemId) {
+        return ((Container.Indexed) items).indexOfId(itemId);
+    }
+
+    /**
+     * Gets the icon Resource for the specified column.
+     * 
+     * @param propertyId
+     *            the propertyId indentifying the column.
+     * @return the icon for the specified column; null if the column has no icon
+     *         set, or if the column is not visible.
+     */
+    public Resource getColumnIcon(Object propertyId) {
+        return columnIcons.get(propertyId);
+    }
+
+    /**
+     * Sets the icon Resource for the specified column.
+     * <p>
+     * Throws IllegalArgumentException if the specified column is not visible.
+     * 
+     * 
+     * @param propertyId
+     *            the propertyId identifying the column.
+     * @param icon
+     *            the icon Resource to set.
+     */
+    public void setColumnIcon(Object propertyId, Resource icon) {
+
+        if (icon == null) {
+            columnIcons.remove(propertyId);
+        } else {
+            columnIcons.put(propertyId, icon);
+        }
+
+        markAsDirty();
+    }
+
+    /**
+     * Gets the header for the specified column.
+     * 
+     * @param propertyId
+     *            the propertyId identifying the column.
+     * @return the header for the specified column if it has one.
+     */
+    public String getColumnHeader(Object propertyId) {
+        if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) {
+            return null;
+        }
+
+        String header = columnHeaders.get(propertyId);
+        if ((header == null && getColumnHeaderMode() == ColumnHeaderMode.EXPLICIT_DEFAULTS_ID)
+                || getColumnHeaderMode() == ColumnHeaderMode.ID) {
+            header = propertyId.toString();
+        }
+
+        return header;
+    }
+
+    /** Sets the column header for the specified column;.
+	 *
+	 * @param propertyId
+	 *            the propertyId identifying the column.
+	 * @param header
+	 *            the header to set.
+	 */
+    public void setColumnHeader(Object propertyId, String header) {
+
+        if (header == null) {
+            columnHeaders.remove(propertyId);
+        } else {
+            columnHeaders.put(propertyId, header);
+        }
+
+        markAsDirty();
+    }
+
+    /**
+     * Gets the specified column's alignment.
+     * 
+     * @param propertyId
+     *            the propertyID identifying the column.
+     * @return the specified column's alignment if it as one; {@link Align#LEFT}
+     *         otherwise.
+     */
+    public Align getColumnAlignment(Object propertyId) {
+        final Align a = columnAlignments.get(propertyId);
+        return a == null ? Align.LEFT : a;
+    }
+
+    /**
+     * Sets the specified column's alignment.
+     * 
+     * <p>
+     * Throws IllegalArgumentException if the alignment is not one of the
+     * following: {@link Align#LEFT}, {@link Align#CENTER} or
+     * {@link Align#RIGHT}
+     * 
+     * 
+     * @param propertyId
+     *            the propertyID identifying the column.
+     * @param alignment
+     *            the desired alignment.
+     */
+    public void setColumnAlignment(Object propertyId, Align alignment) {
+        if (alignment == null || alignment == Align.LEFT) {
+            columnAlignments.remove(propertyId);
+        } else {
+            columnAlignments.put(propertyId, alignment);
+        }
+
+        // Assures the visual refresh. No need to reset the page buffer before
+        // as the content has not changed, only the alignments.
+        refreshRenderedCells();
+    }
+
+    /**
+     * Checks if the specified column is collapsed.
+     * 
+     * @param propertyId
+     *            the propertyID identifying the column.
+     * @return true if the column is collapsed; false otherwise;
+     */
+    public boolean isColumnCollapsed(Object propertyId) {
+        return collapsedColumns != null
+                && collapsedColumns.contains(propertyId);
+    }
+
+    /**
+     * Sets whether the specified column is collapsed or not.
+     * 
+     * 
+     * @param propertyId
+     *            the propertyID identifying the column.
+     * @param collapsed
+     *            the desired collapsedness.
+     * @throws IllegalStateException
+     *             if column collapsing is not allowed
+     */
+    public void setColumnCollapsed(Object propertyId, boolean collapsed)
+            throws IllegalStateException {
+        if (!isColumnCollapsingAllowed()) {
+            throw new IllegalStateException("Column collapsing not allowed!");
+        }
+        if (collapsed && noncollapsibleColumns.contains(propertyId)) {
+            throw new IllegalStateException("The column is noncollapsible!");
+        }
+
+        if (collapsed) {
+            collapsedColumns.add(propertyId);
+        } else {
+            collapsedColumns.remove(propertyId);
+        }
+
+        // Assures the visual refresh
+        refreshRowCache();
+    }
+
+    /**
+     * Checks if column collapsing is allowed.
+     * 
+     * @return true if columns can be collapsed; false otherwise.
+     */
+    public boolean isColumnCollapsingAllowed() {
+        return columnCollapsingAllowed;
+    }
+
+    /**
+     * Sets whether column collapsing is allowed or not.
+     * 
+     * @param collapsingAllowed
+     *            specifies whether column collapsing is allowed.
+     */
+    public void setColumnCollapsingAllowed(boolean collapsingAllowed) {
+        columnCollapsingAllowed = collapsingAllowed;
+
+        if (!collapsingAllowed) {
+            collapsedColumns.clear();
+        }
+
+        // Assures the visual refresh. No need to reset the page buffer before
+        // as the content has not changed, only the alignments.
+        refreshRenderedCells();
+    }
+
+    /**
+     * Sets whether the given column is collapsible. Note that collapsible
+     * columns can only be actually collapsed (via UI or with
+     * {@link #setColumnCollapsed(Object, boolean) setColumnCollapsed()}) if
+     * {@link #isColumnCollapsingAllowed()} is true. By default all columns are
+     * collapsible.
+     * 
+     * @param propertyId
+     *            the propertyID identifying the column.
+     * @param collapsible
+     *            true if the column should be collapsible, false otherwise.
+     */
+    public void setColumnCollapsible(Object propertyId, boolean collapsible) {
+        if (collapsible) {
+            noncollapsibleColumns.remove(propertyId);
+        } else {
+            noncollapsibleColumns.add(propertyId);
+            collapsedColumns.remove(propertyId);
+        }
+        refreshRowCache();
+    }
+
+    /** Checks if the given column is collapsible. Note that even if this
+	 * method returns <code>true</code>, the column can only be actually
+	 * collapsed (via UI or with {@link #setColumnCollapsed(Object, boolean)
+	 * setColumnCollapsed()}) if {@link #isColumnCollapsingAllowed()} is also
+	 * true.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @return true if the column can be collapsed; false otherwise.
+	 */
+    public boolean isColumnCollapsible(Object propertyId) {
+        return !noncollapsibleColumns.contains(propertyId);
+    }
+
+    /**
+     * Checks if column reordering is allowed.
+     * 
+     * @return true if columns can be reordered; false otherwise.
+     */
+    public boolean isColumnReorderingAllowed() {
+        return columnReorderingAllowed;
+    }
+
+    /**
+     * Sets whether column reordering is allowed or not.
+     * 
+     * @param columnReorderingAllowed
+     *            specifies whether column reordering is allowed.
+     */
+    public void setColumnReorderingAllowed(boolean columnReorderingAllowed) {
+        if (columnReorderingAllowed != this.columnReorderingAllowed) {
+            this.columnReorderingAllowed = columnReorderingAllowed;
+            markAsDirty();
+        }
+    }
+
+    /** Sets the column order.
+	 *
+	 * @param columnOrder
+	 *            the new column order
+	 */
+    /*
+     * Arranges visible columns according to given columnOrder. Silently ignores
+     * colimnId:s that are not visible columns, and keeps the internal order of
+     * visible columns left out of the ordering (trailing). Silently does
+     * nothing if columnReordering is not allowed.
+     */
+    private void setColumnOrder(Object[] columnOrder) {
+        if (columnOrder == null || !isColumnReorderingAllowed()) {
+            return;
+        }
+        final LinkedList<Object> newOrder = new LinkedList<Object>();
+        for (int i = 0; i < columnOrder.length; i++) {
+            if (columnOrder[i] != null
+                    && visibleColumns.contains(columnOrder[i])) {
+                visibleColumns.remove(columnOrder[i]);
+                newOrder.add(columnOrder[i]);
+            }
+        }
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext();) {
+            final Object columnId = it.next();
+            if (!newOrder.contains(columnId)) {
+                newOrder.add(columnId);
+            }
+        }
+        visibleColumns = newOrder;
+
+        // Assure visual refresh
+        refreshRowCache();
+    }
+
+    /**
+     * Getter for property currentPageFirstItem.
+     * 
+     * @return the Value of property currentPageFirstItem.
+     */
+    public int getCurrentPageFirstItemIndex() {
+        return currentPageFirstItemIndex;
+    }
+
+    /** Sets the current page first item index.
+	 *
+	 * @param newIndex
+	 *            the new index
+	 * @param needsPageBufferReset
+	 *            the needs page buffer reset
+	 */
+    void setCurrentPageFirstItemIndex(int newIndex, boolean needsPageBufferReset) {
+
+        if (newIndex < 0) {
+            newIndex = 0;
+        }
+
+        /*
+         * minimize Container.size() calls which may be expensive. For example
+         * it may cause sql query.
+         */
+        final int size = size();
+
+        /*
+         * The table is not capable of displaying an item in the container as
+         * the first if there are not enough items following the selected item
+         * so the whole table (pagelength) is filled.
+         */
+        int maxIndex = size - pageLength;
+        if (maxIndex < 0) {
+            maxIndex = 0;
+        }
+
+        /*
+         * If the new index is on the last page we set the index to be the first
+         * item on that last page and make a note of the real index for the
+         * client side to be able to move the scroll position to the correct
+         * position.
+         */
+        int indexOnLastPage = -1;
+        if (newIndex > maxIndex) {
+            indexOnLastPage = newIndex;
+            newIndex = maxIndex;
+        }
+
+        // Refresh first item id
+        if (items instanceof Container.Indexed) {
+            try {
+                currentPageFirstItemId = getIdByIndex(newIndex);
+            } catch (final IndexOutOfBoundsException e) {
+                currentPageFirstItemId = null;
+            }
+            currentPageFirstItemIndex = newIndex;
+
+            if (needsPageBufferReset) {
+                /*
+                 * The flag currentPageFirstItemIndexOnLastPage denotes a user
+                 * set scrolling position on the last page via
+                 * setCurrentPageFirstItemIndex() and shouldn't be changed by
+                 * the table component internally changing the firstvisible item
+                 * on lazy row fetching. Doing so would make the scrolling
+                 * position not be updated correctly when the lazy rows are
+                 * finally rendered.
+                 */
+
+                boolean isLastRowPossiblyPartiallyVisible = true;
+                if (indexOnLastPage != -1) {
+                    /*
+                     * If the requested row was greater than maxIndex, the last
+                     * row should be fully visible (See
+                     * TestCurrentPageFirstItem).
+                     */
+                    isLastRowPossiblyPartiallyVisible = false;
+                }
+
+                int extraRows = isLastRowPossiblyPartiallyVisible ? 0 : 1;
+                currentPageFirstItemIndexOnLastPage = currentPageFirstItemIndex
+                        + extraRows;
+            } else {
+                currentPageFirstItemIndexOnLastPage = -1;
+            }
+
+        } else {
+
+            // For containers not supporting indexes, we must iterate the
+            // container forwards / backwards
+            // next available item forward or backward
+
+            currentPageFirstItemId = firstItemId();
+
+            // Go forwards in the middle of the list (respect borders)
+            while (currentPageFirstItemIndex < newIndex
+                    && !isLastId(currentPageFirstItemId)) {
+                currentPageFirstItemIndex++;
+                currentPageFirstItemId = nextItemId(currentPageFirstItemId);
+            }
+
+            // If we did hit the border
+            if (isLastId(currentPageFirstItemId)) {
+                currentPageFirstItemIndex = size - 1;
+            }
+
+            // Go backwards in the middle of the list (respect borders)
+            while (currentPageFirstItemIndex > newIndex
+                    && !isFirstId(currentPageFirstItemId)) {
+                currentPageFirstItemIndex--;
+                currentPageFirstItemId = prevItemId(currentPageFirstItemId);
+            }
+
+            // If we did hit the border
+            if (isFirstId(currentPageFirstItemId)) {
+                currentPageFirstItemIndex = 0;
+            }
+
+            // Go forwards once more
+            while (currentPageFirstItemIndex < newIndex
+                    && !isLastId(currentPageFirstItemId)) {
+                currentPageFirstItemIndex++;
+                currentPageFirstItemId = nextItemId(currentPageFirstItemId);
+            }
+
+            // If for some reason we do hit border again, override
+            // the user index request
+            if (isLastId(currentPageFirstItemId)) {
+                newIndex = currentPageFirstItemIndex = size - 1;
+            }
+        }
+        if (needsPageBufferReset) {
+            // Assures the visual refresh
+            refreshRowCache();
+        }
+    }
+
+    /**
+     * Setter for property currentPageFirstItem.
+     * 
+     * @param newIndex
+     *            the New value of property currentPageFirstItem.
+     */
+    public void setCurrentPageFirstItemIndex(int newIndex) {
+        setCurrentPageFirstItemIndex(newIndex, true);
+    }
+
+    /**
+     * Getter for property selectable.
+     * 
+     * <p>
+     * The table is not selectable by default.
+     * 
+     * 
+     * @return the Value of property selectable.
+     */
+    public boolean isSelectable() {
+        return selectable;
+    }
+
+    /**
+     * Setter for property selectable.
+     * 
+     * <p>
+     * The table is not selectable by default.
+     * 
+     * 
+     * @param selectable
+     *            the New value of property selectable.
+     */
+    public void setSelectable(boolean selectable) {
+        if (this.selectable != selectable) {
+            this.selectable = selectable;
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Getter for property columnHeaderMode.
+     * 
+     * @return the Value of property columnHeaderMode.
+     */
+    public ColumnHeaderMode getColumnHeaderMode() {
+        return columnHeaderMode;
+    }
+
+    /**
+     * Setter for property columnHeaderMode.
+     * 
+     * @param columnHeaderMode
+     *            the New value of property columnHeaderMode.
+     */
+    public void setColumnHeaderMode(ColumnHeaderMode columnHeaderMode) {
+        if (columnHeaderMode == null) {
+            throw new IllegalArgumentException(
+                    "Column header mode can not be null");
+        }
+        if (columnHeaderMode != this.columnHeaderMode) {
+            this.columnHeaderMode = columnHeaderMode;
+            markAsDirty();
+        }
+
+    }
+
+    /**
+     * Refreshes the rows in the internal cache. Only if
+     * {@link #resetPageBuffer()} is called before this then all values are
+     * guaranteed to be recreated.
+     */
+    protected void refreshRenderedCells() {
+        if (!isAttached()) {
+            return;
+        }
+
+        if (!isContentRefreshesEnabled) {
+            return;
+        }
+
+        // Collects the basic facts about the table page
+        final int pagelen = getPageLength();
+        int rows, totalRows;
+        rows = totalRows = size();
+        int firstIndex = Math
+                .min(getCurrentPageFirstItemIndex(), totalRows - 1);
+        if (rows > 0 && firstIndex >= 0) {
+            rows -= firstIndex;
+        }
+        if (pagelen > 0 && pagelen < rows) {
+            rows = pagelen;
+        }
+
+        // If "to be painted next" variables are set, use them
+        if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
+            rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
+        }
+        if (firstToBeRenderedInClient >= 0) {
+            if (firstToBeRenderedInClient < totalRows) {
+                firstIndex = firstToBeRenderedInClient;
+            } else {
+                firstIndex = totalRows - 1;
+            }
+        } else {
+            // initial load
+
+            // #8805 send one extra row in the beginning in case a partial
+            // row is shown on the UI
+            if (firstIndex > 0) {
+                firstIndex = firstIndex - 1;
+                rows = rows + 1;
+            }
+            firstToBeRenderedInClient = firstIndex;
+        }
+        if (totalRows > 0) {
+            if (rows + firstIndex > totalRows) {
+                rows = totalRows - firstIndex;
+            }
+        } else {
+            rows = 0;
+        }
+
+        // Saves the results to internal buffer
+        pageBuffer = getVisibleCellsNoCache(firstIndex, rows, true);
+
+        if (rows > 0) {
+            pageBufferFirstIndex = firstIndex;
+        }
+        if (getPageLength() != 0) {
+            removeUnnecessaryRows();
+        }
+
+        setRowCacheInvalidated(true);
+        markAsDirty();
+        maybeThrowCacheUpdateExceptions();
+
+    }
+
+    /** Maybe throw cache update exceptions.
+	 */
+    private void maybeThrowCacheUpdateExceptions() {
+        if (!exceptionsDuringCachePopulation.isEmpty()) {
+            Throwable[] causes = new Throwable[exceptionsDuringCachePopulation
+                    .size()];
+            exceptionsDuringCachePopulation.toArray(causes);
+
+            exceptionsDuringCachePopulation.clear();
+            throw new CacheUpdateException(this,
+                    "Error during CustomTable cache update.", causes);
+        }
+
+    }
+
+    /**
+     * Exception thrown when one or more exceptions occurred during updating of
+     * the CustomTable cache.
+     * <p>
+     * Contains all exceptions which occurred during the cache update. The first
+     * occurred exception is set as the cause of this exception. All occurred
+     * exceptions can be accessed using {@link #getCauses()}.
+     * 
+     * 
+     */
+    public static class CacheUpdateException extends RuntimeException {
+        
+        /** The causes. */
+        private Throwable[] causes;
+        
+        /** The table. */
+        private CustomTable table;
+
+        /** Instantiates a new cache update exception.
+		 *
+		 * @param table
+		 *            the table
+		 * @param message
+		 *            the message
+		 * @param causes
+		 *            the causes
+		 */
+        public CacheUpdateException(CustomTable table, String message,
+                Throwable[] causes) {
+            super(maybeSupplementMessage(message, causes.length), causes[0]);
+            this.table = table;
+            this.causes = causes;
+        }
+
+        /** Maybe supplement message.
+		 *
+		 * @param message
+		 *            the message
+		 * @param causeCount
+		 *            the cause count
+		 * @return the string
+		 */
+        private static String maybeSupplementMessage(String message,
+                int causeCount) {
+            if (causeCount > 1) {
+                return message + " Additional causes not shown.";
+            } else {
+                return message;
+            }
+        }
+
+        /** Returns the cause(s) for this exception.
+		 *
+		 * @return the exception(s) which caused this exception
+		 */
+        public Throwable[] getCauses() {
+            return causes;
+        }
+
+        /** Gets the table.
+		 *
+		 * @return the table
+		 */
+        public CustomTable getTable() {
+            return table;
+        }
+
+    }
+
+    /**
+     * Removes rows that fall outside the required cache.
+     */
+    private void removeUnnecessaryRows() {
+        int minPageBufferIndex = getMinPageBufferIndex();
+        int maxPageBufferIndex = getMaxPageBufferIndex();
+
+        int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1;
+
+        /*
+         * Number of rows that were previously cached. This is not necessarily
+         * the same as pageLength if we do not have enough rows in the
+         * container.
+         */
+        int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length;
+
+        if (currentlyCachedRowCount <= maxBufferSize) {
+            // removal unnecessary
+            return;
+        }
+
+        /* Figure out which rows to get rid of. */
+        int firstCacheRowToRemoveInPageBuffer = -1;
+        if (minPageBufferIndex > pageBufferFirstIndex) {
+            firstCacheRowToRemoveInPageBuffer = pageBufferFirstIndex;
+        } else if (maxPageBufferIndex < pageBufferFirstIndex
+                + currentlyCachedRowCount) {
+            firstCacheRowToRemoveInPageBuffer = maxPageBufferIndex + 1;
+        }
+
+        if (firstCacheRowToRemoveInPageBuffer - pageBufferFirstIndex < currentlyCachedRowCount) {
+            /*
+             * Unregister all components that fall beyond the cache limits after
+             * inserting the new rows.
+             */
+            unregisterComponentsAndPropertiesInRows(
+                    firstCacheRowToRemoveInPageBuffer, currentlyCachedRowCount
+                            - firstCacheRowToRemoveInPageBuffer);
+        }
+    }
+
+    /**
+     * Requests that the CustomTable should be repainted as soon as possible.
+     * 
+     * Note that a {@code CustomTable} does not necessarily repaint its contents
+     * when this method has been called. See {@link #refreshRowCache()} for
+     * forcing an update of the contents.
+     * 
+     * @deprecated As of 7.0, use {@link #markAsDirty()} instead
+     */
+
+    @Deprecated
+    @Override
+    public void requestRepaint() {
+        markAsDirty();
+    }
+
+    /**
+     * Requests that the CustomTable should be repainted as soon as possible.
+     * 
+     * Note that a {@code CustomTable} does not necessarily repaint its contents
+     * when this method has been called. See {@link #refreshRowCache()} for
+     * forcing an update of the contents.
+     */
+
+    @Override
+    public void markAsDirty() {
+        // Overridden only for javadoc
+        super.markAsDirty();
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.server.AbstractClientConnector#markAsDirtyRecursive()
+     */
+    @Override
+    public void markAsDirtyRecursive() {
+        super.markAsDirtyRecursive();
+
+        // Avoid sending a partial repaint (#8714)
+        refreshRowCache();
+    }
+
+    /** Removes the rows from cache and fill bottom.
+	 *
+	 * @param firstIndex
+	 *            the first index
+	 * @param rows
+	 *            the rows
+	 */
+    private void removeRowsFromCacheAndFillBottom(int firstIndex, int rows) {
+        int totalCachedRows = pageBuffer[CELL_ITEMID].length;
+        int totalRows = size();
+        int firstIndexInPageBuffer = firstIndex - pageBufferFirstIndex;
+
+        /*
+         * firstIndexInPageBuffer is the first row to be removed. "rows" rows
+         * after that should be removed. If the page buffer does not contain
+         * that many rows, we only remove the rows that actually are in the page
+         * buffer.
+         */
+        if (firstIndexInPageBuffer + rows > totalCachedRows) {
+            rows = totalCachedRows - firstIndexInPageBuffer;
+        }
+
+        /*
+         * Unregister components that will no longer be in the page buffer to
+         * make sure that no components leak.
+         */
+        unregisterComponentsAndPropertiesInRows(firstIndex, rows);
+
+        /*
+         * The number of rows that should be in the cache after this operation
+         * is done (pageBuffer currently contains the expanded items).
+         */
+        int newCachedRowCount = totalCachedRows;
+        if (newCachedRowCount + pageBufferFirstIndex > totalRows) {
+            newCachedRowCount = totalRows - pageBufferFirstIndex;
+        }
+
+        /*
+         * The index at which we should render the first row that does not come
+         * from the previous page buffer.
+         */
+        int firstAppendedRowInPageBuffer = totalCachedRows - rows;
+        int firstAppendedRow = firstAppendedRowInPageBuffer
+                + pageBufferFirstIndex;
+
+        /*
+         * Calculate the maximum number of new rows that we can add to the page
+         * buffer. Less than the rows we removed if the container does not
+         * contain that many items afterwards.
+         */
+        int maxRowsToRender = (totalRows - firstAppendedRow);
+        int rowsToAdd = rows;
+        if (rowsToAdd > maxRowsToRender) {
+            rowsToAdd = maxRowsToRender;
+        }
+
+        Object[][] cells = null;
+        if (rowsToAdd > 0) {
+            cells = getVisibleCellsNoCache(firstAppendedRow, rowsToAdd, false);
+        }
+        /*
+         * Create the new cache buffer by copying the first rows from the old
+         * buffer, moving the following rows upwards and appending more rows if
+         * applicable.
+         */
+        Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount];
+
+        for (int i = 0; i < pageBuffer.length; i++) {
+            for (int row = 0; row < firstIndexInPageBuffer; row++) {
+                // Copy the first rows
+                newPageBuffer[i][row] = pageBuffer[i][row];
+            }
+            for (int row = firstIndexInPageBuffer; row < firstAppendedRowInPageBuffer; row++) {
+                // Move the rows that were after the expanded rows
+                newPageBuffer[i][row] = pageBuffer[i][row + rows];
+            }
+            for (int row = firstAppendedRowInPageBuffer; row < newCachedRowCount; row++) {
+                // Add the newly rendered rows. Only used if rowsToAdd > 0
+                // (cells != null)
+                newPageBuffer[i][row] = cells[i][row
+                        - firstAppendedRowInPageBuffer];
+            }
+        }
+        pageBuffer = newPageBuffer;
+    }
+
+    /** Gets the visible cells update cache rows.
+	 *
+	 * @param firstIndex
+	 *            the first index
+	 * @param rows
+	 *            the rows
+	 * @return the visible cells update cache rows
+	 */
+    private Object[][] getVisibleCellsUpdateCacheRows(int firstIndex, int rows) {
+        Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false);
+        int cacheIx = firstIndex - pageBufferFirstIndex;
+        // update the new rows in the cache.
+        int totalCachedRows = pageBuffer[CELL_ITEMID].length;
+        int end = Math.min(cacheIx + rows, totalCachedRows);
+        for (int ix = cacheIx; ix < end; ix++) {
+            for (int i = 0; i < pageBuffer.length; i++) {
+                pageBuffer[i][ix] = cells[i][ix - cacheIx];
+            }
+        }
+        return cells;
+    }
+
+    /** Gets the visible cells insert into cache.
+	 *
+	 * @param firstIndex
+	 *            The position where new rows should be inserted
+	 * @param rows
+	 *            The maximum number of rows that should be inserted at position
+	 *            firstIndex. Less rows will be inserted if the page buffer is
+	 *            too small.
+	 * @return the visible cells insert into cache
+	 */
+    private Object[][] getVisibleCellsInsertIntoCache(int firstIndex, int rows) {
+        getLogger()
+                .log(Level.FINEST,
+                        "Insert {0} rows at index {1} to existing page buffer requested",
+                        new Object[] { rows, firstIndex });
+
+        int minPageBufferIndex = getMinPageBufferIndex();
+        int maxPageBufferIndex = getMaxPageBufferIndex();
+
+        int maxBufferSize = maxPageBufferIndex - minPageBufferIndex + 1;
+
+        if (getPageLength() == 0) {
+            // If pageLength == 0 then all rows should be rendered
+            maxBufferSize = pageBuffer[CELL_ITEMID].length + rows;
+        }
+        /*
+         * Number of rows that were previously cached. This is not necessarily
+         * the same as maxBufferSize.
+         */
+        int currentlyCachedRowCount = pageBuffer[CELL_ITEMID].length;
+
+        /* If rows > size available in page buffer */
+        if (firstIndex + rows - 1 > maxPageBufferIndex) {
+            rows = maxPageBufferIndex - firstIndex + 1;
+        }
+
+        /*
+         * "rows" rows will be inserted at firstIndex. Find out how many old
+         * rows fall outside the new buffer so we can unregister components in
+         * the cache.
+         */
+
+        /*
+         * if there are rows before the new pageBuffer limits they must be
+         * removed
+         */
+        int lastCacheRowToRemove = minPageBufferIndex - 1;
+        int rowsFromBeginning = lastCacheRowToRemove - pageBufferFirstIndex + 1;
+        if (lastCacheRowToRemove >= pageBufferFirstIndex) {
+            unregisterComponentsAndPropertiesInRows(pageBufferFirstIndex,
+                    rowsFromBeginning);
+        } else {
+            rowsFromBeginning = 0;
+        }
+
+        /*
+         * the rows that fall outside of the new pageBuffer limits after the new
+         * rows are inserted must also be removed
+         */
+        int firstCacheRowToRemove = firstIndex;
+        /*
+         * IF there is space remaining in the buffer after the rows have been
+         * inserted, we can keep more rows.
+         */
+        int numberOfOldRowsAfterInsertedRows = Math.min(pageBufferFirstIndex
+                + currentlyCachedRowCount + rows, maxPageBufferIndex + 1)
+                - (firstIndex + rows - 1);
+        if (numberOfOldRowsAfterInsertedRows > 0) {
+            firstCacheRowToRemove += numberOfOldRowsAfterInsertedRows;
+        }
+        int rowsFromAfter = currentlyCachedRowCount
+                - (firstCacheRowToRemove - pageBufferFirstIndex);
+
+        if (rowsFromAfter > 0) {
+            /*
+             * Unregister all components that fall beyond the cache limits after
+             * inserting the new rows.
+             */
+            unregisterComponentsAndPropertiesInRows(firstCacheRowToRemove,
+                    rowsFromAfter);
+        }
+
+        // Calculate the new cache size
+        int newCachedRowCount = maxBufferSize;
+        if (pageBufferFirstIndex + currentlyCachedRowCount + rows - 1 < maxPageBufferIndex) {
+            // there aren't enough rows to fill the whole potential -> use what
+            // there is
+            newCachedRowCount -= maxPageBufferIndex
+                    - (pageBufferFirstIndex + currentlyCachedRowCount + rows - 1);
+        } else if (minPageBufferIndex < pageBufferFirstIndex) {
+            newCachedRowCount -= pageBufferFirstIndex - minPageBufferIndex;
+        }
+        /* calculate the internal location of the new rows within the new cache */
+        int firstIndexInNewPageBuffer = firstIndex - pageBufferFirstIndex
+                - rowsFromBeginning;
+
+        /* Paint the new rows into a separate buffer */
+        Object[][] cells = getVisibleCellsNoCache(firstIndex, rows, false);
+
+        /*
+         * Create the new cache buffer and fill it with the data from the old
+         * buffer as well as the inserted rows.
+         */
+        Object[][] newPageBuffer = new Object[pageBuffer.length][newCachedRowCount];
+
+        for (int i = 0; i < pageBuffer.length; i++) {
+            for (int row = 0; row < firstIndexInNewPageBuffer; row++) {
+                // Copy the first rows
+                newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row];
+            }
+            for (int row = firstIndexInNewPageBuffer; row < firstIndexInNewPageBuffer
+                    + rows; row++) {
+                // Copy the newly created rows
+                newPageBuffer[i][row] = cells[i][row
+                        - firstIndexInNewPageBuffer];
+            }
+            for (int row = firstIndexInNewPageBuffer + rows; row < newCachedRowCount; row++) {
+                // Move the old rows down below the newly inserted rows
+                newPageBuffer[i][row] = pageBuffer[i][rowsFromBeginning + row
+                        - rows];
+            }
+        }
+        pageBuffer = newPageBuffer;
+        pageBufferFirstIndex = Math.max(pageBufferFirstIndex
+                + rowsFromBeginning, minPageBufferIndex);
+        if (getLogger().isLoggable(Level.FINEST)) {
+            getLogger().log(
+                    Level.FINEST,
+                    "Page Buffer now contains {0} rows ({1}-{2})",
+                    new Object[] {
+                            pageBuffer[CELL_ITEMID].length,
+                            pageBufferFirstIndex,
+                            (pageBufferFirstIndex
+                                    + pageBuffer[CELL_ITEMID].length - 1) });
+        }
+        return cells;
+    }
+
+    /** Gets the max page buffer index.
+	 *
+	 * @return the max page buffer index
+	 */
+    private int getMaxPageBufferIndex() {
+        int total = size();
+        if (getPageLength() == 0) {
+            // everything is shown at once, no caching
+            return total - 1;
+        }
+        // Page buffer must not become larger than pageLength*cacheRate after
+        // the current page
+        int maxPageBufferIndex = getCurrentPageFirstItemIndex()
+                + (int) (getPageLength() * (1 + getCacheRate()));
+        if (shouldHideNullSelectionItem()) {
+            --total;
+        }
+        if (maxPageBufferIndex >= total) {
+            maxPageBufferIndex = total - 1;
+        }
+        return maxPageBufferIndex;
+    }
+
+    /** Gets the min page buffer index.
+	 *
+	 * @return the min page buffer index
+	 */
+    private int getMinPageBufferIndex() {
+        if (getPageLength() == 0) {
+            // everything is shown at once, no caching
+            return 0;
+        }
+        // Page buffer must not become larger than pageLength*cacheRate before
+        // the current page
+        int minPageBufferIndex = getCurrentPageFirstItemIndex()
+                - (int) (getPageLength() * getCacheRate());
+        if (minPageBufferIndex < 0) {
+            minPageBufferIndex = 0;
+        }
+        return minPageBufferIndex;
+    }
+
+    /** Render rows with index "firstIndex" to "firstIndex+rows-1" to a new
+	 * buffer.
+	 * 
+	 * Reuses values from the current page buffer if the rows are found there.
+	 *
+	 * @param firstIndex
+	 *            the first index
+	 * @param rows
+	 *            the rows
+	 * @param replaceListeners
+	 *            the replace listeners
+	 * @return the visible cells no cache
+	 */
+    private Object[][] getVisibleCellsNoCache(int firstIndex, int rows,
+            boolean replaceListeners) {
+        if (getLogger().isLoggable(Level.FINEST)) {
+            getLogger().log(Level.FINEST,
+                    "Render visible cells for rows {0}-{1}",
+                    new Object[] { firstIndex, (firstIndex + rows - 1) });
+        }
+        final Object[] colids = getVisibleColumns();
+        final int cols = colids.length;
+
+        HashSet<Property<?>> oldListenedProperties = listenedProperties;
+        HashSet<Component> oldVisibleComponents = visibleComponents;
+
+        if (replaceListeners) {
+            // initialize the listener collections, this should only be done if
+            // the entire cache is refreshed (through refreshRenderedCells)
+            listenedProperties = new HashSet<Property<?>>();
+            visibleComponents = new HashSet<Component>();
+        }
+
+        Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
+        if (rows == 0) {
+            unregisterPropertiesAndComponents(oldListenedProperties,
+                    oldVisibleComponents);
+            return cells;
+        }
+
+        final RowHeaderMode headmode = getRowHeaderMode();
+        final boolean[] iscomponent = new boolean[cols];
+        for (int i = 0; i < cols; i++) {
+            iscomponent[i] = columnGenerators.containsKey(colids[i])
+                    || Component.class.isAssignableFrom(getType(colids[i]));
+        }
+        int firstIndexNotInCache;
+        if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
+            firstIndexNotInCache = pageBufferFirstIndex
+                    + pageBuffer[CELL_ITEMID].length;
+        } else {
+            firstIndexNotInCache = -1;
+        }
+
+        // Creates the page contents
+        int filledRows = 0;
+        if (items instanceof Container.Indexed) {
+            // more efficient implementation for containers supporting access by
+            // index
+
+            List<?> itemIds = getItemIds(firstIndex, rows);
+            for (int i = 0; i < rows && i < itemIds.size(); i++) {
+                Object id = itemIds.get(i);
+                // Start by parsing the values, id should already be set
+                parseItemIdToCells(cells, id, i, firstIndex, headmode, cols,
+                        colids, firstIndexNotInCache, iscomponent,
+                        oldListenedProperties);
+
+                filledRows++;
+            }
+        } else {
+            // slow back-up implementation for cases where the container does
+            // not support access by index
+
+            // Gets the first item id
+            Object id = firstItemId();
+            for (int i = 0; i < firstIndex; i++) {
+                id = nextItemId(id);
+            }
+            for (int i = 0; i < rows && id != null; i++) {
+                // Start by parsing the values, id should already be set
+                parseItemIdToCells(cells, id, i, firstIndex, headmode, cols,
+                        colids, firstIndexNotInCache, iscomponent,
+                        oldListenedProperties);
+
+                // Gets the next item id for non indexed container
+                id = nextItemId(id);
+
+                filledRows++;
+            }
+        }
+
+        // Assures that all the rows of the cell-buffer are valid
+        if (filledRows != cells[0].length) {
+            final Object[][] temp = new Object[cells.length][filledRows];
+            for (int i = 0; i < cells.length; i++) {
+                for (int j = 0; j < filledRows; j++) {
+                    temp[i][j] = cells[i][j];
+                }
+            }
+            cells = temp;
+        }
+
+        unregisterPropertiesAndComponents(oldListenedProperties,
+                oldVisibleComponents);
+
+        return cells;
+    }
+
+    /** Gets the item ids.
+	 *
+	 * @param firstIndex
+	 *            the first index
+	 * @param rows
+	 *            the rows
+	 * @return the item ids
+	 */
+    protected List<Object> getItemIds(int firstIndex, int rows) {
+        return (List<Object>) ((Container.Indexed) items).getItemIds(
+                firstIndex, rows);
+    }
+
+    /** Update a cache array for a row, register any relevant listeners etc.
+	 * 
+	 * This is an internal method extracted from
+	 * {@link #getVisibleCellsNoCache(int, int, boolean)} and should be removed
+	 * when the CustomTable is rewritten.
+	 *
+	 * @param cells
+	 *            the cells
+	 * @param id
+	 *            the id
+	 * @param i
+	 *            the i
+	 * @param firstIndex
+	 *            the first index
+	 * @param headmode
+	 *            the headmode
+	 * @param cols
+	 *            the cols
+	 * @param colids
+	 *            the colids
+	 * @param firstIndexNotInCache
+	 *            the first index not in cache
+	 * @param iscomponent
+	 *            the iscomponent
+	 * @param oldListenedProperties
+	 *            the old listened properties
+	 */
+    private void parseItemIdToCells(Object[][] cells, Object id, int i,
+            int firstIndex, RowHeaderMode headmode, int cols, Object[] colids,
+            int firstIndexNotInCache, boolean[] iscomponent,
+            HashSet<Property<?>> oldListenedProperties) {
+
+        cells[CELL_ITEMID][i] = id;
+        cells[CELL_KEY][i] = itemIdMapper.key(id);
+        if (headmode != ROW_HEADER_MODE_HIDDEN) {
+            switch (headmode) {
+            case INDEX:
+                cells[CELL_HEADER][i] = String.valueOf(i + firstIndex + 1);
+                break;
+            default:
+                try {
+                    cells[CELL_HEADER][i] = getItemCaption(id);
+                } catch (Exception e) {
+                    exceptionsDuringCachePopulation.add(e);
+                    cells[CELL_HEADER][i] = "";
+                }
+            }
+            try {
+                cells[CELL_ICON][i] = getItemIcon(id);
+            } catch (Exception e) {
+                exceptionsDuringCachePopulation.add(e);
+                cells[CELL_ICON][i] = null;
+            }
+        }
+
+        GeneratedRow generatedRow = rowGenerator != null ? rowGenerator
+                .generateRow(this, id) : null;
+        cells[CELL_GENERATED_ROW][i] = generatedRow;
+
+        for (int j = 0; j < cols; j++) {
+            if (isColumnCollapsed(colids[j])) {
+                continue;
+            }
+            Property<?> p = null;
+            Object value = "";
+            boolean isGeneratedRow = generatedRow != null;
+            boolean isGeneratedColumn = columnGenerators.containsKey(colids[j]);
+            boolean isGenerated = isGeneratedRow || isGeneratedColumn;
+
+            if (!isGenerated) {
+                try {
+                    p = getContainerProperty(id, colids[j]);
+                } catch (Exception e) {
+                    exceptionsDuringCachePopulation.add(e);
+                    value = null;
+                }
+            }
+
+            if (isGeneratedRow) {
+                if (generatedRow.isSpanColumns() && j > 0) {
+                    value = null;
+                } else if (generatedRow.isSpanColumns() && j == 0
+                        && generatedRow.getValue() instanceof Component) {
+                    value = generatedRow.getValue();
+                } else if (generatedRow.getText().length > j) {
+                    value = generatedRow.getText()[j];
+                }
+            } else {
+                // check if current pageBuffer already has row
+                int index = firstIndex + i;
+                if (p != null || isGenerated) {
+                    int indexInOldBuffer = index - pageBufferFirstIndex;
+                    if (index < firstIndexNotInCache
+                            && index >= pageBufferFirstIndex
+                            && pageBuffer[CELL_GENERATED_ROW][indexInOldBuffer] == null
+                            && id.equals(pageBuffer[CELL_ITEMID][indexInOldBuffer])) {
+                        // we already have data in our cache,
+                        // recycle it instead of fetching it via
+                        // getValue/getPropertyValue
+                        value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
+                        if (!isGeneratedColumn && iscomponent[j]
+                                || !(value instanceof Component)) {
+                            listenProperty(p, oldListenedProperties);
+                        }
+                    } else {
+                        if (isGeneratedColumn) {
+                            ColumnGenerator cg = columnGenerators
+                                    .get(colids[j]);
+                            try {
+                                value = cg.generateCell(this, id, colids[j]);
+                            } catch (Exception e) {
+                                exceptionsDuringCachePopulation.add(e);
+                                value = null;
+                            }
+                            if (value != null && !(value instanceof Component)
+                                    && !(value instanceof String)) {
+                                // Avoid errors if a generator returns
+                                // something
+                                // other than a Component or a String
+                                value = value.toString();
+                            }
+                        } else if (iscomponent[j]) {
+                            try {
+                                value = p.getValue();
+                            } catch (Exception e) {
+                                exceptionsDuringCachePopulation.add(e);
+                                value = null;
+                            }
+                            listenProperty(p, oldListenedProperties);
+                        } else if (p != null) {
+                            try {
+                                value = getPropertyValue(id, colids[j], p);
+                            } catch (Exception e) {
+                                exceptionsDuringCachePopulation.add(e);
+                                value = null;
+                            }
+                            /*
+                             * If returned value is Component (via fieldfactory
+                             * or overridden getPropertyValue) we expect it to
+                             * listen property value changes. Otherwise if
+                             * property emits value change events, table will
+                             * start to listen them and refresh content when
+                             * needed.
+                             */
+                            if (!(value instanceof Component)) {
+                                listenProperty(p, oldListenedProperties);
+                            }
+                        } else {
+                            try {
+                                value = getPropertyValue(id, colids[j], null);
+                            } catch (Exception e) {
+                                exceptionsDuringCachePopulation.add(e);
+                                value = null;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (value instanceof Component) {
+                registerComponent((Component) value);
+            }
+            cells[CELL_FIRSTCOL + j][i] = value;
+        }
+    }
+
+    /** Register component.
+	 *
+	 * @param component
+	 *            the component
+	 */
+    protected void registerComponent(Component component) {
+        getLogger().log(
+                Level.FINEST,
+                "Registered {0}: {1}",
+                new Object[] { component.getClass().getSimpleName(),
+                        component.getCaption() });
+        if (!equals(component.getParent())) {
+            component.setParent(this);
+        }
+        visibleComponents.add(component);
+    }
+
+    /** Listen property.
+	 *
+	 * @param p
+	 *            the p
+	 * @param oldListenedProperties
+	 *            the old listened properties
+	 */
+    private void listenProperty(Property<?> p,
+            HashSet<Property<?>> oldListenedProperties) {
+        if (p instanceof Property.ValueChangeNotifier) {
+            if (oldListenedProperties == null
+                    || !oldListenedProperties.contains(p)) {
+                ((Property.ValueChangeNotifier) p).addListener(this);
+            }
+            /*
+             * register listened properties, so we can do proper cleanup to free
+             * memory. Essential if table has loads of data and it is used for a
+             * long time.
+             */
+            listenedProperties.add(p);
+
+        }
+    }
+
+    /** Unregister components and properties in rows.
+	 *
+	 * @param firstIx
+	 *            Index of the first row to process. Global index, not relative
+	 *            to page buffer.
+	 * @param count
+	 *            the count
+	 */
+    private void unregisterComponentsAndPropertiesInRows(int firstIx, int count) {
+        if (getLogger().isLoggable(Level.FINEST)) {
+            getLogger().log(Level.FINEST,
+                    "Unregistering components in rows {0}-{1}",
+                    new Object[] { firstIx, (firstIx + count - 1) });
+        }
+        Object[] colids = getVisibleColumns();
+        if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
+            int bufSize = pageBuffer[CELL_ITEMID].length;
+            int ix = firstIx - pageBufferFirstIndex;
+            ix = ix < 0 ? 0 : ix;
+            if (ix < bufSize) {
+                count = count > bufSize - ix ? bufSize - ix : count;
+                for (int i = 0; i < count; i++) {
+                    for (int c = 0; c < colids.length; c++) {
+                        Object cellVal = pageBuffer[CELL_FIRSTCOL + c][i + ix];
+                        if (cellVal instanceof Component
+                                && visibleComponents.contains(cellVal)) {
+                            visibleComponents.remove(cellVal);
+                            unregisterComponent((Component) cellVal);
+                        } else {
+                            Property<?> p = getContainerProperty(
+                                    pageBuffer[CELL_ITEMID][i + ix], colids[c]);
+                            if (p instanceof ValueChangeNotifier
+                                    && listenedProperties.contains(p)) {
+                                listenedProperties.remove(p);
+                                ((ValueChangeNotifier) p).removeListener(this);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper method to remove listeners and maintain correct component
+     * hierarchy. Detaches properties and components if those are no more
+     * rendered in client.
+     * 
+     * @param oldListenedProperties
+     *            set of properties that where listened in last render
+     * @param oldVisibleComponents
+     *            set of components that where attached in last render
+     */
+    private void unregisterPropertiesAndComponents(
+            HashSet<Property<?>> oldListenedProperties,
+            HashSet<Component> oldVisibleComponents) {
+        if (oldVisibleComponents != null) {
+            for (final Iterator<Component> i = oldVisibleComponents.iterator(); i
+                    .hasNext();) {
+                Component c = i.next();
+                if (!visibleComponents.contains(c)) {
+                    unregisterComponent(c);
+                }
+            }
+        }
+
+        if (oldListenedProperties != null) {
+            for (final Iterator<Property<?>> i = oldListenedProperties
+                    .iterator(); i.hasNext();) {
+                Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next();
+                if (!listenedProperties.contains(o)) {
+                    o.removeListener(this);
+                }
+            }
+        }
+    }
+
+    /** This method cleans up a Component that has been generated when
+	 * CustomTable is in editable mode. The component needs to be detached from
+	 * its parent and if it is a field, it needs to be detached from its
+	 * property data source in order to allow garbage collection to take care of
+	 * removing the unused component from memory.
+	 * 
+	 * Override this method and getPropertyValue(Object, Object, Property) with
+	 * custom logic if you need to deal with buffered fields.
+	 *
+	 * @param component
+	 *            the component
+	 * @see #getPropertyValue(Object, Object, Property)
+	 */
+    protected void unregisterComponent(Component component) {
+        getLogger().log(
+                Level.FINEST,
+                "Unregistered {0}: {1}",
+                new Object[] { component.getClass().getSimpleName(),
+                        component.getCaption() });
+        component.setParent(null);
+        /*
+         * Also remove property data sources to unregister listeners keeping the
+         * fields in memory.
+         */
+        if (component instanceof Field) {
+            Field<?> field = (Field<?>) component;
+            Property<?> associatedProperty = associatedProperties
+                    .remove(component);
+            if (associatedProperty != null
+                    && field.getPropertyDataSource() == associatedProperty) {
+                // Remove the property data source only if it's the one we
+                // added in getPropertyValue
+                field.setPropertyDataSource(null);
+            }
+        }
+    }
+
+    /**
+     * Sets the row header mode.
+     * <p>
+     * The mode can be one of the following ones:
+     * <ul>
+     * <li>{@link #ROW_HEADER_MODE_HIDDEN}: The row captions are hidden.</li>
+     * <li>{@link #ROW_HEADER_MODE_ID}: Items Id-objects <code>toString()</code>
+     * is used as row caption.
+     * <li>{@link #ROW_HEADER_MODE_ITEM}: Item-objects <code>toString()</code>
+     * is used as row caption.
+     * <li>{@link #ROW_HEADER_MODE_PROPERTY}: Property set with
+     * {@link #setItemCaptionPropertyId(Object)} is used as row header.
+     * <li>{@link #ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID}: Items Id-objects
+     * <code>toString()</code> is used as row header. If caption is explicitly
+     * specified, it overrides the id-caption.
+     * <li>{@link #ROW_HEADER_MODE_EXPLICIT}: The row headers must be explicitly
+     * specified.</li>
+     * <li>{@link #ROW_HEADER_MODE_INDEX}: The index of the item is used as row
+     * caption. The index mode can only be used with the containers implementing
+     * <code>Container.Indexed</code> interface.</li>
+     * </ul>
+     * The default value is {@link #ROW_HEADER_MODE_HIDDEN}
+     * 
+     * 
+     * @param mode
+     *            the One of the modes listed above.
+     */
+    public void setRowHeaderMode(RowHeaderMode mode) {
+        if (mode != null) {
+            rowHeaderMode = mode;
+            if (mode != RowHeaderMode.HIDDEN) {
+                setItemCaptionMode(mode.getItemCaptionMode());
+            }
+            // Assures the visual refresh. No need to reset the page buffer
+            // before
+            // as the content has not changed, only the alignments.
+            refreshRenderedCells();
+        }
+    }
+
+    /**
+     * Gets the row header mode.
+     * 
+     * @return the Row header mode.
+     * @see #setRowHeaderMode
+     */
+    public RowHeaderMode getRowHeaderMode() {
+        return rowHeaderMode;
+    }
+
+    /** Adds the new row to table and fill the visible cells (except
+	 * generated columns) with given values.
+	 *
+	 * @param cells
+	 *            the Object array that is used for filling the visible cells
+	 *            new row. The types must be settable to visible column property
+	 *            types.
+	 * @param itemId
+	 *            the Id the new row. If null, a new id is automatically
+	 *            assigned. If given, the table cant already have a item with
+	 *            given id.
+	 * @return Returns item id for the new row. Returns null if operation fails.
+	 * @throws UnsupportedOperationException
+	 *             the unsupported operation exception
+	 */
+    public Object addItem(Object[] cells, Object itemId)
+            throws UnsupportedOperationException {
+
+        // remove generated columns from the list of columns being assigned
+        final LinkedList<Object> availableCols = new LinkedList<Object>();
+        for (Iterator<Object> it = visibleColumns.iterator(); it.hasNext();) {
+            Object id = it.next();
+            if (!columnGenerators.containsKey(id)) {
+                availableCols.add(id);
+            }
+        }
+        // Checks that a correct number of cells are given
+        if (cells.length != availableCols.size()) {
+            return null;
+        }
+
+        // Creates new item
+        Item item;
+        if (itemId == null) {
+            itemId = items.addItem();
+            if (itemId == null) {
+                return null;
+            }
+            item = items.getItem(itemId);
+        } else {
+            item = items.addItem(itemId);
+        }
+        if (item == null) {
+            return null;
+        }
+
+        // Fills the item properties
+        for (int i = 0; i < availableCols.size(); i++) {
+            item.getItemProperty(availableCols.get(i)).setValue(cells[i]);
+        }
+
+        if (!(items instanceof Container.ItemSetChangeNotifier)) {
+            refreshRowCache();
+        }
+
+        return itemId;
+    }
+
+    /**
+     * Discards and recreates the internal row cache. Call this if you make
+     * changes that affect the rows but the information about the changes are
+     * not automatically propagated to the CustomTable.
+     * <p>
+     * Do not call this e.g. if you have updated the data model through a
+     * Property. These types of changes are automatically propagated to the
+     * CustomTable.
+     * <p>
+     * A typical case when this is needed is if you update a generator (e.g.
+     * CellStyleGenerator) and want to ensure that the rows are redrawn with new
+     * styles.
+     * <p>
+     * <i>Note that calling this method is not cheap so avoid calling it
+     * unnecessarily.</i>
+     * 
+     * @since 6.7.2
+     */
+    public void refreshRowCache() {
+        resetPageBuffer();
+        refreshRenderedCells();
+    }
+
+    /**
+     * Sets the Container that serves as the data source of the viewer. As a
+     * side-effect the table's selection value is set to null as the old
+     * selection might not exist in new Container.<br>
+     * <br>
+     * All rows and columns are generated as visible using this method. If the
+     * new container contains properties that are not meant to be shown you
+     * should use
+     * {@link CustomTable#setContainerDataSource(Container, Collection)}
+     * instead, especially if the table is editable.
+     * <p>
+     * Keeps propertyValueConverters if the corresponding id exists in the new
+     * data source and is of a compatible type.
+     * 
+     * 
+     * @param newDataSource
+     *            the new data source.
+     */
+    @Override
+    public void setContainerDataSource(Container newDataSource) {
+        if (newDataSource == null) {
+            newDataSource = new IndexedContainer();
+        }
+        Collection<Object> generated;
+        if (columnGenerators != null) {
+            generated = columnGenerators.keySet();
+        } else {
+            generated = Collections.emptyList();
+        }
+        List<Object> visibleIds = new ArrayList<Object>();
+        if (generated.isEmpty()) {
+            visibleIds.addAll(newDataSource.getContainerPropertyIds());
+        } else {
+            for (Object id : newDataSource.getContainerPropertyIds()) {
+                // don't add duplicates
+                if (!generated.contains(id)) {
+                    visibleIds.add(id);
+                }
+            }
+            // generated columns to the end
+            visibleIds.addAll(generated);
+        }
+        setContainerDataSource(newDataSource, visibleIds);
+    }
+
+    /** Sets the container data source and the columns that will be visible.
+	 * Columns are shown in the collection's iteration order.
+	 * <p>
+	 * Keeps propertyValueConverters if the corresponding id exists in the new
+	 * data source and is of a compatible type.
+	 *
+	 * @param newDataSource
+	 *            the new data source.
+	 * @param visibleIds
+	 *            IDs of the visible columns
+	 * @see CustomTable#setContainerDataSource(Container)
+	 * @see CustomTable#setVisibleColumns(Object[])
+	 * @see CustomTable#setConverter(Object, Converter)
+	 */
+    public void setContainerDataSource(Container newDataSource,
+            Collection<?> visibleIds) {
+
+        disableContentRefreshing();
+
+        if (newDataSource == null) {
+            newDataSource = new IndexedContainer();
+        }
+        if (visibleIds == null) {
+            visibleIds = new ArrayList<Object>();
+        }
+
+        // Retain propertyValueConverters if their corresponding ids are
+        // properties of the new
+        // data source and are of a compatible type
+        if (propertyValueConverters != null) {
+            Collection<?> newPropertyIds = newDataSource
+                    .getContainerPropertyIds();
+            LinkedList<Object> retainableValueConverters = new LinkedList<Object>();
+            for (Object propertyId : newPropertyIds) {
+                Converter<String, ?> converter = getConverter(propertyId);
+                if (converter != null) {
+                    if (typeIsCompatible(converter.getModelType(),
+                            newDataSource.getType(propertyId))) {
+                        retainableValueConverters.add(propertyId);
+                    }
+                }
+            }
+            propertyValueConverters.keySet().retainAll(
+                    retainableValueConverters);
+        }
+
+        // Assures that the data source is ordered by making unordered
+        // containers ordered by wrapping them
+        if (newDataSource instanceof Container.Ordered) {
+            super.setContainerDataSource(newDataSource);
+        } else {
+            super.setContainerDataSource(new ContainerOrderedWrapper(
+                    newDataSource));
+        }
+
+        // Resets page position
+        currentPageFirstItemId = null;
+        currentPageFirstItemIndex = 0;
+
+        // Resets column properties
+        if (collapsedColumns != null) {
+            collapsedColumns.clear();
+        }
+
+        // don't add the same id twice
+        Collection<Object> col = new LinkedList<Object>();
+        for (Iterator<?> it = visibleIds.iterator(); it.hasNext();) {
+            Object id = it.next();
+            if (!col.contains(id)) {
+                col.add(id);
+            }
+        }
+
+        setVisibleColumns(col.toArray());
+
+        // Assure visual refresh
+        resetPageBuffer();
+
+        enableContentRefreshing(true);
+    }
+
+    /** Checks if class b can be safely assigned to class a.
+	 *
+	 * @param a
+	 *            the a
+	 * @param b
+	 *            the b
+	 * @return true, if successful
+	 */
+    private boolean typeIsCompatible(Class<?> a, Class<?> b) {
+        // TODO Implement this check properly
+        // Basically we need to do a a.isAssignableFrom(b)
+        // with special considerations for primitive types.
+        return true;
+    }
+
+    /** Gets items ids from a range of key values.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @param length
+	 *            the length
+	 * @return the item ids in range
+	 */
+    private LinkedHashSet<Object> getItemIdsInRange(Object itemId,
+            final int length) {
+        LinkedHashSet<Object> ids = new LinkedHashSet<Object>();
+        for (int i = 0; i < length; i++) {
+            assert itemId != null; // should not be null unless client-server
+                                   // are out of sync
+            ids.add(itemId);
+            itemId = nextItemId(itemId);
+        }
+        return ids;
+    }
+
+    /** Handles selection if selection is a multiselection.
+	 *
+	 * @param variables
+	 *            The variables
+	 */
+    private void handleSelectedItems(Map<String, Object> variables) {
+        final String[] ka = (String[]) variables.get("selected");
+        final String[] ranges = (String[]) variables.get("selectedRanges");
+
+        Set<Object> renderedButNotSelectedItemIds = getCurrentlyRenderedItemIds();
+
+        @SuppressWarnings("unchecked")
+        HashSet<Object> newValue = new LinkedHashSet<Object>(
+                (Collection<Object>) getValue());
+
+        if (variables.containsKey("clearSelections")) {
+            // the client side has instructed to swipe all previous selections
+            newValue.clear();
+        }
+
+        /*
+         * Then add (possibly some of them back) rows that are currently
+         * selected on the client side (the ones that the client side is aware
+         * of).
+         */
+        for (int i = 0; i < ka.length; i++) {
+            // key to id
+            final Object id = itemIdMapper.get(ka[i]);
+            if (!isNullSelectionAllowed()
+                    && (id == null || id == getNullSelectionItemId())) {
+                // skip empty selection if nullselection is not allowed
+                markAsDirty();
+            } else if (id != null && containsId(id)) {
+                newValue.add(id);
+                renderedButNotSelectedItemIds.remove(id);
+            }
+        }
+
+        /* Add range items aka shift clicked multiselection areas */
+        if (ranges != null) {
+            for (String range : ranges) {
+                String[] split = range.split("-");
+                Object startItemId = itemIdMapper.get(split[0]);
+                int length = Integer.valueOf(split[1]);
+                LinkedHashSet<Object> itemIdsInRange = getItemIdsInRange(
+                        startItemId, length);
+                newValue.addAll(itemIdsInRange);
+                renderedButNotSelectedItemIds.removeAll(itemIdsInRange);
+            }
+        }
+        /*
+         * finally clear all currently rendered rows (the ones that the client
+         * side counterpart is aware of) that the client didn't send as selected
+         */
+        newValue.removeAll(renderedButNotSelectedItemIds);
+
+        if (!isNullSelectionAllowed() && newValue.isEmpty()) {
+            // empty selection not allowed, keep old value
+            markAsDirty();
+            return;
+        }
+
+        setValue(newValue, true);
+
+    }
+
+    /** Gets the currently rendered item ids.
+	 *
+	 * @return the currently rendered item ids
+	 */
+    private Set<Object> getCurrentlyRenderedItemIds() {
+        HashSet<Object> ids = new HashSet<Object>();
+        if (pageBuffer != null) {
+            for (int i = 0; i < pageBuffer[CELL_ITEMID].length; i++) {
+                ids.add(pageBuffer[CELL_ITEMID][i]);
+            }
+        }
+        return ids;
+    }
+
+    /* Component basics */
+
+    /** Invoked when the value of a variable has changed.
+	 *
+	 * @param source
+	 *            the source
+	 * @param variables
+	 *            the variables
+	 * @see com.vaadin.ui.Select#changeVariables(java.lang.Object,
+	 *      java.util.Map)
+	 */
+
+    @Override
+    public void changeVariables(Object source, Map<String, Object> variables) {
+
+        boolean clientNeedsContentRefresh = false;
+
+        handleClickEvent(variables);
+
+        handleColumnResizeEvent(variables);
+
+        handleColumnWidthUpdates(variables);
+
+        disableContentRefreshing();
+
+        if (!isSelectable() && variables.containsKey("selected")) {
+            // Not-selectable is a special case, AbstractSelect does not support
+            // TODO could be optimized.
+            variables = new HashMap<String, Object>(variables);
+            variables.remove("selected");
+        }
+
+        /*
+         * The AbstractSelect cannot handle the multiselection properly, instead
+         * we handle it ourself
+         */
+        else if (isSelectable() && isMultiSelect()
+                && variables.containsKey("selected")
+                && multiSelectMode == MultiSelectMode.DEFAULT) {
+            handleSelectedItems(variables);
+            variables = new HashMap<String, Object>(variables);
+            variables.remove("selected");
+        }
+
+        super.changeVariables(source, variables);
+
+        // Client might update the pagelength if CustomTable height is fixed
+        if (variables.containsKey("pagelength")) {
+            // Sets pageLength directly to avoid repaint that setter causes
+            pageLength = (Integer) variables.get("pagelength");
+        }
+
+        // Page start index
+        if (variables.containsKey("firstvisible")) {
+            final Integer value = (Integer) variables.get("firstvisible");
+            if (value != null) {
+                setCurrentPageFirstItemIndex(value.intValue(), false);
+            }
+        }
+
+        // Sets requested firstrow and rows for the next paint
+        if (variables.containsKey("reqfirstrow")
+                || variables.containsKey("reqrows")) {
+
+            try {
+                firstToBeRenderedInClient = ((Integer) variables
+                        .get("firstToBeRendered")).intValue();
+                lastToBeRenderedInClient = ((Integer) variables
+                        .get("lastToBeRendered")).intValue();
+            } catch (Exception e) {
+                // FIXME: Handle exception
+                getLogger().log(Level.FINER,
+                        "Could not parse the first and/or last rows.", e);
+            }
+
+            // respect suggested rows only if table is not otherwise updated
+            // (row caches emptied by other event)
+            if (!containerChangeToBeRendered) {
+                Integer value = (Integer) variables.get("reqfirstrow");
+                if (value != null) {
+                    reqFirstRowToPaint = value.intValue();
+                }
+
+                value = (Integer) variables.get("reqrows");
+                if (value != null) {
+                    reqRowsToPaint = value.intValue();
+                    int size = size();
+                    // sanity check
+
+                    if (reqFirstRowToPaint >= size) {
+                        reqFirstRowToPaint = size;
+                    }
+
+                    if (reqFirstRowToPaint + reqRowsToPaint > size) {
+                        reqRowsToPaint = size - reqFirstRowToPaint;
+                    }
+                }
+            }
+            if (getLogger().isLoggable(Level.FINEST)) {
+                getLogger().log(
+                        Level.FINEST,
+                        "Client wants rows {0}-{1}",
+                        new Object[] { reqFirstRowToPaint,
+                                (reqFirstRowToPaint + reqRowsToPaint - 1) });
+            }
+            clientNeedsContentRefresh = true;
+        }
+
+        if (isSortEnabled()) {
+            // Sorting
+            boolean doSort = false;
+            if (variables.containsKey("sortcolumn")) {
+                final String colId = (String) variables.get("sortcolumn");
+                if (colId != null && !"".equals(colId) && !"null".equals(colId)) {
+                    final Object id = columnIdMap.get(colId);
+                    setSortContainerPropertyId(id, false);
+                    doSort = true;
+                }
+            }
+            if (variables.containsKey("sortascending")) {
+                final boolean state = ((Boolean) variables.get("sortascending"))
+                        .booleanValue();
+                if (state != sortAscending) {
+                    setSortAscending(state, false);
+                    doSort = true;
+                }
+            }
+            if (doSort) {
+                this.sort();
+                resetPageBuffer();
+            }
+        }
+
+        // Dynamic column hide/show and order
+        // Update visible columns
+        if (isColumnCollapsingAllowed()) {
+            if (variables.containsKey("collapsedcolumns")) {
+                try {
+                    final Object[] ids = (Object[]) variables
+                            .get("collapsedcolumns");
+                    Set<Object> idSet = new HashSet<Object>();
+                    for (Object id : ids) {
+                        idSet.add(columnIdMap.get(id.toString()));
+                    }
+                    for (final Iterator<Object> it = visibleColumns.iterator(); it
+                            .hasNext();) {
+                        Object propertyId = it.next();
+                        if (isColumnCollapsed(propertyId)) {
+                            if (!idSet.contains(propertyId)) {
+                                setColumnCollapsed(propertyId, false);
+                            }
+                        } else if (idSet.contains(propertyId)) {
+                            setColumnCollapsed(propertyId, true);
+                        }
+                    }
+                } catch (final Exception e) {
+                    // FIXME: Handle exception
+                    getLogger().log(Level.FINER,
+                            "Could not determine column collapsing state", e);
+                }
+                clientNeedsContentRefresh = true;
+            }
+        }
+        if (isColumnReorderingAllowed()) {
+            if (variables.containsKey("columnorder")) {
+                try {
+                    final Object[] ids = (Object[]) variables
+                            .get("columnorder");
+                    // need a real Object[], ids can be a String[]
+                    final Object[] idsTemp = new Object[ids.length];
+                    for (int i = 0; i < ids.length; i++) {
+                        idsTemp[i] = columnIdMap.get(ids[i].toString());
+                    }
+                    setColumnOrder(idsTemp);
+                    if (hasListeners(ColumnReorderEvent.class)) {
+                        fireEvent(new ColumnReorderEvent(this));
+                    }
+                } catch (final Exception e) {
+                    // FIXME: Handle exception
+                    getLogger().log(Level.FINER,
+                            "Could not determine column reordering state", e);
+                }
+                clientNeedsContentRefresh = true;
+            }
+        }
+
+        enableContentRefreshing(clientNeedsContentRefresh);
+
+        // Actions
+        if (variables.containsKey("action")) {
+            final StringTokenizer st = new StringTokenizer(
+                    (String) variables.get("action"), ",");
+            if (st.countTokens() == 2) {
+                final Object itemId = itemIdMapper.get(st.nextToken());
+                final Action action = actionMapper.get(st.nextToken());
+
+                if (action != null && (itemId == null || containsId(itemId))
+                        && actionHandlers != null) {
+                    for (Handler ah : actionHandlers) {
+                        ah.handleAction(action, this, itemId);
+                    }
+                }
+            }
+        }
+
+    }
+
+    /** Handles click event.
+	 *
+	 * @param variables
+	 *            the variables
+	 */
+    private void handleClickEvent(Map<String, Object> variables) {
+
+        // Item click event
+        if (variables.containsKey("clickEvent")) {
+            String key = (String) variables.get("clickedKey");
+            Object itemId = itemIdMapper.get(key);
+            Object propertyId = null;
+            String colkey = (String) variables.get("clickedColKey");
+            // click is not necessary on a property
+            if (colkey != null) {
+                propertyId = columnIdMap.get(colkey);
+            }
+            MouseEventDetails evt = MouseEventDetails
+                    .deSerialize((String) variables.get("clickEvent"));
+            Item item = getItem(itemId);
+            if (item != null) {
+                fireEvent(new ItemClickEvent(this, item, itemId, propertyId,
+                        evt));
+            }
+        }
+
+        // Header click event
+        else if (variables.containsKey("headerClickEvent")) {
+
+            MouseEventDetails details = MouseEventDetails
+                    .deSerialize((String) variables.get("headerClickEvent"));
+
+            Object cid = variables.get("headerClickCID");
+            Object propertyId = null;
+            if (cid != null) {
+                propertyId = columnIdMap.get(cid.toString());
+            }
+            fireEvent(new HeaderClickEvent(this, propertyId, details));
+        }
+
+        // Footer click event
+        else if (variables.containsKey("footerClickEvent")) {
+            MouseEventDetails details = MouseEventDetails
+                    .deSerialize((String) variables.get("footerClickEvent"));
+
+            Object cid = variables.get("footerClickCID");
+            Object propertyId = null;
+            if (cid != null) {
+                propertyId = columnIdMap.get(cid.toString());
+            }
+            fireEvent(new FooterClickEvent(this, propertyId, details));
+        }
+    }
+
+    /** Handles the column resize event sent by the client.
+	 *
+	 * @param variables
+	 *            the variables
+	 */
+    private void handleColumnResizeEvent(Map<String, Object> variables) {
+        if (variables.containsKey("columnResizeEventColumn")) {
+            Object cid = variables.get("columnResizeEventColumn");
+            Object propertyId = null;
+            if (cid != null) {
+                propertyId = columnIdMap.get(cid.toString());
+
+                Object prev = variables.get("columnResizeEventPrev");
+                int previousWidth = -1;
+                if (prev != null) {
+                    previousWidth = Integer.valueOf(prev.toString());
+                }
+
+                Object curr = variables.get("columnResizeEventCurr");
+                int currentWidth = -1;
+                if (curr != null) {
+                    currentWidth = Integer.valueOf(curr.toString());
+                }
+
+                fireColumnResizeEvent(propertyId, previousWidth, currentWidth);
+            }
+        }
+    }
+
+    /** Fire column resize event.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @param previousWidth
+	 *            the previous width
+	 * @param currentWidth
+	 *            the current width
+	 */
+    private void fireColumnResizeEvent(Object propertyId, int previousWidth,
+            int currentWidth) {
+        /*
+         * Update the sizes on the server side. If a column previously had a
+         * expand ratio and the user resized the column then the expand ratio
+         * will be turned into a static pixel size.
+         */
+        setColumnWidth(propertyId, currentWidth);
+
+        fireEvent(new ColumnResizeEvent(this, propertyId, previousWidth,
+                currentWidth));
+    }
+
+    /** Handle column width updates.
+	 *
+	 * @param variables
+	 *            the variables
+	 */
+    private void handleColumnWidthUpdates(Map<String, Object> variables) {
+        if (variables.containsKey("columnWidthUpdates")) {
+            String[] events = (String[]) variables.get("columnWidthUpdates");
+            for (String str : events) {
+                String[] eventDetails = str.split(":");
+                Object propertyId = columnIdMap.get(eventDetails[0]);
+                if (propertyId == null) {
+                    propertyId = ROW_HEADER_FAKE_PROPERTY_ID;
+                }
+                int width = Integer.valueOf(eventDetails[1]);
+                setColumnWidth(propertyId, width);
+            }
+        }
+    }
+
+    /**
+     * Go to mode where content updates are not done. This is due we want to
+     * bypass expensive content for some reason (like when we know we may have
+     * other content changes on their way).
+     * 
+     * @return true if content refresh flag was enabled prior this call
+     */
+    protected boolean disableContentRefreshing() {
+        boolean wasDisabled = isContentRefreshesEnabled;
+        isContentRefreshesEnabled = false;
+        return wasDisabled;
+    }
+
+    /**
+     * Go to mode where content content refreshing has effect.
+     * 
+     * @param refreshContent
+     *            true if content refresh needs to be done
+     */
+    protected void enableContentRefreshing(boolean refreshContent) {
+        isContentRefreshesEnabled = true;
+        if (refreshContent) {
+            refreshRenderedCells();
+            // Ensure that client gets a response
+            markAsDirty();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.ui.AbstractField#beforeClientResponse(boolean)
+     */
+    @Override
+    public void beforeClientResponse(boolean initial) {
+        super.beforeClientResponse(initial);
+
+        // Ensure pageBuffer is filled before sending the response to avoid
+        // calls to markAsDirty during paint
+        getVisibleCells();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin.
+     * terminal.PaintTarget)
+     */
+
+    @Override
+    public void paintContent(PaintTarget target) throws PaintException {
+        isBeingPainted = true;
+        try {
+            doPaintContent(target);
+        } finally {
+            isBeingPainted = false;
+        }
+    }
+
+    /** Do paint content.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void doPaintContent(PaintTarget target) throws PaintException {
+        /*
+         * Body actions - Actions which has the target null and can be invoked
+         * by right clicking on the table body.
+         */
+        final Set<Action> actionSet = findAndPaintBodyActions(target);
+
+        final Object[][] cells = getVisibleCells();
+        int rows = findNumRowsToPaint(target, cells);
+
+        int total = size();
+        if (shouldHideNullSelectionItem()) {
+            total--;
+            rows--;
+        }
+
+        // CustomTable attributes
+        paintTableAttributes(target, rows, total);
+
+        paintVisibleColumnOrder(target);
+
+        // Rows
+        if (isPartialRowUpdate() && painted && !target.isFullRepaint()) {
+            paintPartialRowUpdate(target, actionSet);
+        } else if (target.isFullRepaint() || isRowCacheInvalidated()) {
+            paintRows(target, cells, actionSet);
+            setRowCacheInvalidated(false);
+        }
+
+        /*
+         * Send the page buffer indexes to ensure that the client side stays in
+         * sync. Otherwise we _might_ have the situation where the client side
+         * discards too few or too many rows, causing out of sync issues.
+         */
+        int pageBufferLastIndex = pageBufferFirstIndex
+                + pageBuffer[CELL_ITEMID].length - 1;
+        target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_FIRST,
+                pageBufferFirstIndex);
+        target.addAttribute(TableConstants.ATTRIBUTE_PAGEBUFFER_LAST,
+                pageBufferLastIndex);
+
+        paintSorting(target);
+
+        resetVariablesAndPageBuffer(target);
+
+        // Actions
+        paintActions(target, actionSet);
+
+        paintColumnOrder(target);
+
+        // Available columns
+        paintAvailableColumns(target);
+
+        paintVisibleColumns(target);
+
+        if (keyMapperReset) {
+            keyMapperReset = false;
+            target.addAttribute(TableConstants.ATTRIBUTE_KEY_MAPPER_RESET, true);
+        }
+
+        if (dropHandler != null) {
+            dropHandler.getAcceptCriterion().paint(target);
+        }
+
+        painted = true;
+    }
+
+    /** Sets the row cache invalidated.
+	 *
+	 * @param invalidated
+	 *            the new row cache invalidated
+	 */
+    private void setRowCacheInvalidated(boolean invalidated) {
+        rowCacheInvalidated = invalidated;
+    }
+
+    /** Checks if is row cache invalidated.
+	 *
+	 * @return true, if is row cache invalidated
+	 */
+    protected boolean isRowCacheInvalidated() {
+        return rowCacheInvalidated;
+    }
+
+    /** Paint partial row update.
+	 *
+	 * @param target
+	 *            the target
+	 * @param actionSet
+	 *            the action set
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintPartialRowUpdate(PaintTarget target, Set<Action> actionSet)
+            throws PaintException {
+        paintPartialRowUpdates(target, actionSet);
+        paintPartialRowAdditions(target, actionSet);
+    }
+
+    /** Paint partial row updates.
+	 *
+	 * @param target
+	 *            the target
+	 * @param actionSet
+	 *            the action set
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintPartialRowUpdates(PaintTarget target,
+            Set<Action> actionSet) throws PaintException {
+        final boolean[] iscomponent = findCellsWithComponents();
+
+        int firstIx = getFirstUpdatedItemIndex();
+        int count = getUpdatedRowCount();
+
+        target.startTag("urows");
+        target.addAttribute("firsturowix", firstIx);
+        target.addAttribute("numurows", count);
+
+        // Partial row updates bypass the normal caching mechanism.
+        Object[][] cells = getVisibleCellsUpdateCacheRows(firstIx, count);
+        for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
+            final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
+
+            if (shouldHideNullSelectionItem()) {
+                // Remove null selection item if null selection is not allowed
+                continue;
+            }
+
+            paintRow(target, cells, isEditable(), actionSet, iscomponent,
+                    indexInRowbuffer, itemId);
+        }
+        target.endTag("urows");
+        maybeThrowCacheUpdateExceptions();
+    }
+
+    /** Paint partial row additions.
+	 *
+	 * @param target
+	 *            the target
+	 * @param actionSet
+	 *            the action set
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintPartialRowAdditions(PaintTarget target,
+            Set<Action> actionSet) throws PaintException {
+        final boolean[] iscomponent = findCellsWithComponents();
+
+        int firstIx = getFirstAddedItemIndex();
+        int count = getAddedRowCount();
+
+        target.startTag("prows");
+
+        if (!shouldHideAddedRows()) {
+            getLogger().log(Level.FINEST,
+                    "Paint rows for add. Index: {0}, count: {1}.",
+                    new Object[] { firstIx, count });
+
+            // Partial row additions bypass the normal caching mechanism.
+            Object[][] cells = getVisibleCellsInsertIntoCache(firstIx, count);
+            if (cells[0].length < count) {
+                // delete the rows below, since they will fall beyond the cache
+                // page.
+                target.addAttribute("delbelow", true);
+                count = cells[0].length;
+            }
+
+            for (int indexInRowbuffer = 0; indexInRowbuffer < count; indexInRowbuffer++) {
+                final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
+                if (shouldHideNullSelectionItem()) {
+                    // Remove null selection item if null selection is not
+                    // allowed
+                    continue;
+                }
+
+                paintRow(target, cells, isEditable(), actionSet, iscomponent,
+                        indexInRowbuffer, itemId);
+            }
+        } else {
+            getLogger().log(Level.FINEST,
+                    "Paint rows for remove. Index: {0}, count: {1}.",
+                    new Object[] { firstIx, count });
+            removeRowsFromCacheAndFillBottom(firstIx, count);
+            target.addAttribute("hide", true);
+        }
+
+        target.addAttribute("firstprowix", firstIx);
+        target.addAttribute("numprows", count);
+        target.endTag("prows");
+        maybeThrowCacheUpdateExceptions();
+    }
+
+    /**
+     * Subclass and override this to enable partial row updates and additions,
+     * which bypass the normal caching mechanism. This is useful for e.g.
+     * TreeTable.
+     * 
+     * @return true if this update is a partial row update, false if not. For
+     *         plain CustomTable it is always false.
+     */
+    protected boolean isPartialRowUpdate() {
+        return false;
+    }
+
+    /**
+     * Subclass and override this to enable partial row additions, bypassing the
+     * normal caching mechanism. This is useful for e.g. TreeTable, where
+     * expanding a node should only fetch and add the items inside of that node.
+     * 
+     * @return The index of the first added item. For plain CustomTable it is
+     *         always 0.
+     */
+    protected int getFirstAddedItemIndex() {
+        return 0;
+    }
+
+    /**
+     * Subclass and override this to enable partial row additions, bypassing the
+     * normal caching mechanism. This is useful for e.g. TreeTable, where
+     * expanding a node should only fetch and add the items inside of that node.
+     * 
+     * @return the number of rows to be added, starting at the index returned by
+     *         {@link #getFirstAddedItemIndex()}. For plain CustomTable it is
+     *         always 0.
+     */
+    protected int getAddedRowCount() {
+        return 0;
+    }
+
+    /**
+     * Subclass and override this to enable removing of rows, bypassing the
+     * normal caching and lazy loading mechanism. This is useful for e.g.
+     * TreeTable, when you need to hide certain rows as a node is collapsed.
+     * 
+     * This should return true if the rows pointed to by
+     * {@link #getFirstAddedItemIndex()} and {@link #getAddedRowCount()} should
+     * be hidden instead of added.
+     * 
+     * @return whether the rows to add (see {@link #getFirstAddedItemIndex()}
+     *         and {@link #getAddedRowCount()}) should be added or hidden. For
+     *         plain CustomTable it is always false.
+     */
+    protected boolean shouldHideAddedRows() {
+        return false;
+    }
+
+    /**
+     * Subclass and override this to enable partial row updates, bypassing the
+     * normal caching and lazy loading mechanism. This is useful for updating
+     * the state of certain rows, e.g. in the TreeTable the collapsed state of a
+     * single node is updated using this mechanism.
+     * 
+     * @return the index of the first item to be updated. For plain CustomTable
+     *         it is always 0.
+     */
+    protected int getFirstUpdatedItemIndex() {
+        return 0;
+    }
+
+    /**
+     * Subclass and override this to enable partial row updates, bypassing the
+     * normal caching and lazy loading mechanism. This is useful for updating
+     * the state of certain rows, e.g. in the TreeTable the collapsed state of a
+     * single node is updated using this mechanism.
+     * 
+     * @return the number of rows to update, starting at the index returned by
+     *         {@link #getFirstUpdatedItemIndex()}. For plain table it is always
+     *         0.
+     */
+    protected int getUpdatedRowCount() {
+        return 0;
+    }
+
+    /** Paint table attributes.
+	 *
+	 * @param target
+	 *            the target
+	 * @param rows
+	 *            the rows
+	 * @param total
+	 *            the total
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintTableAttributes(PaintTarget target, int rows, int total)
+            throws PaintException {
+        paintTabIndex(target);
+        paintDragMode(target);
+        paintSelectMode(target);
+
+        if (cacheRate != CACHE_RATE_DEFAULT) {
+            target.addAttribute("cr", cacheRate);
+        }
+
+        target.addAttribute("cols", getVisibleColumns().length);
+        target.addAttribute("rows", rows);
+
+        target.addAttribute("firstrow",
+                (reqFirstRowToPaint >= 0 ? reqFirstRowToPaint
+                        : firstToBeRenderedInClient));
+        target.addAttribute("totalrows", total);
+        if (getPageLength() != 0) {
+            target.addAttribute("pagelength", getPageLength());
+        }
+        if (areColumnHeadersEnabled()) {
+            target.addAttribute("colheaders", true);
+        }
+        if (rowHeadersAreEnabled()) {
+            target.addAttribute("rowheaders", true);
+        }
+
+        target.addAttribute("colfooters", columnFootersVisible);
+
+        // The cursors are only shown on pageable table
+        if (getCurrentPageFirstItemIndex() != 0 || getPageLength() > 0) {
+            target.addVariable(this, "firstvisible",
+                    getCurrentPageFirstItemIndex());
+            target.addVariable(this, "firstvisibleonlastpage",
+                    currentPageFirstItemIndexOnLastPage);
+        }
+    }
+
+    /** Resets and paints "to be painted next" variables. Also reset
+	 * pageBuffer
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void resetVariablesAndPageBuffer(PaintTarget target)
+            throws PaintException {
+        reqFirstRowToPaint = -1;
+        reqRowsToPaint = -1;
+        containerChangeToBeRendered = false;
+        target.addVariable(this, "reqrows", reqRowsToPaint);
+        target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
+    }
+
+    /** Are column headers enabled.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean areColumnHeadersEnabled() {
+        return getColumnHeaderMode() != ColumnHeaderMode.HIDDEN;
+    }
+
+    /** Paint visible columns.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintVisibleColumns(PaintTarget target) throws PaintException {
+        target.startTag("visiblecolumns");
+        if (rowHeadersAreEnabled()) {
+            target.startTag("column");
+            target.addAttribute("cid", ROW_HEADER_COLUMN_KEY);
+            paintColumnWidth(target, ROW_HEADER_FAKE_PROPERTY_ID);
+            paintColumnExpandRatio(target, ROW_HEADER_FAKE_PROPERTY_ID);
+            target.endTag("column");
+        }
+        final Collection<?> sortables = getSortableContainerPropertyIds();
+        for (Object colId : visibleColumns) {
+            if (colId != null) {
+                target.startTag("column");
+                target.addAttribute("cid", columnIdMap.key(colId));
+                final String head = getColumnHeader(colId);
+                target.addAttribute("caption", (head != null ? head : ""));
+                final String foot = getColumnFooter(colId);
+                target.addAttribute("fcaption", (foot != null ? foot : ""));
+                if (isColumnCollapsed(colId)) {
+                    target.addAttribute("collapsed", true);
+                }
+                if (areColumnHeadersEnabled()) {
+                    if (getColumnIcon(colId) != null) {
+                        target.addAttribute("icon", getColumnIcon(colId));
+                    }
+                    if (sortables.contains(colId)) {
+                        target.addAttribute("sortable", true);
+                    }
+                }
+                if (!Align.LEFT.equals(getColumnAlignment(colId))) {
+                    target.addAttribute("align", getColumnAlignment(colId)
+                            .toString());
+                }
+                paintColumnWidth(target, colId);
+                paintColumnExpandRatio(target, colId);
+                target.endTag("column");
+            }
+        }
+        target.endTag("visiblecolumns");
+    }
+
+    /** Paint available columns.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintAvailableColumns(PaintTarget target)
+            throws PaintException {
+        if (columnCollapsingAllowed) {
+            final HashSet<Object> collapsedCols = new HashSet<Object>();
+            for (Object colId : visibleColumns) {
+                if (isColumnCollapsed(colId)) {
+                    collapsedCols.add(colId);
+                }
+            }
+            final String[] collapsedKeys = new String[collapsedCols.size()];
+            int nextColumn = 0;
+            for (Object colId : visibleColumns) {
+                if (isColumnCollapsed(colId)) {
+                    collapsedKeys[nextColumn++] = columnIdMap.key(colId);
+                }
+            }
+            target.addVariable(this, "collapsedcolumns", collapsedKeys);
+
+            final String[] noncollapsibleKeys = new String[noncollapsibleColumns
+                    .size()];
+            nextColumn = 0;
+            for (Object colId : noncollapsibleColumns) {
+                noncollapsibleKeys[nextColumn++] = columnIdMap.key(colId);
+            }
+            target.addVariable(this, "noncollapsiblecolumns",
+                    noncollapsibleKeys);
+        }
+
+    }
+
+    /** Paint actions.
+	 *
+	 * @param target
+	 *            the target
+	 * @param actionSet
+	 *            the action set
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintActions(PaintTarget target, final Set<Action> actionSet)
+            throws PaintException {
+        if (!actionSet.isEmpty()) {
+            target.addVariable(this, "action", "");
+            target.startTag("actions");
+            for (Action a : actionSet) {
+                target.startTag("action");
+                if (a.getCaption() != null) {
+                    target.addAttribute("caption", a.getCaption());
+                }
+                if (a.getIcon() != null) {
+                    target.addAttribute("icon", a.getIcon());
+                }
+                target.addAttribute("key", actionMapper.key(a));
+                target.endTag("action");
+            }
+            target.endTag("actions");
+        }
+    }
+
+    /** Paint column order.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintColumnOrder(PaintTarget target) throws PaintException {
+        if (columnReorderingAllowed) {
+            final String[] colorder = new String[visibleColumns.size()];
+            int i = 0;
+            for (Object colId : visibleColumns) {
+                colorder[i++] = columnIdMap.key(colId);
+            }
+            target.addVariable(this, "columnorder", colorder);
+        }
+    }
+
+    /** Paint sorting.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintSorting(PaintTarget target) throws PaintException {
+        // Sorting
+        if (getContainerDataSource() instanceof Container.Sortable) {
+            target.addVariable(this, "sortcolumn",
+                    columnIdMap.key(sortContainerPropertyId));
+            target.addVariable(this, "sortascending", sortAscending);
+        }
+    }
+
+    /** Paint rows.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param actionSet
+	 *            the action set
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintRows(PaintTarget target, final Object[][] cells,
+            final Set<Action> actionSet) throws PaintException {
+        final boolean[] iscomponent = findCellsWithComponents();
+
+        target.startTag("rows");
+        // cells array contains all that are supposed to be visible on client,
+        // but we'll start from the one requested by client
+        int start = 0;
+        if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) {
+            start = reqFirstRowToPaint - firstToBeRenderedInClient;
+        }
+        int end = cells[0].length;
+        if (reqRowsToPaint != -1) {
+            end = start + reqRowsToPaint;
+        }
+        // sanity check
+        if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) {
+            end = lastToBeRenderedInClient + 1;
+        }
+        if (start > cells[CELL_ITEMID].length || start < 0) {
+            start = 0;
+        }
+        if (end > cells[CELL_ITEMID].length) {
+            end = cells[CELL_ITEMID].length;
+        }
+
+        for (int indexInRowbuffer = start; indexInRowbuffer < end; indexInRowbuffer++) {
+            final Object itemId = cells[CELL_ITEMID][indexInRowbuffer];
+
+            if (shouldHideNullSelectionItem()) {
+                // Remove null selection item if null selection is not allowed
+                continue;
+            }
+
+            paintRow(target, cells, isEditable(), actionSet, iscomponent,
+                    indexInRowbuffer, itemId);
+        }
+        target.endTag("rows");
+    }
+
+    /** Find cells with components.
+	 *
+	 * @return the boolean[]
+	 */
+    private boolean[] findCellsWithComponents() {
+        final boolean[] isComponent = new boolean[visibleColumns.size()];
+        int ix = 0;
+        for (Object columnId : visibleColumns) {
+            if (columnGenerators.containsKey(columnId)) {
+                isComponent[ix++] = true;
+            } else {
+                final Class<?> colType = getType(columnId);
+                isComponent[ix++] = colType != null
+                        && Component.class.isAssignableFrom(colType);
+            }
+        }
+        return isComponent;
+    }
+
+    /** Paint visible column order.
+	 *
+	 * @param target
+	 *            the target
+	 */
+    private void paintVisibleColumnOrder(PaintTarget target) {
+        // Visible column order
+        final ArrayList<String> visibleColOrder = new ArrayList<String>();
+        for (Object columnId : visibleColumns) {
+            if (!isColumnCollapsed(columnId)) {
+                visibleColOrder.add(columnIdMap.key(columnId));
+            }
+        }
+        target.addAttribute("vcolorder", visibleColOrder.toArray());
+    }
+
+    /** Find and paint body actions.
+	 *
+	 * @param target
+	 *            the target
+	 * @return the sets the
+	 */
+    private Set<Action> findAndPaintBodyActions(PaintTarget target) {
+        Set<Action> actionSet = new LinkedHashSet<Action>();
+        if (actionHandlers != null) {
+            final ArrayList<String> keys = new ArrayList<String>();
+            for (Handler ah : actionHandlers) {
+                // Getting actions for the null item, which in this case means
+                // the body item
+                final Action[] actions = ah.getActions(null, this);
+                if (actions != null) {
+                    for (Action action : actions) {
+                        actionSet.add(action);
+                        keys.add(actionMapper.key(action));
+                    }
+                }
+            }
+            target.addAttribute("alb", keys.toArray());
+        }
+        return actionSet;
+    }
+
+    /** Should hide null selection item.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean shouldHideNullSelectionItem() {
+        return !isNullSelectionAllowed() && getNullSelectionItemId() != null
+                && containsId(getNullSelectionItemId());
+    }
+
+    /** Find num rows to paint.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @return the int
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private int findNumRowsToPaint(PaintTarget target, final Object[][] cells)
+            throws PaintException {
+        int rows;
+        if (reqRowsToPaint >= 0) {
+            rows = reqRowsToPaint;
+        } else {
+            rows = cells[0].length;
+            if (alwaysRecalculateColumnWidths) {
+                // TODO experimental feature for now: tell the client to
+                // recalculate column widths.
+                // We'll only do this for paints that do not originate from
+                // table scroll/cache requests (i.e when reqRowsToPaint<0)
+                target.addAttribute("recalcWidths", true);
+            }
+        }
+        return rows;
+    }
+
+    /** Paint select mode.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintSelectMode(PaintTarget target) throws PaintException {
+        if (multiSelectMode != MultiSelectMode.DEFAULT) {
+            target.addAttribute("multiselectmode", multiSelectMode.ordinal());
+        }
+        if (isSelectable()) {
+            target.addAttribute("selectmode", (isMultiSelect() ? "multi"
+                    : "single"));
+        } else {
+            target.addAttribute("selectmode", "none");
+        }
+        if (!isNullSelectionAllowed()) {
+            target.addAttribute("nsa", false);
+        }
+
+        // selection support
+        // The select variable is only enabled if selectable
+        if (isSelectable()) {
+            target.addVariable(this, "selected", findSelectedKeys());
+        }
+    }
+
+    /** Find selected keys.
+	 *
+	 * @return the string[]
+	 */
+    private String[] findSelectedKeys() {
+        LinkedList<String> selectedKeys = new LinkedList<String>();
+        if (isMultiSelect()) {
+            HashSet<?> sel = new HashSet<Object>((Set<?>) getValue());
+            Collection<?> vids = getVisibleItemIds();
+            for (Iterator<?> it = vids.iterator(); it.hasNext();) {
+                Object id = it.next();
+                if (sel.contains(id)) {
+                    selectedKeys.add(itemIdMapper.key(id));
+                }
+            }
+        } else {
+            Object value = getValue();
+            if (value == null) {
+                value = getNullSelectionItemId();
+            }
+            if (value != null) {
+                selectedKeys.add(itemIdMapper.key(value));
+            }
+        }
+        return selectedKeys.toArray(new String[selectedKeys.size()]);
+    }
+
+    /** Paint drag mode.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintDragMode(PaintTarget target) throws PaintException {
+        if (dragMode != TableDragMode.NONE) {
+            target.addAttribute("dragmode", dragMode.ordinal());
+        }
+    }
+
+    /** Paint tab index.
+	 *
+	 * @param target
+	 *            the target
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintTabIndex(PaintTarget target) throws PaintException {
+        // The tab ordering number
+        if (getTabIndex() > 0) {
+            target.addAttribute("tabindex", getTabIndex());
+        }
+    }
+
+    /** Paint column width.
+	 *
+	 * @param target
+	 *            the target
+	 * @param columnId
+	 *            the column id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintColumnWidth(PaintTarget target, final Object columnId)
+            throws PaintException {
+        if (columnWidths.containsKey(columnId)) {
+            target.addAttribute("width", getColumnWidth(columnId));
+        }
+    }
+
+    /** Paint column expand ratio.
+	 *
+	 * @param target
+	 *            the target
+	 * @param columnId
+	 *            the column id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintColumnExpandRatio(PaintTarget target,
+            final Object columnId) throws PaintException {
+        if (columnExpandRatios.containsKey(columnId)) {
+            target.addAttribute("er", getColumnExpandRatio(columnId));
+        }
+    }
+
+    /** Row headers are enabled.
+	 *
+	 * @return true, if successful
+	 */
+    private boolean rowHeadersAreEnabled() {
+        return getRowHeaderMode() != ROW_HEADER_MODE_HIDDEN;
+    }
+
+    /** Paint row.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param iseditable
+	 *            the iseditable
+	 * @param actionSet
+	 *            the action set
+	 * @param iscomponent
+	 *            the iscomponent
+	 * @param indexInRowbuffer
+	 *            the index in rowbuffer
+	 * @param itemId
+	 *            the item id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintRow(PaintTarget target, final Object[][] cells,
+            final boolean iseditable, final Set<Action> actionSet,
+            final boolean[] iscomponent, int indexInRowbuffer,
+            final Object itemId) throws PaintException {
+        target.startTag("tr");
+
+        paintRowAttributes(target, cells, actionSet, indexInRowbuffer, itemId);
+
+        // cells
+        int currentColumn = 0;
+        for (final Iterator<Object> it = visibleColumns.iterator(); it
+                .hasNext(); currentColumn++) {
+            final Object columnId = it.next();
+            if (columnId == null || isColumnCollapsed(columnId)) {
+                continue;
+            }
+            /*
+             * For each cell, if a cellStyleGenerator is specified, get the
+             * specific style for the cell. If there is any, add it to the
+             * target.
+             */
+            if (cellStyleGenerator != null) {
+                String cellStyle = cellStyleGenerator.getStyle(this, itemId,
+                        columnId);
+                if (cellStyle != null && !cellStyle.equals("")) {
+                    target.addAttribute("style-" + columnIdMap.key(columnId),
+                            cellStyle);
+                }
+            }
+
+            if ((iscomponent[currentColumn] || iseditable || cells[CELL_GENERATED_ROW][indexInRowbuffer] != null)
+                    && Component.class.isInstance(cells[CELL_FIRSTCOL
+                            + currentColumn][indexInRowbuffer])) {
+                final Component c = (Component) cells[CELL_FIRSTCOL
+                        + currentColumn][indexInRowbuffer];
+                if (c == null
+                        || !LegacyCommunicationManager
+                                .isComponentVisibleToClient(c)) {
+                    target.addText("");
+                } else {
+                    LegacyPaint.paint(c, target);
+                }
+            } else {
+                target.addText((String) cells[CELL_FIRSTCOL + currentColumn][indexInRowbuffer]);
+            }
+            paintCellTooltips(target, itemId, columnId);
+        }
+
+        target.endTag("tr");
+    }
+
+    /** Paint cell tooltips.
+	 *
+	 * @param target
+	 *            the target
+	 * @param itemId
+	 *            the item id
+	 * @param columnId
+	 *            the column id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintCellTooltips(PaintTarget target, Object itemId,
+            Object columnId) throws PaintException {
+        if (itemDescriptionGenerator != null) {
+            String itemDescription = itemDescriptionGenerator
+                    .generateDescription(this, itemId, columnId);
+            if (itemDescription != null && !itemDescription.equals("")) {
+                target.addAttribute("descr-" + columnIdMap.key(columnId),
+                        itemDescription);
+            }
+        }
+    }
+
+    /** Paint row tooltips.
+	 *
+	 * @param target
+	 *            the target
+	 * @param itemId
+	 *            the item id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintRowTooltips(PaintTarget target, Object itemId)
+            throws PaintException {
+        if (itemDescriptionGenerator != null) {
+            String rowDescription = itemDescriptionGenerator
+                    .generateDescription(this, itemId, null);
+            if (rowDescription != null && !rowDescription.equals("")) {
+                target.addAttribute("rowdescr", rowDescription);
+            }
+        }
+    }
+
+    /** Paint row attributes.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param actionSet
+	 *            the action set
+	 * @param indexInRowbuffer
+	 *            the index in rowbuffer
+	 * @param itemId
+	 *            the item id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintRowAttributes(PaintTarget target, final Object[][] cells,
+            final Set<Action> actionSet, int indexInRowbuffer,
+            final Object itemId) throws PaintException {
+        // tr attributes
+
+        paintRowIcon(target, cells, indexInRowbuffer);
+        paintRowHeader(target, cells, indexInRowbuffer);
+        paintGeneratedRowInfo(target, cells, indexInRowbuffer);
+        target.addAttribute("key",
+                Integer.parseInt(cells[CELL_KEY][indexInRowbuffer].toString()));
+
+        if (isSelected(itemId)) {
+            target.addAttribute("selected", true);
+        }
+
+        // Actions
+        if (actionHandlers != null) {
+            final ArrayList<String> keys = new ArrayList<String>();
+            for (Handler ah : actionHandlers) {
+                final Action[] aa = ah.getActions(itemId, this);
+                if (aa != null) {
+                    for (int ai = 0; ai < aa.length; ai++) {
+                        final String key = actionMapper.key(aa[ai]);
+                        actionSet.add(aa[ai]);
+                        keys.add(key);
+                    }
+                }
+            }
+            target.addAttribute("al", keys.toArray());
+        }
+
+        /*
+         * For each row, if a cellStyleGenerator is specified, get the specific
+         * style for the cell, using null as propertyId. If there is any, add it
+         * to the target.
+         */
+        if (cellStyleGenerator != null) {
+            String rowStyle = cellStyleGenerator.getStyle(this, itemId, null);
+            if (rowStyle != null && !rowStyle.equals("")) {
+                target.addAttribute("rowstyle", rowStyle);
+            }
+        }
+
+        paintRowTooltips(target, itemId);
+
+        paintRowAttributes(target, itemId);
+    }
+
+    /** Paint generated row info.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param indexInRowBuffer
+	 *            the index in row buffer
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    private void paintGeneratedRowInfo(PaintTarget target, Object[][] cells,
+            int indexInRowBuffer) throws PaintException {
+        GeneratedRow generatedRow = (GeneratedRow) cells[CELL_GENERATED_ROW][indexInRowBuffer];
+        if (generatedRow != null) {
+            target.addAttribute("gen_html", generatedRow.isHtmlContentAllowed());
+            target.addAttribute("gen_span", generatedRow.isSpanColumns());
+            target.addAttribute("gen_widget",
+                    generatedRow.getValue() instanceof Component);
+        }
+    }
+
+    /** Paint row header.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param indexInRowbuffer
+	 *            the index in rowbuffer
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    protected void paintRowHeader(PaintTarget target, Object[][] cells,
+            int indexInRowbuffer) throws PaintException {
+        if (rowHeadersAreEnabled()) {
+            if (cells[CELL_HEADER][indexInRowbuffer] != null) {
+                target.addAttribute("caption",
+                        (String) cells[CELL_HEADER][indexInRowbuffer]);
+            }
+        }
+
+    }
+
+    /** Paint row icon.
+	 *
+	 * @param target
+	 *            the target
+	 * @param cells
+	 *            the cells
+	 * @param indexInRowbuffer
+	 *            the index in rowbuffer
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    protected void paintRowIcon(PaintTarget target, final Object[][] cells,
+            int indexInRowbuffer) throws PaintException {
+        if (rowHeadersAreEnabled()
+                && cells[CELL_ICON][indexInRowbuffer] != null) {
+            target.addAttribute("icon",
+                    (Resource) cells[CELL_ICON][indexInRowbuffer]);
+        }
+    }
+
+    /** A method where extended CustomTable implementations may add their
+	 * custom attributes for rows.
+	 *
+	 * @param target
+	 *            the target
+	 * @param itemId
+	 *            the item id
+	 * @throws PaintException
+	 *             the paint exception
+	 */
+    protected void paintRowAttributes(PaintTarget target, Object itemId)
+            throws PaintException {
+
+    }
+
+    /**
+     * Gets the cached visible table contents.
+     * 
+     * @return the cached visible table contents.
+     */
+    private Object[][] getVisibleCells() {
+        if (pageBuffer == null) {
+            refreshRenderedCells();
+        }
+        return pageBuffer;
+    }
+
+    /**
+     * Gets the value of property.
+     * 
+     * By default if the table is editable the fieldFactory is used to create
+     * editors for table cells. Otherwise formatPropertyValue is used to format
+     * the value representation.
+     * 
+     * @param rowId
+     *            the Id of the row (same as item Id).
+     * @param colId
+     *            the Id of the column.
+     * @param property
+     *            the Property to be presented.
+     * @return Object Either formatted value or Component for field.
+     * @see #setTableFieldFactory(TableFieldFactory)
+     */
+    protected Object getPropertyValue(Object rowId, Object colId,
+            Property property) {
+        if (isEditable() && fieldFactory != null) {
+            final Field<?> f = fieldFactory.createField(
+                    getContainerDataSource(), rowId, colId, this);
+            if (f != null) {
+                // Remember that we have made this association so we can remove
+                // it when the component is removed
+                associatedProperties.put(f, property);
+                bindPropertyToField(rowId, colId, property, f);
+                return f;
+            }
+        }
+
+        return formatPropertyValue(rowId, colId, property);
+    }
+
+    /** Binds an item property to a field generated by TableFieldFactory. The
+	 * default behavior is to bind property straight to Field. If
+	 * Property.Viewer type property (e.g. PropertyFormatter) is already set for
+	 * field, the property is bound to that Property.Viewer.
+	 *
+	 * @param rowId
+	 *            the row id
+	 * @param colId
+	 *            the col id
+	 * @param property
+	 *            the property
+	 * @param field
+	 *            the field
+	 * @since 6.7.3
+	 */
+    protected void bindPropertyToField(Object rowId, Object colId,
+            Property property, Field field) {
+        // check if field has a property that is Viewer set. In that case we
+        // expect developer has e.g. PropertyFormatter that he wishes to use and
+        // assign the property to the Viewer instead.
+        boolean hasFilterProperty = field.getPropertyDataSource() != null
+                && (field.getPropertyDataSource() instanceof Property.Viewer);
+        if (hasFilterProperty) {
+            ((Property.Viewer) field.getPropertyDataSource())
+                    .setPropertyDataSource(property);
+        } else {
+            field.setPropertyDataSource(property);
+        }
+    }
+
+    /**
+     * Formats table cell property values. By default the property.toString()
+     * and return a empty string for null properties.
+     * 
+     * @param rowId
+     *            the Id of the row (same as item Id).
+     * @param colId
+     *            the Id of the column.
+     * @param property
+     *            the Property to be formatted.
+     * @return the String representation of property and its value.
+     * @since 3.1
+     */
+    protected String formatPropertyValue(Object rowId, Object colId,
+            Property<?> property) {
+        if (property == null) {
+            return "";
+        }
+        Converter<String, Object> converter = null;
+
+        if (hasConverter(colId)) {
+            converter = getConverter(colId);
+        } else {
+            converter = (Converter) ConverterUtil.getConverter(String.class,
+                    property.getType(), getSession());
+        }
+        Object value = property.getValue();
+        if (converter != null) {
+            return converter.convertToPresentation(value, String.class,
+                    getLocale());
+        }
+        return (null != value) ? value.toString() : "";
+    }
+
+    /* Action container */
+
+    /** Registers a new action handler for this container.
+	 *
+	 * @param actionHandler
+	 *            the action handler
+	 * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
+	 */
+
+    @Override
+    public void addActionHandler(Action.Handler actionHandler) {
+
+        if (actionHandler != null) {
+
+            if (actionHandlers == null) {
+                actionHandlers = new LinkedList<Handler>();
+                actionMapper = new KeyMapper<Action>();
+            }
+
+            if (!actionHandlers.contains(actionHandler)) {
+                actionHandlers.add(actionHandler);
+                // Assures the visual refresh. No need to reset the page buffer
+                // before as the content has not changed, only the action
+                // handlers.
+                refreshRenderedCells();
+            }
+
+        }
+    }
+
+    /** Removes a previously registered action handler for the contents of
+	 * this container.
+	 *
+	 * @param actionHandler
+	 *            the action handler
+	 * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
+	 */
+
+    @Override
+    public void removeActionHandler(Action.Handler actionHandler) {
+
+        if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
+
+            actionHandlers.remove(actionHandler);
+
+            if (actionHandlers.isEmpty()) {
+                actionHandlers = null;
+                actionMapper = null;
+            }
+
+            // Assures the visual refresh. No need to reset the page buffer
+            // before as the content has not changed, only the action
+            // handlers.
+            refreshRenderedCells();
+        }
+    }
+
+    /** Removes all action handlers.
+	 */
+    public void removeAllActionHandlers() {
+        actionHandlers = null;
+        actionMapper = null;
+        // Assures the visual refresh. No need to reset the page buffer
+        // before as the content has not changed, only the action
+        // handlers.
+        refreshRenderedCells();
+    }
+
+    /* Property value change listening support */
+
+    /** Notifies this listener that the Property's value has changed.
+	 * 
+	 * Also listens changes in rendered items to refresh content area.
+	 *
+	 * @param event
+	 *            the event
+	 * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
+	 */
+
+    @Override
+    public void valueChange(Property.ValueChangeEvent event) {
+        if (equals(event.getProperty())
+                || event.getProperty() == getPropertyDataSource()) {
+            super.valueChange(event);
+        } else {
+            refreshRowCache();
+            containerChangeToBeRendered = true;
+        }
+        markAsDirty();
+    }
+
+    /**
+     * Clears the current page buffer. Call this before
+     * {@link #refreshRenderedCells()} to ensure that all content is updated
+     * from the properties.
+     */
+    protected void resetPageBuffer() {
+        firstToBeRenderedInClient = -1;
+        lastToBeRenderedInClient = -1;
+        reqFirstRowToPaint = -1;
+        reqRowsToPaint = -1;
+        pageBuffer = null;
+    }
+
+    /**
+     * Notifies the component that it is connected to an application.
+     * 
+     * @see com.vaadin.ui.Component#attach()
+     */
+
+    @Override
+    public void attach() {
+        super.attach();
+
+        refreshRenderedCells();
+    }
+
+    /** Notifies the component that it is detached from the application.
+	 *
+	 * @see com.vaadin.ui.Component#detach()
+	 */
+
+    @Override
+    public void detach() {
+        super.detach();
+    }
+
+    /** Removes all Items from the Container.
+	 *
+	 * @return true, if successful
+	 * @see com.vaadin.data.Container#removeAllItems()
+	 */
+
+    @Override
+    public boolean removeAllItems() {
+        currentPageFirstItemId = null;
+        currentPageFirstItemIndex = 0;
+        return super.removeAllItems();
+    }
+
+    /** Removes the Item identified by <code>ItemId</code> from the
+	 * Container.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return true, if successful
+	 * @see com.vaadin.data.Container#removeItem(Object)
+	 */
+
+    @Override
+    public boolean removeItem(Object itemId) {
+        final Object nextItemId = nextItemId(itemId);
+        final boolean ret = super.removeItem(itemId);
+        if (ret && (itemId != null) && (itemId.equals(currentPageFirstItemId))) {
+            currentPageFirstItemId = nextItemId;
+        }
+        if (!(items instanceof Container.ItemSetChangeNotifier)) {
+            refreshRowCache();
+        }
+        return ret;
+    }
+
+    /** Removes a Property specified by the given Property ID from the
+	 * Container.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @return true, if successful
+	 * @throws UnsupportedOperationException
+	 *             the unsupported operation exception
+	 * @see com.vaadin.data.Container#removeContainerProperty(Object)
+	 */
+
+    @Override
+    public boolean removeContainerProperty(Object propertyId)
+            throws UnsupportedOperationException {
+
+        // If a visible property is removed, remove the corresponding column
+        visibleColumns.remove(propertyId);
+        columnAlignments.remove(propertyId);
+        columnIcons.remove(propertyId);
+        columnHeaders.remove(propertyId);
+        columnFooters.remove(propertyId);
+        // If a propertyValueConverter was defined for the property, remove it.
+        propertyValueConverters.remove(propertyId);
+
+        return super.removeContainerProperty(propertyId);
+    }
+
+    /** Adds a new property to the table and show it as a visible column.
+	 *
+	 * @param propertyId
+	 *            the Id of the proprty.
+	 * @param type
+	 *            the class of the property.
+	 * @param defaultValue
+	 *            the default value given for all existing items.
+	 * @return true, if successful
+	 * @throws UnsupportedOperationException
+	 *             the unsupported operation exception
+	 * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
+	 *      Object)
+	 */
+
+    @Override
+    public boolean addContainerProperty(Object propertyId, Class<?> type,
+            Object defaultValue) throws UnsupportedOperationException {
+
+        boolean visibleColAdded = false;
+        if (!visibleColumns.contains(propertyId)) {
+            visibleColumns.add(propertyId);
+            visibleColAdded = true;
+        }
+
+        if (!super.addContainerProperty(propertyId, type, defaultValue)) {
+            if (visibleColAdded) {
+                visibleColumns.remove(propertyId);
+            }
+            return false;
+        }
+        if (!(items instanceof Container.PropertySetChangeNotifier)) {
+            refreshRowCache();
+        }
+        return true;
+    }
+
+    /** Adds a new property to the table and show it as a visible column.
+	 *
+	 * @param propertyId
+	 *            the Id of the proprty
+	 * @param type
+	 *            the class of the property
+	 * @param defaultValue
+	 *            the default value given for all existing items
+	 * @param columnHeader
+	 *            the Explicit header of the column. If explicit header is not
+	 *            needed, this should be set null.
+	 * @param columnIcon
+	 *            the Icon of the column. If icon is not needed, this should be
+	 *            set null.
+	 * @param columnAlignment
+	 *            the Alignment of the column. Null implies align left.
+	 * @return true, if successful
+	 * @throws UnsupportedOperationException
+	 *             if the operation is not supported.
+	 * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
+	 *      Object)
+	 */
+    public boolean addContainerProperty(Object propertyId, Class<?> type,
+            Object defaultValue, String columnHeader, Resource columnIcon,
+            Align columnAlignment) throws UnsupportedOperationException {
+        if (!this.addContainerProperty(propertyId, type, defaultValue)) {
+            return false;
+        }
+        setColumnAlignment(propertyId, columnAlignment);
+        setColumnHeader(propertyId, columnHeader);
+        setColumnIcon(propertyId, columnIcon);
+        return true;
+    }
+
+    /**
+     * Adds a generated column to the CustomTable.
+     * <p>
+     * A generated column is a column that exists only in the CustomTable, not
+     * as a property in the underlying Container. It shows up just as a regular
+     * column.
+     * 
+     * <p>
+     * A generated column will override a property with the same id, so that the
+     * generated column is shown instead of the column representing the
+     * property. Note that getContainerProperty() will still get the real
+     * property.
+     * 
+     * <p>
+     * CustomTable will not listen to value change events from properties
+     * overridden by generated columns. If the content of your generated column
+     * depends on properties that are not directly visible in the table, attach
+     * value change listener to update the content on all depended properties.
+     * Otherwise your UI might not get updated as expected.
+     * 
+     * <p>
+     * Also note that getVisibleColumns() will return the generated columns,
+     * while getContainerPropertyIds() will not.
+     * 
+     * 
+     * @param id
+     *            the id of the column to be added
+     * @param generatedColumn
+     *            the {@link ColumnGenerator} to use for this column
+     */
+    public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) {
+        if (generatedColumn == null) {
+            throw new IllegalArgumentException(
+                    "Can not add null as a GeneratedColumn");
+        }
+        if (columnGenerators.containsKey(id)) {
+            throw new IllegalArgumentException(
+                    "Can not add the same GeneratedColumn twice, id:" + id);
+        } else {
+            columnGenerators.put(id, generatedColumn);
+            /*
+             * add to visible column list unless already there (overriding
+             * column from DS)
+             */
+            if (!visibleColumns.contains(id)) {
+                visibleColumns.add(id);
+            }
+            refreshRowCache();
+        }
+    }
+
+    /** Returns the ColumnGenerator used to generate the given column.
+	 *
+	 * @param columnId
+	 *            The id of the generated column
+	 * @return The ColumnGenerator used for the given columnId or null.
+	 * @throws IllegalArgumentException
+	 *             the illegal argument exception
+	 */
+    public ColumnGenerator getColumnGenerator(Object columnId)
+            throws IllegalArgumentException {
+        return columnGenerators.get(columnId);
+    }
+
+    /**
+     * Removes a generated column previously added with addGeneratedColumn.
+     * 
+     * @param columnId
+     *            id of the generated column to remove
+     * @return true if the column could be removed (existed in the CustomTable)
+     */
+    public boolean removeGeneratedColumn(Object columnId) {
+        if (columnGenerators.containsKey(columnId)) {
+            columnGenerators.remove(columnId);
+            // remove column from visibleColumns list unless it exists in
+            // container (generator previously overrode this column)
+            if (!items.getContainerPropertyIds().contains(columnId)) {
+                visibleColumns.remove(columnId);
+            }
+            refreshRowCache();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** Returns item identifiers of the items which are currently rendered on
+	 * the client.
+	 * <p>
+	 * Note, that some due to historical reasons the name of the method is bit
+	 * misleading. Some items may be partly or totally out of the viewport of
+	 * the table's scrollable area. Actually detecting rows which can be
+	 * actually seen by the end user may be problematic due to the client server
+	 * architecture. Using {@link #getCurrentPageFirstItemId()} combined with
+	 * {@link #getPageLength()} may produce good enough estimates in some
+	 * situations.
+	 *
+	 * @return the visible item ids
+	 * @see com.vaadin.ui.Select#getVisibleItemIds()
+	 */
+
+    @Override
+    public Collection<?> getVisibleItemIds() {
+
+        final LinkedList<Object> visible = new LinkedList<Object>();
+
+        final Object[][] cells = getVisibleCells();
+        // may be null if the table has not been rendered yet (e.g. not attached
+        // to a layout)
+        if (null != cells) {
+            for (int i = 0; i < cells[CELL_ITEMID].length; i++) {
+                visible.add(cells[CELL_ITEMID][i]);
+            }
+        }
+
+        return visible;
+    }
+
+    /** Container datasource item set change. CustomTable must flush its
+	 * buffers on change.
+	 *
+	 * @param event
+	 *            the event
+	 * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
+	 */
+
+    @Override
+    public void containerItemSetChange(Container.ItemSetChangeEvent event) {
+        if (isBeingPainted) {
+            return;
+        }
+
+        super.containerItemSetChange(event);
+
+        // super method clears the key map, must inform client about this to
+        // avoid getting invalid keys back (#8584)
+        keyMapperReset = true;
+
+        // ensure that page still has first item in page, ignore buffer refresh
+        // (forced in this method)
+        setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
+        refreshRowCache();
+    }
+
+    /** Container datasource property set change. CustomTable must flush its
+	 * buffers on change.
+	 *
+	 * @param event
+	 *            the event
+	 * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
+	 */
+
+    @Override
+    public void containerPropertySetChange(
+            Container.PropertySetChangeEvent event) {
+        if (isBeingPainted) {
+            return;
+        }
+
+        disableContentRefreshing();
+        super.containerPropertySetChange(event);
+
+        // sanitetize visibleColumns. note that we are not adding previously
+        // non-existing properties as columns
+        Collection<?> containerPropertyIds = getContainerDataSource()
+                .getContainerPropertyIds();
+
+        LinkedList<Object> newVisibleColumns = new LinkedList<Object>(
+                visibleColumns);
+        for (Iterator<Object> iterator = newVisibleColumns.iterator(); iterator
+                .hasNext();) {
+            Object id = iterator.next();
+            if (!(containerPropertyIds.contains(id) || columnGenerators
+                    .containsKey(id))) {
+                iterator.remove();
+            }
+        }
+        setVisibleColumns(newVisibleColumns.toArray());
+        // same for collapsed columns
+        for (Iterator<Object> iterator = collapsedColumns.iterator(); iterator
+                .hasNext();) {
+            Object id = iterator.next();
+            if (!(containerPropertyIds.contains(id) || columnGenerators
+                    .containsKey(id))) {
+                iterator.remove();
+            }
+        }
+
+        resetPageBuffer();
+        enableContentRefreshing(true);
+    }
+
+    /** Adding new items is not supported.
+	 *
+	 * @param allowNewOptions
+	 *            the new new items allowed
+	 * @throws UnsupportedOperationException
+	 *             if set to true.
+	 * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
+	 */
+
+    @Override
+    public void setNewItemsAllowed(boolean allowNewOptions)
+            throws UnsupportedOperationException {
+        if (allowNewOptions) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /** Gets the ID of the Item following the Item that corresponds to
+	 * itemId.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return the object
+	 * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
+	 */
+
+    @Override
+    public Object nextItemId(Object itemId) {
+        return ((Container.Ordered) items).nextItemId(itemId);
+    }
+
+    /** Gets the ID of the Item preceding the Item that corresponds to the
+	 * itemId.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return the object
+	 * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
+	 */
+
+    @Override
+    public Object prevItemId(Object itemId) {
+        return ((Container.Ordered) items).prevItemId(itemId);
+    }
+
+    /** Gets the ID of the first Item in the Container.
+	 *
+	 * @return the object
+	 * @see com.vaadin.data.Container.Ordered#firstItemId()
+	 */
+
+    @Override
+    public Object firstItemId() {
+        return ((Container.Ordered) items).firstItemId();
+    }
+
+    /** Gets the ID of the last Item in the Container.
+	 *
+	 * @return the object
+	 * @see com.vaadin.data.Container.Ordered#lastItemId()
+	 */
+
+    @Override
+    public Object lastItemId() {
+        return ((Container.Ordered) items).lastItemId();
+    }
+
+    /** Tests if the Item corresponding to the given Item ID is the first
+	 * Item in the Container.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return true, if is first id
+	 * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
+	 */
+
+    @Override
+    public boolean isFirstId(Object itemId) {
+        return ((Container.Ordered) items).isFirstId(itemId);
+    }
+
+    /** Tests if the Item corresponding to the given Item ID is the last Item
+	 * in the Container.
+	 *
+	 * @param itemId
+	 *            the item id
+	 * @return true, if is last id
+	 * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
+	 */
+
+    @Override
+    public boolean isLastId(Object itemId) {
+        return ((Container.Ordered) items).isLastId(itemId);
+    }
+
+    /** Adds new item after the given item.
+	 *
+	 * @param previousItemId
+	 *            the previous item id
+	 * @return the object
+	 * @throws UnsupportedOperationException
+	 *             the unsupported operation exception
+	 * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
+	 */
+
+    @Override
+    public Object addItemAfter(Object previousItemId)
+            throws UnsupportedOperationException {
+        Object itemId = ((Container.Ordered) items)
+                .addItemAfter(previousItemId);
+        if (!(items instanceof Container.ItemSetChangeNotifier)) {
+            refreshRowCache();
+        }
+        return itemId;
+    }
+
+    /** Adds new item after the given item.
+	 *
+	 * @param previousItemId
+	 *            the previous item id
+	 * @param newItemId
+	 *            the new item id
+	 * @return the item
+	 * @throws UnsupportedOperationException
+	 *             the unsupported operation exception
+	 * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
+	 *      java.lang.Object)
+	 */
+
+    @Override
+    public Item addItemAfter(Object previousItemId, Object newItemId)
+            throws UnsupportedOperationException {
+        Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
+                newItemId);
+        if (!(items instanceof Container.ItemSetChangeNotifier)) {
+            refreshRowCache();
+        }
+        return item;
+    }
+
+    /**
+     * Sets the TableFieldFactory that is used to create editor for table cells.
+     * 
+     * The TableFieldFactory is only used if the CustomTable is editable. By
+     * default the DefaultFieldFactory is used.
+     * 
+     * @param fieldFactory
+     *            the field factory to set.
+     * @see #isEditable
+     * @see DefaultFieldFactory
+     */
+    public void setTableFieldFactory(TableFieldFactory fieldFactory) {
+        this.fieldFactory = fieldFactory;
+
+        // Assure visual refresh
+        refreshRowCache();
+    }
+
+    /**
+     * Gets the TableFieldFactory that is used to create editor for table cells.
+     * 
+     * The FieldFactory is only used if the CustomTable is editable.
+     * 
+     * @return TableFieldFactory used to create the Field instances.
+     * @see #isEditable
+     */
+    public TableFieldFactory getTableFieldFactory() {
+        return fieldFactory;
+    }
+
+    /**
+     * Is table editable.
+     * 
+     * If table is editable a editor of type Field is created for each table
+     * cell. The assigned FieldFactory is used to create the instances.
+     * 
+     * To provide custom editors for table cells create a class implementins the
+     * FieldFactory interface, and assign it to table, and set the editable
+     * property to true.
+     * 
+     * @return true if table is editable, false oterwise.
+     * @see Field
+     * @see fieldFactory
+     * 
+     */
+    public boolean isEditable() {
+        return editable;
+    }
+
+    /**
+     * Sets the editable property.
+     * 
+     * If table is editable a editor of type Field is created for each table
+     * cell. The assigned FieldFactory is used to create the instances.
+     * 
+     * To provide custom editors for table cells create a class implementins the
+     * FieldFactory interface, and assign it to table, and set the editable
+     * property to true.
+     * 
+     * @param editable
+     *            true if table should be editable by user.
+     * @see Field
+     * @see fieldFactory
+     * 
+     */
+    public void setEditable(boolean editable) {
+        this.editable = editable;
+
+        // Assure visual refresh
+        refreshRowCache();
+    }
+
+    /** Sorts the table.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @param ascending
+	 *            the ascending
+	 * @throws UnsupportedOperationException
+	 *             if the container data source does not implement
+	 *             Container.Sortable
+	 * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
+	 *      boolean[])
+	 */
+
+    @Override
+    public void sort(Object[] propertyId, boolean[] ascending)
+            throws UnsupportedOperationException {
+        final Container c = getContainerDataSource();
+        if (c instanceof Container.Sortable) {
+            final int pageIndex = getCurrentPageFirstItemIndex();
+            boolean refreshingPreviouslyEnabled = disableContentRefreshing();
+            ((Container.Sortable) c).sort(propertyId, ascending);
+            setCurrentPageFirstItemIndex(pageIndex);
+            if (refreshingPreviouslyEnabled) {
+                enableContentRefreshing(true);
+            }
+            if (propertyId.length > 0 && ascending.length > 0) {
+                // The first propertyId is the primary sorting criterion,
+                // therefore the sort indicator should be there
+                sortAscending = ascending[0];
+                sortContainerPropertyId = propertyId[0];
+            } else {
+                sortAscending = true;
+                sortContainerPropertyId = null;
+            }
+        } else if (c != null) {
+            throw new UnsupportedOperationException(
+                    "Underlying Data does not allow sorting");
+        }
+    }
+
+    /**
+     * Sorts the table by currently selected sorting column.
+     * 
+     * @throws UnsupportedOperationException
+     *             if the container data source does not implement
+     *             Container.Sortable
+     */
+    public void sort() {
+        if (getSortContainerPropertyId() == null) {
+            return;
+        }
+        sort(new Object[] { sortContainerPropertyId },
+                new boolean[] { sortAscending });
+    }
+
+    /** Gets the container property IDs, which can be used to sort the item.
+	 * <p>
+	 * Note that the {@link #isSortEnabled()} state affects what this method
+	 * returns. Disabling sorting causes this method to always return an empty
+	 * collection.
+	 *
+	 * @return the sortable container property ids
+	 * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
+	 */
+
+    @Override
+    public Collection<?> getSortableContainerPropertyIds() {
+        final Container c = getContainerDataSource();
+        if (c instanceof Container.Sortable && isSortEnabled()) {
+            return ((Container.Sortable) c).getSortableContainerPropertyIds();
+        } else {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    /**
+     * Gets the currently sorted column property ID.
+     * 
+     * @return the Container property id of the currently sorted column.
+     */
+    public Object getSortContainerPropertyId() {
+        return sortContainerPropertyId;
+    }
+
+    /**
+     * Sets the currently sorted column property id.
+     * 
+     * @param propertyId
+     *            the Container property id of the currently sorted column.
+     */
+    public void setSortContainerPropertyId(Object propertyId) {
+        setSortContainerPropertyId(propertyId, true);
+    }
+
+    /** Internal method to set currently sorted column property id. With
+	 * doSort flag actual sorting may be bypassed.
+	 *
+	 * @param propertyId
+	 *            the property id
+	 * @param doSort
+	 *            the do sort
+	 */
+    private void setSortContainerPropertyId(Object propertyId, boolean doSort) {
+        if ((sortContainerPropertyId != null && !sortContainerPropertyId
+                .equals(propertyId))
+                || (sortContainerPropertyId == null && propertyId != null)) {
+            sortContainerPropertyId = propertyId;
+
+            if (doSort) {
+                sort();
+                // Assures the visual refresh. This should not be necessary as
+                // sort() calls refreshRowCache
+                refreshRenderedCells();
+            }
+        }
+    }
+
+    /**
+     * Is the table currently sorted in ascending order.
+     * 
+     * @return <code>true</code> if ascending, <code>false</code> if descending.
+     */
+    public boolean isSortAscending() {
+        return sortAscending;
+    }
+
+    /**
+     * Sets the table in ascending order.
+     * 
+     * @param ascending
+     *            <code>true</code> if ascending, <code>false</code> if
+     *            descending.
+     */
+    public void setSortAscending(boolean ascending) {
+        setSortAscending(ascending, true);
+    }
+
+    /** Internal method to set sort ascending. With doSort flag actual sort
+	 * can be bypassed.
+	 *
+	 * @param ascending
+	 *            the ascending
+	 * @param doSort
+	 *            the do sort
+	 */
+    private void setSortAscending(boolean ascending, boolean doSort) {
+        if (sortAscending != ascending) {
+            sortAscending = ascending;
+            if (doSort) {
+                sort();
+                // Assures the visual refresh. This should not be necessary as
+                // sort() calls refreshRowCache
+                refreshRenderedCells();
+            }
+        }
+    }
+
+    /**
+     * Is sorting disabled altogether.
+     * 
+     * True iff no sortable columns are given even in the case where data source
+     * would support this.
+     * 
+     * @return True iff sorting is disabled.
+     * @deprecated As of 7.0, use {@link #isSortEnabled()} instead
+     */
+    @Deprecated
+    public boolean isSortDisabled() {
+        return !isSortEnabled();
+    }
+
+    /**
+     * Checks if sorting is enabled.
+     * 
+     * @return true if sorting by the user is allowed, false otherwise
+     */
+    public boolean isSortEnabled() {
+        return sortEnabled;
+    }
+
+    /**
+     * Disables the sorting by the user altogether.
+     * 
+     * @param sortDisabled
+     *            True iff sorting is disabled.
+     * @deprecated As of 7.0, use {@link #setSortEnabled(boolean)} instead
+     */
+    @Deprecated
+    public void setSortDisabled(boolean sortDisabled) {
+        setSortEnabled(!sortDisabled);
+    }
+
+    /**
+     * Enables or disables sorting.
+     * <p>
+     * Setting this to false disallows sorting by the user. It is still possible
+     * to call {@link #sort()}.
+     * 
+     * 
+     * @param sortEnabled
+     *            true to allow the user to sort the table, false to disallow it
+     */
+    public void setSortEnabled(boolean sortEnabled) {
+        if (this.sortEnabled != sortEnabled) {
+            this.sortEnabled = sortEnabled;
+            markAsDirty();
+        }
+    }
+
+    /**
+     * Used to create "generated columns"; columns that exist only in the
+     * CustomTable, not in the underlying Container. Implement this interface
+     * and pass it to CustomTable.addGeneratedColumn along with an id for the
+     * column to be generated.
+     * 
+     */
+    public interface ColumnGenerator extends Serializable {
+
+        /**
+         * Called by CustomTable when a cell in a generated column needs to be
+         * generated.
+         * 
+         * @param source
+         *            the source CustomTable
+         * @param itemId
+         *            the itemId (aka rowId) for the of the cell to be generated
+         * @param columnId
+         *            the id for the generated column (as specified in
+         *            addGeneratedColumn)
+         * @return A {@link Component} that should be rendered in the cell or a
+         *         {@link String} that should be displayed in the cell. Other
+         *         return values are not supported.
+         */
+        public abstract Object generateCell(CustomTable source, Object itemId,
+                Object columnId);
+    }
+
+    /**
+     * Set cell style generator for CustomTable.
+     * 
+     * @param cellStyleGenerator
+     *            New cell style generator or null to remove generator.
+     */
+    public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
+        this.cellStyleGenerator = cellStyleGenerator;
+        // Assures the visual refresh. No need to reset the page buffer
+        // before as the content has not changed, only the style generators
+        refreshRenderedCells();
+
+    }
+
+    /** Get the current cell style generator.
+	 *
+	 * @return the customTable cell specific style generator
+	 */
+    public CellStyleGenerator getCellStyleGenerator() {
+        return cellStyleGenerator;
+    }
+
+    /**
+     * Allow to define specific style on cells (and rows) contents. Implements
+     * this interface and pass it to CustomTable.setCellStyleGenerator. Row
+     * styles are generated when porpertyId is null. The CSS class name that
+     * will be added to the cell content is
+     * <tt>v-table-cell-content-[style name]</tt>, and the row style will be
+     * <tt>v-table-row-[style name]</tt>.
+     */
+    public interface CellStyleGenerator extends Serializable {
+
+        /**
+         * Called by CustomTable when a cell (and row) is painted.
+         * 
+         * @param source
+         *            the source CustomTable
+         * @param itemId
+         *            The itemId of the painted cell
+         * @param propertyId
+         *            The propertyId of the cell, null when getting row style
+         * @return The style name to add to this cell or row. (the CSS class
+         *         name will be v-table-cell-content-[style name], or
+         *         v-table-row-[style name] for rows)
+         */
+        public abstract String getStyle(CustomTable source, Object itemId,
+                Object propertyId);
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.event.ItemClickEvent.ItemClickNotifier#addItemClickListener(com.vaadin.event.ItemClickEvent.ItemClickListener)
+     */
+    @Override
+    public void addItemClickListener(ItemClickListener listener) {
+        addListener(TableConstants.ITEM_CLICK_EVENT_ID, ItemClickEvent.class,
+                listener, ItemClickEvent.ITEM_CLICK_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addItemClickListener(ItemClickListener)}
+	 */
+    @Override
+    @Deprecated
+    public void addListener(ItemClickListener listener) {
+        addItemClickListener(listener);
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.event.ItemClickEvent.ItemClickNotifier#removeItemClickListener(com.vaadin.event.ItemClickEvent.ItemClickListener)
+     */
+    @Override
+    public void removeItemClickListener(ItemClickListener listener) {
+        removeListener(TableConstants.ITEM_CLICK_EVENT_ID,
+                ItemClickEvent.class, listener);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeItemClickListener(ItemClickListener)}
+	 */
+    @Override
+    @Deprecated
+    public void removeListener(ItemClickListener listener) {
+        removeItemClickListener(listener);
+    }
+
+    // Identical to AbstractCompoenentContainer.setEnabled();
+
+    /* (non-Javadoc)
+     * @see com.vaadin.ui.AbstractComponent#setEnabled(boolean)
+     */
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        if (getParent() != null && !getParent().isEnabled()) {
+            // some ancestor still disabled, don't update children
+            return;
+        } else {
+            markAsDirtyRecursive();
+        }
+    }
+
+    /** Sets the drag start mode of the CustomTable. Drag start mode controls
+	 * how CustomTable behaves as a drag source.
+	 *
+	 * @param newDragMode
+	 *            the new drag mode
+	 */
+    public void setDragMode(TableDragMode newDragMode) {
+        dragMode = newDragMode;
+        markAsDirty();
+    }
+
+    /** Gets the drag mode.
+	 *
+	 * @return the current start mode of the CustomTable. Drag start mode
+	 *         controls how CustomTable behaves as a drag source.
+	 */
+    public TableDragMode getDragMode() {
+        return dragMode;
+    }
+
+    /**
+     * Concrete implementation of {@link DataBoundTransferable} for data
+     * transferred from a table.
+     * 
+     * @see DataBoundTransferable
+     * 
+     * @since 6.3
+     */
+    public class TableTransferable extends DataBoundTransferable {
+
+        /** Instantiates a new table transferable.
+		 *
+		 * @param rawVariables
+		 *            the raw variables
+		 */
+        protected TableTransferable(Map<String, Object> rawVariables) {
+            super(CustomTable.this, rawVariables);
+            Object object = rawVariables.get("itemId");
+            if (object != null) {
+                setData("itemId", itemIdMapper.get((String) object));
+            }
+            object = rawVariables.get("propertyId");
+            if (object != null) {
+                setData("propertyId", columnIdMap.get((String) object));
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.event.DataBoundTransferable#getItemId()
+         */
+        @Override
+        public Object getItemId() {
+            return getData("itemId");
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.event.DataBoundTransferable#getPropertyId()
+         */
+        @Override
+        public Object getPropertyId() {
+            return getData("propertyId");
+        }
+
+        /* (non-Javadoc)
+         * @see com.vaadin.event.TransferableImpl#getSourceComponent()
+         */
+        @Override
+        public CustomTable getSourceComponent() {
+            return (CustomTable) super.getSourceComponent();
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.event.dd.DragSource#getTransferable(java.util.Map)
+     */
+    @Override
+    public TableTransferable getTransferable(Map<String, Object> rawVariables) {
+        TableTransferable transferable = new TableTransferable(rawVariables);
+        return transferable;
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.event.dd.DropTarget#getDropHandler()
+     */
+    @Override
+    public DropHandler getDropHandler() {
+        return dropHandler;
+    }
+
+    /** Sets the drop handler.
+	 *
+	 * @param dropHandler
+	 *            the new drop handler
+	 */
+    public void setDropHandler(DropHandler dropHandler) {
+        this.dropHandler = dropHandler;
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map)
+     */
+    @Override
+    public AbstractSelectTargetDetails translateDropTargetDetails(
+            Map<String, Object> clientVariables) {
+        return new AbstractSelectTargetDetails(clientVariables);
+    }
+
+    /**
+     * Sets the behavior of how the multi-select mode should behave when the
+     * table is both selectable and in multi-select mode.
+     * <p>
+     * Note, that on some clients the mode may not be respected. E.g. on touch
+     * based devices CTRL/SHIFT base selection method is invalid, so touch based
+     * browsers always use the {@link MultiSelectMode#SIMPLE}.
+     * 
+     * @param mode
+     *            The select mode of the table
+     */
+    public void setMultiSelectMode(MultiSelectMode mode) {
+        multiSelectMode = mode;
+        markAsDirty();
+    }
+
+    /**
+     * Returns the select mode in which multi-select is used.
+     * 
+     * @return The multi select mode
+     */
+    public MultiSelectMode getMultiSelectMode() {
+        return multiSelectMode;
+    }
+
+    /**
+     * Lazy loading accept criterion for CustomTable. Accepted target rows are
+     * loaded from server once per drag and drop operation. Developer must
+     * override one method that decides on which rows the currently dragged data
+     * can be dropped.
+     * 
+     * <p>
+     * Initially pretty much no data is sent to client. On first required
+     * criterion check (per drag request) the client side data structure is
+     * initialized from server and no subsequent requests requests are needed
+     * during that drag and drop operation.
+     */
+    public static abstract class TableDropCriterion extends ServerSideCriterion {
+
+        /** The table. */
+        private CustomTable table;
+
+        /** The allowed item ids. */
+        private Set<Object> allowedItemIds;
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * com.vaadin.event.dd.acceptcriteria.ServerSideCriterion#getIdentifier
+         * ()
+         */
+
+        @Override
+        protected String getIdentifier() {
+            return TableDropCriterion.class.getCanonicalName();
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#accepts(com.vaadin
+         * .event.dd.DragAndDropEvent)
+         */
+        @Override
+        @SuppressWarnings("unchecked")
+        public boolean accept(DragAndDropEvent dragEvent) {
+            AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
+                    .getTargetDetails();
+            table = (CustomTable) dragEvent.getTargetDetails().getTarget();
+            Collection<?> visibleItemIds = table.getVisibleItemIds();
+            allowedItemIds = getAllowedItemIds(dragEvent, table,
+                    (Collection<Object>) visibleItemIds);
+
+            return allowedItemIds.contains(dropTargetData.getItemIdOver());
+        }
+
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * com.vaadin.event.dd.acceptcriteria.AcceptCriterion#paintResponse(
+         * com.vaadin.server.PaintTarget)
+         */
+
+        @Override
+        public void paintResponse(PaintTarget target) throws PaintException {
+            /*
+             * send allowed nodes to client so subsequent requests can be
+             * avoided
+             */
+            Object[] array = allowedItemIds.toArray();
+            for (int i = 0; i < array.length; i++) {
+                String key = table.itemIdMapper.key(array[i]);
+                array[i] = key;
+            }
+            target.addAttribute("allowedIds", array);
+        }
+
+        /** Gets the allowed item ids.
+		 *
+		 * @param dragEvent
+		 *            the drag event
+		 * @param table
+		 *            the table for which the allowed item identifiers are
+		 *            defined
+		 * @param visibleItemIds
+		 *            the list of currently rendered item identifiers, accepted
+		 *            item id's need to be detected only for these visible items
+		 * @return the set of identifiers for items on which the dragEvent will
+		 *         be accepted
+		 */
+        protected abstract Set<Object> getAllowedItemIds(
+                DragAndDropEvent dragEvent, CustomTable table,
+                Collection<Object> visibleItemIds);
+
+    }
+
+    /**
+     * Click event fired when clicking on the CustomTable headers. The event
+     * includes a reference the the CustomTable the event originated from, the
+     * property id of the column which header was pressed and details about the
+     * mouse event itself.
+     */
+    public static class HeaderClickEvent extends ClickEvent {
+        
+        /** The Constant HEADER_CLICK_METHOD. */
+        public static final Method HEADER_CLICK_METHOD;
+
+        static {
+            try {
+                // Set the header click method
+                HEADER_CLICK_METHOD = HeaderClickListener.class
+                        .getDeclaredMethod("headerClick",
+                                new Class[] { HeaderClickEvent.class });
+            } catch (final java.lang.NoSuchMethodException e) {
+                // This should never happen
+                throw new java.lang.RuntimeException(e);
+            }
+        }
+
+        /** The column property id. */
+        // The property id of the column which header was pressed
+        private final Object columnPropertyId;
+
+        /** Instantiates a new header click event.
+		 *
+		 * @param source
+		 *            the source
+		 * @param propertyId
+		 *            the property id
+		 * @param details
+		 *            the details
+		 */
+        public HeaderClickEvent(Component source, Object propertyId,
+                MouseEventDetails details) {
+            super(source, details);
+            columnPropertyId = propertyId;
+        }
+
+        /** Gets the property id of the column which header was pressed.
+		 *
+		 * @return The column propety id
+		 */
+        public Object getPropertyId() {
+            return columnPropertyId;
+        }
+    }
+
+    /**
+     * Click event fired when clicking on the CustomTable footers. The event
+     * includes a reference the the CustomTable the event originated from, the
+     * property id of the column which header was pressed and details about the
+     * mouse event itself.
+     */
+    public static class FooterClickEvent extends ClickEvent {
+        
+        /** The Constant FOOTER_CLICK_METHOD. */
+        public static final Method FOOTER_CLICK_METHOD;
+
+        static {
+            try {
+                // Set the header click method
+                FOOTER_CLICK_METHOD = FooterClickListener.class
+                        .getDeclaredMethod("footerClick",
+                                new Class[] { FooterClickEvent.class });
+            } catch (final java.lang.NoSuchMethodException e) {
+                // This should never happen
+                throw new java.lang.RuntimeException(e);
+            }
+        }
+
+        /** The column property id. */
+        // The property id of the column which header was pressed
+        private final Object columnPropertyId;
+
+        /** Constructor.
+		 *
+		 * @param source
+		 *            The source of the component
+		 * @param propertyId
+		 *            The propertyId of the column
+		 * @param details
+		 *            The mouse details of the click
+		 */
+        public FooterClickEvent(Component source, Object propertyId,
+                MouseEventDetails details) {
+            super(source, details);
+            columnPropertyId = propertyId;
+        }
+
+        /** Gets the property id of the column which header was pressed.
+		 *
+		 * @return The column propety id
+		 */
+        public Object getPropertyId() {
+            return columnPropertyId;
+        }
+    }
+
+    /** Interface for the listener for column header mouse click events. The
+	 * headerClick method is called when the user presses a header column cell.
+	 *
+	 * @see HeaderClickEvent
+	 */
+    public interface HeaderClickListener extends Serializable {
+
+        /** Called when a user clicks a header column cell.
+		 *
+		 * @param event
+		 *            The event which contains information about the column and
+		 *            the mouse click event
+		 */
+        public void headerClick(HeaderClickEvent event);
+    }
+
+    /** Interface for the listener for column footer mouse click events. The
+	 * footerClick method is called when the user presses a footer column cell.
+	 *
+	 * @see FooterClickEvent
+	 */
+    public interface FooterClickListener extends Serializable {
+
+        /** Called when a user clicks a footer column cell.
+		 *
+		 * @param event
+		 *            The event which contains information about the column and
+		 *            the mouse click event
+		 */
+        public void footerClick(FooterClickEvent event);
+    }
+
+    /**
+     * Adds a header click listener which handles the click events when the user
+     * clicks on a column header cell in the CustomTable.
+     * <p>
+     * The listener will receive events which contain information about which
+     * column was clicked and some details about the mouse event.
+     * 
+     * 
+     * @param listener
+     *            The handler which should handle the header click events.
+     */
+    public void addHeaderClickListener(HeaderClickListener listener) {
+        addListener(TableConstants.HEADER_CLICK_EVENT_ID,
+                HeaderClickEvent.class, listener,
+                HeaderClickEvent.HEADER_CLICK_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addHeaderClickListener(HeaderClickListener)}
+	 */
+    @Deprecated
+    public void addListener(HeaderClickListener listener) {
+        addHeaderClickListener(listener);
+    }
+
+    /** Removes a header click listener.
+	 *
+	 * @param listener
+	 *            The listener to remove.
+	 */
+    public void removeHeaderClickListener(HeaderClickListener listener) {
+        removeListener(TableConstants.HEADER_CLICK_EVENT_ID,
+                HeaderClickEvent.class, listener);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeHeaderClickListener(HeaderClickListener)}
+	 */
+    @Deprecated
+    public void removeListener(HeaderClickListener listener) {
+        removeHeaderClickListener(listener);
+    }
+
+    /**
+     * Adds a footer click listener which handles the click events when the user
+     * clicks on a column footer cell in the CustomTable.
+     * <p>
+     * The listener will receive events which contain information about which
+     * column was clicked and some details about the mouse event.
+     * 
+     * 
+     * @param listener
+     *            The handler which should handle the footer click events.
+     */
+    public void addFooterClickListener(FooterClickListener listener) {
+        addListener(TableConstants.FOOTER_CLICK_EVENT_ID,
+                FooterClickEvent.class, listener,
+                FooterClickEvent.FOOTER_CLICK_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addFooterClickListener(FooterClickListener)}
+	 */
+    @Deprecated
+    public void addListener(FooterClickListener listener) {
+        addFooterClickListener(listener);
+    }
+
+    /** Removes a footer click listener.
+	 *
+	 * @param listener
+	 *            The listener to remove.
+	 */
+    public void removeFooterClickListener(FooterClickListener listener) {
+        removeListener(TableConstants.FOOTER_CLICK_EVENT_ID,
+                FooterClickEvent.class, listener);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeFooterClickListener(FooterClickListener)}
+	 */
+    @Deprecated
+    public void removeListener(FooterClickListener listener) {
+        removeFooterClickListener(listener);
+    }
+
+    /** Gets the footer caption beneath the rows.
+	 *
+	 * @param propertyId
+	 *            The propertyId of the column *
+	 * @return The caption of the footer or NULL if not set
+	 */
+    public String getColumnFooter(Object propertyId) {
+        return columnFooters.get(propertyId);
+    }
+
+    /**
+     * Sets the column footer caption. The column footer caption is the text
+     * displayed beneath the column if footers have been set visible.
+     * 
+     * @param propertyId
+     *            The properyId of the column
+     * 
+     * @param footer
+     *            The caption of the footer
+     */
+    public void setColumnFooter(Object propertyId, String footer) {
+        if (footer == null) {
+            columnFooters.remove(propertyId);
+        } else {
+            columnFooters.put(propertyId, footer);
+        }
+
+        markAsDirty();
+    }
+
+    /**
+     * Sets the footer visible in the bottom of the table.
+     * <p>
+     * The footer can be used to add column related data like sums to the bottom
+     * of the CustomTable using setColumnFooter(Object propertyId, String
+     * footer).
+     * 
+     * 
+     * @param visible
+     *            Should the footer be visible
+     */
+    public void setFooterVisible(boolean visible) {
+        if (visible != columnFootersVisible) {
+            columnFootersVisible = visible;
+            markAsDirty();
+        }
+    }
+
+    /** Is the footer currently visible?.
+	 *
+	 * @return Returns true if visible else false
+	 */
+    public boolean isFooterVisible() {
+        return columnFootersVisible;
+    }
+
+    /**
+     * This event is fired when a column is resized. The event contains the
+     * columns property id which was fired, the previous width of the column and
+     * the width of the column after the resize.
+     */
+    public static class ColumnResizeEvent extends Component.Event {
+        
+        /** The Constant COLUMN_RESIZE_METHOD. */
+        public static final Method COLUMN_RESIZE_METHOD;
+
+        static {
+            try {
+                COLUMN_RESIZE_METHOD = ColumnResizeListener.class
+                        .getDeclaredMethod("columnResize",
+                                new Class[] { ColumnResizeEvent.class });
+            } catch (final java.lang.NoSuchMethodException e) {
+                // This should never happen
+                throw new java.lang.RuntimeException(e);
+            }
+        }
+
+        /** The previous width. */
+        private final int previousWidth;
+        
+        /** The current width. */
+        private final int currentWidth;
+        
+        /** The column property id. */
+        private final Object columnPropertyId;
+
+        /** Constructor.
+		 *
+		 * @param source
+		 *            The source of the event
+		 * @param propertyId
+		 *            The columns property id
+		 * @param previous
+		 *            The width in pixels of the column before the resize event
+		 * @param current
+		 *            The width in pixels of the column after the resize event
+		 */
+        public ColumnResizeEvent(Component source, Object propertyId,
+                int previous, int current) {
+            super(source);
+            previousWidth = previous;
+            currentWidth = current;
+            columnPropertyId = propertyId;
+        }
+
+        /**
+         * Get the column property id of the column that was resized.
+         * 
+         * @return The column property id
+         */
+        public Object getPropertyId() {
+            return columnPropertyId;
+        }
+
+        /** Get the width in pixels of the column before the resize event.
+		 *
+		 * @return Width in pixels
+		 */
+        public int getPreviousWidth() {
+            return previousWidth;
+        }
+
+        /** Get the width in pixels of the column after the resize event.
+		 *
+		 * @return Width in pixels
+		 */
+        public int getCurrentWidth() {
+            return currentWidth;
+        }
+    }
+
+    /** Interface for listening to column resize events.
+	 *
+	 * @see ColumnResizeEvent
+	 */
+    public interface ColumnResizeListener extends Serializable {
+
+        /** This method is triggered when the column has been resized.
+		 *
+		 * @param event
+		 *            The event which contains the column property id, the
+		 *            previous width of the column and the current width of the
+		 *            column
+		 */
+        public void columnResize(ColumnResizeEvent event);
+    }
+
+    /**
+     * Adds a column resize listener to the CustomTable. A column resize
+     * listener is called when a user resizes a columns width.
+     * 
+     * @param listener
+     *            The listener to attach to the CustomTable
+     */
+    public void addColumnResizeListener(ColumnResizeListener listener) {
+        addListener(TableConstants.COLUMN_RESIZE_EVENT_ID,
+                ColumnResizeEvent.class, listener,
+                ColumnResizeEvent.COLUMN_RESIZE_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addColumnResizeListener(ColumnResizeListener)}
+	 */
+    @Deprecated
+    public void addListener(ColumnResizeListener listener) {
+        addColumnResizeListener(listener);
+    }
+
+    /**
+     * Removes a column resize listener from the CustomTable.
+     * 
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeColumnResizeListener(ColumnResizeListener listener) {
+        removeListener(TableConstants.COLUMN_RESIZE_EVENT_ID,
+                ColumnResizeEvent.class, listener);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeColumnResizeListener(ColumnResizeListener)}
+	 */
+    @Deprecated
+    public void removeListener(ColumnResizeListener listener) {
+        removeColumnResizeListener(listener);
+    }
+
+    /**
+     * This event is fired when a columns are reordered by the end user user.
+     */
+    public static class ColumnReorderEvent extends Component.Event {
+        
+        /** The Constant METHOD. */
+        public static final Method METHOD;
+
+        static {
+            try {
+                METHOD = ColumnReorderListener.class.getDeclaredMethod(
+                        "columnReorder",
+                        new Class[] { ColumnReorderEvent.class });
+            } catch (final java.lang.NoSuchMethodException e) {
+                // This should never happen
+                throw new java.lang.RuntimeException(e);
+            }
+        }
+
+        /** Constructor.
+		 *
+		 * @param source
+		 *            The source of the event
+		 */
+        public ColumnReorderEvent(Component source) {
+            super(source);
+        }
+
+    }
+
+    /** Interface for listening to column reorder events.
+	 *
+	 * @see ColumnReorderEvent
+	 */
+    public interface ColumnReorderListener extends Serializable {
+
+        /** This method is triggered when the column has been reordered.
+		 *
+		 * @param event
+		 *            the event
+		 */
+        public void columnReorder(ColumnReorderEvent event);
+    }
+
+    /**
+     * Adds a column reorder listener to the CustomTable. A column reorder
+     * listener is called when a user reorders columns.
+     * 
+     * @param listener
+     *            The listener to attach to the CustomTable
+     */
+    public void addColumnReorderListener(ColumnReorderListener listener) {
+        addListener(TableConstants.COLUMN_REORDER_EVENT_ID,
+                ColumnReorderEvent.class, listener, ColumnReorderEvent.METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addColumnReorderListener(ColumnReorderListener)}
+	 */
+    @Deprecated
+    public void addListener(ColumnReorderListener listener) {
+        addColumnReorderListener(listener);
+    }
+
+    /**
+     * Removes a column reorder listener from the CustomTable.
+     * 
+     * @param listener
+     *            The listener to remove
+     */
+    public void removeColumnReorderListener(ColumnReorderListener listener) {
+        removeListener(TableConstants.COLUMN_REORDER_EVENT_ID,
+                ColumnReorderEvent.class, listener);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeColumnReorderListener(ColumnReorderListener)}
+	 */
+    @Deprecated
+    public void removeListener(ColumnReorderListener listener) {
+        removeColumnReorderListener(listener);
+    }
+
+    /** Set the item description generator which generates tooltips for cells
+	 * and rows in the CustomTable.
+	 *
+	 * @param generator
+	 *            The generator to use or null to disable
+	 */
+    public void setItemDescriptionGenerator(ItemDescriptionGenerator generator) {
+        if (generator != itemDescriptionGenerator) {
+            itemDescriptionGenerator = generator;
+            // Assures the visual refresh. No need to reset the page buffer
+            // before as the content has not changed, only the descriptions
+            refreshRenderedCells();
+        }
+    }
+
+    /** Get the item description generator which generates tooltips for cells
+	 * and rows in the CustomTable.
+	 *
+	 * @return the customTable cell specific tooltip generator
+	 */
+    public ItemDescriptionGenerator getItemDescriptionGenerator() {
+        return itemDescriptionGenerator;
+    }
+
+    /**
+     * Row generators can be used to replace certain items in a table with a
+     * generated string. The generator is called each time the table is
+     * rendered, which means that new strings can be generated each time.
+     * 
+     * Row generators can be used for e.g. summary rows or grouping of items.
+     */
+    public interface RowGenerator extends Serializable {
+        /**
+         * Called for every row that is painted in the CustomTable. Returning a
+         * GeneratedRow object will cause the row to be painted based on the
+         * contents of the GeneratedRow. A generated row is by default styled
+         * similarly to a header or footer row.
+         * <p>
+         * The GeneratedRow data object contains the text that should be
+         * rendered in the row. The itemId in the container thus works only as a
+         * placeholder.
+         * <p>
+         * If GeneratedRow.setSpanColumns(true) is used, there will be one
+         * String spanning all columns (use setText("Spanning text")). Otherwise
+         * you can define one String per visible column.
+         * <p>
+         * If GeneratedRow.setRenderAsHtml(true) is used, the strings can
+         * contain HTML markup, otherwise all strings will be rendered as text
+         * (the default).
+         * <p>
+         * A "v-table-generated-row" CSS class is added to all generated rows.
+         * For custom styling of a generated row you can combine a RowGenerator
+         * with a CellStyleGenerator.
+         * <p>
+         * 
+         * @param table
+         *            The CustomTable that is being painted
+         * @param itemId
+         *            The itemId for the row
+         * @return A GeneratedRow describing how the row should be painted or
+         *         null to paint the row with the contents from the container
+         */
+        public GeneratedRow generateRow(CustomTable table, Object itemId);
+    }
+
+    /** The Class GeneratedRow.
+	 */
+    public static class GeneratedRow implements Serializable {
+        
+        /** The html content allowed. */
+        private boolean htmlContentAllowed = false;
+        
+        /** The span columns. */
+        private boolean spanColumns = false;
+        
+        /** The text. */
+        private String[] text = null;
+
+        /** Creates a new generated row. If only one string is passed in,
+		 * columns are automatically spanned.
+		 *
+		 * @param text
+		 *            the text
+		 */
+        public GeneratedRow(String... text) {
+            setHtmlContentAllowed(false);
+            setSpanColumns(text == null || text.length == 1);
+            setText(text);
+        }
+
+        /** Pass one String if spanColumns is used, one String for each
+		 * visible column otherwise.
+		 *
+		 * @param text
+		 *            the new text
+		 */
+        public void setText(String... text) {
+            if (text == null || (text.length == 1 && text[0] == null)) {
+                text = new String[] { "" };
+            }
+            this.text = text;
+        }
+
+        /** Gets the text.
+		 *
+		 * @return the text
+		 */
+        protected String[] getText() {
+            return text;
+        }
+
+        /** Gets the value.
+		 *
+		 * @return the value
+		 */
+        protected Object getValue() {
+            return getText();
+        }
+
+        /** Checks if is html content allowed.
+		 *
+		 * @return true, if is html content allowed
+		 */
+        protected boolean isHtmlContentAllowed() {
+            return htmlContentAllowed;
+        }
+
+        /** If set to true, all strings passed to {@link #setText(String...)}
+		 * will be rendered as HTML.
+		 *
+		 * @param htmlContentAllowed
+		 *            the new html content allowed
+		 */
+        public void setHtmlContentAllowed(boolean htmlContentAllowed) {
+            this.htmlContentAllowed = htmlContentAllowed;
+        }
+
+        /** Checks if is span columns.
+		 *
+		 * @return true, if is span columns
+		 */
+        protected boolean isSpanColumns() {
+            return spanColumns;
+        }
+
+        /** If set to true, only one string will be rendered, spanning the
+		 * entire row.
+		 *
+		 * @param spanColumns
+		 *            the new span columns
+		 */
+        public void setSpanColumns(boolean spanColumns) {
+            this.spanColumns = spanColumns;
+        }
+    }
+
+    /**
+     * Assigns a row generator to the table. The row generator will be able to
+     * replace rows in the table when it is rendered.
+     * 
+     * @param generator
+     *            the new row generator
+     */
+    public void setRowGenerator(RowGenerator generator) {
+        rowGenerator = generator;
+        refreshRowCache();
+    }
+
+    /** Gets the row generator.
+	 *
+	 * @return the current row generator
+	 */
+    public RowGenerator getRowGenerator() {
+        return rowGenerator;
+    }
+
+    /**
+     * Sets a converter for a property id.
+     * <p>
+     * The converter is used to format the the data for the given property id
+     * before displaying it in the table.
+     * 
+     * 
+     * @param propertyId
+     *            The propertyId to format using the converter
+     * @param converter
+     *            The converter to use for the property id
+     */
+    public void setConverter(Object propertyId, Converter<String, ?> converter) {
+        if (!getContainerPropertyIds().contains(propertyId)) {
+            throw new IllegalArgumentException("PropertyId " + propertyId
+                    + " must be in the container");
+        }
+
+        if (!typeIsCompatible(converter.getModelType(), getType(propertyId))) {
+            throw new IllegalArgumentException("Property type ("
+                    + getType(propertyId)
+                    + ") must match converter source type ("
+                    + converter.getModelType() + ")");
+        }
+        propertyValueConverters.put(propertyId,
+                (Converter<String, Object>) converter);
+        refreshRowCache();
+    }
+
+    /**
+     * Checks if there is a converter set explicitly for the given property id.
+     * 
+     * @param propertyId
+     *            The propertyId to check
+     * @return true if a converter has been set for the property id, false
+     *         otherwise
+     */
+    protected boolean hasConverter(Object propertyId) {
+        return propertyValueConverters.containsKey(propertyId);
+    }
+
+    /**
+     * Returns the converter used to format the given propertyId.
+     * 
+     * @param propertyId
+     *            The propertyId to check
+     * @return The converter used to format the propertyId or null if no
+     *         converter has been set
+     */
+    public Converter<String, Object> getConverter(Object propertyId) {
+        return propertyValueConverters.get(propertyId);
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.ui.AbstractComponent#setVisible(boolean)
+     */
+    @Override
+    public void setVisible(boolean visible) {
+        if (visible) {
+            // We need to ensure that the rows are sent to the client when the
+            // CustomTable is made visible if it has been rendered as invisible.
+            setRowCacheInvalidated(true);
+        }
+        super.setVisible(visible);
+    }
+
+    /* (non-Javadoc)
+     * @see com.vaadin.ui.HasComponents#iterator()
+     */
+    @Override
+    public Iterator<Component> iterator() {
+        if (visibleComponents == null) {
+            Collection<Component> empty = Collections.emptyList();
+            return empty.iterator();
+        }
+        return visibleComponents.iterator();
+    }
+
+    /** Gets the component iterator.
+	 *
+	 * @return the component iterator
+	 * @deprecated As of 7.0, use {@link #iterator()} instead.
+	 */
+    @Deprecated
+    public Iterator<Component> getComponentIterator() {
+        return iterator();
+    }
+
+    /** Gets the logger.
+	 *
+	 * @return the logger
+	 */
+    private final Logger getLogger() {
+        if (logger == null) {
+            logger = Logger.getLogger(CustomTable.class.getName());
+        }
+        return logger;
+    }
+}
diff --git a/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTreeTable.java b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTreeTable.java
new file mode 100644
index 0000000..3fcf910
--- /dev/null
+++ b/org.eclipse.osbp.fork.vaadin.addon.filteringtable/src/com/vaadin/ui/CustomTreeTable.java
@@ -0,0 +1,894 @@
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.data.Collapsible;
+import com.vaadin.data.Container;
+import com.vaadin.data.Container.Hierarchical;
+import com.vaadin.data.util.ContainerHierarchicalWrapper;
+import com.vaadin.data.util.HierarchicalContainer;
+import com.vaadin.data.util.HierarchicalContainerOrderedWrapper;
+import com.vaadin.server.PaintException;
+import com.vaadin.server.PaintTarget;
+import com.vaadin.server.Resource;
+import com.vaadin.shared.ui.treetable.TreeTableConstants;
+import com.vaadin.ui.Tree.CollapseEvent;
+import com.vaadin.ui.Tree.CollapseListener;
+import com.vaadin.ui.Tree.ExpandEvent;
+import com.vaadin.ui.Tree.ExpandListener;
+
+/**
+ * TreeTable extends the {@link Table} component so that it can also visualize a
+ * hierarchy of its Items in a similar manner that {@link Tree} does. The tree
+ * hierarchy is always displayed in the first actual column of the TreeTable.
+ * <p>
+ * The TreeTable supports the usual {@link Table} features like lazy loading, so
+ * it should be no problem to display lots of items at once. Only required rows
+ * and some cache rows are sent to the client.
+ * <p>
+ * TreeTable supports standard {@link Hierarchical} container interfaces, but
+ * also a more fine tuned version - {@link Collapsible}. A container
+ * implementing the {@link Collapsible} interface stores the collapsed/expanded
+ * state internally and can this way scale better on the server side than with
+ * standard Hierarchical implementations. Developer must however note that
+ * {@link Collapsible} containers can not be shared among several users as they
+ * share UI state in the container.
+ */
+@SuppressWarnings({ "serial", "deprecation" })
+public class CustomTreeTable extends CustomTable implements Hierarchical {
+
+    private interface ContainerStrategy extends Serializable {
+        public int size();
+
+        public boolean isNodeOpen(Object itemId);
+
+        public int getDepth(Object itemId);
+
+        public void toggleChildVisibility(Object itemId);
+
+        public Object getIdByIndex(int index);
+
+        public int indexOfId(Object id);
+
+        public Object nextItemId(Object itemId);
+
+        public Object lastItemId();
+
+        public Object prevItemId(Object itemId);
+
+        public boolean isLastId(Object itemId);
+
+        public Collection<?> getItemIds();
+
+        public void containerItemSetChange(ItemSetChangeEvent event);
+    }
+
+    private abstract class AbstractStrategy implements ContainerStrategy {
+
+        /**
+         * Consider adding getDepth to {@link Collapsible}, might help
+         * scalability with some container implementations.
+         */
+
+        @Override
+        public int getDepth(Object itemId) {
+            int depth = 0;
+            Hierarchical hierarchicalContainer = getContainerDataSource();
+            while (!hierarchicalContainer.isRoot(itemId)) {
+                depth++;
+                itemId = hierarchicalContainer.getParent(itemId);
+            }
+            return depth;
+        }
+
+        @Override
+        public void containerItemSetChange(ItemSetChangeEvent event) {
+        }
+
+    }
+
+    /**
+     * This strategy is used if current container implements {@link Collapsible}
+     * .
+     * 
+     * open-collapsed logic diverted to container, otherwise use default
+     * implementations.
+     */
+    private class CollapsibleStrategy extends AbstractStrategy {
+
+        private Collapsible c() {
+            return (Collapsible) getContainerDataSource();
+        }
+
+        @Override
+        public void toggleChildVisibility(Object itemId) {
+            c().setCollapsed(itemId, !c().isCollapsed(itemId));
+        }
+
+        @Override
+        public boolean isNodeOpen(Object itemId) {
+            return !c().isCollapsed(itemId);
+        }
+
+        @Override
+        public int size() {
+            return CustomTreeTable.super.size();
+        }
+
+        @Override
+        public Object getIdByIndex(int index) {
+            return CustomTreeTable.super.getIdByIndex(index);
+        }
+
+        @Override
+        public int indexOfId(Object id) {
+            return CustomTreeTable.super.indexOfId(id);
+        }
+
+        @Override
+        public boolean isLastId(Object itemId) {
+            // using the default impl
+            return CustomTreeTable.super.isLastId(itemId);
+        }
+
+        @Override
+        public Object lastItemId() {
+            // using the default impl
+            return CustomTreeTable.super.lastItemId();
+        }
+
+        @Override
+        public Object nextItemId(Object itemId) {
+            return CustomTreeTable.super.nextItemId(itemId);
+        }
+
+        @Override
+        public Object prevItemId(Object itemId) {
+            return CustomTreeTable.super.prevItemId(itemId);
+        }
+
+        @Override
+        public Collection<?> getItemIds() {
+            return CustomTreeTable.super.getItemIds();
+        }
+
+    }
+
+    /**
+     * Strategy for Hierarchical but not Collapsible container like
+     * {@link HierarchicalContainer}.
+     * 
+     * Store collapsed/open states internally, fool Table to use preorder when
+     * accessing items from container via Ordered/Indexed methods.
+     */
+    private class HierarchicalStrategy extends AbstractStrategy {
+
+        private final HashSet<Object> openItems = new HashSet<Object>();
+
+        @Override
+        public boolean isNodeOpen(Object itemId) {
+            return openItems.contains(itemId);
+        }
+
+        @Override
+        public int size() {
+            return getPreOrder().size();
+        }
+
+        @Override
+        public Collection<Object> getItemIds() {
+            return Collections.unmodifiableCollection(getPreOrder());
+        }
+
+        @Override
+        public boolean isLastId(Object itemId) {
+            if (itemId == null) {
+                return false;
+            }
+
+            return itemId.equals(lastItemId());
+        }
+
+        @Override
+        public Object lastItemId() {
+            if (getPreOrder().size() > 0) {
+                return getPreOrder().get(getPreOrder().size() - 1);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public Object nextItemId(Object itemId) {
+            int indexOf = getPreOrder().indexOf(itemId);
+            if (indexOf == -1) {
+                return null;
+            }
+            indexOf++;
+            if (indexOf == getPreOrder().size()) {
+                return null;
+            } else {
+                return getPreOrder().get(indexOf);
+            }
+        }
+
+        @Override
+        public Object prevItemId(Object itemId) {
+            int indexOf = getPreOrder().indexOf(itemId);
+            indexOf--;
+            if (indexOf < 0) {
+                return null;
+            } else {
+                return getPreOrder().get(indexOf);
+            }
+        }
+
+        @Override
+        public void toggleChildVisibility(Object itemId) {
+            boolean removed = openItems.remove(itemId);
+            if (!removed) {
+                openItems.add(itemId);
+                getLogger().log(Level.FINEST, "Item {0} is now expanded",
+                        itemId);
+            } else {
+                getLogger().log(Level.FINEST, "Item {0} is now collapsed",
+                        itemId);
+            }
+            clearPreorderCache();
+        }
+
+        private void clearPreorderCache() {
+            preOrder = null; // clear preorder cache
+        }
+
+        List<Object> preOrder;
+
+        /**
+         * Preorder of ids currently visible
+         * 
+         * @return
+         */
+        private List<Object> getPreOrder() {
+            if (preOrder == null) {
+                preOrder = new ArrayList<Object>();
+                Collection<?> rootItemIds = getContainerDataSource()
+                        .rootItemIds();
+                for (Object id : rootItemIds) {
+                    preOrder.add(id);
+                    addVisibleChildTree(id);
+                }
+            }
+            return preOrder;
+        }
+
+        private void addVisibleChildTree(Object id) {
+            if (isNodeOpen(id)) {
+                Collection<?> children = getContainerDataSource().getChildren(
+                        id);
+                if (children != null) {
+                    for (Object childId : children) {
+                        preOrder.add(childId);
+                        addVisibleChildTree(childId);
+                    }
+                }
+            }
+
+        }
+
+        @Override
+        public int indexOfId(Object id) {
+            return getPreOrder().indexOf(id);
+        }
+
+        @Override
+        public Object getIdByIndex(int index) {
+            return getPreOrder().get(index);
+        }
+
+        @Override
+        public void containerItemSetChange(ItemSetChangeEvent event) {
+            // preorder becomes invalid on sort, item additions etc.
+            clearPreorderCache();
+            super.containerItemSetChange(event);
+        }
+
+    }
+
+    /**
+     * Creates an empty TreeTable with a default container.
+     */
+    public CustomTreeTable() {
+        super(null, new HierarchicalContainer());
+    }
+
+    /**
+     * Creates an empty TreeTable with a default container.
+     * 
+     * @param caption
+     *            the caption for the TreeTable
+     */
+    public CustomTreeTable(String caption) {
+        this();
+        setCaption(caption);
+    }
+
+    /**
+     * Creates a TreeTable instance with given captions and data source.
+     * 
+     * @param caption
+     *            the caption for the component
+     * @param dataSource
+     *            the dataSource that is used to list items in the component
+     */
+    public CustomTreeTable(String caption, Container dataSource) {
+        super(caption, dataSource);
+    }
+
+    private ContainerStrategy cStrategy;
+    private Object focusedRowId = null;
+    private Object hierarchyColumnId;
+
+    /**
+     * The item id that was expanded or collapsed during this request. Reset at
+     * the end of paint and only used for determining if a partial or full paint
+     * should be done.
+     * 
+     * Can safely be reset to null whenever a change occurs that would prevent a
+     * partial update from rendering the correct result, e.g. rows added or
+     * removed during an expand operation.
+     */
+    private Object toggledItemId;
+    private boolean animationsEnabled;
+    private boolean clearFocusedRowPending;
+
+    /**
+     * If the container does not send item set change events, always do a full
+     * repaint instead of a partial update when expanding/collapsing nodes.
+     */
+    private boolean containerSupportsPartialUpdates;
+
+    private ContainerStrategy getContainerStrategy() {
+        if (cStrategy == null) {
+            if (getContainerDataSource() instanceof Collapsible) {
+                cStrategy = new CollapsibleStrategy();
+            } else {
+                cStrategy = new HierarchicalStrategy();
+            }
+        }
+        return cStrategy;
+    }
+
+    @Override
+    protected void paintRowAttributes(PaintTarget target, Object itemId)
+            throws PaintException {
+        super.paintRowAttributes(target, itemId);
+        target.addAttribute("depth", getContainerStrategy().getDepth(itemId));
+        if (getContainerDataSource().areChildrenAllowed(itemId)) {
+            target.addAttribute("ca", true);
+            target.addAttribute("open",
+                    getContainerStrategy().isNodeOpen(itemId));
+        }
+    }
+
+    @Override
+    protected void paintRowIcon(PaintTarget target, Object[][] cells,
+            int indexInRowbuffer) throws PaintException {
+        // always paint if present (in parent only if row headers visible)
+        if (getRowHeaderMode() == ROW_HEADER_MODE_HIDDEN) {
+            Resource itemIcon = getItemIcon(cells[CELL_ITEMID][indexInRowbuffer]);
+            if (itemIcon != null) {
+                target.addAttribute("icon", itemIcon);
+            }
+        } else if (cells[CELL_ICON][indexInRowbuffer] != null) {
+            target.addAttribute("icon",
+                    (Resource) cells[CELL_ICON][indexInRowbuffer]);
+        }
+    }
+
+    @Override
+    public void changeVariables(Object source, Map<String, Object> variables) {
+        super.changeVariables(source, variables);
+
+        if (variables.containsKey("toggleCollapsed")) {
+            String object = (String) variables.get("toggleCollapsed");
+            Object itemId = itemIdMapper.get(object);
+            toggledItemId = itemId;
+            toggleChildVisibility(itemId, false);
+            if (variables.containsKey("selectCollapsed")) {
+                // ensure collapsed is selected unless opened with selection
+                // head
+                if (isSelectable()) {
+                    select(itemId);
+                }
+            }
+        } else if (variables.containsKey("focusParent")) {
+            String key = (String) variables.get("focusParent");
+            Object refId = itemIdMapper.get(key);
+            Object itemId = getParent(refId);
+            focusParent(itemId);
+        }
+    }
+
+    private void focusParent(Object itemId) {
+        boolean inView = false;
+        Object inPageId = getCurrentPageFirstItemId();
+        for (int i = 0; inPageId != null && i < getPageLength(); i++) {
+            if (inPageId.equals(itemId)) {
+                inView = true;
+                break;
+            }
+            inPageId = nextItemId(inPageId);
+            i++;
+        }
+        if (!inView) {
+            setCurrentPageFirstItemId(itemId);
+        }
+        // Select the row if it is selectable.
+        if (isSelectable()) {
+            if (isMultiSelect()) {
+                setValue(Collections.singleton(itemId));
+            } else {
+                setValue(itemId);
+            }
+        }
+        setFocusedRow(itemId);
+    }
+
+    private void setFocusedRow(Object itemId) {
+        focusedRowId = itemId;
+        if (focusedRowId == null) {
+            // Must still inform the client that the focusParent request has
+            // been processed
+            clearFocusedRowPending = true;
+        }
+        markAsDirty();
+    }
+
+    @Override
+    public void paintContent(PaintTarget target) throws PaintException {
+        if (focusedRowId != null) {
+            target.addAttribute("focusedRow", itemIdMapper.key(focusedRowId));
+            focusedRowId = null;
+        } else if (clearFocusedRowPending) {
+            // Must still inform the client that the focusParent request has
+            // been processed
+            target.addAttribute("clearFocusPending", true);
+            clearFocusedRowPending = false;
+        }
+        target.addAttribute("animate", animationsEnabled);
+        if (hierarchyColumnId != null) {
+            Object[] visibleColumns2 = getVisibleColumns();
+            for (int i = 0; i < visibleColumns2.length; i++) {
+                Object object = visibleColumns2[i];
+                if (hierarchyColumnId.equals(object)) {
+                    target.addAttribute(
+                            TreeTableConstants.ATTRIBUTE_HIERARCHY_COLUMN_INDEX,
+                            i);
+                    break;
+                }
+            }
+        }
+        super.paintContent(target);
+        toggledItemId = null;
+    }
+
+    /*
+     * Override methods for partial row updates and additions when expanding /
+     * collapsing nodes.
+     */
+
+    @Override
+    protected boolean isPartialRowUpdate() {
+        return toggledItemId != null && containerSupportsPartialUpdates
+                && !isRowCacheInvalidated();
+    }
+
+    @Override
+    protected int getFirstAddedItemIndex() {
+        return indexOfId(toggledItemId) + 1;
+    }
+
+    @Override
+    protected int getAddedRowCount() {
+        return countSubNodesRecursively(getContainerDataSource(), toggledItemId);
+    }
+
+    private int countSubNodesRecursively(Hierarchical hc, Object itemId) {
+        int count = 0;
+        // we need the number of children for toggledItemId no matter if its
+        // collapsed or expanded. Other items' children are only counted if the
+        // item is expanded.
+        if (getContainerStrategy().isNodeOpen(itemId)
+                || itemId == toggledItemId) {
+            Collection<?> children = hc.getChildren(itemId);
+            if (children != null) {
+                count += children != null ? children.size() : 0;
+                for (Object id : children) {
+                    count += countSubNodesRecursively(hc, id);
+                }
+            }
+        }
+        return count;
+    }
+
+    @Override
+    protected int getFirstUpdatedItemIndex() {
+        return indexOfId(toggledItemId);
+    }
+
+    @Override
+    protected int getUpdatedRowCount() {
+        return 1;
+    }
+
+    @Override
+    protected boolean shouldHideAddedRows() {
+        return !getContainerStrategy().isNodeOpen(toggledItemId);
+    }
+
+    private void toggleChildVisibility(Object itemId, boolean forceFullRefresh) {
+        getContainerStrategy().toggleChildVisibility(itemId);
+        // ensure that page still has first item in page, DON'T clear the
+        // caches.
+        setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex(), false);
+
+        if (isCollapsed(itemId)) {
+            fireCollapseEvent(itemId);
+        } else {
+            fireExpandEvent(itemId);
+        }
+
+        if (containerSupportsPartialUpdates && !forceFullRefresh) {
+            markAsDirty();
+        } else {
+            // For containers that do not send item set change events, always do
+            // full repaint instead of partial row update.
+            refreshRowCache();
+        }
+    }
+
+    @Override
+    public int size() {
+        return getContainerStrategy().size();
+    }
+
+    @Override
+    public Hierarchical getContainerDataSource() {
+        return (Hierarchical) super.getContainerDataSource();
+    }
+
+    @Override
+    public void setContainerDataSource(Container newDataSource) {
+        cStrategy = null;
+
+        // FIXME: This disables partial updates until TreeTable is fixed so it
+        // does not change component hierarchy during paint
+        containerSupportsPartialUpdates = (newDataSource instanceof ItemSetChangeNotifier) && false;
+
+        if (newDataSource != null && !(newDataSource instanceof Hierarchical)) {
+            newDataSource = new ContainerHierarchicalWrapper(newDataSource);
+        }
+
+        if (newDataSource != null && !(newDataSource instanceof Ordered)) {
+            newDataSource = new HierarchicalContainerOrderedWrapper(
+                    (Hierarchical) newDataSource);
+        }
+
+        super.setContainerDataSource(newDataSource);
+    }
+
+    @Override
+    public void containerItemSetChange(
+            com.vaadin.data.Container.ItemSetChangeEvent event) {
+        // Can't do partial repaints if items are added or removed during the
+        // expand/collapse request
+        toggledItemId = null;
+        getContainerStrategy().containerItemSetChange(event);
+        super.containerItemSetChange(event);
+    }
+
+    @Override
+    protected Object getIdByIndex(int index) {
+        return getContainerStrategy().getIdByIndex(index);
+    }
+
+    @Override
+    protected int indexOfId(Object itemId) {
+        return getContainerStrategy().indexOfId(itemId);
+    }
+
+    @Override
+    public Object nextItemId(Object itemId) {
+        return getContainerStrategy().nextItemId(itemId);
+    }
+
+    @Override
+    public Object lastItemId() {
+        return getContainerStrategy().lastItemId();
+    }
+
+    @Override
+    public Object prevItemId(Object itemId) {
+        return getContainerStrategy().prevItemId(itemId);
+    }
+
+    @Override
+    public boolean isLastId(Object itemId) {
+        return getContainerStrategy().isLastId(itemId);
+    }
+
+    @Override
+    public Collection<?> getItemIds() {
+        return getContainerStrategy().getItemIds();
+    }
+
+    @Override
+    public boolean areChildrenAllowed(Object itemId) {
+        return getContainerDataSource().areChildrenAllowed(itemId);
+    }
+
+    @Override
+    public Collection<?> getChildren(Object itemId) {
+        return getContainerDataSource().getChildren(itemId);
+    }
+
+    @Override
+    public Object getParent(Object itemId) {
+        return getContainerDataSource().getParent(itemId);
+    }
+
+    @Override
+    public boolean hasChildren(Object itemId) {
+        return getContainerDataSource().hasChildren(itemId);
+    }
+
+    @Override
+    public boolean isRoot(Object itemId) {
+        return getContainerDataSource().isRoot(itemId);
+    }
+
+    @Override
+    public Collection<?> rootItemIds() {
+        return getContainerDataSource().rootItemIds();
+    }
+
+    @Override
+    public boolean setChildrenAllowed(Object itemId, boolean areChildrenAllowed)
+            throws UnsupportedOperationException {
+        return getContainerDataSource().setChildrenAllowed(itemId,
+                areChildrenAllowed);
+    }
+
+    @Override
+    public boolean setParent(Object itemId, Object newParentId)
+            throws UnsupportedOperationException {
+        return getContainerDataSource().setParent(itemId, newParentId);
+    }
+
+    /**
+     * Sets the Item specified by given identifier as collapsed or expanded. If
+     * the Item is collapsed, its children are not displayed to the user.
+     * 
+     * @param itemId
+     *            the identifier of the Item
+     * @param collapsed
+     *            true if the Item should be collapsed, false if expanded
+     */
+    public void setCollapsed(Object itemId, boolean collapsed) {
+        if (isCollapsed(itemId) != collapsed) {
+            if (null == toggledItemId && !isRowCacheInvalidated()
+                    && getVisibleItemIds().contains(itemId)) {
+                // optimization: partial refresh if only one item is
+                // collapsed/expanded
+                toggledItemId = itemId;
+                toggleChildVisibility(itemId, false);
+            } else {
+                // make sure a full refresh takes place - otherwise neither
+                // partial nor full repaint of table content is performed
+                toggledItemId = null;
+                toggleChildVisibility(itemId, true);
+            }
+        }
+    }
+
+    /**
+     * Checks if Item with given identifier is collapsed in the UI.
+     * 
+     * <p>
+     * 
+     * @param itemId
+     *            the identifier of the checked Item
+     * @return true if the Item with given id is collapsed
+     * @see Collapsible#isCollapsed(Object)
+     */
+    public boolean isCollapsed(Object itemId) {
+        return !getContainerStrategy().isNodeOpen(itemId);
+    }
+
+    /** Explicitly sets the column in which the TreeTable visualizes the
+	 * hierarchy. If hierarchyColumnId is not set, the hierarchy is visualized
+	 * in the first visible column.
+	 *
+	 * @param hierarchyColumnId
+	 *            the new hierarchy column
+	 */
+    public void setHierarchyColumn(Object hierarchyColumnId) {
+        this.hierarchyColumnId = hierarchyColumnId;
+    }
+
+    /**
+     * @return the identifier of column into which the hierarchy will be
+     *         visualized or null if the column is not explicitly defined.
+     */
+    public Object getHierarchyColumnId() {
+        return hierarchyColumnId;
+    }
+
+    /**
+     * Adds an expand listener.
+     * 
+     * @param listener
+     *            the Listener to be added.
+     */
+    public void addExpandListener(ExpandListener listener) {
+        addListener(ExpandEvent.class, listener, ExpandListener.EXPAND_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addExpandListener(ExpandListener)}
+	 */
+    @Deprecated
+    public void addListener(ExpandListener listener) {
+        addExpandListener(listener);
+    }
+
+    /**
+     * Removes an expand listener.
+     * 
+     * @param listener
+     *            the Listener to be removed.
+     */
+    public void removeExpandListener(ExpandListener listener) {
+        removeListener(ExpandEvent.class, listener,
+                ExpandListener.EXPAND_METHOD);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeExpandListener(ExpandListener)}
+	 */
+    @Deprecated
+    public void removeListener(ExpandListener listener) {
+        removeExpandListener(listener);
+    }
+
+    /**
+     * Emits an expand event.
+     * 
+     * @param itemId
+     *            the item id.
+     */
+    protected void fireExpandEvent(Object itemId) {
+        fireEvent(new ExpandEvent(this, itemId));
+    }
+
+    /**
+     * Adds a collapse listener.
+     * 
+     * @param listener
+     *            the Listener to be added.
+     */
+    public void addCollapseListener(CollapseListener listener) {
+        addListener(CollapseEvent.class, listener,
+                CollapseListener.COLLAPSE_METHOD);
+    }
+
+    /** Adds the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #addCollapseListener(CollapseListener)}
+	 */
+    @Deprecated
+    public void addListener(CollapseListener listener) {
+        addCollapseListener(listener);
+    }
+
+    /**
+     * Removes a collapse listener.
+     * 
+     * @param listener
+     *            the Listener to be removed.
+     */
+    public void removeCollapseListener(CollapseListener listener) {
+        removeListener(CollapseEvent.class, listener,
+                CollapseListener.COLLAPSE_METHOD);
+    }
+
+    /** Removes the listener.
+	 *
+	 * @param listener
+	 *            the listener
+	 * @deprecated As of 7.0, replaced by
+	 *             {@link #removeCollapseListener(CollapseListener)}
+	 */
+    @Deprecated
+    public void removeListener(CollapseListener listener) {
+        removeCollapseListener(listener);
+    }
+
+    /**
+     * Emits a collapse event.
+     * 
+     * @param itemId
+     *            the item id.
+     */
+    protected void fireCollapseEvent(Object itemId) {
+        fireEvent(new CollapseEvent(this, itemId));
+    }
+
+    /**
+     * @return true if animations are enabled
+     */
+    public boolean isAnimationsEnabled() {
+        return animationsEnabled;
+    }
+
+    /**
+     * Animations can be enabled by passing true to this method. Currently
+     * expanding rows slide in from the top and collapsing rows slide out the
+     * same way. NOTE! not supported in Internet Explorer 6 or 7.
+     * 
+     * @param animationsEnabled
+     *            true or false whether to enable animations or not.
+     */
+    public void setAnimationsEnabled(boolean animationsEnabled) {
+        this.animationsEnabled = animationsEnabled;
+        markAsDirty();
+    }
+
+    private static final Logger getLogger() {
+        return Logger.getLogger(TreeTable.class.getName());
+    }
+
+    @Override
+    protected List<Object> getItemIds(int firstIndex, int rows) {
+        List<Object> itemIds = new ArrayList<Object>();
+        for (int i = firstIndex; i < firstIndex + rows; i++) {
+            itemIds.add(getIdByIndex(i));
+        }
+        return itemIds;
+    }
+}