3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 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.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @author Nicolas Ferrero
18 * TablePanel is the basis of both {@link Ext.tree.Panel TreePanel} and {@link Ext.grid.Panel GridPanel}.
20 * TablePanel aggregates:
26 * - Ext.grid.header.Container
28 Ext.define('Ext.panel.Table', {
29 extend: 'Ext.panel.Panel',
31 alias: 'widget.tablepanel',
34 'Ext.selection.RowModel',
36 'Ext.grid.header.Container',
40 extraBaseCls: Ext.baseCSSPrefix + 'grid',
41 extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
45 * @property {Boolean} hasView
46 * True to indicate that a view has been injected into the panel.
50 // each panel should dictate what viewType and selType to use
52 * @cfg {String} viewType
53 * An xtype of view to use. This is automatically set to 'gridview' by {@link Ext.grid.Panel Grid}
54 * and to 'treeview' by {@link Ext.tree.Panel Tree}.
59 * @cfg {Object} viewConfig
60 * A config object that will be applied to the grid's UI view. Any of the config options available for
61 * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.
65 * @cfg {Ext.view.Table} view
66 * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to
67 * view (instead of creating an entire View instance).
71 * @cfg {String} selType
72 * An xtype of selection model to use. Defaults to 'rowmodel'. This is used to create selection model if just
73 * a config object or nothing at all given in {@link #selModel} config.
78 * @cfg {Ext.selection.Model/Object} selModel
79 * A {@link Ext.selection.Model selection model} instance or config object. In latter case the {@link #selType}
80 * config option determines to which type of selection model this config is applied.
84 * @cfg {Boolean} multiSelect
85 * True to enable 'MULTI' selection mode on selection model. See {@link Ext.selection.Model#mode}.
89 * @cfg {Boolean} simpleSelect
90 * True to enable 'SIMPLE' selection mode on selection model. See {@link Ext.selection.Model#mode}.
94 * @cfg {Ext.data.Store} store (required)
95 * The {@link Ext.data.Store Store} the grid should use as its data source.
99 * @cfg {Number} scrollDelta
100 * Number of pixels to scroll when scrolling with mousewheel.
105 * @cfg {String/Boolean} scroll
106 * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.
107 * True implies 'both'. False implies 'none'.
112 * @cfg {Ext.grid.column.Column[]} columns
113 * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this
114 * grid. Each column definition provides the header text for the column, and a definition of where the data for that
119 * @cfg {Boolean} forceFit
120 * Ttrue to force the columns to fit into the available width. Headers are first sized according to configuration,
121 * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire
122 * content width is used.
126 * @cfg {Ext.grid.feature.Feature[]} features
127 * An array of grid Features to be added to this grid. See {@link Ext.grid.feature.Feature} for usage.
131 * @cfg {Boolean} [hideHeaders=false]
132 * True to hide column headers.
136 * @cfg {Boolean} deferRowRender
137 * Defaults to true to enable deferred row rendering.
139 * This allows the View to execute a refresh quickly, with the expensive update of the row structure deferred so
140 * that layouts with GridPanels appear, and lay out more quickly.
143 deferRowRender: true,
146 * @cfg {Boolean} sortableColumns
147 * False to disable column sorting via clicking the header and via the Sorting menu items.
149 sortableColumns: true,
152 * @cfg {Boolean} [enableLocking=false]
153 * True to enable locking support for this grid. Alternatively, locking will also be automatically
154 * enabled if any of the columns in the column configuration contain the locked config option.
156 enableLocking: false,
158 verticalScrollDock: 'right',
159 verticalScrollerType: 'gridscroller',
161 horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
162 verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
164 // private property used to determine where to go down to find views
165 // this is here to support locking.
168 invalidateScrollerOnRefresh: true,
171 * @cfg {Boolean} enableColumnMove
172 * False to disable column dragging within this grid.
174 enableColumnMove: true,
177 * @cfg {Boolean} enableColumnResize
178 * False to disable column resizing within this grid.
180 enableColumnResize: true,
183 * @cfg {Boolean} enableColumnHide
184 * False to disable column hiding within this grid.
186 enableColumnHide: true,
188 initComponent: function() {
190 if (!this.viewType) {
191 Ext.Error.raise("You must specify a viewType config.");
194 Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
202 headerCtCfg = me.columns || me.colModel,
207 if (me.hideHeaders) {
211 // Look up the configured Store. If none configured, use the fieldless, empty Store defined in Ext.data.Store.
212 me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');
214 // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
215 // Either way, we extract a columns property referencing an array of Column definitions.
216 if (headerCtCfg instanceof Ext.grid.header.Container) {
217 me.headerCt = headerCtCfg;
218 me.headerCt.border = border;
219 me.columns = me.headerCt.items.items;
221 if (Ext.isArray(headerCtCfg)) {
227 Ext.apply(headerCtCfg, {
228 forceFit: me.forceFit,
229 sortable: me.sortableColumns,
230 enableColumnMove: me.enableColumnMove,
231 enableColumnResize: me.enableColumnResize,
232 enableColumnHide: me.enableColumnHide,
235 me.columns = headerCtCfg.items;
237 // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
238 // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
239 if (me.enableLocking || Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
240 me.self.mixin('lockable', Ext.grid.Lockable);
248 * Fires after a reconfigure.
249 * @param {Ext.panel.Table} this
254 * Fires when the grid view is available (use this for selecting a default row).
255 * @param {Ext.panel.Table} this
259 * @event scrollerhide
260 * Fires when a scroller is hidden.
261 * @param {Ext.grid.Scroller} scroller
262 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
266 * @event scrollershow
267 * Fires when a scroller is shown.
268 * @param {Ext.grid.Scroller} scroller
269 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
274 me.bodyCls = me.bodyCls || '';
275 me.bodyCls += (' ' + me.extraBodyCls);
277 me.cls = me.cls || '';
278 me.cls += (' ' + me.extraBaseCls);
280 // autoScroll is not a valid configuration
281 delete me.autoScroll;
283 // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
284 // than a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.
287 // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
288 // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
290 me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
293 // Extract the array of Column objects
294 me.columns = me.headerCt.items.items;
296 if (me.hideHeaders) {
297 me.headerCt.height = 0;
298 me.headerCt.border = false;
299 me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
300 me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
301 // IE Quirks Mode fix
302 // If hidden configuration option was used, several layout calculations will be bypassed.
303 if (Ext.isIEQuirks) {
304 me.headerCt.style = {
311 if (scroll === true || scroll === 'both') {
312 vertical = horizontal = true;
313 } else if (scroll === 'horizontal') {
315 } else if (scroll === 'vertical') {
317 // All other values become 'none' or false.
319 me.headerCt.availableSpaceOffset = 0;
323 me.verticalScroller = Ext.ComponentManager.create(me.initVerticalScroller());
324 me.mon(me.verticalScroller, {
325 bodyscroll: me.onVerticalScroll,
331 me.horizontalScroller = Ext.ComponentManager.create(me.initHorizontalScroller());
332 me.mon(me.horizontalScroller, {
333 bodyscroll: me.onHorizontalScroll,
338 me.headerCt.on('resize', me.onHeaderResize, me);
339 me.relayHeaderCtEvents(me.headerCt);
340 me.features = me.features || [];
341 if (!Ext.isArray(me.features)) {
342 me.features = [me.features];
344 me.dockedItems = me.dockedItems || [];
345 me.dockedItems.unshift(me.headerCt);
346 me.viewConfig = me.viewConfig || {};
347 me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
349 // AbstractDataView will look up a Store configured as an object
350 // getView converts viewConfig into a View instance
354 afterrender: function () {
355 // hijack the view el's scroll method
356 view.el.scroll = Ext.Function.bind(me.elScroll, me);
357 // We use to listen to document.body wheel events, but that's a
358 // little much. We scope just to the view now.
360 mousewheel: me.onMouseWheel,
370 load: me.onStoreLoad,
374 viewReady: me.onViewReady,
375 resize: me.onViewResize,
377 fn: me.onViewRefresh,
383 this.relayEvents(view, [
385 * @event beforeitemmousedown
386 * @alias Ext.view.View#beforeitemmousedown
388 'beforeitemmousedown',
390 * @event beforeitemmouseup
391 * @alias Ext.view.View#beforeitemmouseup
395 * @event beforeitemmouseenter
396 * @alias Ext.view.View#beforeitemmouseenter
398 'beforeitemmouseenter',
400 * @event beforeitemmouseleave
401 * @alias Ext.view.View#beforeitemmouseleave
403 'beforeitemmouseleave',
405 * @event beforeitemclick
406 * @alias Ext.view.View#beforeitemclick
410 * @event beforeitemdblclick
411 * @alias Ext.view.View#beforeitemdblclick
413 'beforeitemdblclick',
415 * @event beforeitemcontextmenu
416 * @alias Ext.view.View#beforeitemcontextmenu
418 'beforeitemcontextmenu',
420 * @event itemmousedown
421 * @alias Ext.view.View#itemmousedown
426 * @alias Ext.view.View#itemmouseup
430 * @event itemmouseenter
431 * @alias Ext.view.View#itemmouseenter
435 * @event itemmouseleave
436 * @alias Ext.view.View#itemmouseleave
441 * @alias Ext.view.View#itemclick
445 * @event itemdblclick
446 * @alias Ext.view.View#itemdblclick
450 * @event itemcontextmenu
451 * @alias Ext.view.View#itemcontextmenu
455 * @event beforecontainermousedown
456 * @alias Ext.view.View#beforecontainermousedown
458 'beforecontainermousedown',
460 * @event beforecontainermouseup
461 * @alias Ext.view.View#beforecontainermouseup
463 'beforecontainermouseup',
465 * @event beforecontainermouseover
466 * @alias Ext.view.View#beforecontainermouseover
468 'beforecontainermouseover',
470 * @event beforecontainermouseout
471 * @alias Ext.view.View#beforecontainermouseout
473 'beforecontainermouseout',
475 * @event beforecontainerclick
476 * @alias Ext.view.View#beforecontainerclick
478 'beforecontainerclick',
480 * @event beforecontainerdblclick
481 * @alias Ext.view.View#beforecontainerdblclick
483 'beforecontainerdblclick',
485 * @event beforecontainercontextmenu
486 * @alias Ext.view.View#beforecontainercontextmenu
488 'beforecontainercontextmenu',
490 * @event containermouseup
491 * @alias Ext.view.View#containermouseup
495 * @event containermouseover
496 * @alias Ext.view.View#containermouseover
498 'containermouseover',
500 * @event containermouseout
501 * @alias Ext.view.View#containermouseout
505 * @event containerclick
506 * @alias Ext.view.View#containerclick
510 * @event containerdblclick
511 * @alias Ext.view.View#containerdblclick
515 * @event containercontextmenu
516 * @alias Ext.view.View#containercontextmenu
518 'containercontextmenu',
520 * @event selectionchange
521 * @alias Ext.selection.Model#selectionchange
525 * @event beforeselect
526 * @alias Ext.selection.RowModel#beforeselect
531 * @alias Ext.selection.RowModel#select
535 * @event beforedeselect
536 * @alias Ext.selection.RowModel#beforedeselect
541 * @alias Ext.selection.RowModel#deselect
547 me.callParent(arguments);
550 onRender: function(){
551 var vScroll = this.verticalScroller,
552 hScroll = this.horizontalScroller;
555 vScroll.ensureDimension();
558 hScroll.ensureDimension();
560 this.callParent(arguments);
564 initStateEvents: function(){
565 var events = this.stateEvents;
566 // push on stateEvents if they don't exist
567 Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
568 if (Ext.Array.indexOf(events, event)) {
576 * Returns the horizontal scroller config.
578 initHorizontalScroller: function () {
581 xtype: 'gridscroller',
591 * Returns the vertical scroller config.
593 initVerticalScroller: function () {
595 ret = me.verticalScroller || {};
598 xtype: me.verticalScrollerType,
599 dock: me.verticalScrollDock,
606 relayHeaderCtEvents: function (headerCt) {
607 this.relayEvents(headerCt, [
609 * @event columnresize
610 * @alias Ext.grid.header.Container#columnresize
615 * @alias Ext.grid.header.Container#columnmove
620 * @alias Ext.grid.header.Container#columnhide
625 * @alias Ext.grid.header.Container#columnshow
630 * @alias Ext.grid.header.Container#sortchange
636 getState: function(){
638 state = me.callParent(),
639 sorter = me.store.sorters.first();
641 state.columns = (me.headerCt || me).getColumnsState();
645 property: sorter.property,
646 direction: sorter.direction
653 applyState: function(state) {
657 columns = state.columns;
659 delete state.columns;
661 // Ensure superclass has applied *its* state.
662 // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
663 me.callParent(arguments);
666 (me.headerCt || me).applyColumnsState(columns);
670 if (store.remoteSort) {
671 store.sorters.add(Ext.create('Ext.util.Sorter', {
672 property: sorter.property,
673 direction: sorter.direction
677 store.sort(sorter.property, sorter.direction);
683 * Returns the store associated with this Panel.
684 * @return {Ext.data.Store} The store
686 getStore: function(){
691 * Gets the view for this panel.
692 * @return {Ext.view.Table}
694 getView: function() {
699 sm = me.getSelectionModel();
700 me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
701 deferInitialRefresh: me.deferRowRender,
704 headerCt: me.headerCt,
706 features: me.features,
710 uievent: me.processEvent,
714 me.headerCt.view = me.view;
715 me.relayEvents(me.view, ['cellclick', 'celldblclick']);
723 * autoScroll is never valid for all classes which extend TablePanel.
725 setAutoScroll: Ext.emptyFn,
727 // This method hijacks Ext.view.Table's el scroll method.
728 // This enables us to keep the virtualized scrollbars in sync
729 // with the view. It currently does NOT support animation.
730 elScroll: function(direction, distance, animate) {
734 if (direction === "up" || direction === "left") {
735 distance = -distance;
738 if (direction === "down" || direction === "up") {
739 scroller = me.getVerticalScroller();
741 //if the grid does not currently need a vertical scroller don't try to update it (EXTJSIV-3891)
743 scroller.scrollByDeltaY(distance);
746 scroller = me.getHorizontalScroller();
748 //if the grid does not currently need a horizontal scroller don't try to update it (EXTJSIV-3891)
750 scroller.scrollByDeltaX(distance);
757 * Processes UI events from the view. Propagates them to whatever internal Components need to process them.
758 * @param {String} type Event type, eg 'click'
759 * @param {Ext.view.Table} view TableView Component
760 * @param {HTMLElement} cell Cell HtmlElement the event took place within
761 * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
762 * @param {Number} cellIndex Cell index within the row
763 * @param {Ext.EventObject} e Original event
765 processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
769 if (cellIndex !== -1) {
770 header = me.headerCt.getGridColumns()[cellIndex];
771 return header.processEvent.apply(header, arguments);
776 * Requests a recalculation of scrollbars and puts them in if they are needed.
778 determineScrollbars: function() {
779 // Set a flag so that afterComponentLayout does not recurse back into here.
780 if (this.determineScrollbarsRunning) {
783 this.determineScrollbarsRunning = true;
792 verticalScroller = me.verticalScroller,
793 horizontalScroller = me.horizontalScroller,
794 curScrollbars = (verticalScroller && verticalScroller.ownerCt === me ? 1 : 0) |
795 (horizontalScroller && horizontalScroller.ownerCt === me ? 2 : 0),
796 reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both
798 // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars
799 if (!me.collapsed && view && view.viewReady) {
801 // Calculate maximum, *scrollbarless* space which the view has available.
802 // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars
803 box = view.el.getSize();
805 clientWidth = box.width + ((curScrollbars & 1) ? verticalScroller.width : 0);
806 clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0);
808 // Calculate the width of the scrolling block
809 // There will never be a horizontal scrollbar if all columns are flexed.
811 scrollWidth = (me.headerCt.query('[flex]').length && !me.headerCt.layout.tooNarrow) ? 0 : me.headerCt.getFullWidth();
813 // Calculate the height of the scrolling block
814 if (verticalScroller && verticalScroller.el) {
815 scrollHeight = verticalScroller.getSizeCalculation().height;
817 tableEl = view.el.child('table', true);
818 scrollHeight = tableEl ? tableEl.offsetHeight : 0;
822 // Definitely need a vertical scrollbar
823 if (scrollHeight > clientHeight) {
826 // But if scrollable block width goes into the zone required by the vertical scrollbar, we'll also need a horizontal
827 if (horizontalScroller && ((clientWidth - scrollWidth) < verticalScroller.width)) {
832 // View height fits. But we stil may need a horizontal scrollbar, and this might necessitate a vertical one.
835 // Definitely need a horizontal scrollbar
836 if (scrollWidth > clientWidth) {
839 // But if scrollable block height goes into the zone required by the horizontal scrollbar, we'll also need a vertical
840 if (verticalScroller && ((clientHeight - scrollHeight) < horizontalScroller.height)) {
846 // If scrollbar requirements have changed, change 'em...
847 if (reqScrollbars !== curScrollbars) {
849 // Suspend component layout while we add/remove the docked scrollers
850 me.suspendLayout = true;
851 if (reqScrollbars & 1) {
852 me.showVerticalScroller();
854 me.hideVerticalScroller();
856 if (reqScrollbars & 2) {
857 me.showHorizontalScroller();
859 me.hideHorizontalScroller();
861 me.suspendLayout = false;
863 // Lay out the Component.
864 me.doComponentLayout();
866 me.getLayout().layout();
869 delete me.determineScrollbarsRunning;
872 onViewResize: function() {
873 this.determineScrollbars();
876 afterComponentLayout: function() {
877 this.callParent(arguments);
878 this.determineScrollbars();
879 this.invalidateScroller();
882 onHeaderResize: function() {
883 if (!this.componentLayout.layoutBusy && this.view && this.view.rendered) {
884 this.determineScrollbars();
885 this.invalidateScroller();
889 afterCollapse: function() {
891 if (me.verticalScroller) {
892 me.verticalScroller.saveScrollPos();
894 if (me.horizontalScroller) {
895 me.horizontalScroller.saveScrollPos();
897 me.callParent(arguments);
900 afterExpand: function() {
902 me.callParent(arguments);
903 if (me.verticalScroller) {
904 me.verticalScroller.restoreScrollPos();
906 if (me.horizontalScroller) {
907 me.horizontalScroller.restoreScrollPos();
912 * Hides the verticalScroller and removes the horizontalScrollerPresentCls.
914 hideHorizontalScroller: function() {
917 if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
918 me.verticalScroller.setReservedSpace(0);
919 me.removeDocked(me.horizontalScroller, false);
920 me.removeCls(me.horizontalScrollerPresentCls);
921 me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
927 * Shows the horizontalScroller and add the horizontalScrollerPresentCls.
929 showHorizontalScroller: function() {
932 if (me.verticalScroller) {
933 me.verticalScroller.setReservedSpace(Ext.getScrollbarSize().height - 1);
935 if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
936 me.addDocked(me.horizontalScroller);
937 me.addCls(me.horizontalScrollerPresentCls);
938 me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
943 * Hides the verticalScroller and removes the verticalScrollerPresentCls.
945 hideVerticalScroller: function() {
948 me.setHeaderReserveOffset(false);
949 if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
950 me.removeDocked(me.verticalScroller, false);
951 me.removeCls(me.verticalScrollerPresentCls);
952 me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
957 * Shows the verticalScroller and adds the verticalScrollerPresentCls.
959 showVerticalScroller: function() {
962 me.setHeaderReserveOffset(true);
963 if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
964 me.addDocked(me.verticalScroller);
965 me.addCls(me.verticalScrollerPresentCls);
966 me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
970 setHeaderReserveOffset: function (reserveOffset) {
971 var headerCt = this.headerCt,
972 layout = headerCt.layout;
974 // only trigger a layout when reserveOffset is changing
975 if (layout && layout.reserveOffset !== reserveOffset) {
976 layout.reserveOffset = reserveOffset;
977 if (!this.suspendLayout) {
984 * Invalides scrollers that are present and forces a recalculation. (Not related to showing/hiding the scrollers)
986 invalidateScroller: function() {
988 vScroll = me.verticalScroller,
989 hScroll = me.horizontalScroller;
992 vScroll.invalidate();
995 hScroll.invalidate();
999 // refresh the view when a header moves
1000 onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
1001 this.view.refresh();
1004 // Section onHeaderHide is invoked after view.
1005 onHeaderHide: function(headerCt, header) {
1006 this.invalidateScroller();
1009 onHeaderShow: function(headerCt, header) {
1010 this.invalidateScroller();
1013 getVerticalScroller: function() {
1014 return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
1017 getHorizontalScroller: function() {
1018 return this.getScrollerOwner().down('gridscroller[dock=bottom]');
1021 onMouseWheel: function(e) {
1023 vertScroller = me.getVerticalScroller(),
1024 horizScroller = me.getHorizontalScroller(),
1025 scrollDelta = -me.scrollDelta,
1026 deltas = e.getWheelDeltas(),
1027 deltaX = scrollDelta * deltas.x,
1028 deltaY = scrollDelta * deltas.y,
1029 vertScrollerEl, horizScrollerEl,
1030 vertScrollerElDom, horizScrollerElDom,
1031 horizontalCanScrollLeft, horizontalCanScrollRight,
1032 verticalCanScrollDown, verticalCanScrollUp;
1034 // calculate whether or not both scrollbars can scroll right/left and up/down
1035 if (horizScroller) {
1036 horizScrollerEl = horizScroller.scrollEl;
1037 if (horizScrollerEl) {
1038 horizScrollerElDom = horizScrollerEl.dom;
1039 horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
1040 horizontalCanScrollLeft = horizScrollerElDom.scrollLeft !== 0;
1044 vertScrollerEl = vertScroller.scrollEl;
1045 if (vertScrollerEl) {
1046 vertScrollerElDom = vertScrollerEl.dom;
1047 verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
1048 verticalCanScrollUp = vertScrollerElDom.scrollTop !== 0;
1052 if (horizScroller) {
1053 if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
1055 horizScroller.scrollByDeltaX(deltaX);
1059 if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
1061 vertScroller.scrollByDeltaY(deltaY);
1068 * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready
1070 onViewReady: function() {
1072 me.fireEvent('viewready', me);
1073 if (me.deferRowRender) {
1074 me.determineScrollbars();
1075 me.invalidateScroller();
1081 * Determines and invalidates scrollers on view refresh
1083 onViewRefresh: function() {
1086 // Refresh *during* render must be ignored.
1087 if (!me.rendering) {
1088 this.determineScrollbars();
1089 if (this.invalidateScrollerOnRefresh) {
1090 this.invalidateScroller();
1096 * Sets the scrollTop of the TablePanel.
1097 * @param {Number} top
1099 setScrollTop: function(top) {
1101 rootCmp = me.getScrollerOwner(),
1102 verticalScroller = me.getVerticalScroller();
1104 rootCmp.virtualScrollTop = top;
1105 if (verticalScroller) {
1106 verticalScroller.setScrollTop(top);
1110 getScrollerOwner: function() {
1112 if (!this.scrollerOwner) {
1113 rootCmp = this.up('[scrollerOwner]');
1119 * Scrolls the TablePanel by deltaY
1120 * @param {Number} deltaY
1122 scrollByDeltaY: function(deltaY) {
1123 var verticalScroller = this.getVerticalScroller();
1125 if (verticalScroller) {
1126 verticalScroller.scrollByDeltaY(deltaY);
1131 * Scrolls the TablePanel by deltaX
1132 * @param {Number} deltaX
1134 scrollByDeltaX: function(deltaX) {
1135 var horizontalScroller = this.getHorizontalScroller();
1137 if (horizontalScroller) {
1138 horizontalScroller.scrollByDeltaX(deltaX);
1143 * Gets left hand side marker for header resizing.
1146 getLhsMarker: function() {
1149 if (!me.lhsMarker) {
1150 me.lhsMarker = Ext.DomHelper.append(me.el, {
1151 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1154 return me.lhsMarker;
1158 * Gets right hand side marker for header resizing.
1161 getRhsMarker: function() {
1164 if (!me.rhsMarker) {
1165 me.rhsMarker = Ext.DomHelper.append(me.el, {
1166 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1169 return me.rhsMarker;
1173 * Returns the selection model being used and creates it via the configuration if it has not been created already.
1174 * @return {Ext.selection.Model} selModel
1176 getSelectionModel: function(){
1177 if (!this.selModel) {
1181 var mode = 'SINGLE',
1183 if (this.simpleSelect) {
1185 } else if (this.multiSelect) {
1189 Ext.applyIf(this.selModel, {
1190 allowDeselect: this.allowDeselect,
1194 if (!this.selModel.events) {
1195 type = this.selModel.selType || this.selType;
1196 this.selModel = Ext.create('selection.' + type, this.selModel);
1199 if (!this.selModel.hasRelaySetup) {
1200 this.relayEvents(this.selModel, [
1201 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
1203 this.selModel.hasRelaySetup = true;
1206 // lock the selection model if user
1207 // has disabled selection
1208 if (this.disableSelection) {
1209 this.selModel.locked = true;
1211 return this.selModel;
1214 onVerticalScroll: function(event, target) {
1215 var owner = this.getScrollerOwner(),
1216 items = owner.query('tableview'),
1220 for (; i < len; i++) {
1221 items[i].el.dom.scrollTop = target.scrollTop;
1225 onHorizontalScroll: function(event, target) {
1226 var owner = this.getScrollerOwner(),
1227 items = owner.query('tableview'),
1228 center = items[1] || items[0];
1230 center.el.dom.scrollLeft = target.scrollLeft;
1231 this.headerCt.el.dom.scrollLeft = target.scrollLeft;
1234 // template method meant to be overriden
1235 onStoreLoad: Ext.emptyFn,
1237 getEditorParent: function() {
1241 bindStore: function(store) {
1244 me.getView().bindStore(store);
1247 beforeDestroy: function(){
1248 // may be some duplication here since the horizontal and vertical
1249 // scroller may be part of the docked items, but we need to clean
1250 // them up in case they aren't visible.
1251 Ext.destroy(this.horizontalScroller, this.verticalScroller);
1256 * Reconfigures the table with a new store/columns. Either the store or the columns can be ommitted if you don't wish
1258 * @param {Ext.data.Store} store (Optional) The new store.
1259 * @param {Object[]} columns (Optional) An array of column configs
1261 reconfigure: function(store, columns) {
1263 headerCt = me.headerCt;
1266 me.reconfigureLockable(store, columns);
1269 headerCt.suspendLayout = true;
1270 headerCt.removeAll();
1271 headerCt.add(columns);
1274 store = Ext.StoreManager.lookup(store);
1275 me.bindStore(store);
1277 me.getView().refresh();
1280 headerCt.suspendLayout = false;
1281 me.forceComponentLayout();
1284 me.fireEvent('reconfigure', me);