commit extjs-2.2.1
[extjs.git] / source / widgets / grid / ColumnModel.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.grid.ColumnModel\r
11  * @extends Ext.util.Observable\r
12  * This is the default implementation of a ColumnModel used by the Grid. This class is initialized\r
13  * with an Array of column config objects.\r
14  * <br><br>\r
15  * An individual column's config object defines the header string, the {@link Ext.data.Record}\r
16  * field the column draws its data from, an optional rendering function to provide customized\r
17  * data formatting, and the ability to apply a CSS class to all cells in a column through its\r
18  * {@link #id} config option.<br>\r
19  * <br>Usage:<br>\r
20 <pre><code>\r
21  var colModel = new Ext.grid.ColumnModel([\r
22     { header: "Ticker", width: 60, sortable: true},\r
23     { header: "Company Name", width: 150, sortable: true},\r
24     { header: "Market Cap.", width: 100, sortable: true},\r
25     { header: "$ Sales", width: 100, sortable: true, renderer: money},\r
26     { header: "Employees", width: 100, sortable: true, resizable: false}\r
27  ]);\r
28  </code></pre>\r
29  * <p>\r
30  * The config options <b>defined by</b> this class are options which may appear in each\r
31  * individual column definition. In order to use configuration options from the superclass,\r
32  * specify the column configuration Array in the <tt><b>columns<b><tt> config property. eg:<pre><code>\r
33  var colModel = new Ext.grid.ColumnModel({\r
34     listeners: {\r
35         widthchange: function(cm, colIndex, width) {\r
36             saveConfig(colIndex, width);\r
37         }\r
38     },\r
39     columns: [\r
40         { header: "Ticker", width: 60, sortable: true},\r
41         { header: "Company Name", width: 150, sortable: true},\r
42         { header: "Market Cap.", width: 100, sortable: true},\r
43         { header: "$ Sales", width: 100, sortable: true, renderer: money},\r
44         { header: "Employees", width: 100, sortable: true, resizable: false}\r
45      ]\r
46 });\r
47  </code></pre>\r
48  * @constructor\r
49  * @param {Object} config An Array of column config objects. See this class's\r
50  * config objects for details.\r
51 */\r
52 Ext.grid.ColumnModel = function(config){\r
53     /**\r
54      * The width of columns which have no width specified (defaults to 100)\r
55      * @type Number\r
56      */\r
57     this.defaultWidth = 100;\r
58 \r
59     /**\r
60      * Default sortable of columns which have no sortable specified (defaults to false)\r
61      * @type Boolean\r
62      */\r
63     this.defaultSortable = false;\r
64 \r
65     /**\r
66      * The config passed into the constructor\r
67      * @property {Array} config\r
68      */\r
69     if(config.columns){\r
70         Ext.apply(this, config);\r
71         this.setConfig(config.columns, true);\r
72     }else{\r
73         this.setConfig(config, true);\r
74     }\r
75     this.addEvents(\r
76         /**\r
77          * @event widthchange\r
78          * Fires when the width of a column changes.\r
79          * @param {ColumnModel} this\r
80          * @param {Number} columnIndex The column index\r
81          * @param {Number} newWidth The new width\r
82          */\r
83         "widthchange",\r
84         /**\r
85          * @event headerchange\r
86          * Fires when the text of a header changes.\r
87          * @param {ColumnModel} this\r
88          * @param {Number} columnIndex The column index\r
89          * @param {String} newText The new header text\r
90          */\r
91         "headerchange",\r
92         /**\r
93          * @event hiddenchange\r
94          * Fires when a column is hidden or "unhidden".\r
95          * @param {ColumnModel} this\r
96          * @param {Number} columnIndex The column index\r
97          * @param {Boolean} hidden true if hidden, false otherwise\r
98          */\r
99         "hiddenchange",\r
100         /**\r
101          * @event columnmoved\r
102          * Fires when a column is moved.\r
103          * @param {ColumnModel} this\r
104          * @param {Number} oldIndex\r
105          * @param {Number} newIndex\r
106          */\r
107         "columnmoved",\r
108         // deprecated - to be removed\r
109         "columnlockchange",\r
110         /**\r
111          * @event configchange\r
112          * Fires when the configuration is changed\r
113          * @param {ColumnModel} this\r
114          */\r
115         "configchange"\r
116     );\r
117     Ext.grid.ColumnModel.superclass.constructor.call(this);\r
118 };\r
119 Ext.extend(Ext.grid.ColumnModel, Ext.util.Observable, {\r
120     /**\r
121      * @cfg {String} id (optional) Defaults to the column's initial ordinal position.\r
122      * A name which identifies this column. The id is used to create a CSS class name which\r
123      * is applied to all table cells (including headers) in that column. The class name\r
124      * takes the form of <pre>x-grid3-td-<b>id</b></pre>\r
125      * <br><br>\r
126      * Header cells will also recieve this class name, but will also have the class <pr>x-grid3-hd</pre>,\r
127      * so to target header cells, use CSS selectors such as:<pre>.x-grid3-hd.x-grid3-td-<b>id</b></pre>\r
128      * The {@link Ext.grid.GridPanel#autoExpandColumn} grid config option references the column\r
129      * via this identifier.\r
130      */\r
131     /**\r
132      * @cfg {String} header The header text to display in the Grid view.\r
133      */\r
134     /**\r
135      * @cfg {String} dataIndex (optional) The name of the field in the grid's {@link Ext.data.Store}'s\r
136      * {@link Ext.data.Record} definition from which to draw the column's value. If not\r
137      * specified, the column's index is used as an index into the Record's data Array.\r
138      */\r
139     /**\r
140      * @cfg {Number} width (optional) The initial width in pixels of the column. This is ignored if the\r
141      * Grid's {@link Ext.grid.GridView view} is configured with {@link Ext.grid.GridView#forceFit forceFit} true.\r
142      */\r
143     /**\r
144      * @cfg {Boolean} sortable (optional) True if sorting is to be allowed on this column.\r
145      * Defaults to the value of the {@link #defaultSortable} property.\r
146      * Whether local/remote sorting is used is specified in {@link Ext.data.Store#remoteSort}.\r
147      */\r
148     /**\r
149      * @cfg {Boolean} fixed (optional) True if the column width cannot be changed.  Defaults to false.\r
150      */\r
151     /**\r
152      * @cfg {Boolean} resizable (optional) False to disable column resizing. Defaults to true.\r
153      */\r
154     /**\r
155      * @cfg {Boolean} menuDisabled (optional) True to disable the column menu. Defaults to false.\r
156      */\r
157     /**\r
158      * @cfg {Boolean} hidden (optional) True to hide the column. Defaults to false.\r
159      */\r
160     /**\r
161      * @cfg {String} tooltip (optional) A text string to use as the column header's tooltip.  If Quicktips are enabled, this\r
162      * value will be used as the text of the quick tip, otherwise it will be set as the header's HTML title attribute.\r
163      * Defaults to ''.\r
164      */\r
165     /**\r
166      * @cfg {Function} renderer (optional) A function used to generate HTML markup for a cell\r
167      * given the cell's data value. See {@link #setRenderer}. If not specified, the\r
168      * default renderer uses the raw data value.\r
169      */\r
170     /**\r
171      * @cfg {String} align (optional) Set the CSS text-align property of the column.  Defaults to undefined.\r
172      */\r
173     /**\r
174      * @cfg {String} css (optional) Set custom CSS for all table cells in the column (excluding headers).  Defaults to undefined.\r
175      */\r
176     /**\r
177      * @cfg {Boolean} hideable (optional) Specify as <tt>false</tt> to prevent the user from hiding this column\r
178      * (defaults to true).  To disallow column hiding globally for all columns in the grid, use\r
179      * {@link Ext.grid.GridPanel#enableColumnHide} instead.\r
180      */\r
181     /**\r
182      * @cfg {Ext.form.Field} editor (optional) The {@link Ext.form.Field} to use when editing values in this column if\r
183      * editing is supported by the grid.\r
184      */\r
185 \r
186     /**\r
187      * Returns the id of the column at the specified index.\r
188      * @param {Number} index The column index\r
189      * @return {String} the id\r
190      */\r
191     getColumnId : function(index){\r
192         return this.config[index].id;\r
193     },\r
194 \r
195     /**\r
196      * <p>Reconfigures this column model according to the passed Array of column definition objects. For a description of\r
197      * the individual properties of a column definition object, see the <a href="#Ext.grid.ColumnModel-configs">Config Options</a>.</p>\r
198      * <p>Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel} using\r
199      * this ColumnModel will listen for this event and refresh its UI automatically.</p>\r
200      * @param {Array} config Array of Column definition objects.\r
201      */\r
202     setConfig : function(config, initial){\r
203         if(!initial){ // cleanup\r
204             delete this.totalWidth;\r
205             for(var i = 0, len = this.config.length; i < len; i++){\r
206                 var c = this.config[i];\r
207                 if(c.editor){\r
208                     c.editor.destroy();\r
209                 }\r
210             }\r
211         }\r
212         this.config = config;\r
213         this.lookup = {};\r
214         // if no id, create one\r
215         for(var i = 0, len = config.length; i < len; i++){\r
216             var c = config[i];\r
217             if(typeof c.renderer == "string"){\r
218                 c.renderer = Ext.util.Format[c.renderer];\r
219             }\r
220             if(typeof c.id == "undefined"){\r
221                 c.id = i;\r
222             }\r
223             if(c.editor && c.editor.isFormField){\r
224                 c.editor = new Ext.grid.GridEditor(c.editor);\r
225             }\r
226             this.lookup[c.id] = c;\r
227         }\r
228         if(!initial){\r
229             this.fireEvent('configchange', this);\r
230         }\r
231     },\r
232 \r
233     /**\r
234      * Returns the column for a specified id.\r
235      * @param {String} id The column id\r
236      * @return {Object} the column\r
237      */\r
238     getColumnById : function(id){\r
239         return this.lookup[id];\r
240     },\r
241 \r
242     /**\r
243      * Returns the index for a specified column id.\r
244      * @param {String} id The column id\r
245      * @return {Number} the index, or -1 if not found\r
246      */\r
247     getIndexById : function(id){\r
248         for(var i = 0, len = this.config.length; i < len; i++){\r
249             if(this.config[i].id == id){\r
250                 return i;\r
251             }\r
252         }\r
253         return -1;\r
254     },\r
255 \r
256     /**\r
257      * Moves a column from one position to another.\r
258      * @param {Number} oldIndex The index of the column to move.\r
259      * @param {Number} newIndex The position at which to reinsert the coolumn.\r
260      */\r
261     moveColumn : function(oldIndex, newIndex){\r
262         var c = this.config[oldIndex];\r
263         this.config.splice(oldIndex, 1);\r
264         this.config.splice(newIndex, 0, c);\r
265         this.dataMap = null;\r
266         this.fireEvent("columnmoved", this, oldIndex, newIndex);\r
267     },\r
268 \r
269     // deprecated - to be removed\r
270     isLocked : function(colIndex){\r
271         return this.config[colIndex].locked === true;\r
272     },\r
273 \r
274     // deprecated - to be removed\r
275     setLocked : function(colIndex, value, suppressEvent){\r
276         if(this.isLocked(colIndex) == value){\r
277             return;\r
278         }\r
279         this.config[colIndex].locked = value;\r
280         if(!suppressEvent){\r
281             this.fireEvent("columnlockchange", this, colIndex, value);\r
282         }\r
283     },\r
284 \r
285     // deprecated - to be removed\r
286     getTotalLockedWidth : function(){\r
287         var totalWidth = 0;\r
288         for(var i = 0; i < this.config.length; i++){\r
289             if(this.isLocked(i) && !this.isHidden(i)){\r
290                 this.totalWidth += this.getColumnWidth(i);\r
291             }\r
292         }\r
293         return totalWidth;\r
294     },\r
295 \r
296     // deprecated - to be removed\r
297     getLockedCount : function(){\r
298         for(var i = 0, len = this.config.length; i < len; i++){\r
299             if(!this.isLocked(i)){\r
300                 return i;\r
301             }\r
302         }\r
303     },\r
304 \r
305     /**\r
306      * Returns the number of columns.\r
307      * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.\r
308      * @return {Number}\r
309      */\r
310     getColumnCount : function(visibleOnly){\r
311         if(visibleOnly === true){\r
312             var c = 0;\r
313             for(var i = 0, len = this.config.length; i < len; i++){\r
314                 if(!this.isHidden(i)){\r
315                     c++;\r
316                 }\r
317             }\r
318             return c;\r
319         }\r
320         return this.config.length;\r
321     },\r
322 \r
323     /**\r
324      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)\r
325      * @param {Function} fn\r
326      * @param {Object} scope (optional)\r
327      * @return {Array} result\r
328      */\r
329     getColumnsBy : function(fn, scope){\r
330         var r = [];\r
331         for(var i = 0, len = this.config.length; i < len; i++){\r
332             var c = this.config[i];\r
333             if(fn.call(scope||this, c, i) === true){\r
334                 r[r.length] = c;\r
335             }\r
336         }\r
337         return r;\r
338     },\r
339 \r
340     /**\r
341      * Returns true if the specified column is sortable.\r
342      * @param {Number} col The column index\r
343      * @return {Boolean}\r
344      */\r
345     isSortable : function(col){\r
346         if(typeof this.config[col].sortable == "undefined"){\r
347             return this.defaultSortable;\r
348         }\r
349         return this.config[col].sortable;\r
350     },\r
351 \r
352     /**\r
353      * Returns true if the specified column menu is disabled.\r
354      * @param {Number} col The column index\r
355      * @return {Boolean}\r
356      */\r
357     isMenuDisabled : function(col){\r
358         return !!this.config[col].menuDisabled;\r
359     },\r
360 \r
361     /**\r
362      * Returns the rendering (formatting) function defined for the column.\r
363      * @param {Number} col The column index.\r
364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.\r
365      */\r
366     getRenderer : function(col){\r
367         if(!this.config[col].renderer){\r
368             return Ext.grid.ColumnModel.defaultRenderer;\r
369         }\r
370         return this.config[col].renderer;\r
371     },\r
372 \r
373     /**\r
374      * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some\r
375      * default formatting functions.\r
376      * @param {Number} col The column index\r
377      * @param {Function} fn The function to use to process the cell's raw data\r
378      * to return HTML markup for the grid view. The render function is called with\r
379      * the following parameters:<ul>\r
380      * <li><b>value</b> : Object<p class="sub-desc">The data value for the cell.</p></li>\r
381      * <li><b>metadata</b> : Object<p class="sub-desc">An object in which you may set the following attributes:<ul>\r
382      * <li><b>css</b> : String<p class="sub-desc">A CSS class name to add to the cell's TD element.</p></li>\r
383      * <li><b>attr</b> : String<p class="sub-desc">An HTML attribute definition string to apply to the data container element <i>within</i> the table cell\r
384      * (e.g. 'style="color:red;"').</p></li></ul></p></li>\r
385      * <li><b>record</b> : Ext.data.record<p class="sub-desc">The {@link Ext.data.Record} from which the data was extracted.</p></li>\r
386      * <li><b>rowIndex</b> : Number<p class="sub-desc">Row index</p></li>\r
387      * <li><b>colIndex</b> : Number<p class="sub-desc">Column index</p></li>\r
388      * <li><b>store</b> : Ext.data.Store<p class="sub-desc">The {@link Ext.data.Store} object from which the Record was extracted.</p></li></ul>\r
389      */\r
390     setRenderer : function(col, fn){\r
391         this.config[col].renderer = fn;\r
392     },\r
393 \r
394     /**\r
395      * Returns the width for the specified column.\r
396      * @param {Number} col The column index\r
397      * @return {Number}\r
398      */\r
399     getColumnWidth : function(col){\r
400         return this.config[col].width || this.defaultWidth;\r
401     },\r
402 \r
403     /**\r
404      * Sets the width for a column.\r
405      * @param {Number} col The column index\r
406      * @param {Number} width The new width\r
407      */\r
408     setColumnWidth : function(col, width, suppressEvent){\r
409         this.config[col].width = width;\r
410         this.totalWidth = null;\r
411         if(!suppressEvent){\r
412              this.fireEvent("widthchange", this, col, width);\r
413         }\r
414     },\r
415 \r
416     /**\r
417      * Returns the total width of all columns.\r
418      * @param {Boolean} includeHidden True to include hidden column widths\r
419      * @return {Number}\r
420      */\r
421     getTotalWidth : function(includeHidden){\r
422         if(!this.totalWidth){\r
423             this.totalWidth = 0;\r
424             for(var i = 0, len = this.config.length; i < len; i++){\r
425                 if(includeHidden || !this.isHidden(i)){\r
426                     this.totalWidth += this.getColumnWidth(i);\r
427                 }\r
428             }\r
429         }\r
430         return this.totalWidth;\r
431     },\r
432 \r
433     /**\r
434      * Returns the header for the specified column.\r
435      * @param {Number} col The column index\r
436      * @return {String}\r
437      */\r
438     getColumnHeader : function(col){\r
439         return this.config[col].header;\r
440     },\r
441 \r
442     /**\r
443      * Sets the header for a column.\r
444      * @param {Number} col The column index\r
445      * @param {String} header The new header\r
446      */\r
447     setColumnHeader : function(col, header){\r
448         this.config[col].header = header;\r
449         this.fireEvent("headerchange", this, col, header);\r
450     },\r
451 \r
452     /**\r
453      * Returns the tooltip for the specified column.\r
454      * @param {Number} col The column index\r
455      * @return {String}\r
456      */\r
457     getColumnTooltip : function(col){\r
458             return this.config[col].tooltip;\r
459     },\r
460     /**\r
461      * Sets the tooltip for a column.\r
462      * @param {Number} col The column index\r
463      * @param {String} tooltip The new tooltip\r
464      */\r
465     setColumnTooltip : function(col, tooltip){\r
466             this.config[col].tooltip = tooltip;\r
467     },\r
468 \r
469     /**\r
470      * Returns the dataIndex for the specified column.\r
471      * @param {Number} col The column index\r
472      * @return {String} The column's dataIndex\r
473      */\r
474     getDataIndex : function(col){\r
475         return this.config[col].dataIndex;\r
476     },\r
477 \r
478     /**\r
479      * Sets the dataIndex for a column.\r
480      * @param {Number} col The column index\r
481      * @param {String} dataIndex The new dataIndex\r
482      */\r
483     setDataIndex : function(col, dataIndex){\r
484         this.config[col].dataIndex = dataIndex;\r
485     },\r
486 \r
487     /**\r
488      * Finds the index of the first matching column for the given dataIndex.\r
489      * @param {String} col The dataIndex to find\r
490      * @return {Number} The column index, or -1 if no match was found\r
491      */\r
492     findColumnIndex : function(dataIndex){\r
493         var c = this.config;\r
494         for(var i = 0, len = c.length; i < len; i++){\r
495             if(c[i].dataIndex == dataIndex){\r
496                 return i;\r
497             }\r
498         }\r
499         return -1;\r
500     },\r
501 \r
502     /**\r
503      * Returns true if the cell is editable.\r
504      * @param {Number} colIndex The column index\r
505      * @param {Number} rowIndex The row index\r
506      * @return {Boolean}\r
507      */\r
508     isCellEditable : function(colIndex, rowIndex){\r
509         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;\r
510     },\r
511 \r
512     /**\r
513      * Returns the editor defined for the cell/column.\r
514      * @param {Number} colIndex The column index\r
515      * @param {Number} rowIndex The row index\r
516      * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap \r
517      * the {@link Ext.form.Field Field} used to edit the cell.\r
518      */\r
519     getCellEditor : function(colIndex, rowIndex){\r
520         return this.config[colIndex].editor;\r
521     },\r
522 \r
523     /**\r
524      * Sets if a column is editable.\r
525      * @param {Number} col The column index\r
526      * @param {Boolean} editable True if the column is editable\r
527      */\r
528     setEditable : function(col, editable){\r
529         this.config[col].editable = editable;\r
530     },\r
531 \r
532 \r
533     /**\r
534      * Returns true if the column is hidden.\r
535      * @param {Number} colIndex The column index\r
536      * @return {Boolean}\r
537      */\r
538     isHidden : function(colIndex){\r
539         return this.config[colIndex].hidden;\r
540     },\r
541 \r
542 \r
543     /**\r
544      * Returns true if the column width cannot be changed\r
545      */\r
546     isFixed : function(colIndex){\r
547         return this.config[colIndex].fixed;\r
548     },\r
549 \r
550     /**\r
551      * Returns true if the column can be resized\r
552      * @return {Boolean}\r
553      */\r
554     isResizable : function(colIndex){\r
555         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;\r
556     },\r
557     /**\r
558      * Sets if a column is hidden.\r
559      * @param {Number} colIndex The column index\r
560      * @param {Boolean} hidden True if the column is hidden\r
561      */\r
562     setHidden : function(colIndex, hidden){\r
563         var c = this.config[colIndex];\r
564         if(c.hidden !== hidden){\r
565             c.hidden = hidden;\r
566             this.totalWidth = null;\r
567             this.fireEvent("hiddenchange", this, colIndex, hidden);\r
568         }\r
569     },\r
570 \r
571     /**\r
572      * Sets the editor for a column.\r
573      * @param {Number} col The column index\r
574      * @param {Object} editor The editor object\r
575      */\r
576     setEditor : function(col, editor){\r
577         this.config[col].editor = editor;\r
578     }\r
579 });\r
580 \r
581 // private\r
582 Ext.grid.ColumnModel.defaultRenderer = function(value){\r
583     if(typeof value == "string" && value.length < 1){\r
584         return "&#160;";\r
585     }\r
586     return value;\r
587 };\r
588 \r
589 // Alias for backwards compatibility\r
590 Ext.grid.DefaultColumnModel = Ext.grid.ColumnModel;