Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / panel / Table.js
index efd59cf..20793fb 100644 (file)
@@ -1,10 +1,22 @@
+/*
+
+This file is part of Ext JS 4
+
+Copyright (c) 2011 Sencha Inc
+
+Contact:  http://www.sencha.com/contact
+
+GNU General Public License Usage
+This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+
+If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
+
+*/
 /**
  * @class Ext.panel.Table
  * @extends Ext.panel.Panel
- * @xtype tablepanel
- * @private
  * @author Nicolas Ferrero
- * TablePanel is a private class and the basis of both TreePanel and GridPanel.
+ * TablePanel is the basis of both TreePanel and GridPanel.
  *
  * TablePanel aggregates:
  *
@@ -72,9 +84,15 @@ Ext.define('Ext.panel.Table', {
      * Specify as <code>true</code> to hide the headers.
      */
 
+    /**
+     * @cfg {Boolean} deferRowRender <P>Defaults to <code>true</code> to enable deferred row rendering.</p>
+     * <p>This allows the GridView to execute a refresh quickly, with the expensive update of the row
+     * structure deferred so that layouts with GridPanels appear, and lay out more quickly.</p>
+     */
+
     /**
      * @cfg {Boolean} sortableColumns
-     * Defaults to true. Set to false to disable column sorting via clicking the
+     * Defaults to <code>true</code>. Set to <code>false</code> to disable column sorting via clicking the
      * header and via the Sorting menu items.
      */
     sortableColumns: true,
