X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/7a654f8d43fdb43d78b63d90528bed6e86b608cc..HEAD:/docs/source/Table2.html?ds=sidebyside diff --git a/docs/source/Table2.html b/docs/source/Table2.html index 58eb857e..ff0b2e25 100644 --- a/docs/source/Table2.html +++ b/docs/source/Table2.html @@ -1,10 +1,24 @@ -Sencha Documentation Project
/**
- * @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.
+
+
+
+  
+  The source code
+  
+  
+  
+  
+
+
+  
/**
+ * @author Nicolas Ferrero
+ *
+ * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}.
  *
  * TablePanel aggregates:
  *
@@ -13,7 +27,6 @@
  *  - a Store
  *  - Scrollers
  *  - Ext.grid.header.Container
- *
  */
 Ext.define('Ext.panel.Table', {
     extend: 'Ext.panel.Panel',
@@ -27,58 +40,124 @@ Ext.define('Ext.panel.Table', {
         'Ext.grid.Lockable'
     ],
 
-    cls: Ext.baseCSSPrefix + 'grid',
+    extraBaseCls: Ext.baseCSSPrefix + 'grid',
     extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
 
     layout: 'fit',
-    /**
-     * Boolean to indicate that a view has been injected into the panel.
-     * @property hasView
+    /**
+     * @property {Boolean} hasView
+     * True to indicate that a view has been injected into the panel.
      */
     hasView: false,
 
     // each panel should dictate what viewType and selType to use
+    /**
+     * @cfg {String} viewType
+     * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid}
+     * and to 'treeview' by {@link Ext.tree.Panel Tree}.
+     */
     viewType: null,
+
+    /**
+     * @cfg {Object} viewConfig
+     * A config object that will be applied to the grid's UI view. Any of the config options available for
+     * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.
+     */
+
+    /**
+     * @cfg {Ext.view.Table} view
+     * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to
+     * view (instead of creating an entire View instance).
+     */
+
+    /**
+     * @cfg {String} selType
+     * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just
+     * a config object or nothing at all given in {@link #selModel} config.
+     */
     selType: 'rowmodel',
 
-    /**
+    /**
+     * @cfg {Ext.selection.Model/Object} selModel
+     * A {@link Ext.selection.Model selection model} instance or config object.  In latter case the {@link #selType}
+     * config option determines to which type of selection model this config is applied.
+     */
+
+    /**
+     * @cfg {Boolean} multiSelect
+     * True to enable 'MULTI' selection mode on selection model. See {@link Ext.selection.Model#mode}.
+     */
+
+    /**
+     * @cfg {Boolean} simpleSelect
+     * True to enable 'SIMPLE' selection mode on selection model. See {@link Ext.selection.Model#mode}.
+     */
+
+    /**
+     * @cfg {Ext.data.Store} store (required)
+     * The {@link Ext.data.Store Store} the grid should use as its data source.
+     */
+
+    /**
      * @cfg {Number} scrollDelta
      * Number of pixels to scroll when scrolling with mousewheel.
-     * Defaults to 40.
      */
     scrollDelta: 40,
 
-    /**
+    /**
      * @cfg {String/Boolean} scroll
-     * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
-     * Defaults to true.
+     * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.
+     * True implies 'both'. False implies 'none'.
      */
     scroll: true,
 
-    /**
-     * @cfg {Array} columns
-     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
-     * column definition provides the header text for the column, and a definition of where the data for that column comes from.
+    /**
+     * @cfg {Ext.grid.column.Column[]} columns
+     * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this
+     * grid. Each column definition provides the header text for the column, and a definition of where the data for that
+     * column comes from.
      */
 
-    /**
+    /**
      * @cfg {Boolean} forceFit
-     * Specify as <code>true</code> to force the columns to fit into the available width. Headers are first sized according to configuration, whether that be
-     * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
+     * Ttrue to force the columns to fit into the available width. Headers are first sized according to configuration,
+     * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire
+     * content width is used.
+     */
+
+    /**
+     * @cfg {Ext.grid.feature.Feature[]} features
+     * An array of grid Features to be added to this grid. See {@link Ext.grid.feature.Feature} for usage.
      */
 
-    /**
-     * @cfg {Boolean} hideHeaders
-     * Specify as <code>true</code> to hide the headers.
+    /**
+     * @cfg {Boolean} [hideHeaders=false]
+     * True to hide column headers.
      */
 
-    /**
+    /**
+     * @cfg {Boolean} deferRowRender
+     * Defaults to true to enable deferred row rendering.
+     *
+     * This allows the View 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.
+     */
+
+     deferRowRender: true,
+     
+    /**
      * @cfg {Boolean} sortableColumns
-     * Defaults to true. Set to false to disable column sorting via clicking the
-     * header and via the Sorting menu items.
+     * False to disable column sorting via clicking the header and via the Sorting menu items.
      */
     sortableColumns: true,
 
+    /**
+     * @cfg {Boolean} [enableLocking=false]
+     * True to enable locking support for this grid. Alternatively, locking will also be automatically
+     * enabled if any of the columns in the column configuration contain the locked config option.
+     */
+    enableLocking: false,
+
     verticalScrollDock: 'right',
     verticalScrollerType: 'gridscroller',
 
@@ -90,19 +169,30 @@ Ext.define('Ext.panel.Table', {
     scrollerOwner: true,
 
     invalidateScrollerOnRefresh: true,
-    
+
+    /**
+     * @cfg {Boolean} enableColumnMove
+     * False to disable column dragging within this grid.
+     */
     enableColumnMove: true,
+
+    /**
+     * @cfg {Boolean} enableColumnResize
+     * False to disable column resizing within this grid.
+     */
     enableColumnResize: true,
 
+    /**
+     * @cfg {Boolean} enableColumnHide
+     * False to disable column hiding within this grid.
+     */
+    enableColumnHide: true,
 
     initComponent: function() {
         //<debug>
         if (!this.viewType) {
             Ext.Error.raise("You must specify a viewType config.");
         }
-        if (!this.store) {
-            Ext.Error.raise("You must specify a store config");
-        }
         if (this.headers) {
             Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
         }
@@ -117,14 +207,13 @@ 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.injectView = Ext.Function.createBuffered(me.injectView, 30);
-
         if (me.hideHeaders) {
             border = false;
         }
 
+        // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.
+        me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
+
         // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
         // Either way, we extract a columns property referencing an array of Column definitions.
         if (headerCtCfg instanceof Ext.grid.header.Container) {
@@ -143,30 +232,42 @@ Ext.define('Ext.panel.Table', {
                 sortable: me.sortableColumns,
                 enableColumnMove: me.enableColumnMove,
                 enableColumnResize: me.enableColumnResize,
+                enableColumnHide: me.enableColumnHide,
                 border:  border
             });
             me.columns = headerCtCfg.items;
 
              // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
              // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
-             if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
+             if (me.enableLocking || Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
                  me.self.mixin('lockable', Ext.grid.Lockable);
                  me.injectLockable();
              }
         }
 
-        me.store = Ext.data.StoreManager.lookup(me.store);
         me.addEvents(
-            /**
+            /**
+             * @event reconfigure
+             * Fires after a reconfigure.
+             * @param {Ext.panel.Table} this
+             */
+            'reconfigure',
+            /**
+             * @event viewready
+             * Fires when the grid view is available (use this for selecting a default row).
+             * @param {Ext.panel.Table} this
+             */
+            'viewready',
+            /**
              * @event scrollerhide
-             * Fires when a scroller is hidden
+             * Fires when a scroller is hidden.
              * @param {Ext.grid.Scroller} scroller
              * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
              */
             'scrollerhide',
-            /**
+            /**
              * @event scrollershow
-             * Fires when a scroller is shown
+             * Fires when a scroller is shown.
              * @param {Ext.grid.Scroller} scroller
              * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
              */
@@ -175,6 +276,9 @@ Ext.define('Ext.panel.Table', {
 
         me.bodyCls = me.bodyCls || '';
         me.bodyCls += (' ' + me.extraBodyCls);
+        
+        me.cls = me.cls || '';
+        me.cls += (' ' + me.extraBaseCls);
 
         // autoScroll is not a valid configuration
         delete me.autoScroll;
@@ -219,13 +323,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
@@ -233,21 +331,19 @@ 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
                 });
             }
 
-            me.headerCt.on('columnresize', me.onHeaderResize, me);
-            me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
+            me.headerCt.on('resize', me.onHeaderResize, me);
+            me.relayHeaderCtEvents(me.headerCt);
             me.features = me.features || [];
+            if (!Ext.isArray(me.features)) {
+                me.features = [me.features];
+            }
             me.dockedItems = me.dockedItems || [];
             me.dockedItems.unshift(me.headerCt);
             me.viewConfig = me.viewConfig || {};
@@ -257,273 +353,215 @@ Ext.define('Ext.panel.Table', {
             // getView converts viewConfig into a View instance
             view = me.getView();
 
-            if (view) {
-                me.mon(view.store, {
-                    load: me.onStoreLoad,
-                    scope: me
-                });
-                me.mon(view, {
-                    refresh: {
-                        fn: this.onViewRefresh,
-                        scope: me,
-                        buffer: 50
-                    },
-                    itemupdate: me.onViewItemUpdate,
-                    scope: me
-                });
-                this.relayEvents(view, [
-                    /**
-                     * @event beforeitemmousedown
-                     * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmousedown',
-                    /**
-                     * @event beforeitemmouseup
-                     * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseup',
-                    /**
-                     * @event beforeitemmouseenter
-                     * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseenter',
-                    /**
-                     * @event beforeitemmouseleave
-                     * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemmouseleave',
-                    /**
-                     * @event beforeitemclick
-                     * Fires before the click event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemclick',
-                    /**
-                     * @event beforeitemdblclick
-                     * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemdblclick',
-                    /**
-                     * @event beforeitemcontextmenu
-                     * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforeitemcontextmenu',
-                    /**
-                     * @event itemmousedown
-                     * Fires when there is a mouse down on an item
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmousedown',
-                    /**
-                     * @event itemmouseup
-                     * Fires when there is a mouse up on an item
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseup',
-                    /**
-                     * @event itemmouseenter
-                     * Fires when the mouse enters an item.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseenter',
-                    /**
-                     * @event itemmouseleave
-                     * Fires when the mouse leaves an item.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemmouseleave',
-                    /**
-                     * @event itemclick
-                     * Fires when an item is clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemclick',
-                    /**
-                     * @event itemdblclick
-                     * Fires when an item is double clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemdblclick',
-                    /**
-                     * @event itemcontextmenu
-                     * Fires when an item is right clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.data.Model} record The record that belongs to the item
-                     * @param {HTMLElement} item The item's element
-                     * @param {Number} index The item's index
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'itemcontextmenu',
-                    /**
-                     * @event beforecontainermousedown
-                     * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermousedown',
-                    /**
-                     * @event beforecontainermouseup
-                     * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseup',
-                    /**
-                     * @event beforecontainermouseover
-                     * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseover',
-                    /**
-                     * @event beforecontainermouseout
-                     * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainermouseout',
-                    /**
-                     * @event beforecontainerclick
-                     * Fires before the click event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainerclick',
-                    /**
-                     * @event beforecontainerdblclick
-                     * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainerdblclick',
-                    /**
-                     * @event beforecontainercontextmenu
-                     * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'beforecontainercontextmenu',
-                    /**
-                     * @event containermouseup
-                     * Fires when there is a mouse up on the container
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseup',
-                    /**
-                     * @event containermouseover
-                     * Fires when you move the mouse over the container.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseover',
-                    /**
-                     * @event containermouseout
-                     * Fires when you move the mouse out of the container.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containermouseout',
-                    /**
-                     * @event containerclick
-                     * Fires when the container is clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containerclick',
-                    /**
-                     * @event containerdblclick
-                     * Fires when the container is double clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containerdblclick',
-                    /**
-                     * @event containercontextmenu
-                     * Fires when the container is right clicked.
-                     * @param {Ext.view.View} this
-                     * @param {Ext.EventObject} e The raw event object
-                     */
-                    'containercontextmenu',
-
-                    /**
-                     * @event selectionchange
-                     * Fires when the selected nodes change. Relayed event from the underlying selection model.
-                     * @param {Ext.view.View} this
-                     * @param {Array} selections Array of the selected nodes
-                     */
-                    'selectionchange',
-                    /**
-                     * @event beforeselect
-                     * Fires before a selection is made. If any handlers return false, the selection is cancelled.
-                     * @param {Ext.view.View} this
-                     * @param {HTMLElement} node The node to be selected
-                     * @param {Array} selections Array of currently selected nodes
-                     */
-                    'beforeselect'
-                ]);
-            }
+            view.on({
+                afterrender: function () {
+                    // 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
+                    });
+                },
+                single: true
+            });
+            me.items = [view];
+            me.hasView = true;
+
+            me.mon(view.store, {
+                load: me.onStoreLoad,
+                scope: me
+            });
+            me.mon(view, {
+                viewReady: me.onViewReady,
+                resize: me.onViewResize,
+                refresh: {
+                    fn: me.onViewRefresh,
+                    scope: me,
+                    buffer: 50
+                },
+                scope: me
+            });
+            this.relayEvents(view, [
+                /**
+                 * @event beforeitemmousedown
+                 * @alias Ext.view.View#beforeitemmousedown
+                 */
+                'beforeitemmousedown',
+                /**
+                 * @event beforeitemmouseup
+                 * @alias Ext.view.View#beforeitemmouseup
+                 */
+                'beforeitemmouseup',
+                /**
+                 * @event beforeitemmouseenter
+                 * @alias Ext.view.View#beforeitemmouseenter
+                 */
+                'beforeitemmouseenter',
+                /**
+                 * @event beforeitemmouseleave
+                 * @alias Ext.view.View#beforeitemmouseleave
+                 */
+                'beforeitemmouseleave',
+                /**
+                 * @event beforeitemclick
+                 * @alias Ext.view.View#beforeitemclick
+                 */
+                'beforeitemclick',
+                /**
+                 * @event beforeitemdblclick
+                 * @alias Ext.view.View#beforeitemdblclick
+                 */
+                'beforeitemdblclick',
+                /**
+                 * @event beforeitemcontextmenu
+                 * @alias Ext.view.View#beforeitemcontextmenu
+                 */
+                'beforeitemcontextmenu',
+                /**
+                 * @event itemmousedown
+                 * @alias Ext.view.View#itemmousedown
+                 */
+                'itemmousedown',
+                /**
+                 * @event itemmouseup
+                 * @alias Ext.view.View#itemmouseup
+                 */
+                'itemmouseup',
+                /**
+                 * @event itemmouseenter
+                 * @alias Ext.view.View#itemmouseenter
+                 */
+                'itemmouseenter',
+                /**
+                 * @event itemmouseleave
+                 * @alias Ext.view.View#itemmouseleave
+                 */
+                'itemmouseleave',
+                /**
+                 * @event itemclick
+                 * @alias Ext.view.View#itemclick
+                 */
+                'itemclick',
+                /**
+                 * @event itemdblclick
+                 * @alias Ext.view.View#itemdblclick
+                 */
+                'itemdblclick',
+                /**
+                 * @event itemcontextmenu
+                 * @alias Ext.view.View#itemcontextmenu
+                 */
+                'itemcontextmenu',
+                /**
+                 * @event beforecontainermousedown
+                 * @alias Ext.view.View#beforecontainermousedown
+                 */
+                'beforecontainermousedown',
+                /**
+                 * @event beforecontainermouseup
+                 * @alias Ext.view.View#beforecontainermouseup
+                 */
+                'beforecontainermouseup',
+                /**
+                 * @event beforecontainermouseover
+                 * @alias Ext.view.View#beforecontainermouseover
+                 */
+                'beforecontainermouseover',
+                /**
+                 * @event beforecontainermouseout
+                 * @alias Ext.view.View#beforecontainermouseout
+                 */
+                'beforecontainermouseout',
+                /**
+                 * @event beforecontainerclick
+                 * @alias Ext.view.View#beforecontainerclick
+                 */
+                'beforecontainerclick',
+                /**
+                 * @event beforecontainerdblclick
+                 * @alias Ext.view.View#beforecontainerdblclick
+                 */
+                'beforecontainerdblclick',
+                /**
+                 * @event beforecontainercontextmenu
+                 * @alias Ext.view.View#beforecontainercontextmenu
+                 */
+                'beforecontainercontextmenu',
+                /**
+                 * @event containermouseup
+                 * @alias Ext.view.View#containermouseup
+                 */
+                'containermouseup',
+                /**
+                 * @event containermouseover
+                 * @alias Ext.view.View#containermouseover
+                 */
+                'containermouseover',
+                /**
+                 * @event containermouseout
+                 * @alias Ext.view.View#containermouseout
+                 */
+                'containermouseout',
+                /**
+                 * @event containerclick
+                 * @alias Ext.view.View#containerclick
+                 */
+                'containerclick',
+                /**
+                 * @event containerdblclick
+                 * @alias Ext.view.View#containerdblclick
+                 */
+                'containerdblclick',
+                /**
+                 * @event containercontextmenu
+                 * @alias Ext.view.View#containercontextmenu
+                 */
+                'containercontextmenu',
+                /**
+                 * @event selectionchange
+                 * @alias Ext.selection.Model#selectionchange
+                 */
+                'selectionchange',
+                /**
+                 * @event beforeselect
+                 * @alias Ext.selection.RowModel#beforeselect
+                 */
+                'beforeselect',
+                /**
+                 * @event select
+                 * @alias Ext.selection.RowModel#select
+                 */
+                'select',
+                /**
+                 * @event beforedeselect
+                 * @alias Ext.selection.RowModel#beforedeselect
+                 */
+                'beforedeselect',
+                /**
+                 * @event deselect
+                 * @alias Ext.selection.RowModel#deselect
+                 */
+                'deselect'
+            ]);
         }
+
         me.callParent(arguments);
     },
+    
+    onRender: function(){
+        var vScroll = this.verticalScroller,
+            hScroll = this.horizontalScroller;
+
+        if (vScroll) {
+            vScroll.ensureDimension();
+        }
+        if (hScroll) {
+            hScroll.ensureDimension();
+        }
+        this.callParent(arguments);    
+    },
 
     // state management
     initStateEvents: function(){
@@ -537,59 +575,98 @@ 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;
+    },
+
+    relayHeaderCtEvents: function (headerCt) {
+        this.relayEvents(headerCt, [
+            /**
+             * @event columnresize
+             * @alias Ext.grid.header.Container#columnresize
+             */
+            'columnresize',
+            /**
+             * @event columnmove
+             * @alias Ext.grid.header.Container#columnmove
+             */
+            'columnmove',
+            /**
+             * @event columnhide
+             * @alias Ext.grid.header.Container#columnhide
+             */
+            'columnhide',
+            /**
+             * @event columnshow
+             * @alias Ext.grid.header.Container#columnshow
+             */
+            'columnshow',
+            /**
+             * @event sortchange
+             * @alias Ext.grid.header.Container#sortchange
+             */
+            'sortchange'
+        ]);
+    },
+
+    getState: function(){
+        var me = this,
+            state = me.callParent(),
+            sorter = me.store.sorters.first();
+
+        state.columns = (me.headerCt || me).getColumnsState();
+
         if (sorter) {
             state.sort = {
                 property: sorter.property,
                 direction: sorter.direction
             };
         }
+
         return state;
     },
 
     applyState: function(state) {
-        var headers = state.columns,
-            length = headers ? headers.length : 0,
-            headerCt = this.headerCt,
-            items = headerCt.items,
+        var me = this,
             sorter = state.sort,
-            store = this.store,
-            i = 0,
-            index,
-            headerState,
-            header;
+            store = me.store,
+            columns = state.columns;
 
-        for (; i < length; ++i) {
-            headerState = headers[i];
-            header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
-            index = items.indexOf(header);
-            if (i !== index) {
-                headerCt.moveHeader(index, i);
-            }
-            header.sortable = headerState.sortable;
-            if (Ext.isDefined(headerState.width)) {
-                delete header.flex;
-                if (header.rendered) {
-                    header.setWidth(headerState.width);
-                } else {
-                    header.minWidth = header.width = headerState.width;
-                }
-            }
-            header.hidden = headerState.hidden;
+        delete state.columns;
+
+        // Ensure superclass has applied *its* state.
+        // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
+        me.callParent(arguments);
+
+        if (columns) {
+            (me.headerCt || me).applyColumnsState(columns);
         }
 
         if (sorter) {
@@ -605,7 +682,7 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
-    /**
+    /**
      * Returns the store associated with this Panel.
      * @return {Ext.data.Store} The store
      */
@@ -613,7 +690,7 @@ Ext.define('Ext.panel.Table', {
         return this.store;
     },
 
-    /**
+    /**
      * Gets the view for this panel.
      * @return {Ext.view.Table}
      */
@@ -624,6 +701,7 @@ Ext.define('Ext.panel.Table', {
         if (!me.view) {
             sm = me.getSelectionModel();
             me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
+                deferInitialRefresh: me.deferRowRender,
                 xtype: me.viewType,
                 store: me.store,
                 headerCt: me.headerCt,
@@ -642,7 +720,7 @@ Ext.define('Ext.panel.Table', {
         return me.view;
     },
 
-    /**
+    /**
      * @private
      * @override
      * autoScroll is never valid for all classes which extend TablePanel.
@@ -659,62 +737,33 @@ Ext.define('Ext.panel.Table', {
         if (direction === "up" || direction === "left") {
             distance = -distance;
         }
-
+        
         if (direction === "down" || direction === "up") {
             scroller = me.getVerticalScroller();
-            scroller.scrollByDeltaY(distance);
+            
+            //if the grid does not currently need a vertical scroller don't try to update it (EXTJSIV-3891)
+            if (scroller) {
+                scroller.scrollByDeltaY(distance);
+            }
         } else {
             scroller = me.getHorizontalScroller();
-            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.
-     */
-    injectView: function() {
-        if (!this.hasView && !this.collapsed) {
-            var me   = this,
-                view = me.getView();
-
-            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
-            });
-        }
-    },
-
-    afterExpand: function() {
-        this.callParent(arguments);
-        if (!this.hasView) {
-            this.injectView();
+            
+            //if the grid does not currently need a horizontal scroller don't try to update it (EXTJSIV-3891)
+            if (scroller) {
+                scroller.scrollByDeltaX(distance);
+            }
         }
     },
 
-    /**
+    /**
      * @private
-     * Process UI events from the view. Propagate them to whatever internal Components need to process them
+     * Processes UI events from the view. Propagates them to whatever internal Components need to process them.
      * @param {String} type Event type, eg 'click'
-     * @param {TableView} view TableView Component
-     * @param {HtmlElement} cell Cell HtmlElement the event took place within
+     * @param {Ext.view.Table} view TableView Component
+     * @param {HTMLElement} cell Cell HtmlElement the event took place within
      * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
      * @param {Number} cellIndex Cell index within the row
-     * @param {EventObject} e Original event
+     * @param {Ext.EventObject} e Original event
      */
     processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
         var me = this,
@@ -726,64 +775,150 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
-    /**
-     * Request a recalculation of scrollbars and put them in if they are needed.
+    /**
+     * Requests a recalculation of scrollbars and puts them in if they are needed.
      */
     determineScrollbars: function() {
+        // Set a flag so that afterComponentLayout does not recurse back into here.
+        if (this.determineScrollbarsRunning) {
+            return;
+        }
+        this.determineScrollbarsRunning = true;
         var me = this,
-            viewElDom,
-            centerScrollWidth,
-            centerClientWidth,
+            view = me.view,
+            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 && view && view.viewReady) {
+
+            // 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 = view.el.getSize();
+
+            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 = 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;
 
-            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;
+                    }
+                }
+            }
+
+            // 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;
+
+                // Lay out the Component.
+                me.doComponentLayout();
+                // Lay out me.items
+                me.getLayout().layout();
             }
         }
+        delete me.determineScrollbarsRunning;
+    },
+
+    onViewResize: function() {
+        this.determineScrollbars();
+    },
+
+    afterComponentLayout: function() {
+        this.callParent(arguments);
+        this.determineScrollbars();
+        this.invalidateScroller();
     },
 
     onHeaderResize: function() {
-        if (this.view && this.view.rendered) {
+        if (!this.componentLayout.layoutBusy && this.view && this.view.rendered) {
             this.determineScrollbars();
             this.invalidateScroller();
         }
     },
 
-    /**
-     * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
+    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();
+        }
+    },
+
+    /**
+     * Hides the verticalScroller and removes the horizontalScrollerPresentCls.
      */
     hideHorizontalScroller: function() {
         var me = this;
 
         if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
-            me.verticalScroller.offsets.bottom = 0;
+            me.verticalScroller.setReservedSpace(0);
             me.removeDocked(me.horizontalScroller, false);
             me.removeCls(me.horizontalScrollerPresentCls);
             me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
@@ -791,14 +926,14 @@ Ext.define('Ext.panel.Table', {
 
     },
 
-    /**
-     * Show the horizontalScroller and add the horizontalScrollerPresentCls.
+    /**
+     * Shows the horizontalScroller and add the horizontalScrollerPresentCls.
      */
     showHorizontalScroller: function() {
         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.addDocked(me.horizontalScroller);
@@ -807,18 +942,13 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
-    /**
-     * Hide the verticalScroller and remove the verticalScrollerPresentCls.
+    /**
+     * Hides the verticalScroller and removes 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.removeDocked(me.verticalScroller, false);
             me.removeCls(me.verticalScrollerPresentCls);
@@ -826,18 +956,13 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
-    /**
-     * Show the verticalScroller and add the verticalScrollerPresentCls.
+    /**
+     * Shows the verticalScroller and adds 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.addDocked(me.verticalScroller);
             me.addCls(me.verticalScrollerPresentCls);
@@ -845,9 +970,21 @@ Ext.define('Ext.panel.Table', {
         }
     },
 
-    /**
-     * Invalides scrollers that are present and forces a recalculation.
-     * (Not related to showing/hiding the scrollers)
+    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;
+            if (!this.suspendLayout) {
+                headerCt.doLayout();
+            }
+        }
+    },
+
+    /**
+     * Invalides scrollers that are present and forces a recalculation. (Not related to showing/hiding the scrollers)
      */
     invalidateScroller: function() {
         var me = this,
@@ -886,135 +1023,81 @@ 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,
+            deltas = e.getWheelDeltas(),
+            deltaX = scrollDelta * deltas.x,
+            deltaY = scrollDelta * deltas.y,
             vertScrollerEl, horizScrollerEl,
-            origScrollLeft, origScrollTop,
-            newScrollLeft, newScrollTop;
+            vertScrollerElDom, horizScrollerElDom,
+            horizontalCanScrollLeft, horizontalCanScrollRight,
+            verticalCanScrollDown, verticalCanScrollUp;
 
-        // Track original scroll values, so we can see if we've
-        // reached the end of our scroll height/width.
+        // calculate whether or not both scrollbars can scroll right/left and up/down
         if (horizScroller) {
-            horizScrollerEl = horizScroller.el;
+            horizScrollerEl = horizScroller.scrollEl;
             if (horizScrollerEl) {
-                origScrollLeft = horizScrollerEl.dom.scrollLeft;
+                horizScrollerElDom = horizScrollerEl.dom;
+                horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
+                horizontalCanScrollLeft  = horizScrollerElDom.scrollLeft !== 0;
             }
         }
         if (vertScroller) {
-            vertScrollerEl = vertScroller.el;
+            vertScrollerEl = vertScroller.scrollEl;
             if (vertScrollerEl) {
-                origScrollTop = vertScrollerEl.dom.scrollTop;
+                vertScrollerElDom = vertScrollerEl.dom;
+                verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
+                verticalCanScrollUp   = vertScrollerElDom.scrollTop !== 0;
             }
         }
 
-        // Webkit Horizontal Axis
-        if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {
-            deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
-            deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
-            if (horizScroller) {
-                newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
-            }
-            if (vertScroller) {
-                newScrollTop = vertScroller.scrollByDeltaY(deltaY);
-            }
-        } else {
-            // Gecko Horizontal Axis
-            if (browserEvent.axis && browserEvent.axis === 1) {
-                if (horizScroller) {
-                    deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
-                    newScrollLeft = horizScroller.scrollByDeltaX(deltaX);
-                }
-            } else {
-                if (vertScroller) {
-
-                    deltaY = -(scrollDelta * e.getWheelDelta() / 3);
-                    newScrollTop = vertScroller.scrollByDeltaY(deltaY);
-                }
+        if (horizScroller) {
+            if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
+                e.stopEvent();
+                horizScroller.scrollByDeltaX(deltaX);
             }
         }
-
-        // If after given our delta, the scroller has not progressed, then we're
-        // at the end of our scroll range and shouldn't stop the browser event.
-        if ((deltaX !== 0 && newScrollLeft !== origScrollLeft) ||
-            (deltaY !== 0 && newScrollTop !== origScrollTop)) {
-            e.stopEvent();
+        if (vertScroller) {
+            if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
+                e.stopEvent();
+                vertScroller.scrollByDeltaY(deltaY);
+            }
         }
     },
 
-    /**
+    /**
      * @private
-     * Determine and invalidate scrollers on view refresh
+     * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready
      */
-    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]);
+    onViewReady: function() {
+        var me = this;
+        me.fireEvent('viewready', me);
+        if (me.deferRowRender) {
+            me.determineScrollbars();
+            me.invalidateScroller();
         }
     },
 
-    // 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));
-        }
+    /**
+     * @private
+     * Determines and invalidates scrollers on view refresh
+     */
+    onViewRefresh: function() {
+        var me = this;
 
-        // 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]);
-                    }
-                }
-                
+        // Refresh *during* render must be ignored.
+        if (!me.rendering) {
+            this.determineScrollbars();
+            if (this.invalidateScrollerOnRefresh) {
+                this.invalidateScroller();
             }
         }
     },
 
-    /**
+    /**
      * Sets the scrollTop of the TablePanel.
-     * @param {Number} deltaY
+     * @param {Number} top
      */
     setScrollTop: function(top) {
         var me               = this,
@@ -1025,7 +1108,6 @@ Ext.define('Ext.panel.Table', {
         if (verticalScroller) {
             verticalScroller.setScrollTop(top);
         }
-
     },
 
     getScrollerOwner: function() {
@@ -1036,61 +1118,62 @@ Ext.define('Ext.panel.Table', {
         return rootCmp;
     },
 
-    /**
+    /**
      * Scrolls the TablePanel by deltaY
      * @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
+     * @param {Number} deltaX
      */
     scrollByDeltaX: function(deltaX) {
-        this.horizontalScroller.scrollByDeltaX(deltaX);
+        var horizontalScroller = this.getHorizontalScroller();
+
+        if (horizontalScroller) {
+            horizontalScroller.scrollByDeltaX(deltaX);
+        }
     },
 
-    /**
-     * Get left hand side marker for header resizing.
+    /**
+     * Gets left hand side marker for header resizing.
      * @private
      */
     getLhsMarker: function() {
         var me = this;
 
         if (!me.lhsMarker) {
-            me.lhsMarker = Ext.core.DomHelper.append(me.el, {
+            me.lhsMarker = Ext.DomHelper.append(me.el, {
                 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
             }, true);
         }
         return me.lhsMarker;
     },
 
-    /**
-     * Get right hand side marker for header resizing.
+    /**
+     * Gets right hand side marker for header resizing.
      * @private
      */
     getRhsMarker: function() {
         var me = this;
 
         if (!me.rhsMarker) {
-            me.rhsMarker = Ext.core.DomHelper.append(me.el, {
+            me.rhsMarker = Ext.DomHelper.append(me.el, {
                 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
             }, true);
         }
         return me.rhsMarker;
     },
 
-    /**
-     * Returns the selection model being used and creates it via the configuration
-     * if it has not been created already.
+    /**
+     * Returns the selection model being used and creates it via the configuration if it has not been created already.
      * @return {Ext.selection.Model} selModel
      */
     getSelectionModel: function(){
@@ -1117,7 +1200,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;
         }
 
@@ -1143,21 +1228,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;
     },
 
@@ -1173,30 +1246,46 @@ Ext.define('Ext.panel.Table', {
         me.store = store;
         me.getView().bindStore(store);
     },
+    
+    beforeDestroy: function(){
+        // may be some duplication here since the horizontal and vertical
+        // scroller may be part of the docked items, but we need to clean
+        // them up in case they aren't visible.
+        Ext.destroy(this.horizontalScroller, this.verticalScroller);
+        this.callParent();
+    },
 
+    /**
+     * Reconfigures the table with a new store/columns. Either the store or the columns can be ommitted if you don't wish
+     * to change them.
+     * @param {Ext.data.Store} store (Optional) The new store.
+     * @param {Object[]} columns (Optional) 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();
+            if (columns) {
+                headerCt.suspendLayout = true;
+                headerCt.removeAll();
+                headerCt.add(columns);
+            }
+            if (store) {
+                store = Ext.StoreManager.lookup(store);
+                me.bindStore(store);
+            } else {
+                me.getView().refresh();
+            }
+            if (columns) {
+                headerCt.suspendLayout = false;
+                me.forceComponentLayout();
+            }
         }
-    },
-
-    afterComponentLayout: function() {
-        this.callParent(arguments);
-        this.determineScrollbars();
-        this.invalidateScroller();
+        me.fireEvent('reconfigure', me);
     }
-});
\ No newline at end of file +});
+ +