3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
17 * @class Ext.grid.plugin.Editing
19 This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}.
20 The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor}
21 in the {@link Ext.grid.column.Column column configuration}.
23 *Note:* This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and
24 {@link Ext.grid.plugin.RowEditing}.
28 Ext.define('Ext.grid.plugin.Editing', {
29 alias: 'editing.editing',
32 'Ext.grid.column.Column',
37 observable: 'Ext.util.Observable'
41 * @cfg {Number} clicksToEdit
42 * The number of clicks on a grid required to display the editor (defaults to 2).
47 defaultFieldXType: 'textfield',
52 constructor: function(config) {
54 Ext.apply(me, config);
57 // Doc'ed in separate editing plugins
60 // Doc'ed in separate editing plugins
63 // Doc'ed in separate editing plugins
66 me.mixins.observable.constructor.call(me);
67 // TODO: Deprecated, remove in 5.0
68 me.relayEvents(me, ['afteredit'], 'after');
72 init: function(grid) {
78 me.mon(grid, 'reconfigure', me.onReconfigure, me);
81 grid.relayEvents(me, ['beforeedit', 'edit', 'validateedit']);
82 // Marks the grid as editable, so that the SelectionModel
83 // can make appropriate decisions during navigation
84 grid.isEditable = true;
85 grid.editingPlugin = grid.view.editingPlugin = me;
89 * Fires after the grid is reconfigured
92 onReconfigure: function(){
93 this.initFieldAccessors(this.view.getGridColumns());
98 * AbstractComponent calls destroy on all its plugins at destroy time.
100 destroy: function() {
103 headerCt = grid.headerCt,
104 events = grid.events;
106 Ext.destroy(me.keyNav);
107 me.removeFieldAccessors(grid.getView().getGridColumns());
109 // Clear all listeners from all our events, clear all managed listeners we added to other Observables
112 delete me.grid.editingPlugin;
113 delete me.grid.view.editingPlugin;
121 getEditStyle: function() {
122 return this.editStyle;
126 initFieldAccessors: function(column) {
129 if (Ext.isArray(column)) {
130 Ext.Array.forEach(column, me.initFieldAccessors, me);
134 // Augment the Header class to have a getEditor and setEditor method
135 // Important: Only if the header does not have its own implementation.
136 Ext.applyIf(column, {
137 getEditor: function(record, defaultField) {
138 return me.getColumnField(this, defaultField);
141 setEditor: function(field) {
142 me.setColumnField(this, field);
148 removeFieldAccessors: function(column) {
151 if (Ext.isArray(column)) {
152 Ext.Array.forEach(column, me.removeFieldAccessors, me);
156 delete column.getEditor;
157 delete column.setEditor;
161 // remaps to the public API of Ext.grid.column.Column.getEditor
162 getColumnField: function(columnHeader, defaultField) {
163 var field = columnHeader.field;
165 if (!field && columnHeader.editor) {
166 field = columnHeader.editor;
167 delete columnHeader.editor;
170 if (!field && defaultField) {
171 field = defaultField;
175 if (Ext.isString(field)) {
176 field = { xtype: field };
178 if (Ext.isObject(field) && !field.isFormField) {
179 field = Ext.ComponentManager.create(field, this.defaultFieldXType);
180 columnHeader.field = field;
184 name: columnHeader.dataIndex
192 // remaps to the public API of Ext.grid.column.Column.setEditor
193 setColumnField: function(column, field) {
194 if (Ext.isObject(field) && !field.isFormField) {
195 field = Ext.ComponentManager.create(field, this.defaultFieldXType);
197 column.field = field;
201 initEvents: function() {
203 me.initEditTriggers();
204 me.initCancelTriggers();
208 initCancelTriggers: Ext.emptyFn,
211 initEditTriggers: function() {
214 clickEvent = me.clicksToEdit === 1 ? 'click' : 'dblclick';
217 me.mon(view, 'cell' + clickEvent, me.startEditByClick, me);
218 view.on('render', function() {
219 me.keyNav = Ext.create('Ext.util.KeyNav', view.el, {
220 enter: me.onEnterKey,
224 }, me, { single: true });
228 onEnterKey: function(e) {
231 selModel = grid.getSelectionModel(),
233 columnHeader = grid.headerCt.getHeaderAtIndex(0);
235 // Calculate editing start position from SelectionModel
236 // CellSelectionModel
237 if (selModel.getCurrentPosition) {
238 pos = selModel.getCurrentPosition();
239 record = grid.store.getAt(pos.row);
240 columnHeader = grid.headerCt.getHeaderAtIndex(pos.column);
244 record = selModel.getLastSelected();
246 me.startEdit(record, columnHeader);
250 onEscKey: function(e) {
255 startEditByClick: function(view, cell, colIdx, record, row, rowIdx, e) {
256 this.startEdit(record, view.getHeaderAtIndex(colIdx));
261 * @abstract. Template method called before editing begins.
262 * @param {Object} context The current editing context
263 * @return {Boolean} Return false to cancel the editing process
265 beforeEdit: Ext.emptyFn,
268 * Start editing the specified record, using the specified Column definition to define which field is being edited.
269 * @param {Model} record The Store data record which backs the row to be edited.
270 * @param {Model} columnHeader The Column object defining the column to be edited.
272 startEdit: function(record, columnHeader) {
274 context = me.getEditingContext(record, columnHeader);
276 if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) {
280 me.context = context;
285 * @private Collects all information necessary for any subclasses to perform their editing functions.
287 * @param columnHeader
288 * @returns {Object} The editing context based upon the passed record and column
290 getEditingContext: function(record, columnHeader) {
296 view = grid.getView(),
299 // If they'd passed numeric row, column indices, look them up.
300 if (Ext.isNumber(record)) {
302 record = store.getAt(rowIdx);
304 rowIdx = store.indexOf(record);
306 if (Ext.isNumber(columnHeader)) {
307 colIdx = columnHeader;
308 columnHeader = grid.headerCt.getHeaderAtIndex(colIdx);
310 colIdx = columnHeader.getIndex();
313 value = record.get(columnHeader.dataIndex);
317 field: columnHeader.dataIndex,
319 row: view.getNode(rowIdx),
320 column: columnHeader,
327 * Cancel any active edit that is in progress.
329 cancelEdit: function() {
330 this.editing = false;
334 * Complete the edit if there is an active edit in progress.
336 completeEdit: function() {
339 if (me.editing && me.validateEdit()) {
340 me.fireEvent('edit', me.context);
348 validateEdit: function() {
350 context = me.context;
352 return me.fireEvent('validateedit', me, context) !== false && !context.cancel;