3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
8 * @class Ext.ux.tree.TreeGrid
9 * @extends Ext.tree.TreePanel
13 Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, {
17 borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
22 reserveScrollOffset : true,
25 columnsText : 'Columns',
27 initComponent : function() {
29 this.root = new Ext.tree.AsyncTreeNode({text: 'Root'});
32 // initialize the loader
35 l = new Ext.ux.tree.TreeGridLoader({
36 dataUrl: this.dataUrl,
37 requestMethod: this.requestMethod,
40 }else if(Ext.isObject(l) && !l.load){
41 l = new Ext.ux.tree.TreeGridLoader(l);
44 l.createNode = function(attr) {
45 if (!attr.uiProvider) {
46 attr.uiProvider = Ext.ux.tree.TreeGridNodeUI;
48 return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
53 Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);
58 this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort);
61 if(this.columnResize){
62 this.colResizer = new Ext.tree.ColumnResizer(this.columnResize);
63 this.colResizer.init(this);
67 if(!this.internalTpl){
68 this.internalTpl = new Ext.XTemplate(
69 '<div class="x-grid3-header">',
70 '<div class="x-treegrid-header-inner">',
71 '<div class="x-grid3-header-offset">',
72 '<table cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>',
73 '<thead><tr class="x-grid3-hd-row">',
74 '<tpl for="columns">',
75 '<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="', this.id, '-xlhd-{#}">',
76 '<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">',
77 this.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
78 '{header}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
85 '<div class="x-treegrid-root-node">',
86 '<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>',
91 if(!this.colgroupTpl) {
92 this.colgroupTpl = new Ext.XTemplate(
93 '<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>'
98 initColumns : function() {
99 var cs = this.columns,
104 for(i = 0; i < len; i++){
107 c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn';
113 if(this.enableSort !== false && c.sortable !== false) {
115 this.enableSort = true;
119 this.columns = columns;
122 onRender : function(){
123 Ext.tree.TreePanel.superclass.onRender.apply(this, arguments);
125 this.el.addClass('x-treegrid');
127 this.outerCt = this.body.createChild({
128 cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')
131 this.internalTpl.overwrite(this.outerCt, {columns: this.columns});
133 this.mainHd = Ext.get(this.outerCt.dom.firstChild);
134 this.innerHd = Ext.get(this.mainHd.dom.firstChild);
135 this.innerBody = Ext.get(this.outerCt.dom.lastChild);
136 this.innerCt = Ext.get(this.innerBody.dom.firstChild);
138 this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
140 if(this.hideHeaders){
141 this.header.dom.style.display = 'none';
143 else if(this.enableHdMenu !== false){
144 this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'});
145 if(this.enableColumnHide !== false){
146 this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'});
149 beforeshow: this.beforeColMenuShow,
150 itemclick: this.handleHdMenuClick
155 text: this.columnsText,
157 iconCls: 'x-cols-icon'
160 this.hmenu.on('itemclick', this.handleHdMenuClick, this);
164 setRootNode : function(node){
165 node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI;
166 node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node);
168 this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
173 initEvents : function() {
174 Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments);
176 this.mon(this.innerBody, 'scroll', this.syncScroll, this);
177 this.mon(this.innerHd, 'click', this.handleHdDown, this);
178 this.mon(this.mainHd, {
180 mouseover: this.handleHdOver,
181 mouseout: this.handleHdOut
185 onResize : function(w, h) {
186 Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments);
188 var bd = this.innerBody.dom;
189 var hd = this.innerHd.dom;
196 bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px';
200 var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
201 if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){
202 this.setScrollOffset(sw);
205 setTimeout(function(){
206 me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0);
212 updateColumnWidths : function() {
213 var cols = this.columns,
214 colCount = cols.length,
215 groups = this.outerCt.query('colgroup'),
216 groupCount = groups.length,
219 for(i = 0; i<colCount; i++) {
221 for(j = 0; j<groupCount; j++) {
223 g.childNodes[i].style.width = (c.hidden ? 0 : c.width) + 'px';
227 for(i = 0, groups = this.innerHd.query('td'), len = groups.length; i<len; i++) {
228 c = Ext.fly(groups[i]);
229 if(cols[i] && cols[i].hidden) {
230 c.addClass('x-treegrid-hd-hidden');
233 c.removeClass('x-treegrid-hd-hidden');
237 var tcw = this.getTotalColumnWidth();
238 Ext.fly(this.innerHd.dom.firstChild).setWidth(tcw + (this.scrollOffset || 0));
239 this.outerCt.select('table').setWidth(tcw);
240 this.syncHeaderScroll();
243 getVisibleColumns : function() {
249 for(i = 0; i<len; i++) {
257 getTotalColumnWidth : function() {
259 for(var i = 0, cs = this.getVisibleColumns(), len = cs.length; i<len; i++) {
260 total += cs[i].width;
265 setScrollOffset : function(scrollOffset) {
266 this.scrollOffset = scrollOffset;
267 this.updateColumnWidths();
271 handleHdDown : function(e, t){
272 var hd = e.getTarget('.x-treegrid-hd');
274 if(hd && Ext.fly(t).hasClass('x-grid3-hd-btn')){
275 var ms = this.hmenu.items,
277 index = this.findHeaderIndex(hd),
282 Ext.fly(hd).addClass('x-grid3-hd-menu-open');
283 this.hdCtxIndex = index;
285 this.fireEvent('headerbuttonclick', ms, c, hd, index);
287 this.hmenu.on('hide', function(){
288 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
289 }, this, {single:true});
291 this.hmenu.show(t, 'tl-bl?');
294 var index = this.findHeaderIndex(hd);
295 this.fireEvent('headerclick', this.columns[index], hd, index);
300 handleHdOver : function(e, t){
301 var hd = e.getTarget('.x-treegrid-hd');
302 if(hd && !this.headersDisabled){
303 index = this.findHeaderIndex(hd);
304 this.activeHdRef = t;
305 this.activeHdIndex = index;
306 var el = Ext.get(hd);
307 this.activeHdRegion = el.getRegion();
308 el.addClass('x-grid3-hd-over');
309 this.activeHdBtn = el.child('.x-grid3-hd-btn');
310 if(this.activeHdBtn){
311 this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px';
317 handleHdOut : function(e, t){
318 var hd = e.getTarget('.x-treegrid-hd');
319 if(hd && (!Ext.isIE || !e.within(hd, true))){
320 this.activeHdRef = null;
321 Ext.fly(hd).removeClass('x-grid3-hd-over');
322 hd.style.cursor = '';
326 findHeaderIndex : function(hd){
328 var cs = hd.parentNode.childNodes;
329 for(var i = 0, c; c = cs[i]; i++){
338 beforeColMenuShow : function(){
339 var cols = this.columns,
340 colCount = cols.length,
342 this.colMenu.removeAll();
343 for(i = 1; i < colCount; i++){
345 if(c.hideable !== false){
346 this.colMenu.add(new Ext.menu.CheckItem({
351 disabled: c.hideable === false
358 handleHdMenuClick : function(item){
359 var index = this.hdCtxIndex,
360 id = item.getItemId();
362 if(this.fireEvent('headermenuclick', this.columns[index], id, index) !== false) {
363 index = id.substr(4);
364 if(index > 0 && this.columns[index]) {
365 this.setColumnVisible(index, !item.checked);
372 setColumnVisible : function(index, visible) {
373 this.columns[index].hidden = !visible;
374 this.updateColumnWidths();
378 * Scrolls the grid to the top
380 scrollToTop : function(){
381 this.innerBody.dom.scrollTop = 0;
382 this.innerBody.dom.scrollLeft = 0;
386 syncScroll : function(){
387 this.syncHeaderScroll();
388 var mb = this.innerBody.dom;
389 this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
393 syncHeaderScroll : function(){
394 var mb = this.innerBody.dom;
395 this.innerHd.dom.scrollLeft = mb.scrollLeft;
396 this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
399 registerNode : function(n) {
400 Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n);
401 if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) {
402 n.ui = new Ext.ux.tree.TreeGridNodeUI(n);
407 Ext.reg('treegrid', Ext.ux.tree.TreeGrid);