2 * @class Ext.panel.Table
3 * @extends Ext.panel.Panel
6 * @author Nicolas Ferrero
7 * TablePanel is a private class and the basis of both TreePanel and GridPanel.
9 * TablePanel aggregates:
15 * - Ext.grid.header.Container
18 Ext.define('Ext.panel.Table', {
19 extend: 'Ext.panel.Panel',
21 alias: 'widget.tablepanel',
24 'Ext.selection.RowModel',
26 'Ext.grid.header.Container',
30 cls: Ext.baseCSSPrefix + 'grid',
31 extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
35 * Boolean to indicate that a view has been injected into the panel.
40 // each panel should dictate what viewType and selType to use
45 * @cfg {Number} scrollDelta
46 * Number of pixels to scroll when scrolling with mousewheel.
52 * @cfg {String/Boolean} scroll
53 * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
59 * @cfg {Array} columns
60 * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
61 * column definition provides the header text for the column, and a definition of where the data for that column comes from.
65 * @cfg {Boolean} forceFit
66 * 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
67 * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
71 * @cfg {Boolean} hideHeaders
72 * Specify as <code>true</code> to hide the headers.
76 * @cfg {Boolean} sortableColumns
77 * Defaults to true. Set to false to disable column sorting via clicking the
78 * header and via the Sorting menu items.
80 sortableColumns: true,
82 verticalScrollDock: 'right',
83 verticalScrollerType: 'gridscroller',
85 horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
86 verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
88 // private property used to determine where to go down to find views
89 // this is here to support locking.
92 invalidateScrollerOnRefresh: true,
94 enableColumnMove: true,
95 enableColumnResize: true,
98 initComponent: function() {
100 if (!this.viewType) {
101 Ext.Error.raise("You must specify a viewType config.");
104 Ext.Error.raise("You must specify a store config");
107 Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
115 headerCtCfg = me.columns || me.colModel,
120 // Set our determinScrollbars method to reference a buffered call to determinScrollbars which fires on a 30ms buffer.
121 me.determineScrollbars = Ext.Function.createBuffered(me.determineScrollbars, 30);
122 me.invalidateScroller = Ext.Function.createBuffered(me.invalidateScroller, 30);
123 me.injectView = Ext.Function.createBuffered(me.injectView, 30);
125 if (me.hideHeaders) {
129 // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
130 // Either way, we extract a columns property referencing an array of Column definitions.
131 if (headerCtCfg instanceof Ext.grid.header.Container) {
132 me.headerCt = headerCtCfg;
133 me.headerCt.border = border;
134 me.columns = me.headerCt.items.items;
136 if (Ext.isArray(headerCtCfg)) {
142 Ext.apply(headerCtCfg, {
143 forceFit: me.forceFit,
144 sortable: me.sortableColumns,
145 enableColumnMove: me.enableColumnMove,
146 enableColumnResize: me.enableColumnResize,
149 me.columns = headerCtCfg.items;
151 // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
152 // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
153 if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
154 me.self.mixin('lockable', Ext.grid.Lockable);
159 me.store = Ext.data.StoreManager.lookup(me.store);
162 * @event scrollerhide
163 * Fires when a scroller is hidden
164 * @param {Ext.grid.Scroller} scroller
165 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
169 * @event scrollershow
170 * Fires when a scroller is shown
171 * @param {Ext.grid.Scroller} scroller
172 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
177 me.bodyCls = me.bodyCls || '';
178 me.bodyCls += (' ' + me.extraBodyCls);
180 // autoScroll is not a valid configuration
181 delete me.autoScroll;
183 // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
184 // 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.
187 // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
188 // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
190 me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
193 // Extract the array of Column objects
194 me.columns = me.headerCt.items.items;
196 if (me.hideHeaders) {
197 me.headerCt.height = 0;
198 me.headerCt.border = false;
199 me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
200 me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
201 // IE Quirks Mode fix
202 // If hidden configuration option was used, several layout calculations will be bypassed.
203 if (Ext.isIEQuirks) {
204 me.headerCt.style = {
211 if (scroll === true || scroll === 'both') {
212 vertical = horizontal = true;
213 } else if (scroll === 'horizontal') {
215 } else if (scroll === 'vertical') {
217 // All other values become 'none' or false.
219 me.headerCt.availableSpaceOffset = 0;
223 me.verticalScroller = me.verticalScroller || {};
224 Ext.applyIf(me.verticalScroller, {
225 dock: me.verticalScrollDock,
226 xtype: me.verticalScrollerType,
229 me.verticalScroller = Ext.ComponentManager.create(me.verticalScroller);
230 me.mon(me.verticalScroller, {
231 bodyscroll: me.onVerticalScroll,
237 me.horizontalScroller = Ext.ComponentManager.create({
238 xtype: 'gridscroller',
243 me.mon(me.horizontalScroller, {
244 bodyscroll: me.onHorizontalScroll,
249 me.headerCt.on('columnresize', me.onHeaderResize, me);
250 me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
251 me.features = me.features || [];
252 me.dockedItems = me.dockedItems || [];
253 me.dockedItems.unshift(me.headerCt);
254 me.viewConfig = me.viewConfig || {};
255 me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
257 // AbstractDataView will look up a Store configured as an object
258 // getView converts viewConfig into a View instance
263 load: me.onStoreLoad,
268 fn: this.onViewRefresh,
272 itemupdate: me.onViewItemUpdate,
275 this.relayEvents(view, [
277 * @event beforeitemmousedown
278 * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
279 * @param {Ext.view.View} this
280 * @param {Ext.data.Model} record The record that belongs to the item
281 * @param {HTMLElement} item The item's element
282 * @param {Number} index The item's index
283 * @param {Ext.EventObject} e The raw event object
285 'beforeitemmousedown',
287 * @event beforeitemmouseup
288 * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
289 * @param {Ext.view.View} this
290 * @param {Ext.data.Model} record The record that belongs to the item
291 * @param {HTMLElement} item The item's element
292 * @param {Number} index The item's index
293 * @param {Ext.EventObject} e The raw event object
297 * @event beforeitemmouseenter
298 * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
299 * @param {Ext.view.View} this
300 * @param {Ext.data.Model} record The record that belongs to the item
301 * @param {HTMLElement} item The item's element
302 * @param {Number} index The item's index
303 * @param {Ext.EventObject} e The raw event object
305 'beforeitemmouseenter',
307 * @event beforeitemmouseleave
308 * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
309 * @param {Ext.view.View} this
310 * @param {Ext.data.Model} record The record that belongs to the item
311 * @param {HTMLElement} item The item's element
312 * @param {Number} index The item's index
313 * @param {Ext.EventObject} e The raw event object
315 'beforeitemmouseleave',
317 * @event beforeitemclick
318 * Fires before the click event on an item is processed. Returns false to cancel the default action.
319 * @param {Ext.view.View} this
320 * @param {Ext.data.Model} record The record that belongs to the item
321 * @param {HTMLElement} item The item's element
322 * @param {Number} index The item's index
323 * @param {Ext.EventObject} e The raw event object
327 * @event beforeitemdblclick
328 * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
329 * @param {Ext.view.View} this
330 * @param {Ext.data.Model} record The record that belongs to the item
331 * @param {HTMLElement} item The item's element
332 * @param {Number} index The item's index
333 * @param {Ext.EventObject} e The raw event object
335 'beforeitemdblclick',
337 * @event beforeitemcontextmenu
338 * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
339 * @param {Ext.view.View} this
340 * @param {Ext.data.Model} record The record that belongs to the item
341 * @param {HTMLElement} item The item's element
342 * @param {Number} index The item's index
343 * @param {Ext.EventObject} e The raw event object
345 'beforeitemcontextmenu',
347 * @event itemmousedown
348 * Fires when there is a mouse down on an item
349 * @param {Ext.view.View} this
350 * @param {Ext.data.Model} record The record that belongs to the item
351 * @param {HTMLElement} item The item's element
352 * @param {Number} index The item's index
353 * @param {Ext.EventObject} e The raw event object
358 * Fires when there is a mouse up on an item
359 * @param {Ext.view.View} this
360 * @param {Ext.data.Model} record The record that belongs to the item
361 * @param {HTMLElement} item The item's element
362 * @param {Number} index The item's index
363 * @param {Ext.EventObject} e The raw event object
367 * @event itemmouseenter
368 * Fires when the mouse enters an item.
369 * @param {Ext.view.View} this
370 * @param {Ext.data.Model} record The record that belongs to the item
371 * @param {HTMLElement} item The item's element
372 * @param {Number} index The item's index
373 * @param {Ext.EventObject} e The raw event object
377 * @event itemmouseleave
378 * Fires when the mouse leaves an item.
379 * @param {Ext.view.View} this
380 * @param {Ext.data.Model} record The record that belongs to the item
381 * @param {HTMLElement} item The item's element
382 * @param {Number} index The item's index
383 * @param {Ext.EventObject} e The raw event object
388 * Fires when an item is clicked.
389 * @param {Ext.view.View} this
390 * @param {Ext.data.Model} record The record that belongs to the item
391 * @param {HTMLElement} item The item's element
392 * @param {Number} index The item's index
393 * @param {Ext.EventObject} e The raw event object
397 * @event itemdblclick
398 * Fires when an item is double clicked.
399 * @param {Ext.view.View} this
400 * @param {Ext.data.Model} record The record that belongs to the item
401 * @param {HTMLElement} item The item's element
402 * @param {Number} index The item's index
403 * @param {Ext.EventObject} e The raw event object
407 * @event itemcontextmenu
408 * Fires when an item is right clicked.
409 * @param {Ext.view.View} this
410 * @param {Ext.data.Model} record The record that belongs to the item
411 * @param {HTMLElement} item The item's element
412 * @param {Number} index The item's index
413 * @param {Ext.EventObject} e The raw event object
417 * @event beforecontainermousedown
418 * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
419 * @param {Ext.view.View} this
420 * @param {Ext.EventObject} e The raw event object
422 'beforecontainermousedown',
424 * @event beforecontainermouseup
425 * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
426 * @param {Ext.view.View} this
427 * @param {Ext.EventObject} e The raw event object
429 'beforecontainermouseup',
431 * @event beforecontainermouseover
432 * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
433 * @param {Ext.view.View} this
434 * @param {Ext.EventObject} e The raw event object
436 'beforecontainermouseover',
438 * @event beforecontainermouseout
439 * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
440 * @param {Ext.view.View} this
441 * @param {Ext.EventObject} e The raw event object
443 'beforecontainermouseout',
445 * @event beforecontainerclick
446 * Fires before the click event on the container is processed. Returns false to cancel the default action.
447 * @param {Ext.view.View} this
448 * @param {Ext.EventObject} e The raw event object
450 'beforecontainerclick',
452 * @event beforecontainerdblclick
453 * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
454 * @param {Ext.view.View} this
455 * @param {Ext.EventObject} e The raw event object
457 'beforecontainerdblclick',
459 * @event beforecontainercontextmenu
460 * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
461 * @param {Ext.view.View} this
462 * @param {Ext.EventObject} e The raw event object
464 'beforecontainercontextmenu',
466 * @event containermouseup
467 * Fires when there is a mouse up on the container
468 * @param {Ext.view.View} this
469 * @param {Ext.EventObject} e The raw event object
473 * @event containermouseover
474 * Fires when you move the mouse over the container.
475 * @param {Ext.view.View} this
476 * @param {Ext.EventObject} e The raw event object
478 'containermouseover',
480 * @event containermouseout
481 * Fires when you move the mouse out of the container.
482 * @param {Ext.view.View} this
483 * @param {Ext.EventObject} e The raw event object
487 * @event containerclick
488 * Fires when the container is clicked.
489 * @param {Ext.view.View} this
490 * @param {Ext.EventObject} e The raw event object
494 * @event containerdblclick
495 * Fires when the container is double clicked.
496 * @param {Ext.view.View} this
497 * @param {Ext.EventObject} e The raw event object
501 * @event containercontextmenu
502 * Fires when the container is right clicked.
503 * @param {Ext.view.View} this
504 * @param {Ext.EventObject} e The raw event object
506 'containercontextmenu',
509 * @event selectionchange
510 * Fires when the selected nodes change. Relayed event from the underlying selection model.
511 * @param {Ext.view.View} this
512 * @param {Array} selections Array of the selected nodes
516 * @event beforeselect
517 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
518 * @param {Ext.view.View} this
519 * @param {HTMLElement} node The node to be selected
520 * @param {Array} selections Array of currently selected nodes
526 me.callParent(arguments);
530 initStateEvents: function(){
531 var events = this.stateEvents;
532 // push on stateEvents if they don't exist
533 Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
534 if (Ext.Array.indexOf(events, event)) {
541 getState: function(){
545 sorter = this.store.sorters.first();
547 this.headerCt.items.each(function(header){
550 width: header.flex ? undefined : header.width,
551 hidden: header.hidden,
552 sortable: header.sortable
558 property: sorter.property,
559 direction: sorter.direction
565 applyState: function(state) {
566 var headers = state.columns,
567 length = headers ? headers.length : 0,
568 headerCt = this.headerCt,
569 items = headerCt.items,
577 for (; i < length; ++i) {
578 headerState = headers[i];
579 header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
580 index = items.indexOf(header);
582 headerCt.moveHeader(index, i);
584 header.sortable = headerState.sortable;
585 if (Ext.isDefined(headerState.width)) {
587 if (header.rendered) {
588 header.setWidth(headerState.width);
590 header.minWidth = header.width = headerState.width;
593 header.hidden = headerState.hidden;
597 if (store.remoteSort) {
598 store.sorters.add(Ext.create('Ext.util.Sorter', {
599 property: sorter.property,
600 direction: sorter.direction
604 store.sort(sorter.property, sorter.direction);
610 * Returns the store associated with this Panel.
611 * @return {Ext.data.Store} The store
613 getStore: function(){
618 * Gets the view for this panel.
619 * @return {Ext.view.Table}
621 getView: function() {
626 sm = me.getSelectionModel();
627 me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
630 headerCt: me.headerCt,
632 features: me.features,
636 uievent: me.processEvent,
640 me.headerCt.view = me.view;
641 me.relayEvents(me.view, ['cellclick', 'celldblclick']);
649 * autoScroll is never valid for all classes which extend TablePanel.
651 setAutoScroll: Ext.emptyFn,
653 // This method hijacks Ext.view.Table's el scroll method.
654 // This enables us to keep the virtualized scrollbars in sync
655 // with the view. It currently does NOT support animation.
656 elScroll: function(direction, distance, animate) {
660 if (direction === "up" || direction === "left") {
661 distance = -distance;
664 if (direction === "down" || direction === "up") {
665 scroller = me.getVerticalScroller();
666 scroller.scrollByDeltaY(distance);
668 scroller = me.getHorizontalScroller();
669 scroller.scrollByDeltaX(distance);
673 afterLayout: function() {
674 this.callParent(arguments);
681 * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
682 * This is so we can add the View only after the initial size is known. This method is buffered 30ms.
684 injectView: function() {
685 if (!this.hasView && !this.collapsed) {
692 // hijack the view el's scroll method
693 view.el.scroll = Ext.Function.bind(me.elScroll, me);
694 // We use to listen to document.body wheel events, but that's a
695 // little much. We scope just to the view now.
697 mousewheel: me.onMouseWheel,
703 afterExpand: function() {
704 this.callParent(arguments);
712 * Process UI events from the view. Propagate them to whatever internal Components need to process them
713 * @param {String} type Event type, eg 'click'
714 * @param {TableView} view TableView Component
715 * @param {HtmlElement} cell Cell HtmlElement the event took place within
716 * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
717 * @param {Number} cellIndex Cell index within the row
718 * @param {EventObject} e Original event
720 processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
724 if (cellIndex !== -1) {
725 header = me.headerCt.getGridColumns()[cellIndex];
726 return header.processEvent.apply(header, arguments);
731 * Request a recalculation of scrollbars and put them in if they are needed.
733 determineScrollbars: function() {
741 if (!me.collapsed && me.view && me.view.el) {
742 viewElDom = me.view.el.dom;
743 //centerScrollWidth = viewElDom.scrollWidth;
744 centerScrollWidth = me.headerCt.getFullWidth();
746 * clientWidth often returns 0 in IE resulting in an
747 * infinity result, here we use offsetWidth bc there are
748 * no possible scrollbars and we don't care about margins
750 centerClientWidth = viewElDom.offsetWidth;
751 if (me.verticalScroller && me.verticalScroller.el) {
752 scrollHeight = me.verticalScroller.getSizeCalculation().height;
754 scrollHeight = viewElDom.scrollHeight;
757 clientHeight = viewElDom.clientHeight;
759 me.suspendLayout = true;
760 me.scrollbarChanged = false;
761 if (!me.collapsed && scrollHeight > clientHeight) {
762 me.showVerticalScroller();
764 me.hideVerticalScroller();
767 if (!me.collapsed && centerScrollWidth > (centerClientWidth + Ext.getScrollBarWidth() - 2)) {
768 me.showHorizontalScroller();
770 me.hideHorizontalScroller();
772 me.suspendLayout = false;
773 if (me.scrollbarChanged) {
774 me.doComponentLayout();
779 onHeaderResize: function() {
780 if (this.view && this.view.rendered) {
781 this.determineScrollbars();
782 this.invalidateScroller();
787 * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
789 hideHorizontalScroller: function() {
792 if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
793 me.scrollbarChanged = true;
794 me.verticalScroller.offsets.bottom = 0;
795 me.removeDocked(me.horizontalScroller, false);
796 me.removeCls(me.horizontalScrollerPresentCls);
797 me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
803 * Show the horizontalScroller and add the horizontalScrollerPresentCls.
805 showHorizontalScroller: function() {
808 if (me.verticalScroller) {
809 me.verticalScroller.offsets.bottom = Ext.getScrollBarWidth() - 2;
811 if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
812 me.scrollbarChanged = true;
813 me.addDocked(me.horizontalScroller);
814 me.addCls(me.horizontalScrollerPresentCls);
815 me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
820 * Hide the verticalScroller and remove the verticalScrollerPresentCls.
822 hideVerticalScroller: function() {
824 headerCt = me.headerCt;
826 // only trigger a layout when reserveOffset is changing
827 if (headerCt && headerCt.layout.reserveOffset) {
828 headerCt.layout.reserveOffset = false;
831 if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
832 me.scrollbarChanged = true;
833 me.removeDocked(me.verticalScroller, false);
834 me.removeCls(me.verticalScrollerPresentCls);
835 me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
840 * Show the verticalScroller and add the verticalScrollerPresentCls.
842 showVerticalScroller: function() {
844 headerCt = me.headerCt;
846 // only trigger a layout when reserveOffset is changing
847 if (headerCt && !headerCt.layout.reserveOffset) {
848 headerCt.layout.reserveOffset = true;
851 if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
852 me.scrollbarChanged = true;
853 me.addDocked(me.verticalScroller);
854 me.addCls(me.verticalScrollerPresentCls);
855 me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
860 * Invalides scrollers that are present and forces a recalculation.
861 * (Not related to showing/hiding the scrollers)
863 invalidateScroller: function() {
865 vScroll = me.verticalScroller,
866 hScroll = me.horizontalScroller;
869 vScroll.invalidate();
872 hScroll.invalidate();
876 // refresh the view when a header moves
877 onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
881 // Section onHeaderHide is invoked after view.
882 onHeaderHide: function(headerCt, header) {
883 this.invalidateScroller();
886 onHeaderShow: function(headerCt, header) {
887 this.invalidateScroller();
890 getVerticalScroller: function() {
891 return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
894 getHorizontalScroller: function() {
895 return this.getScrollerOwner().down('gridscroller[dock=bottom]');
898 onMouseWheel: function(e) {
900 browserEvent = e.browserEvent,
901 vertScroller = me.getVerticalScroller(),
902 horizScroller = me.getHorizontalScroller(),
903 scrollDelta = me.scrollDelta,
905 vertScrollerEl, horizScrollerEl,
906 vertScrollerElDom, horizScrollerElDom,
907 horizontalCanScrollLeft, horizontalCanScrollRight,
908 verticalCanScrollDown, verticalCanScrollUp;
910 // calculate whether or not both scrollbars can scroll right/left and up/down
912 horizScrollerEl = horizScroller.el;
913 if (horizScrollerEl) {
914 horizScrollerElDom = horizScrollerEl.dom;
915 horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
916 horizontalCanScrollLeft = horizScrollerElDom.scrollLeft !== 0;
920 vertScrollerEl = vertScroller.el;
921 if (vertScrollerEl) {
922 vertScrollerElDom = vertScrollerEl.dom;
923 verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
924 verticalCanScrollUp = vertScrollerElDom.scrollTop !== 0;
928 // Webkit Horizontal Axis
929 if (browserEvent.wheelDeltaX || browserEvent.wheelDeltaY) {
930 deltaX = -browserEvent.wheelDeltaX / 120 * scrollDelta / 3;
931 deltaY = -browserEvent.wheelDeltaY / 120 * scrollDelta / 3;
933 // Gecko Horizontal Axis
934 if (browserEvent.axis && browserEvent.axis === 1) {
935 deltaX = -(scrollDelta * e.getWheelDelta()) / 3;
937 deltaY = -(scrollDelta * e.getWheelDelta() / 3);
942 if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
944 horizScroller.scrollByDeltaX(deltaX);
948 if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
950 vertScroller.scrollByDeltaY(deltaY);
957 * Determine and invalidate scrollers on view refresh
959 onViewRefresh: function() {
961 this.syncCellHeight();
963 this.determineScrollbars();
964 if (this.invalidateScrollerOnRefresh) {
965 this.invalidateScroller();
969 onViewItemUpdate: function(record, index, tr) {
971 this.syncCellHeight([tr]);
975 // BrowserBug: IE will not stretch the td to fit the height of the entire
976 // tr, so manually sync cellheights on refresh and when an item has been
978 syncCellHeight: function(trs) {
987 cellClsSelector = ('.' + Ext.baseCSSPrefix + 'grid-cell');
989 trs = trs || me.view.getNodes();
993 for (; i < trsLn; i++) {
995 tds = Ext.fly(tr).query(cellClsSelector);
998 for (j = 0; j < tdsLn; j++) {
1000 cellHeights.push(td.clientHeight);
1002 rowHeights.push(Ext.Array.max(cellHeights));
1006 for (i = 0; i < trsLn; i++) {
1008 tdsLn = tr.childNodes.length;
1009 for (j = 0; j < tdsLn; j++) {
1010 td = Ext.fly(tr.childNodes[j]);
1011 if (rowHeights[i]) {
1012 if (td.is(cellClsSelector)) {
1013 td.setHeight(rowHeights[i]);
1015 td.down(cellClsSelector).setHeight(rowHeights[i]);
1024 * Sets the scrollTop of the TablePanel.
1025 * @param {Number} deltaY
1027 setScrollTop: function(top) {
1029 rootCmp = me.getScrollerOwner(),
1030 verticalScroller = me.getVerticalScroller();
1032 rootCmp.virtualScrollTop = top;
1033 if (verticalScroller) {
1034 verticalScroller.setScrollTop(top);
1039 getScrollerOwner: function() {
1041 if (!this.scrollerOwner) {
1042 rootCmp = this.up('[scrollerOwner]');
1048 * Scrolls the TablePanel by deltaY
1049 * @param {Number} deltaY
1051 scrollByDeltaY: function(deltaY) {
1052 var rootCmp = this.getScrollerOwner(),
1054 scrollerRight = rootCmp.down('gridscroller[dock=' + this.verticalScrollDock + ']');
1055 if (scrollerRight) {
1056 scrollerRight.scrollByDeltaY(deltaY);
1062 * Scrolls the TablePanel by deltaX
1063 * @param {Number} deltaY
1065 scrollByDeltaX: function(deltaX) {
1066 this.horizontalScroller.scrollByDeltaX(deltaX);
1070 * Get left hand side marker for header resizing.
1073 getLhsMarker: function() {
1076 if (!me.lhsMarker) {
1077 me.lhsMarker = Ext.core.DomHelper.append(me.el, {
1078 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1081 return me.lhsMarker;
1085 * Get right hand side marker for header resizing.
1088 getRhsMarker: function() {
1091 if (!me.rhsMarker) {
1092 me.rhsMarker = Ext.core.DomHelper.append(me.el, {
1093 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1096 return me.rhsMarker;
1100 * Returns the selection model being used and creates it via the configuration
1101 * if it has not been created already.
1102 * @return {Ext.selection.Model} selModel
1104 getSelectionModel: function(){
1105 if (!this.selModel) {
1109 var mode = 'SINGLE',
1111 if (this.simpleSelect) {
1113 } else if (this.multiSelect) {
1117 Ext.applyIf(this.selModel, {
1118 allowDeselect: this.allowDeselect,
1122 if (!this.selModel.events) {
1123 type = this.selModel.selType || this.selType;
1124 this.selModel = Ext.create('selection.' + type, this.selModel);
1127 if (!this.selModel.hasRelaySetup) {
1128 this.relayEvents(this.selModel, ['selectionchange', 'select', 'deselect']);
1129 this.selModel.hasRelaySetup = true;
1132 // lock the selection model if user
1133 // has disabled selection
1134 if (this.disableSelection) {
1135 this.selModel.locked = true;
1137 return this.selModel;
1140 onVerticalScroll: function(event, target) {
1141 var owner = this.getScrollerOwner(),
1142 items = owner.query('tableview'),
1146 for (; i < len; i++) {
1147 items[i].el.dom.scrollTop = target.scrollTop;
1151 onHorizontalScroll: function(event, target) {
1152 var owner = this.getScrollerOwner(),
1153 items = owner.query('tableview'),
1162 center = items[1] || items[0];
1163 centerEl = center.el.dom;
1164 centerScrollWidth = centerEl.scrollWidth;
1165 centerClientWidth = centerEl.offsetWidth;
1166 width = this.horizontalScroller.getWidth();
1168 centerEl.scrollLeft = target.scrollLeft;
1169 this.headerCt.el.dom.scrollLeft = target.scrollLeft;
1172 // template method meant to be overriden
1173 onStoreLoad: Ext.emptyFn,
1175 getEditorParent: function() {
1179 bindStore: function(store) {
1182 me.getView().bindStore(store);
1185 reconfigure: function(store, columns) {
1189 me.reconfigureLockable(store, columns);
1194 me.headerCt.removeAll();
1195 me.headerCt.add(columns);
1198 store = Ext.StoreManager.lookup(store);
1199 me.bindStore(store);
1201 me.getView().refresh();
1205 afterComponentLayout: function() {
1207 me.callParent(arguments);
1208 me.determineScrollbars();
1209 me.invalidateScroller();