Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / examples / ux / LockingGridView.js
1 /*!
2  * Ext JS Library 3.2.0
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.ns('Ext.ux.grid');
8
9 Ext.ux.grid.LockingGridView = Ext.extend(Ext.grid.GridView, {
10     lockText : 'Lock',
11     unlockText : 'Unlock',
12     rowBorderWidth : 1,
13     lockedBorderWidth : 1,
14     
15     /*
16      * This option ensures that height between the rows is synchronized
17      * between the locked and unlocked sides. This option only needs to be used
18      * when the row heights aren't predictable.
19      */
20     syncHeights: false,
21     
22     initTemplates : function(){
23         var ts = this.templates || {};
24         
25         if (!ts.master) {
26             ts.master = new Ext.Template(
27                 '<div class="x-grid3" hidefocus="true">',
28                     '<div class="x-grid3-locked">',
29                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',
30                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
31                     '</div>',
32                     '<div class="x-grid3-viewport x-grid3-unlocked">',
33                         '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
34                         '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
35                     '</div>',
36                     '<div class="x-grid3-resize-marker">&#160;</div>',
37                     '<div class="x-grid3-resize-proxy">&#160;</div>',
38                 '</div>'
39             );
40         }
41         
42         this.templates = ts;
43         
44         Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);
45     },
46     
47     getEditorParent : function(ed){
48         return this.el.dom;
49     },
50     
51     initElements : function(){
52         var E  = Ext.Element,
53             el = this.grid.getGridEl().dom.firstChild,
54             cs = el.childNodes;
55             
56         this.el             = new E(el);
57         this.lockedWrap     = new E(cs[0]);
58         this.lockedHd       = new E(this.lockedWrap.dom.firstChild);
59         this.lockedInnerHd  = this.lockedHd.dom.firstChild;
60         this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);
61         this.lockedBody     = new E(this.lockedScroller.dom.firstChild);
62         this.mainWrap       = new E(cs[1]);
63         this.mainHd         = new E(this.mainWrap.dom.firstChild);
64         
65         if (this.grid.hideHeaders) {
66             this.lockedHd.setDisplayed(false);
67             this.mainHd.setDisplayed(false);
68         }
69         
70         this.innerHd  = this.mainHd.dom.firstChild;
71         this.scroller = new E(this.mainWrap.dom.childNodes[1]);
72         
73         if(this.forceFit){
74             this.scroller.setStyle('overflow-x', 'hidden');
75         }
76         
77         this.mainBody     = new E(this.scroller.dom.firstChild);
78         this.focusEl      = new E(this.scroller.dom.childNodes[1]);
79         this.resizeMarker = new E(cs[2]);
80         this.resizeProxy  = new E(cs[3]);
81         
82         this.focusEl.swallowEvent('click', true);
83     },
84     
85     getLockedRows : function(){
86         return this.hasRows() ? this.lockedBody.dom.childNodes : [];
87     },
88     
89     getLockedRow : function(row){
90         return this.getLockedRows()[row];
91     },
92     
93     getCell : function(row, col){
94         var llen = this.cm.getLockedCount();
95         if(col < llen){
96             return this.getLockedRow(row).getElementsByTagName('td')[col];
97         }
98         return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - llen);
99     },
100     
101     getHeaderCell : function(index){
102         var llen = this.cm.getLockedCount();
103         if(index < llen){
104             return this.lockedHd.dom.getElementsByTagName('td')[index];
105         }
106         return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - llen);
107     },
108     
109     addRowClass : function(row, cls){
110         var r = this.getLockedRow(row);
111         if(r){
112             this.fly(r).addClass(cls);
113         }
114         Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);
115     },
116     
117     removeRowClass : function(row, cls){
118         var r = this.getLockedRow(row);
119         if(r){
120             this.fly(r).removeClass(cls);
121         }
122         Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);
123     },
124     
125     removeRow : function(row) {
126         Ext.removeNode(this.getLockedRow(row));
127         Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);
128     },
129     
130     removeRows : function(firstRow, lastRow){
131         var bd = this.lockedBody.dom;
132         for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
133             Ext.removeNode(bd.childNodes[firstRow]);
134         }
135         Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);
136     },
137     
138     syncScroll : function(e){
139         var mb = this.scroller.dom;
140         this.lockedScroller.dom.scrollTop = mb.scrollTop;
141         Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);
142     },
143     
144     updateSortIcon : function(col, dir){
145         var sc = this.sortClasses,
146             lhds = this.lockedHd.select('td').removeClass(sc),
147             hds = this.mainHd.select('td').removeClass(sc),
148             llen = this.cm.getLockedCount(),
149             cls = sc[dir == 'DESC' ? 1 : 0];
150         if(col < llen){
151             lhds.item(col).addClass(cls);
152         }else{
153             hds.item(col - llen).addClass(cls);
154         }
155     },
156     
157     updateAllColumnWidths : function(){
158         var tw = this.getTotalWidth(),
159             clen = this.cm.getColumnCount(),
160             lw = this.getLockedWidth(),
161             llen = this.cm.getLockedCount(),
162             ws = [], len, i;
163         this.updateLockedWidth();
164         for(i = 0; i < clen; i++){
165             ws[i] = this.getColumnWidth(i);
166             var hd = this.getHeaderCell(i);
167             hd.style.width = ws[i];
168         }
169         var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;
170         for(i = 0, len = ns.length; i < len; i++){
171             row = lns[i];
172             row.style.width = lw;
173             if(row.firstChild){
174                 row.firstChild.style.width = lw;
175                 trow = row.firstChild.rows[0];
176                 for (j = 0; j < llen; j++) {
177                    trow.childNodes[j].style.width = ws[j];
178                 }
179             }
180             row = ns[i];
181             row.style.width = tw;
182             if(row.firstChild){
183                 row.firstChild.style.width = tw;
184                 trow = row.firstChild.rows[0];
185                 for (j = llen; j < clen; j++) {
186                    trow.childNodes[j - llen].style.width = ws[j];
187                 }
188             }
189         }
190         this.onAllColumnWidthsUpdated(ws, tw);
191         this.syncHeaderHeight();
192     },
193     
194     updateColumnWidth : function(col, width){
195         var w = this.getColumnWidth(col),
196             llen = this.cm.getLockedCount(),
197             ns, rw, c, row;
198         this.updateLockedWidth();
199         if(col < llen){
200             ns = this.getLockedRows();
201             rw = this.getLockedWidth();
202             c = col;
203         }else{
204             ns = this.getRows();
205             rw = this.getTotalWidth();
206             c = col - llen;
207         }
208         var hd = this.getHeaderCell(col);
209         hd.style.width = w;
210         for(var i = 0, len = ns.length; i < len; i++){
211             row = ns[i];
212             row.style.width = rw;
213             if(row.firstChild){
214                 row.firstChild.style.width = rw;
215                 row.firstChild.rows[0].childNodes[c].style.width = w;
216             }
217         }
218         this.onColumnWidthUpdated(col, w, this.getTotalWidth());
219         this.syncHeaderHeight();
220     },
221     
222     updateColumnHidden : function(col, hidden){
223         var llen = this.cm.getLockedCount(),
224             ns, rw, c, row,
225             display = hidden ? 'none' : '';
226         this.updateLockedWidth();
227         if(col < llen){
228             ns = this.getLockedRows();
229             rw = this.getLockedWidth();
230             c = col;
231         }else{
232             ns = this.getRows();
233             rw = this.getTotalWidth();
234             c = col - llen;
235         }
236         var hd = this.getHeaderCell(col);
237         hd.style.display = display;
238         for(var i = 0, len = ns.length; i < len; i++){
239             row = ns[i];
240             row.style.width = rw;
241             if(row.firstChild){
242                 row.firstChild.style.width = rw;
243                 row.firstChild.rows[0].childNodes[c].style.display = display;
244             }
245         }
246         this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());
247         delete this.lastViewWidth;
248         this.layout();
249     },
250     
251     doRender : function(cs, rs, ds, startRow, colCount, stripe){
252         var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1,
253             tstyle = 'width:'+this.getTotalWidth()+';',
254             lstyle = 'width:'+this.getLockedWidth()+';',
255             buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;
256         for(var j = 0, len = rs.length; j < len; j++){
257             r = rs[j]; cb = []; lcb = [];
258             var rowIndex = (j+startRow);
259             for(var i = 0; i < colCount; i++){
260                 c = cs[i];
261                 p.id = c.id;
262                 p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
263                     (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');
264                 p.attr = p.cellAttr = '';
265                 p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
266                 p.style = c.style;
267                 if(Ext.isEmpty(p.value)){
268                     p.value = '&#160;';
269                 }
270                 if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
271                     p.css += ' x-grid3-dirty-cell';
272                 }
273                 if(c.locked){
274                     lcb[lcb.length] = ct.apply(p);
275                 }else{
276                     cb[cb.length] = ct.apply(p);
277                 }
278             }
279             var alt = [];
280             if(stripe && ((rowIndex+1) % 2 === 0)){
281                 alt[0] = 'x-grid3-row-alt';
282             }
283             if(r.dirty){
284                 alt[1] = ' x-grid3-dirty-row';
285             }
286             rp.cols = colCount;
287             if(this.getRowClass){
288                 alt[2] = this.getRowClass(r, rowIndex, rp, ds);
289             }
290             rp.alt = alt.join(' ');
291             rp.cells = cb.join('');
292             rp.tstyle = tstyle;
293             buf[buf.length] = rt.apply(rp);
294             rp.cells = lcb.join('');
295             rp.tstyle = lstyle;
296             lbuf[lbuf.length] = rt.apply(rp);
297         }
298         return [buf.join(''), lbuf.join('')];
299     },
300     processRows : function(startRow, skipStripe){
301         if(!this.ds || this.ds.getCount() < 1){
302             return;
303         }
304         var rows = this.getRows(),
305             lrows = this.getLockedRows(),
306             row, lrow;
307         skipStripe = skipStripe || !this.grid.stripeRows;
308         startRow = startRow || 0;
309         for(var i = 0, len = rows.length; i < len; ++i){
310             row = rows[i];
311             lrow = lrows[i];
312             row.rowIndex = i;
313             lrow.rowIndex = i;
314             if(!skipStripe){
315                 row.className = row.className.replace(this.rowClsRe, ' ');
316                 lrow.className = lrow.className.replace(this.rowClsRe, ' ');
317                 if ((i + 1) % 2 === 0){
318                     row.className += ' x-grid3-row-alt';
319                     lrow.className += ' x-grid3-row-alt';
320                 }
321             }
322             if(this.syncHeights){
323                 var el1 = Ext.get(row),
324                     el2 = Ext.get(lrow),
325                     h1 = el1.getHeight(),
326                     h2 = el2.getHeight();
327                 
328                 if(h1 > h2){
329                     el2.setHeight(h1);    
330                 }else if(h2 > h1){
331                     el1.setHeight(h2);
332                 }
333             }
334         }
335         if(startRow === 0){
336             Ext.fly(rows[0]).addClass(this.firstRowCls);
337             Ext.fly(lrows[0]).addClass(this.firstRowCls);
338         }
339         Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
340         Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);
341     },
342     
343     afterRender : function(){
344         if(!this.ds || !this.cm){
345             return;
346         }
347         var bd = this.renderRows() || ['&#160;', '&#160;'];
348         this.mainBody.dom.innerHTML = bd[0];
349         this.lockedBody.dom.innerHTML = bd[1];
350         this.processRows(0, true);
351         if(this.deferEmptyText !== true){
352             this.applyEmptyText();
353         }
354     },
355     
356     renderUI : function(){
357         var header = this.renderHeaders();
358         var body = this.templates.body.apply({rows:'&#160;'});
359         var html = this.templates.master.apply({
360             body: body,
361             header: header[0],
362             ostyle: 'width:'+this.getOffsetWidth()+';',
363             bstyle: 'width:'+this.getTotalWidth()+';',
364             lockedBody: body,
365             lockedHeader: header[1],
366             lstyle: 'width:'+this.getLockedWidth()+';'
367         });
368         var g = this.grid;
369         g.getGridEl().dom.innerHTML = html;
370         this.initElements();
371         Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
372         Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);
373         this.mainHd.on({
374             scope: this,
375             mouseover: this.handleHdOver,
376             mouseout: this.handleHdOut,
377             mousemove: this.handleHdMove
378         });
379         this.lockedHd.on({
380             scope: this,
381             mouseover: this.handleHdOver,
382             mouseout: this.handleHdOut,
383             mousemove: this.handleHdMove
384         });
385         this.scroller.on('scroll', this.syncScroll,  this);
386         if(g.enableColumnResize !== false){
387             this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
388             this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
389             this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));
390         }
391         if(g.enableColumnMove){
392             this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
393             this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));
394             this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));
395             this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
396         }
397         if(g.enableHdMenu !== false){
398             this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
399             this.hmenu.add(
400                 {itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
401                 {itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
402             );
403             if(this.grid.enableColLock !== false){
404                 this.hmenu.add('-',
405                     {itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock'},
406                     {itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock'}
407                 );
408             }
409             if(g.enableColumnHide !== false){
410                 this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
411                 this.colMenu.on({
412                     scope: this,
413                     beforeshow: this.beforeColMenuShow,
414                     itemclick: this.handleHdMenuClick
415                 });
416                 this.hmenu.add('-', {
417                     itemId:'columns',
418                     hideOnClick: false,
419                     text: this.columnsText,
420                     menu: this.colMenu,
421                     iconCls: 'x-cols-icon'
422                 });
423             }
424             this.hmenu.on('itemclick', this.handleHdMenuClick, this);
425         }
426         if(g.trackMouseOver){
427             this.mainBody.on({
428                 scope: this,
429                 mouseover: this.onRowOver,
430                 mouseout: this.onRowOut
431             });
432             this.lockedBody.on({
433                 scope: this,
434                 mouseover: this.onRowOver,
435                 mouseout: this.onRowOut
436             });
437         }
438         
439         if(g.enableDragDrop || g.enableDrag){
440             this.dragZone = new Ext.grid.GridDragZone(g, {
441                 ddGroup : g.ddGroup || 'GridDD'
442             });
443         }
444         this.updateHeaderSortState();
445     },
446     
447     layout : function(){
448         if(!this.mainBody){
449             return;
450         }
451         var g = this.grid;
452         var c = g.getGridEl();
453         var csize = c.getSize(true);
454         var vw = csize.width;
455         if(!g.hideHeaders && (vw < 20 || csize.height < 20)){
456             return;
457         }
458         this.syncHeaderHeight();
459         if(g.autoHeight){
460             this.scroller.dom.style.overflow = 'visible';
461             this.lockedScroller.dom.style.overflow = 'visible';
462             if(Ext.isWebKit){
463                 this.scroller.dom.style.position = 'static';
464                 this.lockedScroller.dom.style.position = 'static';
465             }
466         }else{
467             this.el.setSize(csize.width, csize.height);
468             var hdHeight = this.mainHd.getHeight();
469             var vh = csize.height - (hdHeight);
470         }
471         this.updateLockedWidth();
472         if(this.forceFit){
473             if(this.lastViewWidth != vw){
474                 this.fitColumns(false, false);
475                 this.lastViewWidth = vw;
476             }
477         }else {
478             this.autoExpand();
479             this.syncHeaderScroll();
480         }
481         this.onLayout(vw, vh);
482     },
483     
484     getOffsetWidth : function() {
485         return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';
486     },
487     
488     renderHeaders : function(){
489         var cm = this.cm,
490             ts = this.templates,
491             ct = ts.hcell,
492             cb = [], lcb = [],
493             p = {},
494             len = cm.getColumnCount(),
495             last = len - 1;
496         for(var i = 0; i < len; i++){
497             p.id = cm.getColumnId(i);
498             p.value = cm.getColumnHeader(i) || '';
499             p.style = this.getColumnStyle(i, true);
500             p.tooltip = this.getColumnTooltip(i);
501             p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
502                 (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');
503             if(cm.config[i].align == 'right'){
504                 p.istyle = 'padding-right:16px';
505             } else {
506                 delete p.istyle;
507             }
508             if(cm.isLocked(i)){
509                 lcb[lcb.length] = ct.apply(p);
510             }else{
511                 cb[cb.length] = ct.apply(p);
512             }
513         }
514         return [ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'}),
515                 ts.header.apply({cells: lcb.join(''), tstyle:'width:'+this.getLockedWidth()+';'})];
516     },
517     
518     updateHeaders : function(){
519         var hd = this.renderHeaders();
520         this.innerHd.firstChild.innerHTML = hd[0];
521         this.innerHd.firstChild.style.width = this.getOffsetWidth();
522         this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
523         this.lockedInnerHd.firstChild.innerHTML = hd[1];
524         var lw = this.getLockedWidth();
525         this.lockedInnerHd.firstChild.style.width = lw;
526         this.lockedInnerHd.firstChild.firstChild.style.width = lw;
527     },
528     
529     getResolvedXY : function(resolved){
530         if(!resolved){
531             return null;
532         }
533         var c = resolved.cell, r = resolved.row;
534         return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
535     },
536     
537     syncFocusEl : function(row, col, hscroll){
538         Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
539     },
540     
541     ensureVisible : function(row, col, hscroll){
542         return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
543     },
544     
545     insertRows : function(dm, firstRow, lastRow, isUpdate){
546         var last = dm.getCount() - 1;
547         if(!isUpdate && firstRow === 0 && lastRow >= last){
548             this.refresh();
549         }else{
550             if(!isUpdate){
551                 this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
552             }
553             var html = this.renderRows(firstRow, lastRow),
554                 before = this.getRow(firstRow);
555             if(before){
556                 if(firstRow === 0){
557                     this.removeRowClass(0, this.firstRowCls);
558                 }
559                 Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
560                 before = this.getLockedRow(firstRow);
561                 Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);
562             }else{
563                 this.removeRowClass(last - 1, this.lastRowCls);
564                 Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);
565                 Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);
566             }
567             if(!isUpdate){
568                 this.fireEvent('rowsinserted', this, firstRow, lastRow);
569                 this.processRows(firstRow);
570             }else if(firstRow === 0 || firstRow >= last){
571                 this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);
572             }
573         }
574         this.syncFocusEl(firstRow);
575     },
576     
577     getColumnStyle : function(col, isHeader){
578         var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';
579         style += 'width:'+this.getColumnWidth(col)+';';
580         if(this.cm.isHidden(col)){
581             style += 'display:none;';
582         }
583         var align = this.cm.config[col].align;
584         if(align){
585             style += 'text-align:'+align+';';
586         }
587         return style;
588     },
589     
590     getLockedWidth : function() {
591         return this.cm.getTotalLockedWidth() + 'px';
592     },
593     
594     getTotalWidth : function() {
595         return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';
596     },
597     
598     getColumnData : function(){
599         var cs = [], cm = this.cm, colCount = cm.getColumnCount();
600         for(var i = 0; i < colCount; i++){
601             var name = cm.getDataIndex(i);
602             cs[i] = {
603                 name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
604                 renderer : cm.getRenderer(i),
605                 id : cm.getColumnId(i),
606                 style : this.getColumnStyle(i),
607                 locked : cm.isLocked(i)
608             };
609         }
610         return cs;
611     },
612     
613     renderBody : function(){
614         var markup = this.renderRows() || ['&#160;', '&#160;'];
615         return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})];
616     },
617     
618     refreshRow : function(record){
619         Ext.ux.grid.LockingGridView.superclass.refreshRow.call(this, record);
620         var index = Ext.isNumber(record) ? record : this.ds.indexOf(record);
621         this.getLockedRow(index).rowIndex = index;
622     },
623     
624     refresh : function(headersToo){
625         this.fireEvent('beforerefresh', this);
626         this.grid.stopEditing(true);
627         var result = this.renderBody();
628         this.mainBody.update(result[0]).setWidth(this.getTotalWidth());
629         this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());
630         if(headersToo === true){
631             this.updateHeaders();
632             this.updateHeaderSortState();
633         }
634         this.processRows(0, true);
635         this.layout();
636         this.applyEmptyText();
637         this.fireEvent('refresh', this);
638     },
639     
640     onDenyColumnLock : function(){
641
642     },
643     
644     initData : function(ds, cm){
645         if(this.cm){
646             this.cm.un('columnlockchange', this.onColumnLock, this);
647         }
648         Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);
649         if(this.cm){
650             this.cm.on('columnlockchange', this.onColumnLock, this);
651         }
652     },
653     
654     onColumnLock : function(){
655         this.refresh(true);
656     },
657     
658     handleHdMenuClick : function(item){
659         var index = this.hdCtxIndex,
660             cm = this.cm,
661             id = item.getItemId(),
662             llen = cm.getLockedCount();
663         switch(id){
664             case 'lock':
665                 if(cm.getColumnCount(true) <= llen + 1){
666                     this.onDenyColumnLock();
667                     return;
668                 }
669                 if(llen != index){
670                     cm.setLocked(index, true, true);
671                     cm.moveColumn(index, llen);
672                     this.grid.fireEvent('columnmove', index, llen);
673                 }else{
674                     cm.setLocked(index, true);
675                 }
676             break;
677             case 'unlock':
678                 if(llen - 1 != index){
679                     cm.setLocked(index, false, true);
680                     cm.moveColumn(index, llen - 1);
681                     this.grid.fireEvent('columnmove', index, llen - 1);
682                 }else{
683                     cm.setLocked(index, false);
684                 }
685             break;
686             default:
687                 return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);
688         }
689         return true;
690     },
691     
692     handleHdDown : function(e, t){
693         Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);
694         if(this.grid.enableColLock !== false){
695             if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
696                 var hd = this.findHeaderCell(t),
697                     index = this.getCellIndex(hd),
698                     ms = this.hmenu.items, cm = this.cm;
699                 ms.get('lock').setDisabled(cm.isLocked(index));
700                 ms.get('unlock').setDisabled(!cm.isLocked(index));
701             }
702         }
703     },
704     
705     syncHeaderHeight: function(){
706         this.innerHd.firstChild.firstChild.style.height = 'auto';
707         this.lockedInnerHd.firstChild.firstChild.style.height = 'auto';
708         var hd = this.innerHd.firstChild.firstChild.offsetHeight,
709             lhd = this.lockedInnerHd.firstChild.firstChild.offsetHeight,
710             height = (lhd > hd ? lhd : hd) + 'px';
711         this.innerHd.firstChild.firstChild.style.height = height;
712         this.lockedInnerHd.firstChild.firstChild.style.height = height;
713     },
714     
715     updateLockedWidth: function(){
716         var lw = this.cm.getTotalLockedWidth(),
717             tw = this.cm.getTotalWidth() - lw,
718             csize = this.grid.getGridEl().getSize(true),
719             lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,
720             rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,
721             vw = (csize.width - lw - lp - rp) + 'px',
722             so = this.getScrollOffset();
723         if(!this.grid.autoHeight){
724             var vh = (csize.height - this.mainHd.getHeight()) + 'px';
725             this.lockedScroller.dom.style.height = vh;
726             this.scroller.dom.style.height = vh;
727         }
728         this.lockedWrap.dom.style.width = (lw + rp) + 'px';
729         this.scroller.dom.style.width = vw;
730         this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';
731         if(this.innerHd){
732             this.lockedInnerHd.firstChild.style.width = lw + 'px';
733             this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';
734             this.innerHd.style.width = vw;
735             this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';
736             this.innerHd.firstChild.firstChild.style.width = tw + 'px';
737         }
738         if(this.mainBody){
739             this.lockedBody.dom.style.width = (lw + rp) + 'px';
740             this.mainBody.dom.style.width = (tw + rp) + 'px';
741         }
742     }
743 });
744
745 Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {
746     /**
747      * Returns true if the given column index is currently locked
748      * @param {Number} colIndex The column index
749      * @return {Boolean} True if the column is locked
750      */
751     isLocked : function(colIndex){
752         return this.config[colIndex].locked === true;
753     },
754     
755     /**
756      * Locks or unlocks a given column
757      * @param {Number} colIndex The column index
758      * @param {Boolean} value True to lock, false to unlock
759      * @param {Boolean} suppressEvent Pass false to cause the columnlockchange event not to fire
760      */
761     setLocked : function(colIndex, value, suppressEvent){
762         if (this.isLocked(colIndex) == value) {
763             return;
764         }
765         this.config[colIndex].locked = value;
766         if (!suppressEvent) {
767             this.fireEvent('columnlockchange', this, colIndex, value);
768         }
769     },
770     
771     /**
772      * Returns the total width of all locked columns
773      * @return {Number} The width of all locked columns
774      */
775     getTotalLockedWidth : function(){
776         var totalWidth = 0;
777         for (var i = 0, len = this.config.length; i < len; i++) {
778             if (this.isLocked(i) && !this.isHidden(i)) {
779                 totalWidth += this.getColumnWidth(i);
780             }
781         }
782         
783         return totalWidth;
784     },
785     
786     /**
787      * Returns the total number of locked columns
788      * @return {Number} The number of locked columns
789      */
790     getLockedCount : function() {
791         var len = this.config.length;
792         
793         for (var i = 0; i < len; i++) {
794             if (!this.isLocked(i)) {
795                 return i;
796             }
797         }
798         
799         //if we get to this point all of the columns are locked so we return the total
800         return len;
801     },
802     
803     /**
804      * Moves a column from one position to another
805      * @param {Number} oldIndex The current column index
806      * @param {Number} newIndex The destination column index
807      */
808     moveColumn : function(oldIndex, newIndex){
809         var oldLocked = this.isLocked(oldIndex),
810             newLocked = this.isLocked(newIndex);
811         
812         if (oldIndex < newIndex && oldLocked && !newLocked) {
813             this.setLocked(oldIndex, false, true);
814         } else if (oldIndex > newIndex && !oldLocked && newLocked) {
815             this.setLocked(oldIndex, true, true);
816         }
817         
818         Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);
819     }
820 });