3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
7 Ext.ns('Ext.ux.grid');
\r
10 Ext.grid.GridView.prototype.borderWidth = 0;
\r
13 Ext.ux.grid.ColumnHeaderGroup = Ext.extend(Ext.util.Observable, {
\r
15 constructor: function(config){
\r
16 this.config = config;
\r
19 init: function(grid){
\r
20 Ext.applyIf(grid.colModel, this.config);
\r
21 Ext.apply(grid.getView(), this.viewConfig);
\r
25 initTemplates: function(){
\r
26 this.constructor.prototype.initTemplates.apply(this, arguments);
\r
27 var ts = this.templates || {};
\r
29 ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');
\r
31 this.templates = ts;
\r
32 this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
\r
35 renderHeaders: function(){
\r
36 var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';
\r
38 for(var row = 0, rlen = rows.length; row < rlen; row++){
\r
39 var r = rows[row], cells = [];
\r
40 for(var i = 0, gcol = 0, len = r.length; i < len; i++){
\r
42 group.colspan = group.colspan || 1;
\r
43 var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
\r
44 cells[i] = ts.gcell.apply({
\r
45 cls: 'ux-grid-hd-group-cell',
\r
48 style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),
\r
49 tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',
\r
50 istyle: group.align == 'right' ? 'padding-right:16px' : '',
\r
51 btn: this.grid.enableHdMenu && group.header,
\r
52 value: group.header || ' '
\r
54 gcol += group.colspan;
\r
56 headers[row] = ts.header.apply({
\r
58 cells: cells.join('')
\r
61 headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
\r
62 return headers.join('');
\r
65 onColumnWidthUpdated: function(){
\r
66 this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
\r
67 Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
\r
70 onAllColumnWidthsUpdated: function(){
\r
71 this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
\r
72 Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
\r
75 onColumnHiddenUpdated: function(){
\r
76 this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
\r
77 Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
\r
80 getHeaderCell: function(index){
\r
81 return this.mainHd.query(this.cellSelector)[index];
\r
84 findHeaderCell: function(el){
\r
85 return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
\r
88 findHeaderIndex: function(el){
\r
89 var cell = this.findHeaderCell(el);
\r
90 return cell ? this.getCellIndex(cell) : false;
\r
93 updateSortIcon: function(col, dir){
\r
94 var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);
\r
95 hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
\r
98 handleHdDown: function(e, t){
\r
99 var el = Ext.get(t);
\r
100 if(el.hasClass('x-grid3-hd-btn')){
\r
102 var hd = this.findHeaderCell(t);
\r
103 Ext.fly(hd).addClass('x-grid3-hd-menu-open');
\r
104 var index = this.getCellIndex(hd);
\r
105 this.hdCtxIndex = index;
\r
106 var ms = this.hmenu.items, cm = this.cm;
\r
107 ms.get('asc').setDisabled(!cm.isSortable(index));
\r
108 ms.get('desc').setDisabled(!cm.isSortable(index));
\r
109 this.hmenu.on('hide', function(){
\r
110 Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
\r
114 this.hmenu.show(t, 'tl-bl?');
\r
115 }else if(el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')){
\r
120 handleHdMove: function(e, t){
\r
121 var hd = this.findHeaderCell(this.activeHdRef);
\r
122 if(hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')){
\r
123 var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';
\r
124 if(this.grid.enableColumnResize !== false){
\r
125 if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)){
\r
126 cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize
\r
130 }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
\r
131 cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
\r
138 handleHdOver: function(e, t){
\r
139 var hd = this.findHeaderCell(t);
\r
140 if(hd && !this.headersDisabled){
\r
141 this.activeHdRef = t;
\r
142 this.activeHdIndex = this.getCellIndex(hd);
\r
143 var fly = this.fly(hd);
\r
144 this.activeHdRegion = fly.getRegion();
\r
145 if(!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))){
\r
146 fly.addClass('x-grid3-hd-over');
\r
147 this.activeHdBtn = fly.child('.x-grid3-hd-btn');
\r
148 if(this.activeHdBtn){
\r
149 this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
\r
155 handleHdOut: function(e, t){
\r
156 var hd = this.findHeaderCell(t);
\r
157 if(hd && (!Ext.isIE || !e.within(hd, true))){
\r
158 this.activeHdRef = null;
\r
159 this.fly(hd).removeClass('x-grid3-hd-over');
\r
160 hd.style.cursor = '';
\r
164 handleHdMenuClick: function(item){
\r
165 var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();
\r
168 ds.sort(cm.getDataIndex(index), 'ASC');
\r
171 ds.sort(cm.getDataIndex(index), 'DESC');
\r
174 if(id.substr(0, 5) == 'group'){
\r
175 var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;
\r
176 for(var i = 0, len = r.length; i < len; i++){
\r
178 if(col >= gcol && col < gcol + group.colspan){
\r
181 gcol += group.colspan;
\r
184 var max = cm.getColumnsBy(this.isHideableColumn, this).length;
\r
185 for(var i = gcol, len = gcol + group.colspan; i < len; i++){
\r
186 if(!cm.isHidden(i)){
\r
191 this.onDenyColumnHide();
\r
195 for(var i = gcol, len = gcol + group.colspan; i < len; i++){
\r
196 if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
\r
197 cm.setHidden(i, item.checked);
\r
201 index = cm.getIndexById(id.substr(4));
\r
203 if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
\r
204 this.onDenyColumnHide();
\r
207 cm.setHidden(index, item.checked);
\r
210 item.checked = !item.checked;
\r
212 var updateChildren = function(menu){
\r
213 menu.items.each(function(childItem){
\r
214 if(!childItem.disabled){
\r
215 childItem.setChecked(item.checked, false);
\r
216 if(childItem.menu){
\r
217 updateChildren(childItem.menu);
\r
222 updateChildren(item.menu);
\r
224 var parentMenu = item, parentItem;
\r
225 while(parentMenu = parentMenu.parentMenu){
\r
226 if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){
\r
229 var checked = parentMenu.items.findIndexBy(function(m){
\r
232 parentItem.setChecked(checked, true);
\r
234 item.checked = !item.checked;
\r
239 beforeColMenuShow: function(){
\r
240 var cm = this.cm, rows = this.cm.rows;
\r
241 this.colMenu.removeAll();
\r
242 for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){
\r
243 var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];
\r
244 if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){
\r
245 for(var row = 0, rlen = rows.length; row < rlen; row++){
\r
246 var r = rows[row], group, gcol = 0;
\r
247 for(var i = 0, len = r.length; i < len; i++){
\r
249 if(col >= gcol && col < gcol + group.colspan){
\r
252 gcol += group.colspan;
\r
254 if(group && group.header){
\r
255 if(cm.hierarchicalColMenu){
\r
256 var gid = 'group-' + row + '-' + gcol;
\r
257 var item = menu.items.item(gid);
\r
258 var submenu = item ? item.menu : null;
\r
260 submenu = new Ext.menu.Menu({
\r
263 submenu.on("itemclick", this.handleHdMenuClick, this);
\r
264 var checked = false, disabled = true;
\r
265 for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){
\r
266 if(!cm.isHidden(c)){
\r
269 if(cm.config[c].hideable !== false){
\r
275 text: group.header,
\r
277 hideOnClick: false,
\r
284 text.push(group.header);
\r
289 menu.add(new Ext.menu.CheckItem({
\r
290 itemId: "col-" + cm.getColumnId(col),
\r
291 text: text.join(' '),
\r
292 checked: !cm.isHidden(col),
\r
293 hideOnClick: false,
\r
294 disabled: cm.config[col].hideable === false
\r
300 renderUI: function(){
\r
301 this.constructor.prototype.renderUI.apply(this, arguments);
\r
302 Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);
\r
303 Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);
\r
308 allowHeaderDrag: function(e){
\r
309 return !e.getTarget(null, null, true).hasClass('ux-grid-hd-group-cell');
\r
313 columnDropConfig: {
\r
314 getTargetFromEvent: function(e){
\r
315 var t = Ext.lib.Event.getTarget(e);
\r
316 return this.view.findHeaderCell(t);
\r
319 positionIndicator: function(h, n, e){
\r
320 var data = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
\r
321 if(data === false){
\r
324 var px = data.px + this.proxyOffsets[0];
\r
325 this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);
\r
326 this.proxyTop.show();
\r
327 this.proxyBottom.setLeftTop(px, data.r.bottom);
\r
328 this.proxyBottom.show();
\r
332 onNodeDrop: function(n, dd, e, data){
\r
333 var h = data.header;
\r
335 var d = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
\r
339 var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;
\r
340 for(var row = d.row, rlen = rows.length; row < rlen; row++){
\r
341 var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;
\r
342 for(var i = 0, gcol = 0; i < len; i++){
\r
344 if(d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan){
\r
347 if(d.oldIndex + d.colspan - 1 >= gcol && d.oldIndex + d.colspan - 1 < gcol + group.colspan){
\r
348 span = i - fromIx + 1;
\r
350 if(d.newIndex >= gcol && d.newIndex < gcol + group.colspan){
\r
353 gcol += group.colspan;
\r
355 var groups = r.splice(fromIx, span);
\r
356 rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);
\r
358 for(var c = 0; c < d.colspan; c++){
\r
359 var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);
\r
360 cm.moveColumn(oldIx, newIx);
\r
361 this.grid.fireEvent("columnmove", oldIx, newIx);
\r
369 getGroupStyle: function(group, gcol){
\r
370 var width = 0, hidden = true;
\r
371 for(var i = gcol, len = gcol + group.colspan; i < len; i++){
\r
372 if(!this.cm.isHidden(i)){
\r
373 var cw = this.cm.getColumnWidth(i);
\r
374 if(typeof cw == 'number'){
\r
381 width: (Ext.isBorderBox ? width : Math.max(width - this.borderWidth, 0)) + 'px',
\r
386 updateGroupStyles: function(col){
\r
387 var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;
\r
388 for(var row = 0; row < tables.length; row++){
\r
389 tables[row].style.width = tw;
\r
390 if(row < rows.length){
\r
391 var cells = tables[row].firstChild.firstChild.childNodes;
\r
392 for(var i = 0, gcol = 0; i < cells.length; i++){
\r
393 var group = rows[row][i];
\r
394 if((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)){
\r
395 var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
\r
396 cells[i].style.width = gs.width;
\r
397 cells[i].style.display = gs.hidden ? 'none' : '';
\r
399 gcol += group.colspan;
\r
405 getGroupRowIndex: function(el){
\r
407 var m = el.className.match(this.hrowRe);
\r
409 return parseInt(m[1], 10);
\r
412 return this.cm.rows.length;
\r
415 getGroupSpan: function(row, col){
\r
419 colspan: this.cm.getColumnCount()
\r
422 var r = this.cm.rows[row];
\r
424 for(var i = 0, gcol = 0, len = r.length; i < len; i++){
\r
426 if(col >= gcol && col < gcol + group.colspan){
\r
429 colspan: group.colspan
\r
432 gcol += group.colspan;
\r
445 getDragDropData: function(h, n, e){
\r
446 if(h.parentNode != n.parentNode){
\r
449 var cm = this.grid.colModel, x = Ext.lib.Event.getPageX(e), r = Ext.lib.Dom.getRegion(n.firstChild), px, pt;
\r
450 if((r.right - x) <= (r.right - r.left) / 2){
\r
451 px = r.right + this.view.borderWidth;
\r
457 var oldIndex = this.view.getCellIndex(h), newIndex = this.view.getCellIndex(n);
\r
458 if(cm.isFixed(newIndex)){
\r
461 var row = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupRowIndex.call(this.view, h),
\r
462 oldGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, oldIndex),
\r
463 newGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, newIndex),
\r
464 oldIndex = oldGroup.col;
\r
465 newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);
\r
466 if(newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan){
\r
469 var parentGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row - 1, oldIndex);
\r
470 if(newIndex < parentGroup.col || newIndex > parentGroup.col + parentGroup.colspan){
\r
478 oldIndex: oldIndex,
\r
479 newIndex: newIndex,
\r
480 colspan: oldGroup.colspan
\r