@@ -90,10 +108,24 @@ Ext.define('Ext.panel.Table', {
     scrollerOwner: true,
 
     invalidateScrollerOnRefresh: true,
-    
+
+    /**
+     * @cfg {Boolean} enableColumnMove
+     * Defaults to <code>true</code>. Set to <code>false</code> to disable column dragging within this grid.
+     */
     enableColumnMove: true,
+
+    /**
+     * @cfg {Boolean} enableColumnResize
+     * Defaults to <code>true</code>. Set to <code>false</code> to disable column resizing within this grid.
+     */
     enableColumnResize: true,
 
+    /**
+     * @cfg {Boolean} enableColumnHide
+     * Defaults to <code>true</code>. Set to <code>false</code> to disable column hiding within this grid.
+     */
+    enableColumnHide: true,
 
     initComponent: function() {
         //<debug>
@@ -117,10 +149,14 @@ Ext.define('Ext.panel.Table', {
             view,
             border = me.border;
 
-        // Set our determinScrollbars method to reference a buffered call to determinScrollbars which fires on a 30ms buffer.
-        me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
-        me.invalidateScroller = Ext.Function.createBuffered(me.invalidateScroller, 30);
-        me.injectView = Ext.Function.createBuffered(me.injectView, 30);
+        // We cannot buffer this because that will wait for the 30msec from afterLayout (or what
+        // ever event triggers it) and we may be in the middle of an animation; that is a bad
+        // time to injectView because it causes a layout (by calling add on the container). A
+        // throttled func will be called immediately on first call and then block subsequent
+        // (rapid fire) calls for 30msec before allowing another call to go through. Similar
+        // results, but the action moves from the trailing edge of the interval to the leading
+        // edge.
+        me.injectView = Ext.Function.createThrottled(me.injectView, 30, me);
 
         if (me.hideHeaders) {
             border = false;
@@ -144,6 +180,7 @@ Ext.define('Ext.panel.Table', {
                 sortable: me.sortableColumns,
                 enableColumnMove: me.enableColumnMove,
                 enableColumnResize: me.enableColumnResize,
+                enableColumnHide: me.enableColumnHide,
                 border:  border
             });
             me.columns = headerCtCfg.items;
@@ -158,6 +195,12 @@ Ext.define('Ext.panel.Table', {
 
         me.store = Ext.data.StoreManager.lookup(me.store);
         me.addEvents(
+            /**
+             * @event reconfigure
+             * Fires after a reconfigure
+             * @param {Ext.panel.Table} this
+             */
+            'reconfigure',
             /**
              * @event scrollerhide
              * Fires when a scroller is hidden
@@ -220,13 +263,7 @@ Ext.define('Ext.panel.Table', {
             }
 
             if (vertical) {
-                me.verticalScroller = me.verticalScroller || {};
-                Ext.applyIf(me.verticalScroller, {
-                    dock: me.verticalScrollDock,
-                    xtype: me.verticalScrollerType,
-                    store: me.store
-                });
-                me.verticalScroller = Ext.ComponentManager.create(me.verticalScroller);
+                me.verticalScroller = Ext.ComponentManager.create(me.initVerticalScroller());
                 me.mon(me.verticalScroller, {
                     bodyscroll: me.onVerticalScroll,
                     scope: me
@@ -234,12 +271,7 @@ Ext.define('Ext.panel.Table', {
             }
 
             if (horizontal) {
-                me.horizontalScroller = Ext.ComponentManager.create({
-                    xtype: 'gridscroller',
-                    section: me,
-                    dock: 'bottom',
-                    store: me.store
-                });
+                me.horizontalScroller = Ext.ComponentManager.create(me.initHorizontalScroller());
                 me.mon(me.horizontalScroller, {
                     bodyscroll: me.onHorizontalScroll,
                     scope: me
@@ -264,12 +296,7 @@ Ext.define('Ext.panel.Table', {
                     scope: me
                 });
                 me.mon(view, {
-                    refresh: {
-                        fn: this.onViewRefresh,
-                        scope: me,
-                        buffer: 50
-                    },
-                    itemupdate: me.onViewItemUpdate,
+                    refresh: me.onViewRefresh,
                     scope: me
                 });
                 this.relayEvents(view, [
@@ -538,21 +565,72 @@ Ext.define('Ext.panel.Table', {
         this.callParent();
     },
 
-    getState: function(){
-        var state = {
-            columns: []
-        },
-        sorter = this.store.sorters.first();
-
-        this.headerCt.items.each(function(header){
-            state.columns.push({
-                id: header.headerId,
-                width: header.flex ? undefined : header.width,
-                hidden: header.hidden,
-                sortable: header.sortable
-            });
+    /**
+     * Returns the horizontal scroller config.
+     */
+    initHorizontalScroller: function () {
+        var me = this,
+            ret = {
+                xtype: 'gridscroller',
+                dock: 'bottom',
+                section: me,
+                store: me.store
+            };
+
+        return ret;
+    },
+
+    /**
+     * Returns the vertical scroller config.
+     */
+    initVerticalScroller: function () {
+        var me = this,
+            ret = me.verticalScroller || {};
+
+        Ext.applyIf(ret, {
+            xtype: me.verticalScrollerType,
+            dock: me.verticalScrollDock,
+            store: me.store
         });
 
+        return ret;
+    },
+
+    getState: function(){
+        var state = this.callParent(),
+            sorter = this.store.sorters.first(),
+            headers = this.headerCt.items.items,
+            header,
+            len = headers.length,
+            i = 0;
+
+        state.columns = [];
+        for (; i < len; i++) {
+            header = headers[i];
+            state.columns[i] = {
+                id: header.headerId
+            };
+
+            // We only store state which has changed from the initial state.
+            // So that current software settings do not override future software settings.
+            // Only user-changed state should be saved.
+            if (header.hidden !== (header.initialConfig.hidden||header.self.prototype.hidden)) {
+                state.columns[i].hidden = header.hidden;
+            }
+            if (header.sortable !== header.initialConfig.sortable) {
+                state.columns[i].sortable = header.sortable;
+            }
+            if (header.flex) {
+                if (header.flex !== header.initialConfig.flex) {
+                    state.columns[i].flex = header.flex;
+                }
+            } else {
+                if (header.width !== header.initialConfig.width) {
+                    state.columns[i].width = header.width;
+                }
+            }
+        }
+
         if (sorter) {
             state.sort = {
                 property: sorter.property,
@@ -574,6 +652,12 @@ Ext.define('Ext.panel.Table', {
             headerState,
             header;
 
+        headerCt.suspendLayout = true;
+
+        // Ensure superclass has applied *its* state.
+        // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
+        this.callParent(arguments);
+
         for (; i < length; ++i) {
             headerState = headers[i];
             header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
@@ -581,17 +665,32 @@ Ext.define('Ext.panel.Table', {
             if (i !== index) {
                 headerCt.moveHeader(index, i);
             }
-            header.sortable = headerState.sortable;
-            if (Ext.isDefined(headerState.width)) {
+
+            // Only state properties which were saved should be restored.
+            // (Only user-changed properties were saved by getState)
+            if (Ext.isDefined(headerState.hidden)) {
+                header.hidden = headerState.hidden;
+            }
+            if (Ext.isDefined(headerState.sortable)) {
+                header.sortable = headerState.sortable;
+            }
+            if (Ext.isDefined(headerState.flex)) {
+                delete header.width;
+                header.flex = headerState.flex;
+            } else if (Ext.isDefined(headerState.width)) {
                 delete header.flex;
+                header.minWidth = headerState.width;
                 if (header.rendered) {
                     header.setWidth(headerState.width);
                 } else {
-                    header.minWidth = header.width = headerState.width;
+                    header.width = headerState.width;
                 }
             }
-            header.hidden = headerState.hidden;
         }
+        headerCt.suspendLayout = false;
+
+        // After setting width and flexes while layout is suspended, column Container's Container layout needs running.
+        headerCt.doLayout();
 
         if (sorter) {
             if (store.remoteSort) {
@@ -625,6 +724,7 @@ Ext.define('Ext.panel.Table', {
         if (!me.view) {
             sm = me.getSelectionModel();
             me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
+                deferRowRender: me.deferRowRender,
                 xtype: me.viewType,
                 store: me.store,
                 headerCt: me.headerCt,
@@ -669,17 +769,11 @@ Ext.define('Ext.panel.Table', {
             scroller.scrollByDeltaX(distance);
         }
     },
-    
-    afterLayout: function() {
-        this.callParent(arguments);
-        this.injectView();
-    },
-    
 
     /**
      * @private
      * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
-     * This is so we can add the View only after the initial size is known. This method is buffered 30ms.
+     * This is so we can add the View only after the initial size is known. This method is throttled 30ms.
      */
     injectView: function() {
         if (!this.hasView && !this.collapsed) {
@@ -689,18 +783,33 @@ Ext.define('Ext.panel.Table', {
             me.hasView = true;
             me.add(view);
 
-            // hijack the view el's scroll method
-            view.el.scroll = Ext.Function.bind(me.elScroll, me);
-            // We use to listen to document.body wheel events, but that's a
-            // little much. We scope just to the view now.
-            me.mon(view.el, {
-                mousewheel: me.onMouseWheel,
-                scope: me
-            });
+            function viewReady () {
+                // hijack the view el's scroll method
+                view.el.scroll = Ext.Function.bind(me.elScroll, me);
+                // We use to listen to document.body wheel events, but that's a
+                // little much. We scope just to the view now.
+                me.mon(view.el, {
+                    mousewheel: me.onMouseWheel,
+                    scope: me
+                });
+                if (!me.height) {
+                    me.doComponentLayout();
+                }
+            }
+
+            if (view.rendered) {
+                viewReady();
+            } else {
+                view.on({
+                    afterrender: viewReady,
+                    single: true
+                });
+            }
         }
     },
 
     afterExpand: function() {
+        // TODO - this is *not* called when part of an accordion!
         this.callParent(arguments);
         if (!this.hasView) {
             this.injectView();
@@ -732,50 +841,105 @@ Ext.define('Ext.panel.Table', {
      */
     determineScrollbars: function() {
         var me = this,
-            viewElDom,
-            centerScrollWidth,
-            centerClientWidth,
+            box,
+            tableEl,
+            scrollWidth,
+            clientWidth,
             scrollHeight,
-            clientHeight;
-
-        if (!me.collapsed && me.view && me.view.el) {
-            viewElDom = me.view.el.dom;
-            //centerScrollWidth = viewElDom.scrollWidth;
-            centerScrollWidth = me.headerCt.getFullWidth();
-            /**
-             * clientWidth often returns 0 in IE resulting in an
-             * infinity result, here we use offsetWidth bc there are
-             * no possible scrollbars and we don't care about margins
-             */
-            centerClientWidth = viewElDom.offsetWidth;
-            if (me.verticalScroller && me.verticalScroller.el) {
-                scrollHeight = me.verticalScroller.getSizeCalculation().height;
+            clientHeight,
+            verticalScroller = me.verticalScroller,
+            horizontalScroller = me.horizontalScroller,
+            curScrollbars = (verticalScroller   && verticalScroller.ownerCt === me ? 1 : 0) |
+                            (horizontalScroller && horizontalScroller.ownerCt === me ? 2 : 0),
+            reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both
+
+        // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars
+        if (!me.collapsed && me.view && me.view.el && me.view.el.dom.firstChild) {
+
+            // Calculate maximum, *scrollbarless* space which the view has available.
+            // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars
+            box = me.layout.getLayoutTargetSize();
+            clientWidth  = box.width  + ((curScrollbars & 1) ? verticalScroller.width : 0);
+            clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0);
+
+            // Calculate the width of the scrolling block
+            // There will never be a horizontal scrollbar if all columns are flexed.
+
+            scrollWidth = (me.headerCt.query('[flex]').length && !me.headerCt.layout.tooNarrow) ? 0 : me.headerCt.getFullWidth();
+
+            // Calculate the height of the scrolling block
+            if (verticalScroller && verticalScroller.el) {
+                scrollHeight = verticalScroller.getSizeCalculation().height;
             } else {
-                scrollHeight = viewElDom.scrollHeight;
+                tableEl = me.view.el.child('table', true);
+                scrollHeight = tableEl ? tableEl.offsetHeight : 0;
             }
 
-            clientHeight = viewElDom.clientHeight;
+            // View is too high.
+            // Definitely need a vertical scrollbar
+            if (scrollHeight > clientHeight) {
+                reqScrollbars = 1;
 
-            me.suspendLayout = true;
-            me.scrollbarChanged = false;
-            if (!me.collapsed && scrollHeight > clientHeight) {
-                me.showVerticalScroller();
-            } else {
-                me.hideVerticalScroller();
+                // But if scrollable block width goes into the zone required by the vertical scrollbar, we'll also need a horizontal
+                if (horizontalScroller && ((clientWidth - scrollWidth) < verticalScroller.width)) {
+                    reqScrollbars = 3;
+                }
             }
 
-            if (!me.collapsed && centerScrollWidth > (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
-                me.showHorizontalScroller();
-            } else {
-                me.hideHorizontalScroller();
+            // View height fits. But we stil may need a horizontal scrollbar, and this might necessitate a vertical one.
+            else {
+                // View is too wide.
+                // Definitely need a horizontal scrollbar
+                if (scrollWidth > clientWidth) {
+                    reqScrollbars = 2;
+
+                    // But if scrollable block height goes into the zone required by the horizontal scrollbar, we'll also need a vertical
+                    if (verticalScroller && ((clientHeight - scrollHeight) < horizontalScroller.height)) {
+                        reqScrollbars = 3;
+                    }
+                }
             }
-            me.suspendLayout = false;
-            if (me.scrollbarChanged) {
-                me.doComponentLayout();
+
+            // If scrollbar requirements have changed, change 'em...
+            if (reqScrollbars !== curScrollbars) {
+
+                // Suspend component layout while we add/remove the docked scrollers
+                me.suspendLayout = true;
+                if (reqScrollbars & 1) {
+                    me.showVerticalScroller();
+                } else {
+                    me.hideVerticalScroller();
+                }
+                if (reqScrollbars & 2) {
+                    me.showHorizontalScroller();
+                } else {
+                    me.hideHorizontalScroller();
+                }
+                me.suspendLayout = false;
+
+                // After docked scrollers are correctly configured, lay out the Component.
+                // Set a flag so that afterComponentLayout does not recurse back into here.
+                me.changingScrollBars = true;
+                me.doComponentLayout(me.getWidth(), me.getHeight(), false, me.ownerCt);
+                me.changingScrollBars = false;
             }
         }
     },
 
+    afterComponentLayout: function() {
+        var me = this;
+        me.callParent(arguments);
+
+        // Insert the View the first time the Panel has a Component layout performed.
+        me.injectView();
+
+        // Avoid recursion
+        if (!me.changingScrollBars) {
+            me.determineScrollbars();
+        }
+        me.invalidateScroller();
+    },
+
     onHeaderResize: function() {
         if (this.view && this.view.rendered) {
             this.determineScrollbars();
@@ -783,6 +947,28 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
+    afterCollapse: function() {
+        var me = this;
+        if (me.verticalScroller) {
+            me.verticalScroller.saveScrollPos();
+        }
+        if (me.horizontalScroller) {
+            me.horizontalScroller.saveScrollPos();
+        }
+        me.callParent(arguments);
+    },
+
+    afterExpand: function() {
+        var me = this;
+        me.callParent(arguments);
+        if (me.verticalScroller) {
+            me.verticalScroller.restoreScrollPos();
+        }
+        if (me.horizontalScroller) {
+            me.horizontalScroller.restoreScrollPos();
+        }
+    },
+
     /**
      * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
      */
@@ -790,8 +976,7 @@ Ext.define('Ext.panel.Table', {
         var me = this;
 
         if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
-            me.scrollbarChanged = true;
-            me.verticalScroller.offsets.bottom = 0;
+            me.verticalScroller.setReservedSpace(0);
             me.removeDocked(me.horizontalScroller, false);
             me.removeCls(me.horizontalScrollerPresentCls);
             me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
@@ -806,10 +991,9 @@ Ext.define('Ext.panel.Table', {
         var me = this;
 
         if (me.verticalScroller) {
-            me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
+            me.verticalScroller.setReservedSpace(Ext.getScrollbarSize().height - 1);
         }
         if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
-            me.scrollbarChanged = true;
             me.addDocked(me.horizontalScroller);
             me.addCls(me.horizontalScrollerPresentCls);
             me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
@@ -820,16 +1004,10 @@ Ext.define('Ext.panel.Table', {
      * Hide the verticalScroller and remove the verticalScrollerPresentCls.
      */
     hideVerticalScroller: function() {
-        var me = this,
-            headerCt = me.headerCt;
+        var me = this;
 
-        // only trigger a layout when reserveOffset is changing
-        if (headerCt && headerCt.layout.reserveOffset) {
-            headerCt.layout.reserveOffset = false;
-            headerCt.doLayout();
-        }
+        me.setHeaderReserveOffset(false);
         if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
-            me.scrollbarChanged = true;
             me.removeDocked(me.verticalScroller, false);
             me.removeCls(me.verticalScrollerPresentCls);
             me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
@@ -840,22 +1018,27 @@ Ext.define('Ext.panel.Table', {
      * Show the verticalScroller and add the verticalScrollerPresentCls.
      */
     showVerticalScroller: function() {
-        var me = this,
-            headerCt = me.headerCt;
+        var me = this;
 
-        // only trigger a layout when reserveOffset is changing
-        if (headerCt && !headerCt.layout.reserveOffset) {
-            headerCt.layout.reserveOffset = true;
-            headerCt.doLayout();
-        }
+        me.setHeaderReserveOffset(true);
         if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
-            me.scrollbarChanged = true;
             me.addDocked(me.verticalScroller);
             me.addCls(me.verticalScrollerPresentCls);
             me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
         }
     },
 
+    setHeaderReserveOffset: function (reserveOffset) {
+        var headerCt = this.headerCt,
+            layout = headerCt.layout;
+
+        // only trigger a layout when reserveOffset is changing
+        if (layout && layout.reserveOffset !== reserveOffset) {
+            layout.reserveOffset = reserveOffset;
+            headerCt.doLayout();
+        }
+    },
+
     /**
      * Invalides scrollers that are present and forces a recalculation.
      * (Not related to showing/hiding the scrollers)
@@ -897,11 +1080,12 @@ Ext.define('Ext.panel.Table', {
 
     onMouseWheel: function(e) {
         var me = this,
-            browserEvent = e.browserEvent,
             vertScroller = me.getVerticalScroller(),
             horizScroller = me.getHorizontalScroller(),
-            scrollDelta = me.scrollDelta,
-            deltaY, deltaX,
+            scrollDelta = me.scrollDelta / -5,
+            deltas = e.getWheelDeltas(),
+            deltaX = scrollDelta * deltas.x,
+            deltaY = scrollDelta * deltas.y,
             vertScrollerEl, horizScrollerEl,
             vertScrollerElDom, horizScrollerElDom,
             horizontalCanScrollLeft, horizontalCanScrollRight,
@@ -909,7 +1093,7 @@ Ext.define('Ext.panel.Table', {
 
         // calculate whether or not both scrollbars can scroll right/left and up/down
         if (horizScroller) {
-            horizScrollerEl = horizScroller.el;
+            horizScrollerEl = horizScroller.scrollEl;
             if (horizScrollerEl) {
                 horizScrollerElDom = horizScrollerEl.dom;
                 horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
@@ -917,7 +1101,7 @@ Ext.define('Ext.panel.Table', {
             }
         }
         if (vertScroller) {
-            vertScrollerEl = vertScroller.el;
+            vertScrollerEl = vertScroller.scrollEl;
             if (vertScrollerEl) {
                 vertScrollerElDom = vertScrollerEl.dom;
                 verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
@@ -925,19 +1109,6 @@ Ext.define('Ext.panel.Table', {
             }
         }
 
-        // Webkit Horizontal Axis
-        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {        
-            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
-            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
-        } else {
-            // Gecko Horizontal Axis
-            if (browserEvent.axis && browserEvent.axis === 1) {
-                deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
-            } else {
-                deltaY = -(scrollDelta * e.getWheelDelta() / 3);
-            }
-        }
-        
         if (horizScroller) {
             if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
                 e.stopEvent();
@@ -947,7 +1118,7 @@ Ext.define('Ext.panel.Table', {
         if (vertScroller) {
             if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
                 e.stopEvent();
-                vertScroller.scrollByDeltaY(deltaY);    
+                vertScroller.scrollByDeltaY(deltaY);
             }
         }
     },
@@ -957,69 +1128,12 @@ Ext.define('Ext.panel.Table', {
      * Determine and invalidate scrollers on view refresh
      */
     onViewRefresh: function() {
-        if (Ext.isIE) {
-            this.syncCellHeight();
-        }
         this.determineScrollbars();
         if (this.invalidateScrollerOnRefresh) {
             this.invalidateScroller();
         }
     },
 
-    onViewItemUpdate: function(record, index, tr) {
-        if (Ext.isIE) {
-            this.syncCellHeight([tr]);
-        }
-    },
-
-    // BrowserBug: IE will not stretch the td to fit the height of the entire
-    // tr, so manually sync cellheights on refresh and when an item has been
-    // updated.
-    syncCellHeight: function(trs) {
-        var me    = this,
-            i     = 0,
-            tds,
-            j, tdsLn,
-            tr, td,
-            trsLn,
-            rowHeights = [],
-            cellHeights,
-            cellClsSelector = ('.' + Ext.baseCSSPrefix + 'grid-cell');
-
-        trs   = trs || me.view.getNodes();
-        
-        trsLn = trs.length;
-        // Reading loop
-        for (; i < trsLn; i++) {
-            tr = trs[i];
-            tds = Ext.fly(tr).query(cellClsSelector);
-            tdsLn = tds.length;
-            cellHeights = [];
-            for (j = 0; j < tdsLn; j++) {
-                td = tds[j];
-                cellHeights.push(td.clientHeight);
-            }
-            rowHeights.push(Ext.Array.max(cellHeights));
-        }
-
-        // Setting loop
-        for (i = 0; i < trsLn; i++) {
-            tr = trs[i];
-            tdsLn = tr.childNodes.length;
-            for (j = 0; j < tdsLn; j++) {
-                td = Ext.fly(tr.childNodes[j]);
-                if (rowHeights[i]) {
-                    if (td.is(cellClsSelector)) {
-                        td.setHeight(rowHeights[i]);
-                    } else {
-                        td.down(cellClsSelector).setHeight(rowHeights[i]);
-                    }
-                }
-                
-            }
-        }
-    },
-
     /**
      * Sets the scrollTop of the TablePanel.
      * @param {Number} deltaY
@@ -1033,7 +1147,6 @@ Ext.define('Ext.panel.Table', {
         if (verticalScroller) {
             verticalScroller.setScrollTop(top);
         }
-
     },
 
     getScrollerOwner: function() {
@@ -1049,21 +1162,23 @@ Ext.define('Ext.panel.Table', {
      * @param {Number} deltaY
      */
     scrollByDeltaY: function(deltaY) {
-        var rootCmp = this.getScrollerOwner(),
-            scrollerRight;
-        scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
-        if (scrollerRight) {
-            scrollerRight.scrollByDeltaY(deltaY);
+        var verticalScroller = this.getVerticalScroller();
+
+        if (verticalScroller) {
+            verticalScroller.scrollByDeltaY(deltaY);
         }
     },
 
-
     /**
      * Scrolls the TablePanel by deltaX
      * @param {Number} deltaY
      */
     scrollByDeltaX: function(deltaX) {
-        this.horizontalScroller.scrollByDeltaX(deltaX);
+        var horizontalScroller = this.getVerticalScroller();
+
+        if (horizontalScroller) {
+            horizontalScroller.scrollByDeltaX(deltaX);
+        }
     },
 
     /**
@@ -1125,7 +1240,9 @@ Ext.define('Ext.panel.Table', {
         }
 
         if (!this.selModel.hasRelaySetup) {
-            this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
+            this.relayEvents(this.selModel, [
+                'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
+            ]);
             this.selModel.hasRelaySetup = true;
         }
 
@@ -1151,21 +1268,9 @@ Ext.define('Ext.panel.Table', {
     onHorizontalScroll: function(event, target) {
         var owner = this.getScrollerOwner(),
             items = owner.query('tableview'),
-            i = 0,
-            len = items.length,
-            center,
-            centerEl,
-            centerScrollWidth,
-            centerClientWidth,
-            width;
-
-        center = items[1] || items[0];
-        centerEl = center.el.dom;
-        centerScrollWidth = centerEl.scrollWidth;
-        centerClientWidth = centerEl.offsetWidth;
-        width = this.horizontalScroller.getWidth();
-
-        centerEl.scrollLeft = target.scrollLeft;
+            center = items[1] || items[0];
+
+        center.el.dom.scrollLeft = target.scrollLeft;
         this.headerCt.el.dom.scrollLeft = target.scrollLeft;
     },
 
@@ -1182,30 +1287,36 @@ Ext.define('Ext.panel.Table', {
         me.getView().bindStore(store);
     },
 
+    /**
+     * Reconfigure the table with a new store/column.
+     * Either the store or the column can be ommitted if you don't wish to change them.
+     * @param {Ext.data.Store} store The new store.
+     * @param {Array} columns An array of column configs
+     */
     reconfigure: function(store, columns) {
-        var me = this;
+        var me = this,
+            headerCt = me.headerCt;
 
         if (me.lockable) {
             me.reconfigureLockable(store, columns);
-            return;
-        }
-
-        if (columns) {
-            me.headerCt.removeAll();
-            me.headerCt.add(columns);
-        }
-        if (store) {
-            store = Ext.StoreManager.lookup(store);
-            me.bindStore(store);
         } else {
-            me.getView().refresh();
+            headerCt.suspendLayout = true;
+            headerCt.removeAll();
+            if (columns) {
+                headerCt.add(columns);
+            } else {
+                headerCt.doLayout();
+            }
+            if (store) {
+                store = Ext.StoreManager.lookup(store);
+                me.bindStore(store);
+            } else {
+                me.getView().refresh();
+            }
+            if (columns) {
+                me.forceComponentLayout();
+            }
         }
-    },
-    
-    afterComponentLayout: function() {
-        var me = this;
-        me.callParent(arguments);
-        me.determineScrollbars();
-        me.invalidateScroller();
+        me.fireEvent('reconfigure', me);
     }
-});
\ No newline at end of file
+});