commit extjs-2.2.1
[extjs.git] / source / widgets / grid / EditorGrid.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.EditorGridPanel\r
11  * @extends Ext.grid.GridPanel\r
12  * <p>This class extends the GridPanel to provide cell editing on selected columns.</p>\r
13  * The editable columns are specified by providing an {@link Ext.grid.ColumnModel#editor editor}\r
14  * in the column configuration.</p>\r
15  * <p>Editability of columns may be controlled programatically by inserting an implementation\r
16  * of {@link Ext.grid.ColumnModel#isCellEditable isCellEditable} into your ColumnModel.</p>\r
17  * <p>Editing is performed on the value of the <i>field</i> specified by the column's\r
18  * {@link Ext.grid.ColumnModel#dataIndex dataIndex} in the backing {@link Ext.data.Store Store}\r
19  * (so if you are using a {@link Ext.grid.ColumnModel#setRenderer renderer} in order to display\r
20  * transformed data, this must be accounted for).</p>\r
21  * <p>If a value-to-description mapping is used to render a column, then a {Ext.form.Field#ComboBox ComboBox}\r
22  * which uses the same {@link Ext.form.Field#valueField value}-to-{@link Ext.form.Field#displayFieldField description}\r
23  * mapping would be an appropriate editor.</p>\r
24  * If there is a more complex mismatch between the visible data in the grid, and the editable data in\r
25  * the {@link Edt.data.Store Store}, then code to transform the data both before and after editing can be\r
26  * injected using the {@link #beforeedit} and {@link #afteredit} events.\r
27  * @constructor\r
28  * @param {Object} config The config object\r
29  */\r
30 Ext.grid.EditorGridPanel = Ext.extend(Ext.grid.GridPanel, {\r
31     /**\r
32      * @cfg {Number} clicksToEdit\r
33      * <p>The number of clicks on a cell required to display the cell's editor (defaults to 2).</p>\r
34      * <p>Setting this option to 'auto' means that mousedown <i>on the selected cell</i> starts\r
35      * editing that cell.</p>\r
36      */\r
37     clicksToEdit: 2,\r
38 \r
39     // private\r
40     isEditor : true,\r
41     // private\r
42     detectEdit: false,\r
43 \r
44         /**\r
45          * @cfg {Boolean} autoEncode\r
46          * True to automatically HTML encode and decode values pre and post edit (defaults to false)\r
47          */\r
48         autoEncode : false,\r
49 \r
50         /**\r
51          * @cfg {Boolean} trackMouseOver @hide\r
52          */\r
53     // private\r
54     trackMouseOver: false, // causes very odd FF errors\r
55 \r
56     // private\r
57     initComponent : function(){\r
58         Ext.grid.EditorGridPanel.superclass.initComponent.call(this);\r
59 \r
60         if(!this.selModel){\r
61             /**\r
62              * @cfg {Object} selModel Any subclass of AbstractSelectionModel that will provide the selection model for\r
63              * the grid (defaults to {@link Ext.grid.CellSelectionModel} if not specified). Note that the SelectionModel\r
64              * must be compatible with the model of selecting cells individually, and should support a method named\r
65              * <tt>getSelectedCell</tt> (for these reasons, {@link Ext.grid.RowSelectionModel} is not compatible).\r
66              */\r
67             this.selModel = new Ext.grid.CellSelectionModel();\r
68         }\r
69 \r
70         this.activeEditor = null;\r
71 \r
72             this.addEvents(\r
73             /**\r
74              * @event beforeedit\r
75              * Fires before cell editing is triggered. The edit event object has the following properties <br />\r
76              * <ul style="padding:5px;padding-left:16px;">\r
77              * <li>grid - This grid</li>\r
78              * <li>record - The record being edited</li>\r
79              * <li>field - The field name being edited</li>\r
80              * <li>value - The value for the field being edited.</li>\r
81              * <li>row - The grid row index</li>\r
82              * <li>column - The grid column index</li>\r
83              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>\r
84              * </ul>\r
85              * @param {Object} e An edit event (see above for description)\r
86              */\r
87             "beforeedit",\r
88             /**\r
89              * @event afteredit\r
90              * Fires after a cell is edited. The edit event object has the following properties <br />\r
91              * <ul style="padding:5px;padding-left:16px;">\r
92              * <li>grid - This grid</li>\r
93              * <li>record - The record being edited</li>\r
94              * <li>field - The field name being edited</li>\r
95              * <li>value - The value being set</li>\r
96              * <li>originalValue - The original value for the field, before the edit.</li>\r
97              * <li>row - The grid row index</li>\r
98              * <li>column - The grid column index</li>\r
99              * </ul>\r
100              * @param {Object} e An edit event (see above for description)\r
101              */\r
102             "afteredit",\r
103             /**\r
104              * @event validateedit\r
105              * Fires after a cell is edited, but before the value is set in the record. Return false\r
106              * to cancel the change. The edit event object has the following properties <br />\r
107              * <ul style="padding:5px;padding-left:16px;">\r
108              * <li>grid - This grid</li>\r
109              * <li>record - The record being edited</li>\r
110              * <li>field - The field name being edited</li>\r
111              * <li>value - The value being set</li>\r
112              * <li>originalValue - The original value for the field, before the edit.</li>\r
113              * <li>row - The grid row index</li>\r
114              * <li>column - The grid column index</li>\r
115              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>\r
116              * </ul>\r
117              * @param {Object} e An edit event (see above for description)\r
118              */\r
119             "validateedit"\r
120         );\r
121     },\r
122 \r
123     // private\r
124     initEvents : function(){\r
125         Ext.grid.EditorGridPanel.superclass.initEvents.call(this);\r
126 \r
127         this.on("bodyscroll", this.stopEditing, this, [true]);\r
128         this.on("columnresize", this.stopEditing, this, [true]);\r
129 \r
130         if(this.clicksToEdit == 1){\r
131             this.on("cellclick", this.onCellDblClick, this);\r
132         }else {\r
133             if(this.clicksToEdit == 'auto' && this.view.mainBody){\r
134                 this.view.mainBody.on("mousedown", this.onAutoEditClick, this);\r
135             }\r
136             this.on("celldblclick", this.onCellDblClick, this);\r
137         }\r
138     },\r
139 \r
140     // private\r
141     onCellDblClick : function(g, row, col){\r
142         this.startEditing(row, col);\r
143     },\r
144 \r
145     // private\r
146     onAutoEditClick : function(e, t){\r
147         if(e.button !== 0){\r
148             return;\r
149         }\r
150         var row = this.view.findRowIndex(t);\r
151         var col = this.view.findCellIndex(t);\r
152         if(row !== false && col !== false){\r
153             this.stopEditing();\r
154             if(this.selModel.getSelectedCell){ // cell sm\r
155                 var sc = this.selModel.getSelectedCell();\r
156                 if(sc && sc.cell[0] === row && sc.cell[1] === col){\r
157                     this.startEditing(row, col);\r
158                 }\r
159             }else{\r
160                 if(this.selModel.isSelected(row)){\r
161                     this.startEditing(row, col);\r
162                 }\r
163             }\r
164         }\r
165     },\r
166 \r
167     // private\r
168     onEditComplete : function(ed, value, startValue){\r
169         this.editing = false;\r
170         this.activeEditor = null;\r
171         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);\r
172                 var r = ed.record;\r
173         var field = this.colModel.getDataIndex(ed.col);\r
174         value = this.postEditValue(value, startValue, r, field);\r
175         if(String(value) !== String(startValue)){\r
176             var e = {\r
177                 grid: this,\r
178                 record: r,\r
179                 field: field,\r
180                 originalValue: startValue,\r
181                 value: value,\r
182                 row: ed.row,\r
183                 column: ed.col,\r
184                 cancel:false\r
185             };\r
186             if(this.fireEvent("validateedit", e) !== false && !e.cancel){\r
187                 r.set(field, e.value);\r
188                 delete e.cancel;\r
189                 this.fireEvent("afteredit", e);\r
190             }\r
191         }\r
192         this.view.focusCell(ed.row, ed.col);\r
193     },\r
194 \r
195     /**\r
196      * Starts editing the specified for the specified row/column\r
197      * @param {Number} rowIndex\r
198      * @param {Number} colIndex\r
199      */\r
200     startEditing : function(row, col){\r
201         this.stopEditing();\r
202         if(this.colModel.isCellEditable(col, row)){\r
203             this.view.ensureVisible(row, col, true);\r
204             var r = this.store.getAt(row);\r
205             var field = this.colModel.getDataIndex(col);\r
206             var e = {\r
207                 grid: this,\r
208                 record: r,\r
209                 field: field,\r
210                 value: r.data[field],\r
211                 row: row,\r
212                 column: col,\r
213                 cancel:false\r
214             };\r
215             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){\r
216                 this.editing = true;\r
217                 var ed = this.colModel.getCellEditor(col, row);\r
218                 if(!ed.rendered){\r
219                     ed.render(this.view.getEditorParent(ed));\r
220                 }\r
221                 (function(){ // complex but required for focus issues in safari, ie and opera\r
222                     ed.row = row;\r
223                     ed.col = col;\r
224                     ed.record = r;\r
225                     ed.on("complete", this.onEditComplete, this, {single: true});\r
226                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);\r
227                     /**\r
228                      * The currently active editor or null\r
229                       * @type Ext.Editor\r
230                      */\r
231                     this.activeEditor = ed;\r
232                     var v = this.preEditValue(r, field);\r
233                     ed.startEdit(this.view.getCell(row, col).firstChild, v === undefined ? '' : v);\r
234                 }).defer(50, this);\r
235             }\r
236         }\r
237     },\r
238 \r
239     // private\r
240         preEditValue : function(r, field){\r
241         var value = r.data[field];\r
242                 return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlDecode(value) : value;\r
243         },\r
244 \r
245     // private\r
246         postEditValue : function(value, originalValue, r, field){\r
247                 return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;\r
248         },\r
249 \r
250     /**\r
251      * Stops any active editing\r
252      * @param {Boolean} cancel (optional) True to cancel any changes\r
253      */\r
254     stopEditing : function(cancel){\r
255         if(this.activeEditor){\r
256             this.activeEditor[cancel === true ? 'cancelEdit' : 'completeEdit']();\r
257         }\r
258         this.activeEditor = null;\r
259     },\r
260 \r
261     // private\r
262     onDestroy: function() {\r
263         if(this.rendered){\r
264             var cols = this.colModel.config;\r
265             for(var i = 0, len = cols.length; i < len; i++){\r
266                 var c = cols[i];\r
267                 Ext.destroy(c.editor);\r
268             }\r
269         }\r
270         Ext.grid.EditorGridPanel.superclass.onDestroy.call(this);\r
271     }\r
272 });\r
273 Ext.reg('editorgrid', Ext.grid.EditorGridPanel);