/**
 * @class Ext.grid.ColumnModel
 * @extends Ext.util.Observable
 *

After the data has been read into the client side cache ({@link Ext.data.Store Store}),
 * the ColumnModel is used to configure how and what parts of that data will be displayed in the
 * vertical slices (columns) of the grid. The Ext.grid.ColumnModel Class is the default implementation
 * of a ColumnModel used by implentations of {@link Ext.grid.GridPanel GridPanel}.


 *

Data is mapped into the store's records and then indexed into the ColumnModel using the
 *
{@link Ext.grid.Column#dataIndex dataIndex}:


 *

{data source} == mapping ==> {data store} ==
{@link Ext.grid.Column#dataIndex dataIndex} ==> {ColumnModel}
 *

 *

Each {@link Ext.grid.Column Column} in the grid's ColumnModel is configured with a
 *
{@link Ext.grid.Column#dataIndex dataIndex} to specify how the data within
 * each record in the store is indexed into the ColumnModel.


 *

There are two ways to initialize the ColumnModel class:


 *

Initialization Method 1: an Array



 var colModel = new Ext.grid.ColumnModel([
    { header: "Ticker", width: 60, sortable: true},
    { header: "Company Name", width: 150, sortable: true, id: 'company'},
    { header: "Market Cap.", width: 100, sortable: true},
    { header: "$ Sales", width: 100, sortable: true, renderer: money},
    { header: "Employees", width: 100, sortable: true, resizable: false}
 ]);
 

 *

The ColumnModel may be initialized with an Array of {@link Ext.grid.Column} column configuration
 * objects to define the initial layout / display of the columns in the Grid. The order of each
 * {@link Ext.grid.Column} column configuration object within the specified Array defines the initial
 * order of the column display.  A Column's display may be initially hidden using the
 *
{@link Ext.grid.Column#hidden hidden} config property (and then shown using the column
 * header menu).  Field's that are not included in the ColumnModel will not be displayable at all.


 *

How each column in the grid correlates (maps) to the {@link Ext.data.Record} field in the
 * {@link Ext.data.Store Store} the column draws its data from is configured through the
 *
{@link Ext.grid.Column#dataIndex dataIndex}.  If the
 *
{@link Ext.grid.Column#dataIndex dataIndex} is not explicitly defined (as shown in the
 * example above) it will use the column configuration's index in the Array as the index.


 *

See {@link Ext.grid.Column} for additional configuration options for each column.


 *

Initialization Method 2: an Object


 *

In order to use configuration options from Ext.grid.ColumnModel, an Object may be used to
 * initialize the ColumnModel.  The column configuration Array will be specified in the
{@link #columns}
 * config property. The
{@link #defaults} config property can be used to apply defaults
 * for all columns, e.g.:


 var colModel = new Ext.grid.ColumnModel({
    columns: [
        { header: "Ticker", width: 60, menuDisabled: false},
        { header: "Company Name", width: 150, id: 'company'},
        { header: "Market Cap."},
        { header: "$ Sales", renderer: money},
        { header: "Employees", resizable: false}
    ],
    defaults: {
        sortable: true,
        menuDisabled: true,
        width: 100
    },
    listeners: {
        {@link #hiddenchange}: function(cm, colIndex, hidden) {
            saveConfig(colIndex, hidden);
        }
    }
});
 

 *

In both examples above, the ability to apply a CSS class to all cells in a column (including the
 * header) is demonstrated through the use of the
{@link Ext.grid.Column#id id} config
 * option. This column could be styled by including the following css:


 //add this css *after* the core css is loaded
.x-grid3-td-company {
    color: red; // entire column will have red font
}
// modify the header row only, adding an icon to the column header
.x-grid3-hd-company {
    background: transparent
        url(../../resources/images/icons/silk/building.png)
        no-repeat 3px 3px ! important;
        padding-left:20px;
}
 

 * Note that the "Company Name" column could be specified as the
 *
{@link Ext.grid.GridPanel}.{@link Ext.grid.GridPanel#autoExpandColumn autoExpandColumn}.
 * @constructor
 * @param {Mixed} config Specify either an Array of {@link Ext.grid.Column} configuration objects or specify
 * a configuration Object (see introductory section discussion utilizing Initialization Method 2 above).
 */

