/ext-4.0.7/docs/source/Lockable.html
HTML | 733 lines | 634 code | 99 blank | 0 comment | 0 complexity | 3c7601a636189997dfc2fd0da6b274fb MD5 | raw file
1<!DOCTYPE html>
2<html>
3<head>
4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
10 </style>
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14 }
15 </script>
16</head>
17<body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-grid-Lockable'>/**
19</span> * @class Ext.grid.Lockable
20 * @private
21 *
22 * Lockable is a private mixin which injects lockable behavior into any
23 * TablePanel subclass such as GridPanel or TreePanel. TablePanel will
24 * automatically inject the Ext.grid.Lockable mixin in when one of the
25 * these conditions are met:
26 *
27 * - The TablePanel has the lockable configuration set to true
28 * - One of the columns in the TablePanel has locked set to true/false
29 *
30 * Each TablePanel subclass must register an alias. It should have an array
31 * of configurations to copy to the 2 separate tablepanel's that will be generated
32 * to note what configurations should be copied. These are named normalCfgCopy and
33 * lockedCfgCopy respectively.
34 *
35 * Columns which are locked must specify a fixed width. They do NOT support a
36 * flex width.
37 *
38 * Configurations which are specified in this class will be available on any grid or
39 * tree which is using the lockable functionality.
40 */
41Ext.define('Ext.grid.Lockable', {
42
43 requires: ['Ext.grid.LockingView'],
44
45<span id='Ext-grid-Lockable-cfg-syncRowHeight'> /**
46</span> * @cfg {Boolean} syncRowHeight Synchronize rowHeight between the normal and
47 * locked grid view. This is turned on by default. If your grid is guaranteed
48 * to have rows of all the same height, you should set this to false to
49 * optimize performance.
50 */
51 syncRowHeight: true,
52
53<span id='Ext-grid-Lockable-cfg-subGridXType'> /**
54</span> * @cfg {String} subGridXType The xtype of the subgrid to specify. If this is
55 * not specified lockable will determine the subgrid xtype to create by the
56 * following rule. Use the superclasses xtype if the superclass is NOT
57 * tablepanel, otherwise use the xtype itself.
58 */
59
60<span id='Ext-grid-Lockable-cfg-lockedViewConfig'> /**
61</span> * @cfg {Object} lockedViewConfig A view configuration to be applied to the
62 * locked side of the grid. Any conflicting configurations between lockedViewConfig
63 * and viewConfig will be overwritten by the lockedViewConfig.
64 */
65
66<span id='Ext-grid-Lockable-cfg-normalViewConfig'> /**
67</span> * @cfg {Object} normalViewConfig A view configuration to be applied to the
68 * normal/unlocked side of the grid. Any conflicting configurations between normalViewConfig
69 * and viewConfig will be overwritten by the normalViewConfig.
70 */
71
72 // private variable to track whether or not the spacer is hidden/visible
73 spacerHidden: true,
74
75 headerCounter: 0,
76
77 // i8n text
78 unlockText: 'Unlock',
79 lockText: 'Lock',
80
81 determineXTypeToCreate: function() {
82 var me = this,
83 typeToCreate;
84
85 if (me.subGridXType) {
86 typeToCreate = me.subGridXType;
87 } else {
88 var xtypes = this.getXTypes().split('/'),
89 xtypesLn = xtypes.length,
90 xtype = xtypes[xtypesLn - 1],
91 superxtype = xtypes[xtypesLn - 2];
92
93 if (superxtype !== 'tablepanel') {
94 typeToCreate = superxtype;
95 } else {
96 typeToCreate = xtype;
97 }
98 }
99
100 return typeToCreate;
101 },
102
103 // injectLockable will be invoked before initComponent's parent class implementation
104 // is called, so throughout this method this. are configurations
105 injectLockable: function() {
106 // ensure lockable is set to true in the TablePanel
107 this.lockable = true;
108 // Instruct the TablePanel it already has a view and not to create one.
109 // We are going to aggregate 2 copies of whatever TablePanel we are using
110 this.hasView = true;
111
112 var me = this,
113 // xtype of this class, 'treepanel' or 'gridpanel'
114 // (Note: this makes it a requirement that any subclass that wants to use lockable functionality needs to register an
115 // alias.)
116 xtype = me.determineXTypeToCreate(),
117 // share the selection model
118 selModel = me.getSelectionModel(),
119 lockedGrid = {
120 xtype: xtype,
121 // Lockable does NOT support animations for Tree
122 enableAnimations: false,
123 scroll: false,
124 scrollerOwner: false,
125 selModel: selModel,
126 border: false,
127 cls: Ext.baseCSSPrefix + 'grid-inner-locked'
128 },
129 normalGrid = {
130 xtype: xtype,
131 enableAnimations: false,
132 scrollerOwner: false,
133 selModel: selModel,
134 border: false
135 },
136 i = 0,
137 columns,
138 lockedHeaderCt,
139 normalHeaderCt;
140
141 me.addCls(Ext.baseCSSPrefix + 'grid-locked');
142
143 // copy appropriate configurations to the respective
144 // aggregated tablepanel instances and then delete them
145 // from the master tablepanel.
146 Ext.copyTo(normalGrid, me, me.normalCfgCopy);
147 Ext.copyTo(lockedGrid, me, me.lockedCfgCopy);
148 for (; i < me.normalCfgCopy.length; i++) {
149 delete me[me.normalCfgCopy[i]];
150 }
151 for (i = 0; i < me.lockedCfgCopy.length; i++) {
152 delete me[me.lockedCfgCopy[i]];
153 }
154
155 me.addEvents(
156<span id='Ext-grid-Lockable-event-lockcolumn'> /**
157</span> * @event lockcolumn
158 * Fires when a column is locked.
159 * @param {Ext.grid.Panel} this The gridpanel.
160 * @param {Ext.grid.column.Column} column The column being locked.
161 */
162 'lockcolumn',
163
164<span id='Ext-grid-Lockable-event-unlockcolumn'> /**
165</span> * @event unlockcolumn
166 * Fires when a column is unlocked.
167 * @param {Ext.grid.Panel} this The gridpanel.
168 * @param {Ext.grid.column.Column} column The column being unlocked.
169 */
170 'unlockcolumn'
171 );
172
173 me.addStateEvents(['lockcolumn', 'unlockcolumn']);
174
175 me.lockedHeights = [];
176 me.normalHeights = [];
177
178 columns = me.processColumns(me.columns);
179
180 lockedGrid.width = columns.lockedWidth + Ext.num(selModel.headerWidth, 0);
181 lockedGrid.columns = columns.locked;
182 normalGrid.columns = columns.normal;
183
184 me.store = Ext.StoreManager.lookup(me.store);
185 lockedGrid.store = me.store;
186 normalGrid.store = me.store;
187
188 // normal grid should flex the rest of the width
189 normalGrid.flex = 1;
190 lockedGrid.viewConfig = me.lockedViewConfig || {};
191 lockedGrid.viewConfig.loadingUseMsg = false;
192 normalGrid.viewConfig = me.normalViewConfig || {};
193
194 Ext.applyIf(lockedGrid.viewConfig, me.viewConfig);
195 Ext.applyIf(normalGrid.viewConfig, me.viewConfig);
196
197 me.normalGrid = Ext.ComponentManager.create(normalGrid);
198 me.lockedGrid = Ext.ComponentManager.create(lockedGrid);
199
200 me.view = Ext.create('Ext.grid.LockingView', {
201 locked: me.lockedGrid,
202 normal: me.normalGrid,
203 panel: me
204 });
205
206 if (me.syncRowHeight) {
207 me.lockedGrid.getView().on({
208 refresh: me.onLockedGridAfterRefresh,
209 itemupdate: me.onLockedGridAfterUpdate,
210 scope: me
211 });
212
213 me.normalGrid.getView().on({
214 refresh: me.onNormalGridAfterRefresh,
215 itemupdate: me.onNormalGridAfterUpdate,
216 scope: me
217 });
218 }
219
220 lockedHeaderCt = me.lockedGrid.headerCt;
221 normalHeaderCt = me.normalGrid.headerCt;
222
223 lockedHeaderCt.lockedCt = true;
224 lockedHeaderCt.lockableInjected = true;
225 normalHeaderCt.lockableInjected = true;
226
227 lockedHeaderCt.on({
228 columnshow: me.onLockedHeaderShow,
229 columnhide: me.onLockedHeaderHide,
230 columnmove: me.onLockedHeaderMove,
231 sortchange: me.onLockedHeaderSortChange,
232 columnresize: me.onLockedHeaderResize,
233 scope: me
234 });
235
236 normalHeaderCt.on({
237 columnmove: me.onNormalHeaderMove,
238 sortchange: me.onNormalHeaderSortChange,
239 scope: me
240 });
241
242 me.normalGrid.on({
243 scrollershow: me.onScrollerShow,
244 scrollerhide: me.onScrollerHide,
245 scope: me
246 });
247
248 me.lockedGrid.on('afterlayout', me.onLockedGridAfterLayout, me, {single: true});
249
250 me.modifyHeaderCt();
251 me.items = [me.lockedGrid, me.normalGrid];
252
253 me.relayHeaderCtEvents(lockedHeaderCt);
254 me.relayHeaderCtEvents(normalHeaderCt);
255
256 me.layout = {
257 type: 'hbox',
258 align: 'stretch'
259 };
260 },
261
262 processColumns: function(columns){
263 // split apart normal and lockedWidths
264 var i = 0,
265 len = columns.length,
266 lockedWidth = 1,
267 lockedHeaders = [],
268 normalHeaders = [],
269 column;
270
271 for (; i < len; ++i) {
272 column = columns[i];
273 // mark the column as processed so that the locked attribute does not
274 // trigger trying to aggregate the columns again.
275 column.processed = true;
276 if (column.locked) {
277 // <debug>
278 if (column.flex) {
279 Ext.Error.raise("Columns which are locked do NOT support a flex width. You must set a width on the " + columns[i].text + "column.");
280 }
281 // </debug>
282 if (!column.hidden) {
283 lockedWidth += column.width || Ext.grid.header.Container.prototype.defaultWidth;
284 }
285 lockedHeaders.push(column);
286 } else {
287 normalHeaders.push(column);
288 }
289 if (!column.headerId) {
290 column.headerId = (column.initialConfig || column).id || ('L' + (++this.headerCounter));
291 }
292 }
293 return {
294 lockedWidth: lockedWidth,
295 locked: lockedHeaders,
296 normal: normalHeaders
297 };
298 },
299
300 // create a new spacer after the table is refreshed
301 onLockedGridAfterLayout: function() {
302 var me = this,
303 lockedView = me.lockedGrid.getView();
304 lockedView.on({
305 beforerefresh: me.destroySpacer,
306 scope: me
307 });
308 },
309
310 // trigger a pseudo refresh on the normal side
311 onLockedHeaderMove: function() {
312 if (this.syncRowHeight) {
313 this.onNormalGridAfterRefresh();
314 }
315 },
316
317 // trigger a pseudo refresh on the locked side
318 onNormalHeaderMove: function() {
319 if (this.syncRowHeight) {
320 this.onLockedGridAfterRefresh();
321 }
322 },
323
324 // create a spacer in lockedsection and store a reference
325 // TODO: Should destroy before refreshing content
326 getSpacerEl: function() {
327 var me = this,
328 w,
329 view,
330 el;
331
332 if (!me.spacerEl) {
333 // This affects scrolling all the way to the bottom of a locked grid
334 // additional test, sort a column and make sure it synchronizes
335 w = Ext.getScrollBarWidth() + (Ext.isIE ? 2 : 0);
336 view = me.lockedGrid.getView();
337 el = view.el;
338
339 me.spacerEl = Ext.DomHelper.append(el, {
340 cls: me.spacerHidden ? (Ext.baseCSSPrefix + 'hidden') : '',
341 style: 'height: ' + w + 'px;'
342 }, true);
343 }
344 return me.spacerEl;
345 },
346
347 destroySpacer: function() {
348 var me = this;
349 if (me.spacerEl) {
350 me.spacerEl.destroy();
351 delete me.spacerEl;
352 }
353 },
354
355 // cache the heights of all locked rows and sync rowheights
356 onLockedGridAfterRefresh: function() {
357 var me = this,
358 view = me.lockedGrid.getView(),
359 el = view.el,
360 rowEls = el.query(view.getItemSelector()),
361 ln = rowEls.length,
362 i = 0;
363
364 // reset heights each time.
365 me.lockedHeights = [];
366
367 for (; i < ln; i++) {
368 me.lockedHeights[i] = rowEls[i].clientHeight;
369 }
370 me.syncRowHeights();
371 },
372
373 // cache the heights of all normal rows and sync rowheights
374 onNormalGridAfterRefresh: function() {
375 var me = this,
376 view = me.normalGrid.getView(),
377 el = view.el,
378 rowEls = el.query(view.getItemSelector()),
379 ln = rowEls.length,
380 i = 0;
381
382 // reset heights each time.
383 me.normalHeights = [];
384
385 for (; i < ln; i++) {
386 me.normalHeights[i] = rowEls[i].clientHeight;
387 }
388 me.syncRowHeights();
389 },
390
391 // rows can get bigger/smaller
392 onLockedGridAfterUpdate: function(record, index, node) {
393 this.lockedHeights[index] = node.clientHeight;
394 this.syncRowHeights();
395 },
396
397 // rows can get bigger/smaller
398 onNormalGridAfterUpdate: function(record, index, node) {
399 this.normalHeights[index] = node.clientHeight;
400 this.syncRowHeights();
401 },
402
403 // match the rowheights to the biggest rowheight on either
404 // side
405 syncRowHeights: function() {
406 var me = this,
407 lockedHeights = me.lockedHeights,
408 normalHeights = me.normalHeights,
409 calcHeights = [],
410 ln = lockedHeights.length,
411 i = 0,
412 lockedView, normalView,
413 lockedRowEls, normalRowEls,
414 vertScroller = me.getVerticalScroller(),
415 scrollTop;
416
417 // ensure there are an equal num of locked and normal
418 // rows before synchronization
419 if (lockedHeights.length && normalHeights.length) {
420 lockedView = me.lockedGrid.getView();
421 normalView = me.normalGrid.getView();
422 lockedRowEls = lockedView.el.query(lockedView.getItemSelector());
423 normalRowEls = normalView.el.query(normalView.getItemSelector());
424
425 // loop thru all of the heights and sync to the other side
426 for (; i < ln; i++) {
427 // ensure both are numbers
428 if (!isNaN(lockedHeights[i]) && !isNaN(normalHeights[i])) {
429 if (lockedHeights[i] > normalHeights[i]) {
430 Ext.fly(normalRowEls[i]).setHeight(lockedHeights[i]);
431 } else if (lockedHeights[i] < normalHeights[i]) {
432 Ext.fly(lockedRowEls[i]).setHeight(normalHeights[i]);
433 }
434 }
435 }
436
437 // invalidate the scroller and sync the scrollers
438 me.normalGrid.invalidateScroller();
439
440 // synchronize the view with the scroller, if we have a virtualScrollTop
441 // then the user is using a PagingScroller
442 if (vertScroller && vertScroller.setViewScrollTop) {
443 vertScroller.setViewScrollTop(me.virtualScrollTop);
444 } else {
445 // We don't use setScrollTop here because if the scrollTop is
446 // set to the exact same value some browsers won't fire the scroll
447 // event. Instead, we directly set the scrollTop.
448 scrollTop = normalView.el.dom.scrollTop;
449 normalView.el.dom.scrollTop = scrollTop;
450 lockedView.el.dom.scrollTop = scrollTop;
451 }
452
453 // reset the heights
454 me.lockedHeights = [];
455 me.normalHeights = [];
456 }
457 },
458
459 // track when scroller is shown
460 onScrollerShow: function(scroller, direction) {
461 if (direction === 'horizontal') {
462 this.spacerHidden = false;
463 this.getSpacerEl().removeCls(Ext.baseCSSPrefix + 'hidden');
464 }
465 },
466
467 // track when scroller is hidden
468 onScrollerHide: function(scroller, direction) {
469 if (direction === 'horizontal') {
470 this.spacerHidden = true;
471 if (this.spacerEl) {
472 this.spacerEl.addCls(Ext.baseCSSPrefix + 'hidden');
473 }
474 }
475 },
476
477
478 // inject Lock and Unlock text
479 modifyHeaderCt: function() {
480 var me = this;
481 me.lockedGrid.headerCt.getMenuItems = me.getMenuItems(true);
482 me.normalGrid.headerCt.getMenuItems = me.getMenuItems(false);
483 },
484
485 onUnlockMenuClick: function() {
486 this.unlock();
487 },
488
489 onLockMenuClick: function() {
490 this.lock();
491 },
492
493 getMenuItems: function(locked) {
494 var me = this,
495 unlockText = me.unlockText,
496 lockText = me.lockText,
497 unlockCls = Ext.baseCSSPrefix + 'hmenu-unlock',
498 lockCls = Ext.baseCSSPrefix + 'hmenu-lock',
499 unlockHandler = Ext.Function.bind(me.onUnlockMenuClick, me),
500 lockHandler = Ext.Function.bind(me.onLockMenuClick, me);
501
502 // runs in the scope of headerCt
503 return function() {
504 var o = Ext.grid.header.Container.prototype.getMenuItems.call(this);
505 o.push('-',{
506 cls: unlockCls,
507 text: unlockText,
508 handler: unlockHandler,
509 disabled: !locked
510 });
511 o.push({
512 cls: lockCls,
513 text: lockText,
514 handler: lockHandler,
515 disabled: locked
516 });
517 return o;
518 };
519 },
520
521 // going from unlocked section to locked
522<span id='Ext-grid-Lockable-method-lock'> /**
523</span> * Locks the activeHeader as determined by which menu is open OR a header
524 * as specified.
525 * @param {Ext.grid.column.Column} header (Optional) Header to unlock from the locked section. Defaults to the header which has the menu open currently.
526 * @param {Number} toIdx (Optional) The index to move the unlocked header to. Defaults to appending as the last item.
527 * @private
528 */
529 lock: function(activeHd, toIdx) {
530 var me = this,
531 normalGrid = me.normalGrid,
532 lockedGrid = me.lockedGrid,
533 normalHCt = normalGrid.headerCt,
534 lockedHCt = lockedGrid.headerCt;
535
536 activeHd = activeHd || normalHCt.getMenu().activeHeader;
537
538 // if column was previously flexed, get/set current width
539 // and remove the flex
540 if (activeHd.flex) {
541 activeHd.width = activeHd.getWidth();
542 delete activeHd.flex;
543 }
544
545 normalHCt.remove(activeHd, false);
546 lockedHCt.suspendLayout = true;
547 activeHd.locked = true;
548 if (Ext.isDefined(toIdx)) {
549 lockedHCt.insert(toIdx, activeHd);
550 } else {
551 lockedHCt.add(activeHd);
552 }
553 lockedHCt.suspendLayout = false;
554 me.syncLockedSection();
555
556 me.fireEvent('lockcolumn', me, activeHd);
557 },
558
559 syncLockedSection: function() {
560 var me = this;
561 me.syncLockedWidth();
562 me.lockedGrid.getView().refresh();
563 me.normalGrid.getView().refresh();
564 },
565
566 // adjust the locked section to the width of its respective
567 // headerCt
568 syncLockedWidth: function() {
569 var me = this,
570 width = me.lockedGrid.headerCt.getFullWidth(true);
571 me.lockedGrid.setWidth(width+1); // +1 for border pixel
572 me.doComponentLayout();
573 },
574
575 onLockedHeaderResize: function() {
576 this.syncLockedWidth();
577 },
578
579 onLockedHeaderHide: function() {
580 this.syncLockedWidth();
581 },
582
583 onLockedHeaderShow: function() {
584 this.syncLockedWidth();
585 },
586
587 onLockedHeaderSortChange: function(headerCt, header, sortState) {
588 if (sortState) {
589 // no real header, and silence the event so we dont get into an
590 // infinite loop
591 this.normalGrid.headerCt.clearOtherSortStates(null, true);
592 }
593 },
594
595 onNormalHeaderSortChange: function(headerCt, header, sortState) {
596 if (sortState) {
597 // no real header, and silence the event so we dont get into an
598 // infinite loop
599 this.lockedGrid.headerCt.clearOtherSortStates(null, true);
600 }
601 },
602
603 // going from locked section to unlocked
604<span id='Ext-grid-Lockable-method-unlock'> /**
605</span> * Unlocks the activeHeader as determined by which menu is open OR a header
606 * as specified.
607 * @param {Ext.grid.column.Column} header (Optional) Header to unlock from the locked section. Defaults to the header which has the menu open currently.
608 * @param {Number} toIdx (Optional) The index to move the unlocked header to. Defaults to 0.
609 * @private
610 */
611 unlock: function(activeHd, toIdx) {
612 var me = this,
613 normalGrid = me.normalGrid,
614 lockedGrid = me.lockedGrid,
615 normalHCt = normalGrid.headerCt,
616 lockedHCt = lockedGrid.headerCt;
617
618 if (!Ext.isDefined(toIdx)) {
619 toIdx = 0;
620 }
621 activeHd = activeHd || lockedHCt.getMenu().activeHeader;
622
623 lockedHCt.remove(activeHd, false);
624 me.syncLockedWidth();
625 me.lockedGrid.getView().refresh();
626 activeHd.locked = false;
627 normalHCt.insert(toIdx, activeHd);
628 me.normalGrid.getView().refresh();
629
630 me.fireEvent('unlockcolumn', me, activeHd);
631 },
632
633 applyColumnsState: function (columns) {
634 var me = this,
635 lockedGrid = me.lockedGrid,
636 lockedHeaderCt = lockedGrid.headerCt,
637 normalHeaderCt = me.normalGrid.headerCt,
638 lockedCols = lockedHeaderCt.items,
639 normalCols = normalHeaderCt.items,
640 existing,
641 locked = [],
642 normal = [],
643 lockedDefault,
644 lockedWidth = 1;
645
646 Ext.each(columns, function (col) {
647 function matches (item) {
648 return item.headerId == col.id;
649 }
650
651 lockedDefault = true;
652 if (!(existing = lockedCols.findBy(matches))) {
653 existing = normalCols.findBy(matches);
654 lockedDefault = false;
655 }
656
657 if (existing) {
658 if (existing.applyColumnState) {
659 existing.applyColumnState(col);
660 }
661 if (!Ext.isDefined(existing.locked)) {
662 existing.locked = lockedDefault;
663 }
664 if (existing.locked) {
665 locked.push(existing);
666 if (!existing.hidden && Ext.isNumber(existing.width)) {
667 lockedWidth += existing.width;
668 }
669 } else {
670 normal.push(existing);
671 }
672 }
673 });
674
675 // state and config must have the same columns (compare counts for now):
676 if (locked.length + normal.length == lockedCols.getCount() + normalCols.getCount()) {
677 lockedHeaderCt.removeAll(false);
678 normalHeaderCt.removeAll(false);
679
680 lockedHeaderCt.add(locked);
681 normalHeaderCt.add(normal);
682
683 lockedGrid.setWidth(lockedWidth);
684 }
685 },
686
687 getColumnsState: function () {
688 var me = this,
689 locked = me.lockedGrid.headerCt.getColumnsState(),
690 normal = me.normalGrid.headerCt.getColumnsState();
691
692 return locked.concat(normal);
693 },
694
695 // we want to totally override the reconfigure behaviour here, since we're creating 2 sub-grids
696 reconfigureLockable: function(store, columns) {
697 var me = this,
698 lockedGrid = me.lockedGrid,
699 normalGrid = me.normalGrid;
700
701 if (columns) {
702 lockedGrid.headerCt.suspendLayout = true;
703 normalGrid.headerCt.suspendLayout = true;
704 lockedGrid.headerCt.removeAll();
705 normalGrid.headerCt.removeAll();
706
707 columns = me.processColumns(columns);
708 lockedGrid.setWidth(columns.lockedWidth);
709 lockedGrid.headerCt.add(columns.locked);
710 normalGrid.headerCt.add(columns.normal);
711 }
712
713 if (store) {
714 store = Ext.data.StoreManager.lookup(store);
715 me.store = store;
716 lockedGrid.bindStore(store);
717 normalGrid.bindStore(store);
718 } else {
719 lockedGrid.getView().refresh();
720 normalGrid.getView().refresh();
721 }
722
723 if (columns) {
724 lockedGrid.headerCt.suspendLayout = false;
725 normalGrid.headerCt.suspendLayout = false;
726 lockedGrid.headerCt.forceComponentLayout();
727 normalGrid.headerCt.forceComponentLayout();
728 }
729 }
730});
731</pre>
732</body>
733</html>