X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/grid/plugin/Editing.js diff --git a/src/grid/plugin/Editing.js b/src/grid/plugin/Editing.js new file mode 100644 index 00000000..9dc59560 --- /dev/null +++ b/src/grid/plugin/Editing.js @@ -0,0 +1,331 @@ + +/** + * @class Ext.grid.plugin.Editing + +This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}. +The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor} +in the {@link Ext.grid.column.Column column configuration}. + +*Note:* This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and +{@link Ext.grid.plugin.RowEditing}. + + * @markdown + */ +Ext.define('Ext.grid.plugin.Editing', { + alias: 'editing.editing', + + requires: [ + 'Ext.grid.column.Column', + 'Ext.util.KeyNav' + ], + + mixins: { + observable: 'Ext.util.Observable' + }, + + /** + * @cfg {Number} clicksToEdit + * The number of clicks on a grid required to display the editor (defaults to 2). + */ + clicksToEdit: 2, + + // private + defaultFieldXType: 'textfield', + + // cell, row, form + editStyle: '', + + constructor: function(config) { + var me = this; + Ext.apply(me, config); + + me.addEvents( + // Doc'ed in separate editing plugins + 'beforeedit', + + // Doc'ed in separate editing plugins + 'edit', + + // Doc'ed in separate editing plugins + 'validateedit' + ); + me.mixins.observable.constructor.call(me); + // TODO: Deprecated, remove in 5.0 + me.relayEvents(me, ['afteredit'], 'after'); + }, + + // private + init: function(grid) { + var me = this; + + me.grid = grid; + me.view = grid.view; + me.initEvents(); + me.initFieldAccessors(me.view.getGridColumns()); + + grid.relayEvents(me, ['beforeedit', 'edit', 'validateedit']); + // Marks the grid as editable, so that the SelectionModel + // can make appropriate decisions during navigation + grid.isEditable = true; + grid.editingPlugin = grid.view.editingPlugin = me; + }, + + /** + * @private + * AbstractComponent calls destroy on all its plugins at destroy time. + */ + destroy: function() { + var me = this, + grid = me.grid, + headerCt = grid.headerCt, + events = grid.events; + + Ext.destroy(me.keyNav); + me.removeFieldAccessors(grid.getView().getGridColumns()); + + // Clear all listeners from all our events, clear all managed listeners we added to other Observables + me.clearListeners(); + + delete me.grid.editingPlugin; + delete me.grid.view.editingPlugin; + delete me.grid; + delete me.view; + delete me.editor; + delete me.keyNav; + }, + + // private + getEditStyle: function() { + return this.editStyle; + }, + + // private + initFieldAccessors: function(column) { + var me = this; + + if (Ext.isArray(column)) { + Ext.Array.forEach(column, me.initFieldAccessors, me); + return; + } + + // Augment the Header class to have a getEditor and setEditor method + // Important: Only if the header does not have its own implementation. + Ext.applyIf(column, { + getEditor: function(record, defaultField) { + return me.getColumnField(this, defaultField); + }, + + setEditor: function(field) { + me.setColumnField(this, field); + } + }); + }, + + // private + removeFieldAccessors: function(column) { + var me = this; + + if (Ext.isArray(column)) { + Ext.Array.forEach(column, me.removeFieldAccessors, me); + return; + } + + delete column.getEditor; + delete column.setEditor; + }, + + // private + // remaps to the public API of Ext.grid.column.Column.getEditor + getColumnField: function(columnHeader, defaultField) { + var field = columnHeader.field; + + if (!field && columnHeader.editor) { + field = columnHeader.editor; + delete columnHeader.editor; + } + + if (!field && defaultField) { + field = defaultField; + } + + if (field) { + if (Ext.isString(field)) { + field = { xtype: field }; + } + if (Ext.isObject(field) && !field.isFormField) { + field = Ext.ComponentManager.create(field, this.defaultFieldXType); + columnHeader.field = field; + } + + Ext.apply(field, { + name: columnHeader.dataIndex + }); + + return field; + } + }, + + // private + // remaps to the public API of Ext.grid.column.Column.setEditor + setColumnField: function(column, field) { + if (Ext.isObject(field) && !field.isFormField) { + field = Ext.ComponentManager.create(field, this.defaultFieldXType); + } + column.field = field; + }, + + // private + initEvents: function() { + var me = this; + me.initEditTriggers(); + me.initCancelTriggers(); + }, + + // @abstract + initCancelTriggers: Ext.emptyFn, + + // private + initEditTriggers: function() { + var me = this, + view = me.view, + clickEvent = me.clicksToEdit === 1 ? 'click' : 'dblclick'; + + // Start editing + me.mon(view, 'cell' + clickEvent, me.startEditByClick, me); + view.on('render', function() { + me.keyNav = Ext.create('Ext.util.KeyNav', view.el, { + enter: me.onEnterKey, + esc: me.onEscKey, + scope: me + }); + }, me, { single: true }); + }, + + // private + onEnterKey: function(e) { + var me = this, + grid = me.grid, + selModel = grid.getSelectionModel(), + record, + columnHeader = grid.headerCt.getHeaderAtIndex(0); + + // Calculate editing start position from SelectionModel + // CellSelectionModel + if (selModel.getCurrentPosition) { + pos = selModel.getCurrentPosition(); + record = grid.store.getAt(pos.row); + columnHeader = grid.headerCt.getHeaderAtIndex(pos.column); + } + // RowSelectionModel + else { + record = selModel.getLastSelected(); + } + me.startEdit(record, columnHeader); + }, + + // private + onEscKey: function(e) { + this.cancelEdit(); + }, + + // private + startEditByClick: function(view, cell, colIdx, record, row, rowIdx, e) { + this.startEdit(record, view.getHeaderAtIndex(colIdx)); + }, + + /** + * @private + * @abstract. Template method called before editing begins. + * @param {Object} context The current editing context + * @return {Boolean} Return false to cancel the editing process + */ + beforeEdit: Ext.emptyFn, + + /** + * Start editing the specified record, using the specified Column definition to define which field is being edited. + * @param {Model} record The Store data record which backs the row to be edited. + * @param {Model} columnHeader The Column object defining the column to be edited. + */ + startEdit: function(record, columnHeader) { + var me = this, + context = me.getEditingContext(record, columnHeader); + + if (me.beforeEdit(context) === false || me.fireEvent('beforeedit', context) === false || context.cancel) { + return false; + } + + me.context = context; + me.editing = true; + }, + + /** + * @private Collects all information necessary for any subclasses to perform their editing functions. + * @param record + * @param columnHeader + * @returns {Object} The editing context based upon the passed record and column + */ + getEditingContext: function(record, columnHeader) { + var me = this, + grid = me.grid, + store = grid.store, + rowIdx, + colIdx, + view = grid.getView(), + value; + + // If they'd passed numeric row, column indices, look them up. + if (Ext.isNumber(record)) { + rowIdx = record; + record = store.getAt(rowIdx); + } else { + rowIdx = store.indexOf(record); + } + if (Ext.isNumber(columnHeader)) { + colIdx = columnHeader; + columnHeader = grid.headerCt.getHeaderAtIndex(colIdx); + } else { + colIdx = columnHeader.getIndex(); + } + + value = record.get(columnHeader.dataIndex); + return { + grid: grid, + record: record, + field: columnHeader.dataIndex, + value: value, + row: view.getNode(rowIdx), + column: columnHeader, + rowIdx: rowIdx, + colIdx: colIdx + }; + }, + + /** + * Cancel any active edit that is in progress. + */ + cancelEdit: function() { + this.editing = false; + }, + + /** + * Complete the edit if there is an active edit in progress. + */ + completeEdit: function() { + var me = this; + + if (me.editing && me.validateEdit()) { + me.fireEvent('edit', me.context); + } + + delete me.context; + me.editing = false; + }, + + // @abstract + validateEdit: function() { + var me = this, + context = me.context; + + return me.fireEvent('validateedit', me, context) !== false && !context.cancel; + } +}); \ No newline at end of file