Ext.grid.ColumnModel = function(config){
   
/**
     * An Array of {@link Ext.grid.Column Column definition} objects representing the configuration
     * of this ColumnModel.  See {@link Ext.grid.Column} for the configuration properties that may
     * be specified.
     * @property config
     * @type Array
     */

   
if(config.columns){
       
Ext.apply(this, config);
       
this.setConfig(config.columns, true);
   
}else{
       
this.setConfig(config, true);
   
}
   
this.addEvents(
       
/**
         * @event widthchange
         * Fires when the width of a column is programmaticially changed using
         *
{@link #setColumnWidth}.
         * Note internal resizing suppresses the event from firing. See also
         * {@link Ext.grid.GridPanel}.
{@link #columnresize}.
         * @param {ColumnModel} this
         * @param {Number} columnIndex The column index
         * @param {Number} newWidth The new width
         */

       
"widthchange",
       
/**
         * @event headerchange
         * Fires when the text of a header changes.
         * @param {ColumnModel} this
         * @param {Number} columnIndex The column index
         * @param {String} newText The new header text
         */

       
"headerchange",
       
/**
         * @event hiddenchange
         * Fires when a column is hidden or "unhidden".
         * @param {ColumnModel} this
         * @param {Number} columnIndex The column index
         * @param {Boolean} hidden true if hidden, false otherwise
         */

       
"hiddenchange",
       
/**
         * @event columnmoved
         * Fires when a column is moved.
         * @param {ColumnModel} this
         * @param {Number} oldIndex
         * @param {Number} newIndex
         */

       
"columnmoved",
       
/**
         * @event configchange
         * Fires when the configuration is changed
         * @param {ColumnModel} this
         */

       
"configchange"
   
);
   
Ext.grid.ColumnModel.superclass.constructor.call(this);
};
Ext.extend(Ext.grid.ColumnModel, Ext.util.Observable, {
   
/**
     * @cfg {Number} defaultWidth (optional) The width of columns which have no
{@link #width}
     * specified (defaults to
100).  This property shall preferably be configured through the
     *
{@link #defaults} config property.
     */

    defaultWidth
: 100,
   
/**
     * @cfg {Boolean} defaultSortable (optional) Default sortable of columns which have no
     * sortable specified (defaults to
false).  This property shall preferably be configured
     * through the
{@link #defaults} config property.
     */

    defaultSortable
: false,
   
/**
     * @cfg {Array} columns An Array of object literals.  The config options defined by
     *
{@link Ext.grid.Column} are the options which may appear in the object literal for each
     * individual column definition.
     */

   
/**
     * @cfg {Object} defaults Object literal which will be used to apply {@link Ext.grid.Column}
     * configuration options to all
{@link #columns}.  Configuration options specified with
     * individual {@link Ext.grid.Column column} configs will supersede these
{@link #defaults}.
     */


   
/**
     * Returns the id of the column at the specified index.
     * @param {Number} index The column index
     * @return {String} the id
     */

    getColumnId
: function(index){
       
return this.config[index].id;
   
},

    getColumnAt
: function(index){
       
return this.config[index];
   
},

   
/**
     *

Reconfigures this column model according to the passed Array of column definition objects.
     * For a description of the individual properties of a column definition object, see the
     *
Config Options.


     *

Causes the {@link #configchange} event to be fired. A {@link Ext.grid.GridPanel GridPanel}
     * using this ColumnModel will listen for this event and refresh its UI automatically.


     * @param {Array} config Array of Column definition objects.
     * @param {Boolean} initial Specify
true to bypass cleanup which deletes the totalWidth
     * and destroys existing editors.
     */

    setConfig
: function(config, initial){
       
var i, c, len;
       
if(!initial){ // cleanup
           
delete this.totalWidth;
           
for(i = 0, len = this.config.length; i < len; i++){
                c
= this.config[i];
               
if(c.editor){
                    c
.editor.destroy();
               
}
           
}
       
}

       
// backward compatibility
       
this.defaults = Ext.apply({
            width
: this.defaultWidth,
            sortable
: this.defaultSortable
       
}, this.defaults);

       
this.config = config;
       
this.lookup = {};
       
// if no id, create one
       
for(i = 0, len = config.length; i < len; i++){
            c
= Ext.applyIf(config[i], this.defaults);
           
if(!c.isColumn){
               
var cls = Ext.grid.Column.types[c.xtype || 'gridcolumn'];
                c
= new cls(c);
                config
[i] = c;
           
}
           
this.lookup[c.id] = c;
       
}
       
if(!initial){
           
this.fireEvent('configchange', this);
       
}
   
},

   
/**
     * Returns the column for a specified id.
     * @param {String} id The column id
     * @return {Object} the column
     */

    getColumnById
: function(id){
       
return this.lookup[id];
   
},

   
/**
     * Returns the index for a specified column id.
     * @param {String} id The column id
     * @return {Number} the index, or -1 if not found
     */

    getIndexById
: function(id){
       
for(var i = 0, len = this.config.length; i < len; i++){
           
if(this.config[i].id == id){
               
return i;
           
}
       
}
       
return -1;
   
},

   
/**
     * Moves a column from one position to another.
     * @param {Number} oldIndex The index of the column to move.
     * @param {Number} newIndex The position at which to reinsert the coolumn.
     */

    moveColumn
: function(oldIndex, newIndex){
       
var c = this.config[oldIndex];
       
this.config.splice(oldIndex, 1);
       
this.config.splice(newIndex, 0, c);
       
this.dataMap = null;
       
this.fireEvent("columnmoved", this, oldIndex, newIndex);
   
},

   
/**
     * Returns the number of columns.
     * @param {Boolean} visibleOnly Optional. Pass as true to only include visible columns.
     * @return {Number}
     */

    getColumnCount
: function(visibleOnly){
       
if(visibleOnly === true){
           
var c = 0;
           
for(var i = 0, len = this.config.length; i < len; i++){
               
if(!this.isHidden(i)){
                    c
++;
               
}
           
}
           
return c;
       
}
       
return this.config.length;
   
},

   
/**
     * Returns the column configs that return true by the passed function that is called
     * with (columnConfig, index)

// returns an array of column config objects for all hidden columns
var columns = grid.getColumnModel().getColumnsBy(function(c){
  return c.hidden;
});

     * @param {Function} fn
     * @param {Object} scope (optional)
     * @return {Array} result
     */

    getColumnsBy
: function(fn, scope){
       
var r = [];
       
for(var i = 0, len = this.config.length; i < len; i++){
           
var c = this.config[i];
           
if(fn.call(scope||this, c, i) === true){
                r
[r.length] = c;
           
}
       
}
       
return r;
   
},

   
/**
     * Returns true if the specified column is sortable.
     * @param {Number} col The column index
     * @return {Boolean}
     */

    isSortable
: function(col){
       
return this.config[col].sortable;
   
},

   
/**
     * Returns true if the specified column menu is disabled.
     * @param {Number} col The column index
     * @return {Boolean}
     */

    isMenuDisabled
: function(col){
       
return !!this.config[col].menuDisabled;
   
},

   
/**
     * Returns the rendering (formatting) function defined for the column.
     * @param {Number} col The column index.
     * @return {Function} The function used to render the cell. See {@link #setRenderer}.
     */

    getRenderer
: function(col){
       
if(!this.config[col].renderer){
           
return Ext.grid.ColumnModel.defaultRenderer;
       
}
       
return this.config[col].renderer;
   
},

   
/**
     * Sets the rendering (formatting) function for a column.  See {@link Ext.util.Format} for some
     * default formatting functions.
     * @param {Number} col The column index
     * @param {Function} fn The function to use to process the cell's raw data
     * to return HTML markup for the grid view. The render function is called with
     * the following parameters:

     */

    setRenderer
: function(col, fn){
       
this.config[col].renderer = fn;
   
},

   
/**
     * Returns the width for the specified column.
     * @param {Number} col The column index
     * @return {Number}
     */

    getColumnWidth
: function(col){
       
return this.config[col].width;
   
},

   
/**
     * Sets the width for a column.
     * @param {Number} col The column index
     * @param {Number} width The new width
     * @param {Boolean} suppressEvent True to suppress firing the
{@link #widthchange}
     * event. Defaults to false.
     */

    setColumnWidth
: function(col, width, suppressEvent){
       
this.config[col].width = width;
       
this.totalWidth = null;
       
if(!suppressEvent){
             
this.fireEvent("widthchange", this, col, width);
       
}
   
},

   
/**
     * Returns the total width of all columns.
     * @param {Boolean} includeHidden True to include hidden column widths
     * @return {Number}
     */

    getTotalWidth
: function(includeHidden){
       
if(!this.totalWidth){
           
this.totalWidth = 0;
           
for(var i = 0, len = this.config.length; i < len; i++){
               
if(includeHidden || !this.isHidden(i)){
                   
this.totalWidth += this.getColumnWidth(i);
               
}
           
}
       
}
       
return this.totalWidth;
   
},

   
/**
     * Returns the header for the specified column.
     * @param {Number} col The column index
     * @return {String}
     */

    getColumnHeader
: function(col){
       
return this.config[col].header;
   
},

   
/**
     * Sets the header for a column.
     * @param {Number} col The column index
     * @param {String} header The new header
     */

    setColumnHeader
: function(col, header){
       
this.config[col].header = header;
       
this.fireEvent("headerchange", this, col, header);
   
},

   
/**
     * Returns the tooltip for the specified column.
     * @param {Number} col The column index
     * @return {String}
     */

    getColumnTooltip
: function(col){
           
return this.config[col].tooltip;
   
},
   
/**
     * Sets the tooltip for a column.
     * @param {Number} col The column index
     * @param {String} tooltip The new tooltip
     */

    setColumnTooltip
: function(col, tooltip){
           
this.config[col].tooltip = tooltip;
   
},

   
/**
     * Returns the dataIndex for the specified column.

// Get field name for the column
var fieldName = grid.getColumnModel().getDataIndex(columnIndex);

     * @param {Number} col The column index
     * @return {String} The column's dataIndex
     */

    getDataIndex
: function(col){
       
return this.config[col].dataIndex;
   
},

   
/**
     * Sets the dataIndex for a column.
     * @param {Number} col The column index
     * @param {String} dataIndex The new dataIndex
     */

    setDataIndex
: function(col, dataIndex){
       
this.config[col].dataIndex = dataIndex;
   
},

   
/**
     * Finds the index of the first matching column for the given dataIndex.
     * @param {String} col The dataIndex to find
     * @return {Number} The column index, or -1 if no match was found
     */

    findColumnIndex
: function(dataIndex){
       
var c = this.config;
       
for(var i = 0, len = c.length; i < len; i++){
           
if(c[i].dataIndex == dataIndex){
               
return i;
           
}
       
}
       
return -1;
   
},

   
/**
     * Returns true if the cell is editable.

var store = new Ext.data.Store({...});
var colModel = new Ext.grid.ColumnModel({
  columns: [...],
  isCellEditable: function(col, row) {
    var record = store.getAt(row);
    if (record.get('readonly')) { // replace with your condition
      return false;
    }
    return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, col, row);
  }
});
var grid = new Ext.grid.GridPanel({
  store: store,
  colModel: colModel,
  ...
});

     * @param {Number} colIndex The column index
     * @param {Number} rowIndex The row index
     * @return {Boolean}
     */

    isCellEditable
: function(colIndex, rowIndex){
       
return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
   
},

   
/**
     * Returns the editor defined for the cell/column.
     * @param {Number} colIndex The column index
     * @param {Number} rowIndex The row index
     * @return {Ext.Editor} The {@link Ext.Editor Editor} that was created to wrap
     * the {@link Ext.form.Field Field} used to edit the cell.
     */

    getCellEditor
: function(colIndex, rowIndex){
       
return this.config[colIndex].getCellEditor(rowIndex);
   
},

   
/**
     * Sets if a column is editable.
     * @param {Number} col The column index
     * @param {Boolean} editable True if the column is editable
     */

    setEditable
: function(col, editable){
       
this.config[col].editable = editable;
   
},


   
/**
     * Returns true if the column is hidden.
     * @param {Number} colIndex The column index
     * @return {Boolean}
     */

    isHidden
: function(colIndex){
       
return this.config[colIndex].hidden;
   
},


   
/**
     * Returns true if the column width cannot be changed
     */

    isFixed
: function(colIndex){
       
return this.config[colIndex].fixed;
   
},

   
/**
     * Returns true if the column can be resized
     * @return {Boolean}
     */

    isResizable
: function(colIndex){
       
return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
   
},
   
/**
     * Sets if a column is hidden.

myGrid.getColumnModel().setHidden(0, true); // hide column 0 (0 = the first column).

     * @param {Number} colIndex The column index
     * @param {Boolean} hidden True if the column is hidden
     */

    setHidden
: function(colIndex, hidden){
       
var c = this.config[colIndex];
       
if(c.hidden !== hidden){
            c
.hidden = hidden;
           
this.totalWidth = null;
           
this.fireEvent("hiddenchange", this, colIndex, hidden);
       
}
   
},

   
/**
     * Sets the editor for a column and destroys the prior editor.
     * @param {Number} col The column index
     * @param {Object} editor The editor object
     */

    setEditor
: function(col, editor){
       
Ext.destroy(this.config[col].editor);
       
this.config[col].editor = editor;
   
},

   
/**
     * Destroys this column model by purging any event listeners, and removing any editors.
     */

    destroy
: function(){
       
for(var i = 0, c = this.config, len = c.length; i < len; i++){
           
Ext.destroy(c[i].editor);
       
}
       
this.purgeListeners();
   
}
});

// private
Ext.grid.ColumnModel.defaultRenderer = function(value){
   
if(typeof value == "string" && value.length < 1){
       
return " ";
   
}
   
return value;
};