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 * @class Ext.panel.Table
17 * @extends Ext.panel.Panel
18 * @author Nicolas Ferrero
19 * TablePanel is the basis of both TreePanel and GridPanel.
21 * TablePanel aggregates:
27 * - Ext.grid.header.Container
30 Ext.define('Ext.panel.Table', {
31 extend: 'Ext.panel.Panel',
33 alias: 'widget.tablepanel',
36 'Ext.selection.RowModel',
38 'Ext.grid.header.Container',
42 cls: Ext.baseCSSPrefix + 'grid',
43 extraBodyCls: Ext.baseCSSPrefix + 'grid-body',
47 * Boolean to indicate that a view has been injected into the panel.
52 // each panel should dictate what viewType and selType to use
57 * @cfg {Number} scrollDelta
58 * Number of pixels to scroll when scrolling with mousewheel.
64 * @cfg {String/Boolean} scroll
65 * Valid values are 'both', 'horizontal' or 'vertical'. true implies 'both'. false implies 'none'.
71 * @cfg {Array} columns
72 * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this grid. Each
73 * column definition provides the header text for the column, and a definition of where the data for that column comes from.
77 * @cfg {Boolean} forceFit
78 * 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
79 * a specific width, or flex. Then they are all proportionally changed in width so that the entire content width is used..
83 * @cfg {Boolean} hideHeaders
84 * Specify as <code>true</code> to hide the headers.
88 * @cfg {Boolean} deferRowRender <P>Defaults to <code>true</code> to enable deferred row rendering.</p>
89 * <p>This allows the GridView to execute a refresh quickly, with the expensive update of the row
90 * structure deferred so that layouts with GridPanels appear, and lay out more quickly.</p>
94 * @cfg {Boolean} sortableColumns
95 * Defaults to <code>true</code>. Set to <code>false</code> to disable column sorting via clicking the
96 * header and via the Sorting menu items.
98 sortableColumns: true,
100 verticalScrollDock: 'right',
101 verticalScrollerType: 'gridscroller',
103 horizontalScrollerPresentCls: Ext.baseCSSPrefix + 'horizontal-scroller-present',
104 verticalScrollerPresentCls: Ext.baseCSSPrefix + 'vertical-scroller-present',
106 // private property used to determine where to go down to find views
107 // this is here to support locking.
110 invalidateScrollerOnRefresh: true,
113 * @cfg {Boolean} enableColumnMove
114 * Defaults to <code>true</code>. Set to <code>false</code> to disable column dragging within this grid.
116 enableColumnMove: true,
119 * @cfg {Boolean} enableColumnResize
120 * Defaults to <code>true</code>. Set to <code>false</code> to disable column resizing within this grid.
122 enableColumnResize: true,
125 * @cfg {Boolean} enableColumnHide
126 * Defaults to <code>true</code>. Set to <code>false</code> to disable column hiding within this grid.
128 enableColumnHide: true,
130 initComponent: function() {
132 if (!this.viewType) {
133 Ext.Error.raise("You must specify a viewType config.");
136 Ext.Error.raise("You must specify a store config");
139 Ext.Error.raise("The headers config is not supported. Please specify columns instead.");
147 headerCtCfg = me.columns || me.colModel,
152 // We cannot buffer this because that will wait for the 30msec from afterLayout (or what
153 // ever event triggers it) and we may be in the middle of an animation; that is a bad
154 // time to injectView because it causes a layout (by calling add on the container). A
155 // throttled func will be called immediately on first call and then block subsequent
156 // (rapid fire) calls for 30msec before allowing another call to go through. Similar
157 // results, but the action moves from the trailing edge of the interval to the leading
159 me.injectView = Ext.Function.createThrottled(me.injectView, 30, me);
161 if (me.hideHeaders) {
165 // The columns/colModel config may be either a fully instantiated HeaderContainer, or an array of Column definitions, or a config object of a HeaderContainer
166 // Either way, we extract a columns property referencing an array of Column definitions.
167 if (headerCtCfg instanceof Ext.grid.header.Container) {
168 me.headerCt = headerCtCfg;
169 me.headerCt.border = border;
170 me.columns = me.headerCt.items.items;
172 if (Ext.isArray(headerCtCfg)) {
178 Ext.apply(headerCtCfg, {
179 forceFit: me.forceFit,
180 sortable: me.sortableColumns,
181 enableColumnMove: me.enableColumnMove,
182 enableColumnResize: me.enableColumnResize,
183 enableColumnHide: me.enableColumnHide,
186 me.columns = headerCtCfg.items;
188 // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a
189 // special view will be injected by the Ext.grid.Lockable mixin, so no processing of .
190 if (Ext.ComponentQuery.query('{locked !== undefined}{processed != true}', me.columns).length) {
191 me.self.mixin('lockable', Ext.grid.Lockable);
196 me.store = Ext.data.StoreManager.lookup(me.store);
200 * Fires after a reconfigure
201 * @param {Ext.panel.Table} this
205 * @event scrollerhide
206 * Fires when a scroller is hidden
207 * @param {Ext.grid.Scroller} scroller
208 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
212 * @event scrollershow
213 * Fires when a scroller is shown
214 * @param {Ext.grid.Scroller} scroller
215 * @param {String} orientation Orientation, can be 'vertical' or 'horizontal'
220 me.bodyCls = me.bodyCls || '';
221 me.bodyCls += (' ' + me.extraBodyCls);
223 // autoScroll is not a valid configuration
224 delete me.autoScroll;
226 // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)
227 // 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.
230 // If we were not configured with a ready-made headerCt (either by direct config with a headerCt property, or by passing
231 // a HeaderContainer instance as the 'columns' property, then go ahead and create one from the config object created above.
233 me.headerCt = Ext.create('Ext.grid.header.Container', headerCtCfg);
236 // Extract the array of Column objects
237 me.columns = me.headerCt.items.items;
239 if (me.hideHeaders) {
240 me.headerCt.height = 0;
241 me.headerCt.border = false;
242 me.headerCt.addCls(Ext.baseCSSPrefix + 'grid-header-ct-hidden');
243 me.addCls(Ext.baseCSSPrefix + 'grid-header-hidden');
244 // IE Quirks Mode fix
245 // If hidden configuration option was used, several layout calculations will be bypassed.
246 if (Ext.isIEQuirks) {
247 me.headerCt.style = {
254 if (scroll === true || scroll === 'both') {
255 vertical = horizontal = true;
256 } else if (scroll === 'horizontal') {
258 } else if (scroll === 'vertical') {
260 // All other values become 'none' or false.
262 me.headerCt.availableSpaceOffset = 0;
266 me.verticalScroller = Ext.ComponentManager.create(me.initVerticalScroller());
267 me.mon(me.verticalScroller, {
268 bodyscroll: me.onVerticalScroll,
274 me.horizontalScroller = Ext.ComponentManager.create(me.initHorizontalScroller());
275 me.mon(me.horizontalScroller, {
276 bodyscroll: me.onHorizontalScroll,
281 me.headerCt.on('columnresize', me.onHeaderResize, me);
282 me.relayEvents(me.headerCt, ['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange']);
283 me.features = me.features || [];
284 me.dockedItems = me.dockedItems || [];
285 me.dockedItems.unshift(me.headerCt);
286 me.viewConfig = me.viewConfig || {};
287 me.viewConfig.invalidateScrollerOnRefresh = me.invalidateScrollerOnRefresh;
289 // AbstractDataView will look up a Store configured as an object
290 // getView converts viewConfig into a View instance
295 load: me.onStoreLoad,
299 refresh: me.onViewRefresh,
302 this.relayEvents(view, [
304 * @event beforeitemmousedown
305 * Fires before the mousedown event on an item is processed. Returns false to cancel the default action.
306 * @param {Ext.view.View} this
307 * @param {Ext.data.Model} record The record that belongs to the item
308 * @param {HTMLElement} item The item's element
309 * @param {Number} index The item's index
310 * @param {Ext.EventObject} e The raw event object
312 'beforeitemmousedown',
314 * @event beforeitemmouseup
315 * Fires before the mouseup event on an item is processed. Returns false to cancel the default action.
316 * @param {Ext.view.View} this
317 * @param {Ext.data.Model} record The record that belongs to the item
318 * @param {HTMLElement} item The item's element
319 * @param {Number} index The item's index
320 * @param {Ext.EventObject} e The raw event object
324 * @event beforeitemmouseenter
325 * Fires before the mouseenter event on an item is processed. Returns false to cancel the default action.
326 * @param {Ext.view.View} this
327 * @param {Ext.data.Model} record The record that belongs to the item
328 * @param {HTMLElement} item The item's element
329 * @param {Number} index The item's index
330 * @param {Ext.EventObject} e The raw event object
332 'beforeitemmouseenter',
334 * @event beforeitemmouseleave
335 * Fires before the mouseleave event on an item is processed. Returns false to cancel the default action.
336 * @param {Ext.view.View} this
337 * @param {Ext.data.Model} record The record that belongs to the item
338 * @param {HTMLElement} item The item's element
339 * @param {Number} index The item's index
340 * @param {Ext.EventObject} e The raw event object
342 'beforeitemmouseleave',
344 * @event beforeitemclick
345 * Fires before the click event on an item is processed. Returns false to cancel the default action.
346 * @param {Ext.view.View} this
347 * @param {Ext.data.Model} record The record that belongs to the item
348 * @param {HTMLElement} item The item's element
349 * @param {Number} index The item's index
350 * @param {Ext.EventObject} e The raw event object
354 * @event beforeitemdblclick
355 * Fires before the dblclick event on an item is processed. Returns false to cancel the default action.
356 * @param {Ext.view.View} this
357 * @param {Ext.data.Model} record The record that belongs to the item
358 * @param {HTMLElement} item The item's element
359 * @param {Number} index The item's index
360 * @param {Ext.EventObject} e The raw event object
362 'beforeitemdblclick',
364 * @event beforeitemcontextmenu
365 * Fires before the contextmenu event on an item is processed. Returns false to cancel the default action.
366 * @param {Ext.view.View} this
367 * @param {Ext.data.Model} record The record that belongs to the item
368 * @param {HTMLElement} item The item's element
369 * @param {Number} index The item's index
370 * @param {Ext.EventObject} e The raw event object
372 'beforeitemcontextmenu',
374 * @event itemmousedown
375 * Fires when there is a mouse down on an item
376 * @param {Ext.view.View} this
377 * @param {Ext.data.Model} record The record that belongs to the item
378 * @param {HTMLElement} item The item's element
379 * @param {Number} index The item's index
380 * @param {Ext.EventObject} e The raw event object
385 * Fires when there is a mouse up on an item
386 * @param {Ext.view.View} this
387 * @param {Ext.data.Model} record The record that belongs to the item
388 * @param {HTMLElement} item The item's element
389 * @param {Number} index The item's index
390 * @param {Ext.EventObject} e The raw event object
394 * @event itemmouseenter
395 * Fires when the mouse enters an item.
396 * @param {Ext.view.View} this
397 * @param {Ext.data.Model} record The record that belongs to the item
398 * @param {HTMLElement} item The item's element
399 * @param {Number} index The item's index
400 * @param {Ext.EventObject} e The raw event object
404 * @event itemmouseleave
405 * Fires when the mouse leaves an item.
406 * @param {Ext.view.View} this
407 * @param {Ext.data.Model} record The record that belongs to the item
408 * @param {HTMLElement} item The item's element
409 * @param {Number} index The item's index
410 * @param {Ext.EventObject} e The raw event object
415 * Fires when an item is clicked.
416 * @param {Ext.view.View} this
417 * @param {Ext.data.Model} record The record that belongs to the item
418 * @param {HTMLElement} item The item's element
419 * @param {Number} index The item's index
420 * @param {Ext.EventObject} e The raw event object
424 * @event itemdblclick
425 * Fires when an item is double clicked.
426 * @param {Ext.view.View} this
427 * @param {Ext.data.Model} record The record that belongs to the item
428 * @param {HTMLElement} item The item's element
429 * @param {Number} index The item's index
430 * @param {Ext.EventObject} e The raw event object
434 * @event itemcontextmenu
435 * Fires when an item is right clicked.
436 * @param {Ext.view.View} this
437 * @param {Ext.data.Model} record The record that belongs to the item
438 * @param {HTMLElement} item The item's element
439 * @param {Number} index The item's index
440 * @param {Ext.EventObject} e The raw event object
444 * @event beforecontainermousedown
445 * Fires before the mousedown event on the container is processed. Returns false to cancel the default action.
446 * @param {Ext.view.View} this
447 * @param {Ext.EventObject} e The raw event object
449 'beforecontainermousedown',
451 * @event beforecontainermouseup
452 * Fires before the mouseup event on the container is processed. Returns false to cancel the default action.
453 * @param {Ext.view.View} this
454 * @param {Ext.EventObject} e The raw event object
456 'beforecontainermouseup',
458 * @event beforecontainermouseover
459 * Fires before the mouseover event on the container is processed. Returns false to cancel the default action.
460 * @param {Ext.view.View} this
461 * @param {Ext.EventObject} e The raw event object
463 'beforecontainermouseover',
465 * @event beforecontainermouseout
466 * Fires before the mouseout event on the container is processed. Returns false to cancel the default action.
467 * @param {Ext.view.View} this
468 * @param {Ext.EventObject} e The raw event object
470 'beforecontainermouseout',
472 * @event beforecontainerclick
473 * Fires before the click event on the container is processed. Returns false to cancel the default action.
474 * @param {Ext.view.View} this
475 * @param {Ext.EventObject} e The raw event object
477 'beforecontainerclick',
479 * @event beforecontainerdblclick
480 * Fires before the dblclick event on the container is processed. Returns false to cancel the default action.
481 * @param {Ext.view.View} this
482 * @param {Ext.EventObject} e The raw event object
484 'beforecontainerdblclick',
486 * @event beforecontainercontextmenu
487 * Fires before the contextmenu event on the container is processed. Returns false to cancel the default action.
488 * @param {Ext.view.View} this
489 * @param {Ext.EventObject} e The raw event object
491 'beforecontainercontextmenu',
493 * @event containermouseup
494 * Fires when there is a mouse up on the container
495 * @param {Ext.view.View} this
496 * @param {Ext.EventObject} e The raw event object
500 * @event containermouseover
501 * Fires when you move the mouse over the container.
502 * @param {Ext.view.View} this
503 * @param {Ext.EventObject} e The raw event object
505 'containermouseover',
507 * @event containermouseout
508 * Fires when you move the mouse out of the container.
509 * @param {Ext.view.View} this
510 * @param {Ext.EventObject} e The raw event object
514 * @event containerclick
515 * Fires when the container is clicked.
516 * @param {Ext.view.View} this
517 * @param {Ext.EventObject} e The raw event object
521 * @event containerdblclick
522 * Fires when the container is double clicked.
523 * @param {Ext.view.View} this
524 * @param {Ext.EventObject} e The raw event object
528 * @event containercontextmenu
529 * Fires when the container is right clicked.
530 * @param {Ext.view.View} this
531 * @param {Ext.EventObject} e The raw event object
533 'containercontextmenu',
536 * @event selectionchange
537 * Fires when the selected nodes change. Relayed event from the underlying selection model.
538 * @param {Ext.view.View} this
539 * @param {Array} selections Array of the selected nodes
543 * @event beforeselect
544 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
545 * @param {Ext.view.View} this
546 * @param {HTMLElement} node The node to be selected
547 * @param {Array} selections Array of currently selected nodes
553 me.callParent(arguments);
557 initStateEvents: function(){
558 var events = this.stateEvents;
559 // push on stateEvents if they don't exist
560 Ext.each(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange'], function(event){
561 if (Ext.Array.indexOf(events, event)) {
569 * Returns the horizontal scroller config.
571 initHorizontalScroller: function () {
574 xtype: 'gridscroller',
584 * Returns the vertical scroller config.
586 initVerticalScroller: function () {
588 ret = me.verticalScroller || {};
591 xtype: me.verticalScrollerType,
592 dock: me.verticalScrollDock,
599 getState: function(){
600 var state = this.callParent(),
601 sorter = this.store.sorters.first(),
602 headers = this.headerCt.items.items,
604 len = headers.length,
608 for (; i < len; i++) {
614 // We only store state which has changed from the initial state.
615 // So that current software settings do not override future software settings.
616 // Only user-changed state should be saved.
617 if (header.hidden !== (header.initialConfig.hidden||header.self.prototype.hidden)) {
618 state.columns[i].hidden = header.hidden;
620 if (header.sortable !== header.initialConfig.sortable) {
621 state.columns[i].sortable = header.sortable;
624 if (header.flex !== header.initialConfig.flex) {
625 state.columns[i].flex = header.flex;
628 if (header.width !== header.initialConfig.width) {
629 state.columns[i].width = header.width;
636 property: sorter.property,
637 direction: sorter.direction
643 applyState: function(state) {
644 var headers = state.columns,
645 length = headers ? headers.length : 0,
646 headerCt = this.headerCt,
647 items = headerCt.items,
655 headerCt.suspendLayout = true;
657 // Ensure superclass has applied *its* state.
658 // AbstractComponent saves dimensions (and anchor/flex) plus collapsed state.
659 this.callParent(arguments);
661 for (; i < length; ++i) {
662 headerState = headers[i];
663 header = headerCt.down('gridcolumn[headerId=' + headerState.id + ']');
664 index = items.indexOf(header);
666 headerCt.moveHeader(index, i);
669 // Only state properties which were saved should be restored.
670 // (Only user-changed properties were saved by getState)
671 if (Ext.isDefined(headerState.hidden)) {
672 header.hidden = headerState.hidden;
674 if (Ext.isDefined(headerState.sortable)) {
675 header.sortable = headerState.sortable;
677 if (Ext.isDefined(headerState.flex)) {
679 header.flex = headerState.flex;
680 } else if (Ext.isDefined(headerState.width)) {
682 header.minWidth = headerState.width;
683 if (header.rendered) {
684 header.setWidth(headerState.width);
686 header.width = headerState.width;
690 headerCt.suspendLayout = false;
692 // After setting width and flexes while layout is suspended, column Container's Container layout needs running.
696 if (store.remoteSort) {
697 store.sorters.add(Ext.create('Ext.util.Sorter', {
698 property: sorter.property,
699 direction: sorter.direction
703 store.sort(sorter.property, sorter.direction);
709 * Returns the store associated with this Panel.
710 * @return {Ext.data.Store} The store
712 getStore: function(){
717 * Gets the view for this panel.
718 * @return {Ext.view.Table}
720 getView: function() {
725 sm = me.getSelectionModel();
726 me.view = me.createComponent(Ext.apply({}, me.viewConfig, {
727 deferRowRender: me.deferRowRender,
730 headerCt: me.headerCt,
732 features: me.features,
736 uievent: me.processEvent,
740 me.headerCt.view = me.view;
741 me.relayEvents(me.view, ['cellclick', 'celldblclick']);
749 * autoScroll is never valid for all classes which extend TablePanel.
751 setAutoScroll: Ext.emptyFn,
753 // This method hijacks Ext.view.Table's el scroll method.
754 // This enables us to keep the virtualized scrollbars in sync
755 // with the view. It currently does NOT support animation.
756 elScroll: function(direction, distance, animate) {
760 if (direction === "up" || direction === "left") {
761 distance = -distance;
764 if (direction === "down" || direction === "up") {
765 scroller = me.getVerticalScroller();
766 scroller.scrollByDeltaY(distance);
768 scroller = me.getHorizontalScroller();
769 scroller.scrollByDeltaX(distance);
775 * Called after this Component has achieved its correct initial size, after all layouts have done their thing.
776 * This is so we can add the View only after the initial size is known. This method is throttled 30ms.
778 injectView: function() {
779 if (!this.hasView && !this.collapsed) {
786 function viewReady () {
787 // hijack the view el's scroll method
788 view.el.scroll = Ext.Function.bind(me.elScroll, me);
789 // We use to listen to document.body wheel events, but that's a
790 // little much. We scope just to the view now.
792 mousewheel: me.onMouseWheel,
796 me.doComponentLayout();
804 afterrender: viewReady,
811 afterExpand: function() {
812 // TODO - this is *not* called when part of an accordion!
813 this.callParent(arguments);
821 * Process UI events from the view. Propagate them to whatever internal Components need to process them
822 * @param {String} type Event type, eg 'click'
823 * @param {TableView} view TableView Component
824 * @param {HtmlElement} cell Cell HtmlElement the event took place within
825 * @param {Number} recordIndex Index of the associated Store Model (-1 if none)
826 * @param {Number} cellIndex Cell index within the row
827 * @param {EventObject} e Original event
829 processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
833 if (cellIndex !== -1) {
834 header = me.headerCt.getGridColumns()[cellIndex];
835 return header.processEvent.apply(header, arguments);
840 * Request a recalculation of scrollbars and put them in if they are needed.
842 determineScrollbars: function() {
850 verticalScroller = me.verticalScroller,
851 horizontalScroller = me.horizontalScroller,
852 curScrollbars = (verticalScroller && verticalScroller.ownerCt === me ? 1 : 0) |
853 (horizontalScroller && horizontalScroller.ownerCt === me ? 2 : 0),
854 reqScrollbars = 0; // 1 = vertical, 2 = horizontal, 3 = both
856 // If we are not collapsed, and the view has been rendered AND filled, then we can determine scrollbars
857 if (!me.collapsed && me.view && me.view.el && me.view.el.dom.firstChild) {
859 // Calculate maximum, *scrollbarless* space which the view has available.
860 // It will be the Fit Layout's calculated size, plus the widths of any currently shown scrollbars
861 box = me.layout.getLayoutTargetSize();
862 clientWidth = box.width + ((curScrollbars & 1) ? verticalScroller.width : 0);
863 clientHeight = box.height + ((curScrollbars & 2) ? horizontalScroller.height : 0);
865 // Calculate the width of the scrolling block
866 // There will never be a horizontal scrollbar if all columns are flexed.
868 scrollWidth = (me.headerCt.query('[flex]').length && !me.headerCt.layout.tooNarrow) ? 0 : me.headerCt.getFullWidth();
870 // Calculate the height of the scrolling block
871 if (verticalScroller && verticalScroller.el) {
872 scrollHeight = verticalScroller.getSizeCalculation().height;
874 tableEl = me.view.el.child('table', true);
875 scrollHeight = tableEl ? tableEl.offsetHeight : 0;
879 // Definitely need a vertical scrollbar
880 if (scrollHeight > clientHeight) {
883 // But if scrollable block width goes into the zone required by the vertical scrollbar, we'll also need a horizontal
884 if (horizontalScroller && ((clientWidth - scrollWidth) < verticalScroller.width)) {
889 // View height fits. But we stil may need a horizontal scrollbar, and this might necessitate a vertical one.
892 // Definitely need a horizontal scrollbar
893 if (scrollWidth > clientWidth) {
896 // But if scrollable block height goes into the zone required by the horizontal scrollbar, we'll also need a vertical
897 if (verticalScroller && ((clientHeight - scrollHeight) < horizontalScroller.height)) {
903 // If scrollbar requirements have changed, change 'em...
904 if (reqScrollbars !== curScrollbars) {
906 // Suspend component layout while we add/remove the docked scrollers
907 me.suspendLayout = true;
908 if (reqScrollbars & 1) {
909 me.showVerticalScroller();
911 me.hideVerticalScroller();
913 if (reqScrollbars & 2) {
914 me.showHorizontalScroller();
916 me.hideHorizontalScroller();
918 me.suspendLayout = false;
920 // After docked scrollers are correctly configured, lay out the Component.
921 // Set a flag so that afterComponentLayout does not recurse back into here.
922 me.changingScrollBars = true;
923 me.doComponentLayout(me.getWidth(), me.getHeight(), false, me.ownerCt);
924 me.changingScrollBars = false;
929 afterComponentLayout: function() {
931 me.callParent(arguments);
933 // Insert the View the first time the Panel has a Component layout performed.
937 if (!me.changingScrollBars) {
938 me.determineScrollbars();
940 me.invalidateScroller();
943 onHeaderResize: function() {
944 if (this.view && this.view.rendered) {
945 this.determineScrollbars();
946 this.invalidateScroller();
950 afterCollapse: function() {
952 if (me.verticalScroller) {
953 me.verticalScroller.saveScrollPos();
955 if (me.horizontalScroller) {
956 me.horizontalScroller.saveScrollPos();
958 me.callParent(arguments);
961 afterExpand: function() {
963 me.callParent(arguments);
964 if (me.verticalScroller) {
965 me.verticalScroller.restoreScrollPos();
967 if (me.horizontalScroller) {
968 me.horizontalScroller.restoreScrollPos();
973 * Hide the verticalScroller and remove the horizontalScrollerPresentCls.
975 hideHorizontalScroller: function() {
978 if (me.horizontalScroller && me.horizontalScroller.ownerCt === me) {
979 me.verticalScroller.setReservedSpace(0);
980 me.removeDocked(me.horizontalScroller, false);
981 me.removeCls(me.horizontalScrollerPresentCls);
982 me.fireEvent('scrollerhide', me.horizontalScroller, 'horizontal');
988 * Show the horizontalScroller and add the horizontalScrollerPresentCls.
990 showHorizontalScroller: function() {
993 if (me.verticalScroller) {
994 me.verticalScroller.setReservedSpace(Ext.getScrollbarSize().height - 1);
996 if (me.horizontalScroller && me.horizontalScroller.ownerCt !== me) {
997 me.addDocked(me.horizontalScroller);
998 me.addCls(me.horizontalScrollerPresentCls);
999 me.fireEvent('scrollershow', me.horizontalScroller, 'horizontal');
1004 * Hide the verticalScroller and remove the verticalScrollerPresentCls.
1006 hideVerticalScroller: function() {
1009 me.setHeaderReserveOffset(false);
1010 if (me.verticalScroller && me.verticalScroller.ownerCt === me) {
1011 me.removeDocked(me.verticalScroller, false);
1012 me.removeCls(me.verticalScrollerPresentCls);
1013 me.fireEvent('scrollerhide', me.verticalScroller, 'vertical');
1018 * Show the verticalScroller and add the verticalScrollerPresentCls.
1020 showVerticalScroller: function() {
1023 me.setHeaderReserveOffset(true);
1024 if (me.verticalScroller && me.verticalScroller.ownerCt !== me) {
1025 me.addDocked(me.verticalScroller);
1026 me.addCls(me.verticalScrollerPresentCls);
1027 me.fireEvent('scrollershow', me.verticalScroller, 'vertical');
1031 setHeaderReserveOffset: function (reserveOffset) {
1032 var headerCt = this.headerCt,
1033 layout = headerCt.layout;
1035 // only trigger a layout when reserveOffset is changing
1036 if (layout && layout.reserveOffset !== reserveOffset) {
1037 layout.reserveOffset = reserveOffset;
1038 headerCt.doLayout();
1043 * Invalides scrollers that are present and forces a recalculation.
1044 * (Not related to showing/hiding the scrollers)
1046 invalidateScroller: function() {
1048 vScroll = me.verticalScroller,
1049 hScroll = me.horizontalScroller;
1052 vScroll.invalidate();
1055 hScroll.invalidate();
1059 // refresh the view when a header moves
1060 onHeaderMove: function(headerCt, header, fromIdx, toIdx) {
1061 this.view.refresh();
1064 // Section onHeaderHide is invoked after view.
1065 onHeaderHide: function(headerCt, header) {
1066 this.invalidateScroller();
1069 onHeaderShow: function(headerCt, header) {
1070 this.invalidateScroller();
1073 getVerticalScroller: function() {
1074 return this.getScrollerOwner().down('gridscroller[dock=' + this.verticalScrollDock + ']');
1077 getHorizontalScroller: function() {
1078 return this.getScrollerOwner().down('gridscroller[dock=bottom]');
1081 onMouseWheel: function(e) {
1083 vertScroller = me.getVerticalScroller(),
1084 horizScroller = me.getHorizontalScroller(),
1085 scrollDelta = me.scrollDelta / -5,
1086 deltas = e.getWheelDeltas(),
1087 deltaX = scrollDelta * deltas.x,
1088 deltaY = scrollDelta * deltas.y,
1089 vertScrollerEl, horizScrollerEl,
1090 vertScrollerElDom, horizScrollerElDom,
1091 horizontalCanScrollLeft, horizontalCanScrollRight,
1092 verticalCanScrollDown, verticalCanScrollUp;
1094 // calculate whether or not both scrollbars can scroll right/left and up/down
1095 if (horizScroller) {
1096 horizScrollerEl = horizScroller.scrollEl;
1097 if (horizScrollerEl) {
1098 horizScrollerElDom = horizScrollerEl.dom;
1099 horizontalCanScrollRight = horizScrollerElDom.scrollLeft !== horizScrollerElDom.scrollWidth - horizScrollerElDom.clientWidth;
1100 horizontalCanScrollLeft = horizScrollerElDom.scrollLeft !== 0;
1104 vertScrollerEl = vertScroller.scrollEl;
1105 if (vertScrollerEl) {
1106 vertScrollerElDom = vertScrollerEl.dom;
1107 verticalCanScrollDown = vertScrollerElDom.scrollTop !== vertScrollerElDom.scrollHeight - vertScrollerElDom.clientHeight;
1108 verticalCanScrollUp = vertScrollerElDom.scrollTop !== 0;
1112 if (horizScroller) {
1113 if ((deltaX < 0 && horizontalCanScrollLeft) || (deltaX > 0 && horizontalCanScrollRight)) {
1115 horizScroller.scrollByDeltaX(deltaX);
1119 if ((deltaY < 0 && verticalCanScrollUp) || (deltaY > 0 && verticalCanScrollDown)) {
1121 vertScroller.scrollByDeltaY(deltaY);
1128 * Determine and invalidate scrollers on view refresh
1130 onViewRefresh: function() {
1131 this.determineScrollbars();
1132 if (this.invalidateScrollerOnRefresh) {
1133 this.invalidateScroller();
1138 * Sets the scrollTop of the TablePanel.
1139 * @param {Number} deltaY
1141 setScrollTop: function(top) {
1143 rootCmp = me.getScrollerOwner(),
1144 verticalScroller = me.getVerticalScroller();
1146 rootCmp.virtualScrollTop = top;
1147 if (verticalScroller) {
1148 verticalScroller.setScrollTop(top);
1152 getScrollerOwner: function() {
1154 if (!this.scrollerOwner) {
1155 rootCmp = this.up('[scrollerOwner]');
1161 * Scrolls the TablePanel by deltaY
1162 * @param {Number} deltaY
1164 scrollByDeltaY: function(deltaY) {
1165 var verticalScroller = this.getVerticalScroller();
1167 if (verticalScroller) {
1168 verticalScroller.scrollByDeltaY(deltaY);
1173 * Scrolls the TablePanel by deltaX
1174 * @param {Number} deltaY
1176 scrollByDeltaX: function(deltaX) {
1177 var horizontalScroller = this.getVerticalScroller();
1179 if (horizontalScroller) {
1180 horizontalScroller.scrollByDeltaX(deltaX);
1185 * Get left hand side marker for header resizing.
1188 getLhsMarker: function() {
1191 if (!me.lhsMarker) {
1192 me.lhsMarker = Ext.core.DomHelper.append(me.el, {
1193 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1196 return me.lhsMarker;
1200 * Get right hand side marker for header resizing.
1203 getRhsMarker: function() {
1206 if (!me.rhsMarker) {
1207 me.rhsMarker = Ext.core.DomHelper.append(me.el, {
1208 cls: Ext.baseCSSPrefix + 'grid-resize-marker'
1211 return me.rhsMarker;
1215 * Returns the selection model being used and creates it via the configuration
1216 * if it has not been created already.
1217 * @return {Ext.selection.Model} selModel
1219 getSelectionModel: function(){
1220 if (!this.selModel) {
1224 var mode = 'SINGLE',
1226 if (this.simpleSelect) {
1228 } else if (this.multiSelect) {
1232 Ext.applyIf(this.selModel, {
1233 allowDeselect: this.allowDeselect,
1237 if (!this.selModel.events) {
1238 type = this.selModel.selType || this.selType;
1239 this.selModel = Ext.create('selection.' + type, this.selModel);
1242 if (!this.selModel.hasRelaySetup) {
1243 this.relayEvents(this.selModel, [
1244 'selectionchange', 'beforeselect', 'beforedeselect', 'select', 'deselect'
1246 this.selModel.hasRelaySetup = true;
1249 // lock the selection model if user
1250 // has disabled selection
1251 if (this.disableSelection) {
1252 this.selModel.locked = true;
1254 return this.selModel;
1257 onVerticalScroll: function(event, target) {
1258 var owner = this.getScrollerOwner(),
1259 items = owner.query('tableview'),
1263 for (; i < len; i++) {
1264 items[i].el.dom.scrollTop = target.scrollTop;
1268 onHorizontalScroll: function(event, target) {
1269 var owner = this.getScrollerOwner(),
1270 items = owner.query('tableview'),
1271 center = items[1] || items[0];
1273 center.el.dom.scrollLeft = target.scrollLeft;
1274 this.headerCt.el.dom.scrollLeft = target.scrollLeft;
1277 // template method meant to be overriden
1278 onStoreLoad: Ext.emptyFn,
1280 getEditorParent: function() {
1284 bindStore: function(store) {
1287 me.getView().bindStore(store);
1291 * Reconfigure the table with a new store/column.
1292 * Either the store or the column can be ommitted if you don't wish to change them.
1293 * @param {Ext.data.Store} store The new store.
1294 * @param {Array} columns An array of column configs
1296 reconfigure: function(store, columns) {
1298 headerCt = me.headerCt;
1301 me.reconfigureLockable(store, columns);
1303 headerCt.suspendLayout = true;
1304 headerCt.removeAll();
1306 headerCt.add(columns);
1308 headerCt.doLayout();
1311 store = Ext.StoreManager.lookup(store);
1312 me.bindStore(store);
1314 me.getView().refresh();
1317 me.forceComponentLayout();
1320 me.fireEvent('reconfigure', me);