source: trunk/web/addons/job_monarch/lib/extjs/source/widgets/grid/GridView.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 54.2 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.grid.GridView
11 * @extends Ext.util.Observable
12 * <p>This class encapsulates the user interface of an {@link Ext.grid.GridPanel}.
13 * Methods of this class may be used to access user interface elements to enable
14 * special display effects. Do not change the DOM structure of the user interface.</p>
15 * <p>This class does not provide ways to manipulate the underlying data. The data
16 * model of a Grid is held in an {@link Ext.data.Store}.</p>
17 * @constructor
18 * @param {Object} config
19 */
20Ext.grid.GridView = function(config){
21    Ext.apply(this, config);
22    // These events are only used internally by the grid components
23    this.addEvents(
24      /**
25         * @event beforerowremoved
26         * Internal UI Event. Fired before a row is removed.
27         * @param {Ext.grid.GridView} view
28         * @param {Number} rowIndex The index of the row to be removed.
29         * @param {Ext.data.Record} record The Record to be removed
30       */
31      "beforerowremoved",
32      /**
33         * @event beforerowsinserted
34         * Internal UI Event. Fired before rows are inserted.
35         * @param {Ext.grid.GridView} view
36         * @param {Number} firstRow The index of the first row to be inserted.
37         * @param {Number} lastRow The index of the last row to be inserted.
38       */
39      "beforerowsinserted",
40      /**
41         * @event beforerefresh
42         * Internal UI Event. Fired before the view is refreshed.
43         * @param {Ext.grid.GridView} view
44       */
45      "beforerefresh",
46      /**
47         * @event rowremoved
48         * Internal UI Event. Fired after a row is removed.
49         * @param {Ext.grid.GridView} view
50         * @param {Number} rowIndex The index of the row that was removed.
51         * @param {Ext.data.Record} record The Record that was removed
52       */
53      "rowremoved",
54      /**
55         * @event rowsinserted
56         * Internal UI Event. Fired after rows are inserted.
57         * @param {Ext.grid.GridView} view
58         * @param {Number} firstRow The index of the first inserted.
59         * @param {Number} lastRow The index of the last row inserted.
60       */
61      "rowsinserted",
62      /**
63         * @event rowupdated
64         * Internal UI Event. Fired after a row has been updated.
65         * @param {Ext.grid.GridView} view
66         * @param {Number} firstRow The index of the row updated.
67         * @param {Ext.data.record} record The Record backing the row updated.
68       */
69      "rowupdated",
70      /**
71         * @event refresh
72         * Internal UI Event. Fired after the GridView's body has been refreshed.
73         * @param {Ext.grid.GridView} view
74       */
75      "refresh"
76  );
77    Ext.grid.GridView.superclass.constructor.call(this);
78};
79
80Ext.extend(Ext.grid.GridView, Ext.util.Observable, {
81    /**
82     * Override this function to apply custom CSS classes to rows during rendering.  You can also supply custom
83     * parameters to the row template for the current row to customize how it is rendered using the <b>rowParams</b>
84     * parameter.  This function should return the CSS class name (or empty string '' for none) that will be added
85     * to the row's wrapping div.  To apply multiple class names, simply return them space-delimited within the string
86     * (e.g., 'my-class another-class').
87     * @param {Record} record The {@link Ext.data.Record} corresponding to the current row
88     * @param {Number} index The row index
89     * @param {Object} rowParams A config object that is passed to the row template during rendering that allows
90     * customization of various aspects of a body row, if applicable.  Note that this object will only be applied if
91     * {@link #enableRowBody} = true, otherwise it will be ignored. The object may contain any of these properties:<ul>
92     * <li><code>body</code> : String <div class="sub-desc">An HTML fragment to be rendered as the cell's body content (defaults to '').</div></li>
93     * <li><code>bodyStyle</code> : String <div class="sub-desc">A CSS style string that will be applied to the row's TR style attribute (defaults to '').</div></li>
94     * <li><code>cols</code> : Number <div class="sub-desc">The column count to apply to the body row's TD colspan attribute (defaults to the current
95     * column count of the grid).</div></li>
96     * </ul>
97     * @param {Store} store The {@link Ext.data.Store} this grid is bound to
98     * @method getRowClass
99     * @return {String} a CSS class name to add to the row.
100     */
101    /**
102     * @cfg {Boolean} enableRowBody True to add a second TR element per row that can be used to provide a row body
103     * that spans beneath the data row.  Use the {@link #getRowClass} method's rowParams config to customize the row body.
104     */
105    /**
106     * @cfg {String} emptyText Default text to display in the grid body when no rows are available (defaults to '').
107     */
108    /**
109     * @property dragZone
110     * @type Ext.grid.GridDragZone
111     * <p><b>This will only be present if the owning GridPanel was configured with {@link Ext.grid.GridPanel#enableDragDrop enableDragDrop} <tt>true</tt>.</b></p>
112     * <p><b>This will only be present after the owning GridPanel has been rendered</b>.</p>
113     * <p>A customized implementation of a {@link Ext.dd.DragZone DragZone} which provides default implementations of the
114     * template methods of DragZone to enable dragging of the selected rows of a GridPanel. See {@link Ext.grid.GridDragZone} for details.</p>
115     */
116    /**
117     * @cfg {Boolean} deferEmptyText True to defer emptyText being applied until the store's first load
118     */
119    deferEmptyText: true,
120    /**
121     * The amount of space to reserve for the scrollbar (defaults to 19 pixels)
122     * @type Number
123     */
124    scrollOffset: 19,
125    /**
126     * @cfg {Boolean} autoFill True to auto expand the columns to fit the grid <b>when the grid is created</b>.
127     */
128    autoFill: false,
129    /**
130     * @cfg {Boolean} forceFit True to auto expand/contract the size of the columns to fit the grid width and prevent horizontal scrolling.
131     * This option overrides any (@link Ext.grid.ColumnModel#width width} settings in the ColumnModel.
132     */
133    forceFit: false,
134    /**
135     * The CSS classes applied to a header when it is sorted. (defaults to ["sort-asc", "sort-desc"])
136     * @type Array
137     */
138    sortClasses : ["sort-asc", "sort-desc"],
139    /**
140     * The text displayed in the "Sort Ascending" menu item
141     * @type String
142     */
143    sortAscText : "Sort Ascending",
144    /**
145     * The text displayed in the "Sort Descending" menu item
146     * @type String
147     */
148    sortDescText : "Sort Descending",
149    /**
150     * The text displayed in the "Columns" menu item
151     * @type String
152     */
153    columnsText : "Columns",
154
155    // private
156    borderWidth: 2,
157    tdClass: 'x-grid3-cell',
158    hdCls: 'x-grid3-hd',
159
160    /**
161     * @cfg {Number} cellSelectorDepth The number of levels to search for cells in event delegation (defaults to 4)
162     */
163    cellSelectorDepth: 4,
164    /**
165     * @cfg {Number} rowSelectorDepth The number of levels to search for rows in event delegation (defaults to 10)
166     */
167    rowSelectorDepth: 10,
168
169    /**
170     * @cfg {String} cellSelector The selector used to find cells internally
171     */
172    cellSelector: 'td.x-grid3-cell',
173    /**
174     * @cfg {String} rowSelector The selector used to find rows internally
175     */
176    rowSelector: 'div.x-grid3-row',
177
178    /* -------------------------------- UI Specific ----------------------------- */
179
180    // private
181    initTemplates : function(){
182        var ts = this.templates || {};
183        if(!ts.master){
184            ts.master = new Ext.Template(
185                    '<div class="x-grid3" hidefocus="true">',
186                        '<div class="x-grid3-viewport">',
187                            '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset">{header}</div></div><div class="x-clear"></div></div>',
188                            '<div class="x-grid3-scroller"><div class="x-grid3-body">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
189                        "</div>",
190                        '<div class="x-grid3-resize-marker">&#160;</div>',
191                        '<div class="x-grid3-resize-proxy">&#160;</div>',
192                    "</div>"
193                    );
194        }
195
196        if(!ts.header){
197            ts.header = new Ext.Template(
198                    '<table border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
199                    '<thead><tr class="x-grid3-hd-row">{cells}</tr></thead>',
200                    "</table>"
201                    );
202        }
203
204        if(!ts.hcell){
205            ts.hcell = new Ext.Template(
206                    '<td class="x-grid3-hd x-grid3-cell x-grid3-td-{id} {css}" style="{style}"><div {tooltip} {attr} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
207                    '{value}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
208                    "</div></td>"
209                    );
210        }
211
212        if(!ts.body){
213            ts.body = new Ext.Template('{rows}');
214        }
215
216        if(!ts.row){
217            ts.row = new Ext.Template(
218                    '<div class="x-grid3-row {alt}" style="{tstyle}"><table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
219                    '<tbody><tr>{cells}</tr>',
220                    (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
221                    '</tbody></table></div>'
222                    );
223        }
224
225        if(!ts.cell){
226            ts.cell = new Ext.Template(
227                    '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
228                    '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
229                    "</td>"
230                    );
231        }
232
233        for(var k in ts){
234            var t = ts[k];
235            if(t && typeof t.compile == 'function' && !t.compiled){
236                t.disableFormats = true;
237                t.compile();
238            }
239        }
240
241        this.templates = ts;
242        this.colRe = new RegExp("x-grid3-td-([^\\s]+)", "");
243    },
244
245    // private
246    fly : function(el){
247        if(!this._flyweight){
248            this._flyweight = new Ext.Element.Flyweight(document.body);
249        }
250        this._flyweight.dom = el;
251        return this._flyweight;
252    },
253
254    // private
255    getEditorParent : function(){
256        return this.scroller.dom;
257    },
258
259    // private
260    initElements : function(){
261        var E = Ext.Element;
262
263        var el = this.grid.getGridEl().dom.firstChild;
264        var cs = el.childNodes;
265
266        this.el = new E(el);
267
268        this.mainWrap = new E(cs[0]);
269        this.mainHd = new E(this.mainWrap.dom.firstChild);
270
271        if(this.grid.hideHeaders){
272            this.mainHd.setDisplayed(false);
273        }
274
275        this.innerHd = this.mainHd.dom.firstChild;
276        this.scroller = new E(this.mainWrap.dom.childNodes[1]);
277        if(this.forceFit){
278            this.scroller.setStyle('overflow-x', 'hidden');
279        }
280        /**
281         * The GridView's body Element which encapsulates all rows in the Grid. {@link Ext.Element Element}. Read-only.
282         * <p>This Element is only available after the GridPanel has been rendered.</p>
283         * @type Ext.Element
284         * @property mainBody
285         */
286        this.mainBody = new E(this.scroller.dom.firstChild);
287
288        this.focusEl = new E(this.scroller.dom.childNodes[1]);
289        this.focusEl.swallowEvent("click", true);
290
291        this.resizeMarker = new E(cs[1]);
292        this.resizeProxy = new E(cs[2]);
293    },
294
295    // private
296    getRows : function(){
297        return this.hasRows() ? this.mainBody.dom.childNodes : [];
298    },
299
300    // finder methods, used with delegation
301
302    // private
303    findCell : function(el){
304        if(!el){
305            return false;
306        }
307        return this.fly(el).findParent(this.cellSelector, this.cellSelectorDepth);
308    },
309
310    // private
311    findCellIndex : function(el, requiredCls){
312        var cell = this.findCell(el);
313        if(cell && (!requiredCls || this.fly(cell).hasClass(requiredCls))){
314            return this.getCellIndex(cell);
315        }
316        return false;
317    },
318
319    // private
320    getCellIndex : function(el){
321        if(el){
322            var m = el.className.match(this.colRe);
323            if(m && m[1]){
324                return this.cm.getIndexById(m[1]);
325            }
326        }
327        return false;
328    },
329
330    // private
331    findHeaderCell : function(el){
332        var cell = this.findCell(el);
333        return cell && this.fly(cell).hasClass(this.hdCls) ? cell : null;
334    },
335
336    // private
337    findHeaderIndex : function(el){
338        return this.findCellIndex(el, this.hdCls);
339    },
340
341/**
342 * Return the HtmlElement representing the grid row which contains the passed element.
343 * @param {Element} el The target element
344 * @return The row element, or null if the target element is not within a row of this GridView.
345 */
346    findRow : function(el){
347        if(!el){
348            return false;
349        }
350        return this.fly(el).findParent(this.rowSelector, this.rowSelectorDepth);
351    },
352
353/**
354 * Return the index of the grid row which contains the passed element.
355 * @param {Element} el The target element
356 * @return The row index, or <b>false</b> if the target element is not within a row of this GridView.
357 */
358    findRowIndex : function(el){
359        var r = this.findRow(el);
360        return r ? r.rowIndex : false;
361    },
362
363    // getter methods for fetching elements dynamically in the grid
364
365/**
366 * Return the &lt;TR> HtmlElement which represents a Grid row for the specified index.
367 * @param {Number} index The row index
368 * @return {HtmlElement} The &lt;TR> element.
369 */
370    getRow : function(row){
371        return this.getRows()[row];
372    },
373
374/**
375 * Returns the grid's &lt;TD> HtmlElement at the specified coordinates.
376 * @param {Number} row The row index in which to find the cell.
377 * @param {Number} col The column index of the cell.
378 * @return {HtmlElement} The &lt;TD> at the specified coordinates.
379 */
380    getCell : function(row, col){
381        return this.getRow(row).getElementsByTagName('td')[col];
382    },
383
384/**
385 * Return the &lt;TD> HtmlElement which represents the Grid's header cell for the specified column index.
386 * @param {Number} index The column index
387 * @return {HtmlElement} The &lt;TD> element.
388 */
389    getHeaderCell : function(index){
390      return this.mainHd.dom.getElementsByTagName('td')[index];
391    },
392
393    // manipulating elements
394
395    // private - use getRowClass to apply custom row classes
396    addRowClass : function(row, cls){
397        var r = this.getRow(row);
398        if(r){
399            this.fly(r).addClass(cls);
400        }
401    },
402
403    // private
404    removeRowClass : function(row, cls){
405        var r = this.getRow(row);
406        if(r){
407            this.fly(r).removeClass(cls);
408        }
409    },
410
411    // private
412    removeRow : function(row){
413        Ext.removeNode(this.getRow(row));
414        this.syncFocusEl(row);
415    },
416   
417    // private
418    removeRows : function(firstRow, lastRow){
419        var bd = this.mainBody.dom;
420        for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
421            Ext.removeNode(bd.childNodes[firstRow]);
422        }
423        this.syncFocusEl(firstRow);
424    },
425
426    // scrolling stuff
427
428    // private
429    getScrollState : function(){
430        var sb = this.scroller.dom;
431        return {left: sb.scrollLeft, top: sb.scrollTop};
432    },
433
434    // private
435    restoreScroll : function(state){
436        var sb = this.scroller.dom;
437        sb.scrollLeft = state.left;
438        sb.scrollTop = state.top;
439    },
440
441    /**
442     * Scrolls the grid to the top
443     */
444    scrollToTop : function(){
445        this.scroller.dom.scrollTop = 0;
446        this.scroller.dom.scrollLeft = 0;
447    },
448
449    // private
450    syncScroll : function(){
451      this.syncHeaderScroll();
452      var mb = this.scroller.dom;
453        this.grid.fireEvent("bodyscroll", mb.scrollLeft, mb.scrollTop);
454    },
455
456    // private
457    syncHeaderScroll : function(){
458        var mb = this.scroller.dom;
459        this.innerHd.scrollLeft = mb.scrollLeft;
460        this.innerHd.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
461    },
462
463    // private
464    updateSortIcon : function(col, dir){
465        var sc = this.sortClasses;
466        var hds = this.mainHd.select('td').removeClass(sc);
467        hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
468    },
469
470    // private
471    updateAllColumnWidths : function(){
472        var tw = this.getTotalWidth();
473        var clen = this.cm.getColumnCount();
474        var ws = [];
475        for(var i = 0; i < clen; i++){
476            ws[i] = this.getColumnWidth(i);
477        }
478
479        this.innerHd.firstChild.firstChild.style.width = tw;
480
481        for(var i = 0; i < clen; i++){
482            var hd = this.getHeaderCell(i);
483            hd.style.width = ws[i];
484        }
485
486        var ns = this.getRows(), row, trow;
487        for(var i = 0, len = ns.length; i < len; i++){
488            row = ns[i];
489            row.style.width = tw;
490            if(row.firstChild){
491                row.firstChild.style.width = tw;
492                trow = row.firstChild.rows[0];
493                for (var j = 0; j < clen; j++) {
494                   trow.childNodes[j].style.width = ws[j];
495                }
496            }
497        }
498
499        this.onAllColumnWidthsUpdated(ws, tw);
500    },
501
502    // private
503    updateColumnWidth : function(col, width){
504        var w = this.getColumnWidth(col);
505        var tw = this.getTotalWidth();
506
507        this.innerHd.firstChild.firstChild.style.width = tw;
508        var hd = this.getHeaderCell(col);
509        hd.style.width = w;
510
511        var ns = this.getRows(), row;
512        for(var i = 0, len = ns.length; i < len; i++){
513            row = ns[i];
514            row.style.width = tw;
515            if(row.firstChild){
516                row.firstChild.style.width = tw;
517                row.firstChild.rows[0].childNodes[col].style.width = w;
518            }
519        }
520
521        this.onColumnWidthUpdated(col, w, tw);
522    },
523
524    // private
525    updateColumnHidden : function(col, hidden){
526        var tw = this.getTotalWidth();
527
528        this.innerHd.firstChild.firstChild.style.width = tw;
529
530        var display = hidden ? 'none' : '';
531
532        var hd = this.getHeaderCell(col);
533        hd.style.display = display;
534
535        var ns = this.getRows(), row;
536        for(var i = 0, len = ns.length; i < len; i++){
537            row = ns[i];
538            row.style.width = tw;
539            if(row.firstChild){
540                row.firstChild.style.width = tw;
541                row.firstChild.rows[0].childNodes[col].style.display = display;
542            }
543        }
544
545        this.onColumnHiddenUpdated(col, hidden, tw);
546
547        delete this.lastViewWidth; // force recalc
548        this.layout();
549    },
550
551    // private
552    doRender : function(cs, rs, ds, startRow, colCount, stripe){
553        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1;
554        var tstyle = 'width:'+this.getTotalWidth()+';';
555        // buffers
556        var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
557        for(var j = 0, len = rs.length; j < len; j++){
558            r = rs[j]; cb = [];
559            var rowIndex = (j+startRow);
560            for(var i = 0; i < colCount; i++){
561                c = cs[i];
562                p.id = c.id;
563                p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
564                p.attr = p.cellAttr = "";
565                p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
566                p.style = c.style;
567                if(p.value == undefined || p.value === "") p.value = "&#160;";
568                if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
569                    p.css += ' x-grid3-dirty-cell';
570                }
571                cb[cb.length] = ct.apply(p);
572            }
573            var alt = [];
574            if(stripe && ((rowIndex+1) % 2 == 0)){
575                alt[0] = "x-grid3-row-alt";
576            }
577            if(r.dirty){
578                alt[1] = " x-grid3-dirty-row";
579            }
580            rp.cols = colCount;
581            if(this.getRowClass){
582                alt[2] = this.getRowClass(r, rowIndex, rp, ds);
583            }
584            rp.alt = alt.join(" ");
585            rp.cells = cb.join("");
586            buf[buf.length] =  rt.apply(rp);
587        }
588        return buf.join("");
589    },
590
591    // private
592    processRows : function(startRow, skipStripe){
593        if(this.ds.getCount() < 1){
594            return;
595        }
596        skipStripe = skipStripe || !this.grid.stripeRows;
597        startRow = startRow || 0;
598        var rows = this.getRows();
599        var cls = ' x-grid3-row-alt ';
600        rows[0].className += ' x-grid3-row-first';
601        rows[rows.length - 1].className += ' x-grid3-row-last';
602        for(var i = startRow, len = rows.length; i < len; i++){
603            var row = rows[i];
604            row.rowIndex = i;
605            if(!skipStripe){
606                var isAlt = ((i+1) % 2 == 0);
607                var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
608                if(isAlt == hasAlt){
609                    continue;
610                }
611                if(isAlt){
612                    row.className += " x-grid3-row-alt";
613                }else{
614                    row.className = row.className.replace("x-grid3-row-alt", "");
615                }
616            }
617        }
618    },
619
620    afterRender: function(){
621        this.mainBody.dom.innerHTML = this.renderRows();
622        this.processRows(0, true);
623
624        if(this.deferEmptyText !== true){
625            this.applyEmptyText();
626        }
627    },
628
629    // private
630    renderUI : function(){
631
632        var header = this.renderHeaders();
633        var body = this.templates.body.apply({rows:''});
634
635
636        var html = this.templates.master.apply({
637            body: body,
638            header: header
639        });
640
641        var g = this.grid;
642
643        g.getGridEl().dom.innerHTML = html;
644
645        this.initElements();
646
647        // get mousedowns early
648        Ext.fly(this.innerHd).on("click", this.handleHdDown, this);
649        this.mainHd.on("mouseover", this.handleHdOver, this);
650        this.mainHd.on("mouseout", this.handleHdOut, this);
651        this.mainHd.on("mousemove", this.handleHdMove, this);
652
653        this.scroller.on('scroll', this.syncScroll,  this);
654        if(g.enableColumnResize !== false){
655            this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
656        }
657
658        if(g.enableColumnMove){
659            this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
660            this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
661        }
662
663        if(g.enableHdMenu !== false){
664            if(g.enableColumnHide !== false){
665                this.colMenu = new Ext.menu.Menu({id:g.id + "-hcols-menu"});
666                this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
667                this.colMenu.on("itemclick", this.handleHdMenuClick, this);
668            }
669            this.hmenu = new Ext.menu.Menu({id: g.id + "-hctx"});
670            this.hmenu.add(
671                {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
672                {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
673            );
674            if(g.enableColumnHide !== false){
675                this.hmenu.add('-',
676                    {id:"columns", text: this.columnsText, menu: this.colMenu, iconCls: 'x-cols-icon'}
677                );
678            }
679            this.hmenu.on("itemclick", this.handleHdMenuClick, this);
680
681            //g.on("headercontextmenu", this.handleHdCtx, this);
682        }
683
684        if(g.trackMouseOver){
685            this.mainBody.on("mouseover", this.onRowOver, this);
686            this.mainBody.on("mouseout", this.onRowOut, this);
687        }
688        if(g.enableDragDrop || g.enableDrag){
689            this.dragZone = new Ext.grid.GridDragZone(g, {
690                ddGroup : g.ddGroup || 'GridDD'
691            });
692        }
693
694        this.updateHeaderSortState();
695
696    },
697
698    // private
699    layout : function(){
700        if(!this.mainBody){
701            return; // not rendered
702        }
703        var g = this.grid;
704        var c = g.getGridEl();
705        var csize = c.getSize(true);
706        var vw = csize.width;
707
708        if(vw < 20 || csize.height < 20){ // display: none?
709            return;
710        }
711
712        if(g.autoHeight){
713            this.scroller.dom.style.overflow = 'visible';
714            if(Ext.isSafari){
715                this.scroller.dom.style.position = 'static';
716            }
717        }else{
718            this.el.setSize(csize.width, csize.height);
719
720            var hdHeight = this.mainHd.getHeight();
721            var vh = csize.height - (hdHeight);
722
723            this.scroller.setSize(vw, vh);
724            if(this.innerHd){
725                this.innerHd.style.width = (vw)+'px';
726            }
727        }
728        if(this.forceFit){
729            if(this.lastViewWidth != vw){
730                this.fitColumns(false, false);
731                this.lastViewWidth = vw;
732            }
733        }else {
734            this.autoExpand();
735            this.syncHeaderScroll();
736        }
737        this.onLayout(vw, vh);
738    },
739
740    // template functions for subclasses and plugins
741    // these functions include precalculated values
742    onLayout : function(vw, vh){
743        // do nothing
744    },
745
746    onColumnWidthUpdated : function(col, w, tw){
747        //template method
748        this.focusEl.setWidth(tw);
749    },
750
751    onAllColumnWidthsUpdated : function(ws, tw){
752        //template method
753        this.focusEl.setWidth(tw);
754    },
755
756    onColumnHiddenUpdated : function(col, hidden, tw){
757        // template method
758        this.focusEl.setWidth(tw);
759    },
760
761    updateColumnText : function(col, text){
762        // template method
763    },
764
765    afterMove : function(colIndex){
766        // template method
767    },
768
769    /* ----------------------------------- Core Specific -------------------------------------------*/
770    // private
771    init: function(grid){
772        this.grid = grid;
773
774        this.initTemplates();
775        this.initData(grid.store, grid.colModel);
776        this.initUI(grid);
777    },
778
779    // private
780    getColumnId : function(index){
781      return this.cm.getColumnId(index);
782    },
783
784    // private
785    renderHeaders : function(){
786        var cm = this.cm, ts = this.templates;
787        var ct = ts.hcell;
788
789        var cb = [], sb = [], p = {};
790        var len = cm.getColumnCount();
791        var last = len - 1;
792        for(var i = 0; i < len; i++){
793            p.id = cm.getColumnId(i);
794            p.value = cm.getColumnHeader(i) || "";
795            p.style = this.getColumnStyle(i, true);
796            p.tooltip = this.getColumnTooltip(i);
797            p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
798            if(cm.config[i].align == 'right'){
799                p.istyle = 'padding-right:16px';
800            } else {
801                delete p.istyle;
802            }
803            cb[cb.length] = ct.apply(p);
804        }
805        return ts.header.apply({cells: cb.join(""), tstyle:'width:'+this.getTotalWidth()+';'});
806    },
807
808    // private
809    getColumnTooltip : function(i){
810        var tt = this.cm.getColumnTooltip(i);
811        if(tt){
812            if(Ext.QuickTips.isEnabled()){
813                return 'ext:qtip="'+tt+'"';
814            }else{
815                return 'title="'+tt+'"';
816            }
817        }
818        return "";
819    },
820
821    // private
822    beforeUpdate : function(){
823        this.grid.stopEditing(true);
824    },
825
826    // private
827    updateHeaders : function(){
828        this.innerHd.firstChild.innerHTML = this.renderHeaders();
829    },
830
831    /**
832     * Focuses the specified row.
833     * @param {Number} row The row index
834     */
835    focusRow : function(row){
836        this.focusCell(row, 0, false);
837    },
838
839    /**
840     * Focuses the specified cell.
841     * @param {Number} row The row index
842     * @param {Number} col The column index
843     */
844    focusCell : function(row, col, hscroll){
845                this.syncFocusEl(this.ensureVisible(row, col, hscroll));
846        if(Ext.isGecko){
847            this.focusEl.focus();
848        }else{
849            this.focusEl.focus.defer(1, this.focusEl);
850        }
851    },
852
853        resolveCell : function(row, col, hscroll){
854                if(typeof row != "number"){
855            row = row.rowIndex;
856        }
857        if(!this.ds){
858            return null;
859        }
860        if(row < 0 || row >= this.ds.getCount()){
861            return null;
862        }
863        col = (col !== undefined ? col : 0);
864
865        var rowEl = this.getRow(row), cellEl;
866        if(!(hscroll === false && col === 0)){
867            while(this.cm.isHidden(col)){
868                col++;
869            }
870            cellEl = this.getCell(row, col);
871        }
872
873                return {row: rowEl, cell: cellEl};
874        },
875
876        getResolvedXY : function(resolved){
877                if(!resolved){
878                        return null;
879                }
880                var s = this.scroller.dom, c = resolved.cell, r = resolved.row;
881                return c ? Ext.fly(c).getXY() : [this.el.getX(), Ext.fly(r).getY()];
882        },
883
884        syncFocusEl : function(row, col, hscroll){
885                var xy = row;
886                if(!Ext.isArray(xy)){
887                        row = Math.min(row, Math.max(0, this.getRows().length-1));
888                xy = this.getResolvedXY(this.resolveCell(row, col, hscroll));
889                }
890        this.focusEl.setXY(xy||this.scroller.getXY());
891    },
892
893        ensureVisible : function(row, col, hscroll){
894        var resolved = this.resolveCell(row, col, hscroll);
895                if(!resolved || !resolved.row){
896                        return;
897                }
898
899                var rowEl = resolved.row, cellEl = resolved.cell;
900
901                var c = this.scroller.dom;
902
903        var ctop = 0;
904        var p = rowEl, stop = this.el.dom;
905        while(p && p != stop){
906            ctop += p.offsetTop;
907            p = p.offsetParent;
908        }
909        ctop -= this.mainHd.dom.offsetHeight;
910
911        var cbot = ctop + rowEl.offsetHeight;
912
913        var ch = c.clientHeight;
914        var stop = parseInt(c.scrollTop, 10);
915        var sbot = stop + ch;
916
917                if(ctop < stop){
918          c.scrollTop = ctop;
919        }else if(cbot > sbot){
920            c.scrollTop = cbot-ch;
921        }
922
923        if(hscroll !== false){
924            var cleft = parseInt(cellEl.offsetLeft, 10);
925            var cright = cleft + cellEl.offsetWidth;
926
927            var sleft = parseInt(c.scrollLeft, 10);
928            var sright = sleft + c.clientWidth;
929            if(cleft < sleft){
930                c.scrollLeft = cleft;
931            }else if(cright > sright){
932                c.scrollLeft = cright-c.clientWidth;
933            }
934        }
935        return this.getResolvedXY(resolved);
936    },
937
938    // private
939    insertRows : function(dm, firstRow, lastRow, isUpdate){
940        if(!isUpdate && firstRow === 0 && lastRow >= dm.getCount()-1){
941            this.refresh();
942        }else{
943            if(!isUpdate){
944                this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
945            }
946            var html = this.renderRows(firstRow, lastRow);
947            var before = this.getRow(firstRow);
948            if(before){
949                Ext.DomHelper.insertHtml('beforeBegin', before, html);
950            }else{
951                Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html);
952            }
953            if(!isUpdate){
954                this.fireEvent("rowsinserted", this, firstRow, lastRow);
955                this.processRows(firstRow);
956            }
957        }
958        this.syncFocusEl(firstRow);
959    },
960
961    // private
962    deleteRows : function(dm, firstRow, lastRow){
963        if(dm.getRowCount()<1){
964            this.refresh();
965        }else{
966            this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
967
968            this.removeRows(firstRow, lastRow);
969
970            this.processRows(firstRow);
971            this.fireEvent("rowsdeleted", this, firstRow, lastRow);
972        }
973    },
974
975    // private
976    getColumnStyle : function(col, isHeader){
977        var style = !isHeader ? (this.cm.config[col].css || '') : '';
978        style += 'width:'+this.getColumnWidth(col)+';';
979        if(this.cm.isHidden(col)){
980            style += 'display:none;';
981        }
982        var align = this.cm.config[col].align;
983        if(align){
984            style += 'text-align:'+align+';';
985        }
986        return style;
987    },
988
989    // private
990    getColumnWidth : function(col){
991        var w = this.cm.getColumnWidth(col);
992        if(typeof w == 'number'){
993            return (Ext.isBorderBox ? w : (w-this.borderWidth > 0 ? w-this.borderWidth:0)) + 'px';
994        }
995        return w;
996    },
997
998    // private
999    getTotalWidth : function(){
1000        return this.cm.getTotalWidth()+'px';
1001    },
1002
1003    // private
1004    fitColumns : function(preventRefresh, onlyExpand, omitColumn){
1005        var cm = this.cm, leftOver, dist, i;
1006        var tw = cm.getTotalWidth(false);
1007        var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;
1008
1009        if(aw < 20){ // not initialized, so don't screw up the default widths
1010            return;
1011        }
1012        var extra = aw - tw;
1013
1014        if(extra === 0){
1015            return false;
1016        }
1017
1018        var vc = cm.getColumnCount(true);
1019        var ac = vc-(typeof omitColumn == 'number' ? 1 : 0);
1020        if(ac === 0){
1021            ac = 1;
1022            omitColumn = undefined;
1023        }
1024        var colCount = cm.getColumnCount();
1025        var cols = [];
1026        var extraCol = 0;
1027        var width = 0;
1028        var w;
1029        for (i = 0; i < colCount; i++){
1030            if(!cm.isHidden(i) && !cm.isFixed(i) && i !== omitColumn){
1031                w = cm.getColumnWidth(i);
1032                cols.push(i);
1033                extraCol = i;
1034                cols.push(w);
1035                width += w;
1036            }
1037        }
1038        var frac = (aw - cm.getTotalWidth())/width;
1039        while (cols.length){
1040            w = cols.pop();
1041            i = cols.pop();
1042            cm.setColumnWidth(i, Math.max(this.grid.minColumnWidth, Math.floor(w + w*frac)), true);
1043        }
1044
1045        if((tw = cm.getTotalWidth(false)) > aw){
1046            var adjustCol = ac != vc ? omitColumn : extraCol;
1047             cm.setColumnWidth(adjustCol, Math.max(1,
1048                     cm.getColumnWidth(adjustCol)- (tw-aw)), true);
1049        }
1050
1051        if(preventRefresh !== true){
1052            this.updateAllColumnWidths();
1053        }
1054
1055
1056        return true;
1057    },
1058
1059    // private
1060    autoExpand : function(preventUpdate){
1061        var g = this.grid, cm = this.cm;
1062        if(!this.userResized && g.autoExpandColumn){
1063            var tw = cm.getTotalWidth(false);
1064            var aw = this.grid.getGridEl().getWidth(true)-this.scrollOffset;
1065            if(tw != aw){
1066                var ci = cm.getIndexById(g.autoExpandColumn);
1067                var currentWidth = cm.getColumnWidth(ci);
1068                var cw = Math.min(Math.max(((aw-tw)+currentWidth), g.autoExpandMin), g.autoExpandMax);
1069                if(cw != currentWidth){
1070                    cm.setColumnWidth(ci, cw, true);
1071                    if(preventUpdate !== true){
1072                        this.updateColumnWidth(ci, cw);
1073                    }
1074                }
1075            }
1076        }
1077    },
1078
1079    // private
1080    getColumnData : function(){
1081        // build a map for all the columns
1082        var cs = [], cm = this.cm, colCount = cm.getColumnCount();
1083        for(var i = 0; i < colCount; i++){
1084            var name = cm.getDataIndex(i);
1085            cs[i] = {
1086                name : (typeof name == 'undefined' ? this.ds.fields.get(i).name : name),
1087                renderer : cm.getRenderer(i),
1088                id : cm.getColumnId(i),
1089                style : this.getColumnStyle(i)
1090            };
1091        }
1092        return cs;
1093    },
1094
1095    // private
1096    renderRows : function(startRow, endRow){
1097        // pull in all the crap needed to render rows
1098        var g = this.grid, cm = g.colModel, ds = g.store, stripe = g.stripeRows;
1099        var colCount = cm.getColumnCount();
1100
1101        if(ds.getCount() < 1){
1102            return "";
1103        }
1104
1105        var cs = this.getColumnData();
1106
1107        startRow = startRow || 0;
1108        endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
1109
1110        // records to render
1111        var rs = ds.getRange(startRow, endRow);
1112
1113        return this.doRender(cs, rs, ds, startRow, colCount, stripe);
1114    },
1115
1116    // private
1117    renderBody : function(){
1118        var markup = this.renderRows();
1119        return this.templates.body.apply({rows: markup});
1120    },
1121
1122    // private
1123    refreshRow : function(record){
1124        var ds = this.ds, index;
1125        if(typeof record == 'number'){
1126            index = record;
1127            record = ds.getAt(index);
1128        }else{
1129            index = ds.indexOf(record);
1130        }
1131        var cls = [];
1132        this.insertRows(ds, index, index, true);
1133        this.getRow(index).rowIndex = index;
1134        this.onRemove(ds, record, index+1, true);
1135        this.fireEvent("rowupdated", this, index, record);
1136    },
1137
1138    /**
1139     * Refreshs the grid UI
1140     * @param {Boolean} headersToo (optional) True to also refresh the headers
1141     */
1142    refresh : function(headersToo){
1143        this.fireEvent("beforerefresh", this);
1144        this.grid.stopEditing(true);
1145
1146        var result = this.renderBody();
1147        this.mainBody.update(result);
1148
1149        if(headersToo === true){
1150            this.updateHeaders();
1151            this.updateHeaderSortState();
1152        }
1153        this.processRows(0, true);
1154        this.layout();
1155        this.applyEmptyText();
1156        this.fireEvent("refresh", this);
1157    },
1158
1159    // private
1160    applyEmptyText : function(){
1161        if(this.emptyText && !this.hasRows()){
1162            this.mainBody.update('<div class="x-grid-empty">' + this.emptyText + '</div>');
1163        }
1164    },
1165
1166    // private
1167    updateHeaderSortState : function(){
1168        var state = this.ds.getSortState();
1169        if(!state){
1170            return;
1171        }
1172        if(!this.sortState || (this.sortState.field != state.field || this.sortState.direction != state.direction)){
1173            this.grid.fireEvent('sortchange', this.grid, state);
1174        }
1175        this.sortState = state;
1176        var sortColumn = this.cm.findColumnIndex(state.field);
1177        if(sortColumn != -1){
1178            var sortDir = state.direction;
1179            this.updateSortIcon(sortColumn, sortDir);
1180        }
1181    },
1182
1183    // private
1184    destroy : function(){
1185        if(this.colMenu){
1186            Ext.menu.MenuMgr.unregister(this.colMenu);
1187            this.colMenu.destroy();
1188            delete this.colMenu;
1189        }
1190        if(this.hmenu){
1191            Ext.menu.MenuMgr.unregister(this.hmenu);
1192            this.hmenu.destroy();
1193            delete this.hmenu;
1194        }
1195        if(this.grid.enableColumnMove){
1196            var dds = Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
1197            if(dds){
1198                for(var dd in dds){
1199                    if(!dds[dd].config.isTarget && dds[dd].dragElId){
1200                        var elid = dds[dd].dragElId;
1201                        dds[dd].unreg();
1202                        Ext.get(elid).remove();
1203                    } else if(dds[dd].config.isTarget){
1204                        dds[dd].proxyTop.remove();
1205                        dds[dd].proxyBottom.remove();
1206                        dds[dd].unreg();
1207                    }
1208                    if(Ext.dd.DDM.locationCache[dd]){
1209                        delete Ext.dd.DDM.locationCache[dd];
1210                    }
1211                }
1212                delete Ext.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
1213            }
1214        }
1215       
1216        if(this.dragZone){
1217            this.dragZone.unreg();
1218        }
1219       
1220        Ext.fly(this.innerHd).removeAllListeners();
1221        Ext.removeNode(this.innerHd);
1222       
1223        Ext.destroy(this.resizeMarker, this.resizeProxy, this.focusEl, this.mainBody, 
1224                    this.scroller, this.mainHd, this.mainWrap, this.dragZone, 
1225                    this.splitZone, this.columnDrag, this.columnDrop);
1226
1227        this.initData(null, null);
1228        Ext.EventManager.removeResizeListener(this.onWindowResize, this);
1229        this.purgeListeners();
1230    },
1231
1232    // private
1233    onDenyColumnHide : function(){
1234
1235    },
1236
1237    // private
1238    render : function(){
1239        if(this.autoFill){
1240            var ct = this.grid.ownerCt;
1241            if (ct && ct.getLayout()){
1242                ct.on('afterlayout', function(){ 
1243                    this.fitColumns(true, true);
1244                    this.updateHeaders(); 
1245                }, this, {single: true}); 
1246            }else{ 
1247                this.fitColumns(true, true); 
1248            }
1249        }else if(this.forceFit){
1250            this.fitColumns(true, false);
1251        }else if(this.grid.autoExpandColumn){
1252            this.autoExpand(true);
1253        }
1254
1255        this.renderUI();
1256    },
1257
1258    /* --------------------------------- Model Events and Handlers --------------------------------*/
1259    // private
1260    initData : function(ds, cm){
1261        if(this.ds){
1262            this.ds.un("load", this.onLoad, this);
1263            this.ds.un("datachanged", this.onDataChange, this);
1264            this.ds.un("add", this.onAdd, this);
1265            this.ds.un("remove", this.onRemove, this);
1266            this.ds.un("update", this.onUpdate, this);
1267            this.ds.un("clear", this.onClear, this);
1268        }
1269        if(ds){
1270            ds.on("load", this.onLoad, this);
1271            ds.on("datachanged", this.onDataChange, this);
1272            ds.on("add", this.onAdd, this);
1273            ds.on("remove", this.onRemove, this);
1274            ds.on("update", this.onUpdate, this);
1275            ds.on("clear", this.onClear, this);
1276        }
1277        this.ds = ds;
1278
1279        if(this.cm){
1280            this.cm.un("configchange", this.onColConfigChange, this);
1281            this.cm.un("widthchange", this.onColWidthChange, this);
1282            this.cm.un("headerchange", this.onHeaderChange, this);
1283            this.cm.un("hiddenchange", this.onHiddenChange, this);
1284            this.cm.un("columnmoved", this.onColumnMove, this);
1285            this.cm.un("columnlockchange", this.onColumnLock, this);
1286        }
1287        if(cm){
1288            delete this.lastViewWidth;
1289            cm.on("configchange", this.onColConfigChange, this);
1290            cm.on("widthchange", this.onColWidthChange, this);
1291            cm.on("headerchange", this.onHeaderChange, this);
1292            cm.on("hiddenchange", this.onHiddenChange, this);
1293            cm.on("columnmoved", this.onColumnMove, this);
1294            cm.on("columnlockchange", this.onColumnLock, this);
1295        }
1296        this.cm = cm;
1297    },
1298
1299    // private
1300    onDataChange : function(){
1301        this.refresh();
1302        this.updateHeaderSortState();
1303        this.syncFocusEl(0);
1304    },
1305
1306    // private
1307    onClear : function(){
1308        this.refresh();
1309        this.syncFocusEl(0);
1310    },
1311
1312    // private
1313    onUpdate : function(ds, record){
1314        this.refreshRow(record);
1315    },
1316
1317    // private
1318    onAdd : function(ds, records, index){
1319        this.insertRows(ds, index, index + (records.length-1));
1320    },
1321
1322    // private
1323    onRemove : function(ds, record, index, isUpdate){
1324        if(isUpdate !== true){
1325            this.fireEvent("beforerowremoved", this, index, record);
1326        }
1327        this.removeRow(index);
1328        if(isUpdate !== true){
1329            this.processRows(index);
1330            this.applyEmptyText();
1331            this.fireEvent("rowremoved", this, index, record);
1332        }
1333    },
1334
1335    // private
1336    onLoad : function(){
1337        this.scrollToTop();
1338    },
1339
1340    // private
1341    onColWidthChange : function(cm, col, width){
1342        this.updateColumnWidth(col, width);
1343    },
1344
1345    // private
1346    onHeaderChange : function(cm, col, text){
1347        this.updateHeaders();
1348    },
1349
1350    // private
1351    onHiddenChange : function(cm, col, hidden){
1352        this.updateColumnHidden(col, hidden);
1353    },
1354
1355    // private
1356    onColumnMove : function(cm, oldIndex, newIndex){
1357        this.indexMap = null;
1358        var s = this.getScrollState();
1359        this.refresh(true);
1360        this.restoreScroll(s);
1361        this.afterMove(newIndex);
1362    },
1363
1364    // private
1365    onColConfigChange : function(){
1366        delete this.lastViewWidth;
1367        this.indexMap = null;
1368        this.refresh(true);
1369    },
1370
1371    /* -------------------- UI Events and Handlers ------------------------------ */
1372    // private
1373    initUI : function(grid){
1374        grid.on("headerclick", this.onHeaderClick, this);
1375    },
1376
1377    // private
1378    initEvents : function(){
1379
1380    },
1381
1382    // private
1383    onHeaderClick : function(g, index){
1384        if(this.headersDisabled || !this.cm.isSortable(index)){
1385            return;
1386        }
1387        g.stopEditing(true);
1388        g.store.sort(this.cm.getDataIndex(index));
1389    },
1390
1391    // private
1392    onRowOver : function(e, t){
1393        var row;
1394        if((row = this.findRowIndex(t)) !== false){
1395            this.addRowClass(row, "x-grid3-row-over");
1396        }
1397    },
1398
1399    // private
1400    onRowOut : function(e, t){
1401        var row;
1402        if((row = this.findRowIndex(t)) !== false && !e.within(this.getRow(row), true)){
1403            this.removeRowClass(row, "x-grid3-row-over");
1404        }
1405    },
1406
1407    // private
1408    handleWheel : function(e){
1409        e.stopPropagation();
1410    },
1411
1412    // private
1413    onRowSelect : function(row){
1414        this.addRowClass(row, "x-grid3-row-selected");
1415    },
1416
1417    // private
1418    onRowDeselect : function(row){
1419        this.removeRowClass(row, "x-grid3-row-selected");
1420    },
1421
1422    // private
1423    onCellSelect : function(row, col){
1424        var cell = this.getCell(row, col);
1425        if(cell){
1426            this.fly(cell).addClass("x-grid3-cell-selected");
1427        }
1428    },
1429
1430    // private
1431    onCellDeselect : function(row, col){
1432        var cell = this.getCell(row, col);
1433        if(cell){
1434            this.fly(cell).removeClass("x-grid3-cell-selected");
1435        }
1436    },
1437
1438    // private
1439    onColumnSplitterMoved : function(i, w){
1440        this.userResized = true;
1441        var cm = this.grid.colModel;
1442        cm.setColumnWidth(i, w, true);
1443
1444        if(this.forceFit){
1445            this.fitColumns(true, false, i);
1446            this.updateAllColumnWidths();
1447        }else{
1448            this.updateColumnWidth(i, w);
1449            this.syncHeaderScroll();
1450        }
1451
1452        this.grid.fireEvent("columnresize", i, w);
1453    },
1454
1455    // private
1456    handleHdMenuClick : function(item){
1457        var index = this.hdCtxIndex;
1458        var cm = this.cm, ds = this.ds;
1459        switch(item.id){
1460            case "asc":
1461                ds.sort(cm.getDataIndex(index), "ASC");
1462                break;
1463            case "desc":
1464                ds.sort(cm.getDataIndex(index), "DESC");
1465                break;
1466            default:
1467                index = cm.getIndexById(item.id.substr(4));
1468                if(index != -1){
1469                    if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
1470                        this.onDenyColumnHide();
1471                        return false;
1472                    }
1473                    cm.setHidden(index, item.checked);
1474                }
1475        }
1476        return true;
1477    },
1478
1479    // private
1480    isHideableColumn : function(c){
1481        return !c.hidden && !c.fixed;
1482    },
1483
1484    // private
1485    beforeColMenuShow : function(){
1486        var cm = this.cm,  colCount = cm.getColumnCount();
1487        this.colMenu.removeAll();
1488        for(var i = 0; i < colCount; i++){
1489            if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
1490                this.colMenu.add(new Ext.menu.CheckItem({
1491                    id: "col-"+cm.getColumnId(i),
1492                    text: cm.getColumnHeader(i),
1493                    checked: !cm.isHidden(i),
1494                    hideOnClick:false,
1495                    disabled: cm.config[i].hideable === false
1496                }));
1497            }
1498        }
1499    },
1500
1501    // private
1502    handleHdDown : function(e, t){
1503        if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
1504            e.stopEvent();
1505            var hd = this.findHeaderCell(t);
1506            Ext.fly(hd).addClass('x-grid3-hd-menu-open');
1507            var index = this.getCellIndex(hd);
1508            this.hdCtxIndex = index;
1509            var ms = this.hmenu.items, cm = this.cm;
1510            ms.get("asc").setDisabled(!cm.isSortable(index));
1511            ms.get("desc").setDisabled(!cm.isSortable(index));
1512            this.hmenu.on("hide", function(){
1513                Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
1514            }, this, {single:true});
1515            this.hmenu.show(t, "tl-bl?");
1516        }
1517    },
1518
1519    // private
1520    handleHdOver : function(e, t){
1521        var hd = this.findHeaderCell(t);
1522        if(hd && !this.headersDisabled){
1523            this.activeHd = hd;
1524            this.activeHdIndex = this.getCellIndex(hd);
1525            var fly = this.fly(hd);
1526            this.activeHdRegion = fly.getRegion();
1527            if(!this.cm.isMenuDisabled(this.activeHdIndex)){
1528                fly.addClass("x-grid3-hd-over");
1529                this.activeHdBtn = fly.child('.x-grid3-hd-btn');
1530                if(this.activeHdBtn){
1531                    this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
1532                }
1533            }
1534        }
1535    },
1536
1537    // private
1538    handleHdMove : function(e, t){
1539        if(this.activeHd && !this.headersDisabled){
1540            var hw = this.splitHandleWidth || 5;
1541            var r = this.activeHdRegion;
1542            var x = e.getPageX();
1543            var ss = this.activeHd.style;
1544            if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex-1)){
1545                ss.cursor = Ext.isAir ? 'move' : Ext.isSafari ? 'e-resize' : 'col-resize'; // col-resize not always supported
1546            }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
1547                ss.cursor = Ext.isAir ? 'move' : Ext.isSafari ? 'w-resize' : 'col-resize';
1548            }else{
1549                ss.cursor = '';
1550            }
1551        }
1552    },
1553
1554    // private
1555    handleHdOut : function(e, t){
1556        var hd = this.findHeaderCell(t);
1557        if(hd && (!Ext.isIE || !e.within(hd, true))){
1558            this.activeHd = null;
1559            this.fly(hd).removeClass("x-grid3-hd-over");
1560            hd.style.cursor = '';
1561        }
1562    },
1563
1564    // private
1565    hasRows : function(){
1566        var fc = this.mainBody.dom.firstChild;
1567        return fc && fc.className != 'x-grid-empty';
1568    },
1569
1570    // back compat
1571    bind : function(d, c){
1572        this.initData(d, c);
1573    }
1574});
1575
1576
1577// private
1578// This is a support class used internally by the Grid components
1579Ext.grid.GridView.SplitDragZone = function(grid, hd){
1580    this.grid = grid;
1581    this.view = grid.getView();
1582    this.marker = this.view.resizeMarker;
1583    this.proxy = this.view.resizeProxy;
1584    Ext.grid.GridView.SplitDragZone.superclass.constructor.call(this, hd,
1585        "gridSplitters" + this.grid.getGridEl().id, {
1586        dragElId : Ext.id(this.proxy.dom), resizeFrame:false
1587    });
1588    this.scroll = false;
1589    this.hw = this.view.splitHandleWidth || 5;
1590};
1591Ext.extend(Ext.grid.GridView.SplitDragZone, Ext.dd.DDProxy, {
1592
1593    b4StartDrag : function(x, y){
1594        this.view.headersDisabled = true;
1595        var h = this.view.mainWrap.getHeight();
1596        this.marker.setHeight(h);
1597        this.marker.show();
1598        this.marker.alignTo(this.view.getHeaderCell(this.cellIndex), 'tl-tl', [-2, 0]);
1599        this.proxy.setHeight(h);
1600        var w = this.cm.getColumnWidth(this.cellIndex);
1601        var minw = Math.max(w-this.grid.minColumnWidth, 0);
1602        this.resetConstraints();
1603        this.setXConstraint(minw, 1000);
1604        this.setYConstraint(0, 0);
1605        this.minX = x - minw;
1606        this.maxX = x + 1000;
1607        this.startPos = x;
1608        Ext.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
1609    },
1610
1611
1612    handleMouseDown : function(e){
1613        var t = this.view.findHeaderCell(e.getTarget());
1614        if(t){
1615            var xy = this.view.fly(t).getXY(), x = xy[0], y = xy[1];
1616            var exy = e.getXY(), ex = exy[0], ey = exy[1];
1617            var w = t.offsetWidth, adjust = false;
1618            if((ex - x) <= this.hw){
1619                adjust = -1;
1620            }else if((x+w) - ex <= this.hw){
1621                adjust = 0;
1622            }
1623            if(adjust !== false){
1624                this.cm = this.grid.colModel;
1625                var ci = this.view.getCellIndex(t);
1626                if(adjust == -1){
1627                  if (ci + adjust < 0) {
1628                    return;
1629                  }
1630                    while(this.cm.isHidden(ci+adjust)){
1631                        --adjust;
1632                        if(ci+adjust < 0){
1633                            return;
1634                        }
1635                    }
1636                }
1637                this.cellIndex = ci+adjust;
1638                this.split = t.dom;
1639                if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
1640                    Ext.grid.GridView.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
1641                }
1642            }else if(this.view.columnDrag){
1643                this.view.columnDrag.callHandleMouseDown(e);
1644            }
1645        }
1646    },
1647
1648    endDrag : function(e){
1649        this.marker.hide();
1650        var v = this.view;
1651        var endX = Math.max(this.minX, e.getPageX());
1652        var diff = endX - this.startPos;
1653        v.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
1654        setTimeout(function(){
1655            v.headersDisabled = false;
1656        }, 50);
1657    },
1658
1659    autoOffset : function(){
1660        this.setDelta(0,0);
1661    }
1662});
Note: See TracBrowser for help on using the repository browser.