]>
Commit | Line | Data |
---|---|---|
1 | /**\r | |
2 | * This class provides an abstract grid editing plugin on selected {@link Ext.grid.column.Column columns}.\r | |
3 | * The editable columns are specified by providing an {@link Ext.grid.column.Column#editor editor}\r | |
4 | * in the {@link Ext.grid.column.Column column configuration}.\r | |
5 | *\r | |
6 | * **Note:** This class should not be used directly. See {@link Ext.grid.plugin.CellEditing} and\r | |
7 | * {@link Ext.grid.plugin.RowEditing}.\r | |
8 | */\r | |
9 | Ext.define('Ext.grid.plugin.Editing', {\r | |
10 | extend: 'Ext.plugin.Abstract',\r | |
11 | alias: 'editing.editing',\r | |
12 | \r | |
13 | requires: [\r | |
14 | 'Ext.grid.column.Column',\r | |
15 | 'Ext.util.KeyNav',\r | |
16 | // Requiring Ext.form.field.Base and Ext.view.Table ensures that grid editor sass\r | |
17 | // variables can derive from both form field vars and grid vars in the neutral theme\r | |
18 | 'Ext.form.field.Base',\r | |
19 | 'Ext.view.Table'\r | |
20 | ],\r | |
21 | \r | |
22 | mixins: [\r | |
23 | 'Ext.mixin.Observable'\r | |
24 | ],\r | |
25 | \r | |
26 | /**\r | |
27 | * @cfg {Number} clicksToEdit\r | |
28 | * The number of clicks on a grid required to display the editor.\r | |
29 | * The only accepted values are **1** and **2**.\r | |
30 | */\r | |
31 | clicksToEdit: 2,\r | |
32 | \r | |
33 | /**\r | |
34 | * @cfg {String} triggerEvent\r | |
35 | * The event which triggers editing. Supersedes the {@link #clicksToEdit} configuration. May be one of:\r | |
36 | *\r | |
37 | * * cellclick\r | |
38 | * * celldblclick\r | |
39 | * * cellfocus\r | |
40 | * * rowfocus\r | |
41 | */\r | |
42 | triggerEvent: undefined,\r | |
43 | \r | |
44 | /**\r | |
45 | * @property {Boolean} editing\r | |
46 | * Set to `true` while the editing plugin is active and an Editor is visible.\r | |
47 | */\r | |
48 | \r | |
49 | relayedEvents: [\r | |
50 | 'beforeedit',\r | |
51 | 'edit',\r | |
52 | 'validateedit',\r | |
53 | 'canceledit'\r | |
54 | ],\r | |
55 | \r | |
56 | /**\r | |
57 | * @cfg {String} default UI for editor fields\r | |
58 | */\r | |
59 | defaultFieldUI: 'default',\r | |
60 | \r | |
61 | // @private\r | |
62 | defaultFieldXType: 'textfield',\r | |
63 | \r | |
64 | // cell, row, form\r | |
65 | editStyle: '',\r | |
66 | \r | |
67 | /**\r | |
68 | * @event beforeedit\r | |
69 | * Fires before editing is triggered. Return false from event handler to stop the editing.\r | |
70 | *\r | |
71 | * @param {Ext.grid.plugin.Editing} editor\r | |
72 | * @param {Object} context The editing context with the following properties:\r | |
73 | * @param {Ext.grid.Panel} context.grid The owning grid Panel.\r | |
74 | * @param {Ext.data.Model} context.record The record being edited.\r | |
75 | * @param {String} context.field The name of the field being edited.\r | |
76 | * @param {Mixed} context.value The field's current value.\r | |
77 | * @param {HTMLElement} context.row The grid row element.\r | |
78 | * @param {Ext.grid.column.Column} context.column The Column being edited.\r | |
79 | * @param {Number} context.rowIdx The index of the row being edited.\r | |
80 | * @param {Number} context.colIdx The index of the column being edited.\r | |
81 | * @param {Boolean} context.cancel Set this to `true` to cancel the edit or return false from your handler.\r | |
82 | * @param {Mixed} context.originalValue Alias for value (only when using {@link Ext.grid.plugin.CellEditing CellEditing}).\r | |
83 | */\r | |
84 | \r | |
85 | /**\r | |
86 | * @event edit\r | |
87 | * Fires after editing. Usage example:\r | |
88 | *\r | |
89 | * grid.on('edit', function(editor, e) {\r | |
90 | * // commit the changes right after editing finished\r | |
91 | * e.record.commit();\r | |
92 | * });\r | |
93 | *\r | |
94 | * @param {Ext.grid.plugin.Editing} editor\r | |
95 | * @param {Object} context The editing context with the following properties:\r | |
96 | * @param {Ext.grid.Panel} context.grid The owning grid Panel.\r | |
97 | * @param {Ext.data.Model} context.record The record being edited.\r | |
98 | * @param {String} context.field The name of the field being edited.\r | |
99 | * @param {Mixed} context.value The field's current value.\r | |
100 | * @param {HTMLElement} context.row The grid row element.\r | |
101 | * @param {Ext.grid.column.Column} context.column The Column being edited.\r | |
102 | * @param {Number} context.rowIdx The index of the row being edited.\r | |
103 | * @param {Number} context.colIdx The index of the column being edited.\r | |
104 | */\r | |
105 | \r | |
106 | /**\r | |
107 | * @event validateedit\r | |
108 | * Fires after editing, but before the value is set in the record. Return false from event handler to\r | |
109 | * cancel the change.\r | |
110 | *\r | |
111 | * Usage example showing how to remove the red triangle (dirty record indicator) from some records (not all). By\r | |
112 | * observing the grid's validateedit event, it can be cancelled if the edit occurs on a targeted row (for example)\r | |
113 | * and then setting the field's new value in the Record directly:\r | |
114 | *\r | |
115 | * grid.on('validateedit', function (editor, context) {\r | |
116 | * var myTargetRow = 6;\r | |
117 | *\r | |
118 | * if (context.rowIdx === myTargetRow) {\r | |
119 | * context.record.data[context.field] = context.value;\r | |
120 | * }\r | |
121 | * });\r | |
122 | *\r | |
123 | * @param {Ext.grid.plugin.Editing} editor\r | |
124 | * @param {Object} context The editing context with the following properties:\r | |
125 | * @param {Ext.grid.Panel} context.grid The owning grid Panel.\r | |
126 | * @param {Ext.data.Model} context.record The record being edited.\r | |
127 | * @param {String} context.field The name of the field being edited.\r | |
128 | * @param {Mixed} context.value The field's current value.\r | |
129 | * @param {HTMLElement} context.row The grid row element.\r | |
130 | * @param {Ext.grid.column.Column} context.column The Column being edited.\r | |
131 | * @param {Number} context.rowIdx The index of the row being edited.\r | |
132 | * @param {Number} context.colIdx The index of the column being edited.\r | |
133 | */\r | |
134 | \r | |
135 | /**\r | |
136 | * @event canceledit\r | |
137 | * Fires when the user started editing but then cancelled the edit.\r | |
138 | * @param {Ext.grid.plugin.Editing} editor\r | |
139 | * @param {Object} context The editing context with the following properties:\r | |
140 | * @param {Ext.grid.Panel} context.grid The owning grid Panel.\r | |
141 | * @param {Ext.data.Model} context.record The record being edited.\r | |
142 | * @param {String} context.field The name of the field being edited.\r | |
143 | * @param {Mixed} context.value The field's current value.\r | |
144 | * @param {HTMLElement} context.row The grid row element.\r | |
145 | * @param {Ext.grid.column.Column} context.column The Column being edited.\r | |
146 | * @param {Number} context.rowIdx The index of the row being edited.\r | |
147 | * @param {Number} context.colIdx The index of the column being edited.\r | |
148 | */\r | |
149 | \r | |
150 | constructor: function(config) {\r | |
151 | var me = this;\r | |
152 | \r | |
153 | me.callParent([config]);\r | |
154 | me.mixins.observable.constructor.call(me);\r | |
155 | // TODO: Deprecated, remove in 5.0\r | |
156 | me.on("edit", function(editor, e) {\r | |
157 | me.fireEvent("afteredit", editor, e);\r | |
158 | });\r | |
159 | },\r | |
160 | \r | |
161 | // @private\r | |
162 | init: function(grid) {\r | |
163 | var me = this,\r | |
164 | ownerLockable = grid.ownerLockable;\r | |
165 | \r | |
166 | me.grid = grid;\r | |
167 | me.view = grid.view;\r | |
168 | me.initEvents();\r | |
169 | \r | |
170 | // Set up fields at render and reconfigure time\r | |
171 | if (grid.rendered) {\r | |
172 | me.setup();\r | |
173 | } else {\r | |
174 | me.mon(grid, {\r | |
175 | beforereconfigure: me.onBeforeReconfigure,\r | |
176 | reconfigure: me.onReconfigure,\r | |
177 | scope: me,\r | |
178 | beforerender: {\r | |
179 | fn: me.onBeforeRender,\r | |
180 | single: true,\r | |
181 | scope: me\r | |
182 | }\r | |
183 | });\r | |
184 | }\r | |
185 | \r | |
186 | grid.editorEventRelayers = grid.relayEvents(me, me.relayedEvents);\r | |
187 | \r | |
188 | // If the editable grid is owned by a lockable, relay up another level.\r | |
189 | if (ownerLockable) {\r | |
190 | ownerLockable.editorEventRelayers = ownerLockable.relayEvents(me, me.relayedEvents);\r | |
191 | }\r | |
192 | // Marks the grid as editable, so that the SelectionModel\r | |
193 | // can make appropriate decisions during navigation\r | |
194 | grid.isEditable = true;\r | |
195 | grid.editingPlugin = grid.view.editingPlugin = me;\r | |
196 | },\r | |
197 | \r | |
198 | onBeforeReconfigure: function() {\r | |
199 | this.reconfiguring = true;\r | |
200 | },\r | |
201 | \r | |
202 | /**\r | |
203 | * Fires after the grid is reconfigured\r | |
204 | * @protected\r | |
205 | */\r | |
206 | onReconfigure: function() {\r | |
207 | this.setup();\r | |
208 | delete this.reconfiguring;\r | |
209 | },\r | |
210 | \r | |
211 | onBeforeRender: function() {\r | |
212 | this.setup();\r | |
213 | },\r | |
214 | \r | |
215 | setup: function() {\r | |
216 | // In a Lockable assembly, the owner's view aggregates all grid columns across both sides.\r | |
217 | // We grab all columns here.\r | |
218 | this.initFieldAccessors(this.grid.getTopLevelColumnManager().getColumns());\r | |
219 | },\r | |
220 | \r | |
221 | destroy: function() {\r | |
222 | var me = this,\r | |
223 | grid = me.grid;\r | |
224 | \r | |
225 | Ext.destroy(me.keyNav);\r | |
226 | \r | |
227 | // Clear all listeners from all our events, clear all managed listeners we added to other Observables\r | |
228 | me.clearListeners();\r | |
229 | \r | |
230 | if (grid) {\r | |
231 | if (grid.ownerLockable) {\r | |
232 | Ext.destroy(grid.ownerLockable.editorEventRelayers);\r | |
233 | grid.ownerLockable.editorEventRelayers = null;\r | |
234 | }\r | |
235 | \r | |
236 | Ext.destroy(grid.editorEventRelayers);\r | |
237 | grid.editorEventRelayers = null;\r | |
238 | \r | |
239 | grid.editingPlugin = grid.view.editingPlugin = me.grid = me.view = me.editor = me.keyNav = null;\r | |
240 | }\r | |
241 | \r | |
242 | me.callParent();\r | |
243 | },\r | |
244 | \r | |
245 | // @private\r | |
246 | getEditStyle: function() {\r | |
247 | return this.editStyle;\r | |
248 | },\r | |
249 | \r | |
250 | // @private\r | |
251 | initFieldAccessors: function(columns) {\r | |
252 | // If we have been passed a group header, process its leaf headers\r | |
253 | if (columns.isGroupHeader) {\r | |
254 | columns = columns.getGridColumns();\r | |
255 | }\r | |
256 | \r | |
257 | // Ensure we are processing an array\r | |
258 | else if (!Ext.isArray(columns)) {\r | |
259 | columns = [columns];\r | |
260 | }\r | |
261 | \r | |
262 | var me = this,\r | |
263 | c,\r | |
264 | cLen = columns.length,\r | |
265 | getEditor = function(record, defaultField) {\r | |
266 | return me.getColumnField(this, defaultField);\r | |
267 | },\r | |
268 | hasEditor = function() {\r | |
269 | return me.hasColumnField(this);\r | |
270 | },\r | |
271 | setEditor = function(field) {\r | |
272 | me.setColumnField(this, field);\r | |
273 | },\r | |
274 | column;\r | |
275 | \r | |
276 | for (c = 0; c < cLen; c++) {\r | |
277 | column = columns[c];\r | |
278 | \r | |
279 | if (!column.getEditor) {\r | |
280 | column.getEditor = getEditor;\r | |
281 | }\r | |
282 | if (!column.hasEditor) {\r | |
283 | column.hasEditor = hasEditor;\r | |
284 | }\r | |
285 | if (!column.setEditor) {\r | |
286 | column.setEditor = setEditor;\r | |
287 | }\r | |
288 | }\r | |
289 | },\r | |
290 | \r | |
291 | // @private\r | |
292 | removeFieldAccessors: function(columns) {\r | |
293 | // If we have been passed a group header, process its leaf headers\r | |
294 | if (columns.isGroupHeader) {\r | |
295 | columns = columns.getGridColumns();\r | |
296 | }\r | |
297 | \r | |
298 | // Ensure we are processing an array\r | |
299 | else if (!Ext.isArray(columns)) {\r | |
300 | columns = [columns];\r | |
301 | }\r | |
302 | \r | |
303 | var c,\r | |
304 | cLen = columns.length,\r | |
305 | column;\r | |
306 | \r | |
307 | for (c = 0; c < cLen; c++) {\r | |
308 | column = columns[c];\r | |
309 | column.getEditor = column.hasEditor = column.setEditor = column.field = column.editor = null;\r | |
310 | }\r | |
311 | },\r | |
312 | \r | |
313 | // @private\r | |
314 | // remaps to the public API of Ext.grid.column.Column.getEditor\r | |
315 | getColumnField: function(columnHeader, defaultField) {\r | |
316 | var me = this,\r | |
317 | field = columnHeader.field;\r | |
318 | \r | |
319 | if (!(field && field.isFormField)) {\r | |
320 | field = columnHeader.field = me.createColumnField(columnHeader, defaultField);\r | |
321 | }\r | |
322 | \r | |
323 | if (field && field.ui === 'default' && !field.hasOwnProperty('ui')) {\r | |
324 | field.ui = me.defaultFieldUI;\r | |
325 | }\r | |
326 | return field;\r | |
327 | },\r | |
328 | \r | |
329 | // @private\r | |
330 | // remaps to the public API of Ext.grid.column.Column.hasEditor\r | |
331 | hasColumnField: function(columnHeader) {\r | |
332 | return !!(columnHeader.field && columnHeader.field.isComponent);\r | |
333 | },\r | |
334 | \r | |
335 | // @private\r | |
336 | // remaps to the public API of Ext.grid.column.Column.setEditor\r | |
337 | setColumnField: function(columnHeader, field) {\r | |
338 | columnHeader.field = field;\r | |
339 | columnHeader.field = this.createColumnField(columnHeader);\r | |
340 | },\r | |
341 | \r | |
342 | createColumnField: function (column, defaultField) {\r | |
343 | var field = column.field,\r | |
344 | dataIndex;\r | |
345 | \r | |
346 | if (!field && column.editor) {\r | |
347 | field = column.editor;\r | |
348 | column.editor = null;\r | |
349 | }\r | |
350 | \r | |
351 | if (!field && defaultField) {\r | |
352 | field = defaultField;\r | |
353 | }\r | |
354 | \r | |
355 | if (field) {\r | |
356 | dataIndex = column.dataIndex;\r | |
357 | \r | |
358 | if (field.isComponent) {\r | |
359 | field.column = column;\r | |
360 | } else {\r | |
361 | if (Ext.isString(field)) {\r | |
362 | field = {\r | |
363 | name: dataIndex,\r | |
364 | xtype: field,\r | |
365 | column: column\r | |
366 | };\r | |
367 | } else {\r | |
368 | field = Ext.apply({\r | |
369 | name: dataIndex,\r | |
370 | column: column\r | |
371 | }, field);\r | |
372 | }\r | |
373 | field = Ext.ComponentManager.create(field, this.defaultFieldXType);\r | |
374 | }\r | |
375 | \r | |
376 | // Stamp on the dataIndex which will serve as a reliable lookup regardless\r | |
377 | // of how the editor was defined (as a config or as an existing component).\r | |
378 | // See EXTJSIV-11650.\r | |
379 | field.dataIndex = dataIndex;\r | |
380 | \r | |
381 | field.isEditorComponent = true;\r | |
382 | column.field = field;\r | |
383 | }\r | |
384 | return field;\r | |
385 | },\r | |
386 | \r | |
387 | // @private\r | |
388 | initEvents: function() {\r | |
389 | var me = this;\r | |
390 | me.initEditTriggers();\r | |
391 | me.initCancelTriggers();\r | |
392 | },\r | |
393 | \r | |
394 | // @abstract\r | |
395 | initCancelTriggers: Ext.emptyFn,\r | |
396 | \r | |
397 | // @private\r | |
398 | initEditTriggers: function() {\r | |
399 | var me = this,\r | |
400 | view = me.view;\r | |
401 | \r | |
402 | // Listen for the edit trigger event.\r | |
403 | if (me.triggerEvent === 'cellfocus') {\r | |
404 | me.mon(view, 'cellfocus', me.onCellFocus, me);\r | |
405 | } else if (me.triggerEvent === 'rowfocus') {\r | |
406 | me.mon(view, 'rowfocus', me.onRowFocus, me);\r | |
407 | } else {\r | |
408 | \r | |
409 | // Prevent the View from processing when the SelectionModel focuses.\r | |
410 | // This is because the SelectionModel processes the mousedown event, and\r | |
411 | // focusing causes a scroll which means that the subsequent mouseup might\r | |
412 | // take place at a different document XY position, and will therefore\r | |
413 | // not trigger a click.\r | |
414 | // This Editor must call the View's focusCell method directly when we recieve a request to edit\r | |
415 | if (view.getSelectionModel().isCellModel) {\r | |
416 | view.onCellFocus = me.beforeViewCellFocus.bind(me);\r | |
417 | }\r | |
418 | \r | |
419 | // Listen for whichever click event we are configured to use\r | |
420 | me.mon(view, me.triggerEvent || ('cell' + (me.clicksToEdit === 1 ? 'click' : 'dblclick')), me.onCellClick, me);\r | |
421 | }\r | |
422 | \r | |
423 | // add/remove header event listeners need to be added immediately because\r | |
424 | // columns can be added/removed before render\r | |
425 | me.initAddRemoveHeaderEvents();\r | |
426 | \r | |
427 | // Attach new bindings to the View's NavigationModel which processes cellkeydown events.\r | |
428 | me.view.getNavigationModel().addKeyBindings({\r | |
429 | esc: me.onEscKey,\r | |
430 | scope: me\r | |
431 | });\r | |
432 | },\r | |
433 | \r | |
434 | // Override of View's method so that we can pre-empt the View's processing if the view is being triggered by a mousedown\r | |
435 | beforeViewCellFocus: function(position) {\r | |
436 | // Pass call on to view if the navigation is from the keyboard, or we are not going to edit this cell.\r | |
437 | if (this.view.selModel.keyNavigation || !this.editing || !this.isCellEditable || !this.isCellEditable(position.row, position.columnHeader)) {\r | |
438 | this.view.focusCell.apply(this.view, arguments);\r | |
439 | }\r | |
440 | },\r | |
441 | \r | |
442 | // @private Used if we are triggered by the rowfocus event\r | |
443 | onRowFocus: function(record, row, rowIdx) {\r | |
444 | this.startEdit(row, 0);\r | |
445 | },\r | |
446 | \r | |
447 | // @private Used if we are triggered by the cellfocus event\r | |
448 | onCellFocus: function(record, cell, position) {\r | |
449 | this.startEdit(position.row, position.column);\r | |
450 | },\r | |
451 | \r | |
452 | // @private Used if we are triggered by a cellclick event\r | |
453 | // *IMPORTANT* Due to V4.0.0 history, the colIdx here is the index within ALL columns, including hidden.\r | |
454 | onCellClick: function(view, cell, colIdx, record, row, rowIdx, e) {\r | |
455 | // Make sure that the column has an editor. In the case of CheckboxModel,\r | |
456 | // calling startEdit doesn't make sense when the checkbox is clicked.\r | |
457 | // Also, cancel editing if the element that was clicked was a tree expander.\r | |
458 | var expanderSelector = view.expanderSelector,\r | |
459 | // Use getColumnManager() in this context because colIdx includes hidden columns.\r | |
460 | columnHeader = view.ownerCt.getColumnManager().getHeaderAtIndex(colIdx),\r | |
461 | editor = columnHeader.getEditor(record);\r | |
462 | \r | |
463 | if (this.shouldStartEdit(editor) && (!expanderSelector || !e.getTarget(expanderSelector))) {\r | |
464 | view.ownerGrid.setActionableMode(true, e.position);\r | |
465 | }\r | |
466 | },\r | |
467 | \r | |
468 | initAddRemoveHeaderEvents: function(){\r | |
469 | var me = this,\r | |
470 | headerCt = me.grid.headerCt;\r | |
471 | \r | |
472 | me.mon(headerCt, {\r | |
473 | scope: me,\r | |
474 | add: me.onColumnAdd,\r | |
475 | columnmove: me.onColumnMove,\r | |
476 | beforedestroy: me.beforeGridHeaderDestroy\r | |
477 | });\r | |
478 | },\r | |
479 | \r | |
480 | // @private\r | |
481 | onColumnAdd: function(ct, column) {\r | |
482 | this.initFieldAccessors(column);\r | |
483 | },\r | |
484 | \r | |
485 | // Template method which may be implemented in subclasses (RowEditing and CellEditing)\r | |
486 | onColumnMove: Ext.emptyFn,\r | |
487 | \r | |
488 | // @private\r | |
489 | onEscKey: function(e) {\r | |
490 | if (this.editing) {\r | |
491 | var targetComponent = Ext.getCmp(e.getTarget().getAttribute('componentId'));\r | |
492 | \r | |
493 | // ESCAPE when a picker is expanded does not cancel the edit\r | |
494 | if (!(targetComponent && targetComponent.isPickerField && targetComponent.isExpanded)) {\r | |
495 | return this.cancelEdit();\r | |
496 | }\r | |
497 | }\r | |
498 | },\r | |
499 | \r | |
500 | /**\r | |
501 | * @method\r | |
502 | * @private\r | |
503 | * @template\r | |
504 | * Template method called before editing begins.\r | |
505 | * @param {Object} context The current editing context\r | |
506 | * @return {Boolean} Return false to cancel the editing process\r | |
507 | */\r | |
508 | beforeEdit: Ext.emptyFn,\r | |
509 | \r | |
510 | shouldStartEdit: function(editor) {\r | |
511 | return !!editor;\r | |
512 | },\r | |
513 | \r | |
514 | /**\r | |
515 | * @private\r | |
516 | * Collects all information necessary for any subclasses to perform their editing functions.\r | |
517 | * @param {Ext.data.Model/Number} record The record or record index to edit.\r | |
518 | * @param {Ext.grid.column.Column/Number} columnHeader The column of column index to edit.\r | |
519 | * @return {Ext.grid.CellContext/undefined} The editing context based upon the passed record and column\r | |
520 | */\r | |
521 | getEditingContext: function(record, columnHeader) {\r | |
522 | var me = this,\r | |
523 | grid = me.grid,\r | |
524 | colMgr = grid.visibleColumnManager,\r | |
525 | view,\r | |
526 | gridRow,\r | |
527 | rowIdx, colIdx,\r | |
528 | result,\r | |
529 | layoutView = me.grid.lockable ? me.grid : me.view;\r | |
530 | \r | |
531 | // The view must have had a layout to show the editor correctly, defer until that time.\r | |
532 | // In case a grid's startup code invokes editing immediately.\r | |
533 | if (!layoutView.componentLayoutCounter) {\r | |
534 | layoutView.on({\r | |
535 | boxready: Ext.Function.bind(me.startEdit, me, [record, columnHeader]),\r | |
536 | single: true\r | |
537 | });\r | |
538 | return;\r | |
539 | }\r | |
540 | \r | |
541 | // If disabled or grid collapsed, or view not truly visible, don't calculate a context - we cannot edit\r | |
542 | if (me.disabled || me.grid.collapsed || !me.grid.view.isVisible(true)) {\r | |
543 | return;\r | |
544 | }\r | |
545 | \r | |
546 | // They've asked to edit by column number.\r | |
547 | // Note that in a locked grid, the columns are enumerated in a unified set for this purpose.\r | |
548 | if (Ext.isNumber(columnHeader)) {\r | |
549 | columnHeader = colMgr.getHeaderAtIndex(columnHeader);\r | |
550 | }\r | |
551 | \r | |
552 | // No corresponding column. Possible if all columns have been moved to the other side of a lockable grid pair\r | |
553 | if (!columnHeader) {\r | |
554 | return;\r | |
555 | }\r | |
556 | \r | |
557 | // Coerce the column to the closest visible column\r | |
558 | if (columnHeader.hidden) {\r | |
559 | columnHeader = columnHeader.next(':not([hidden])') || columnHeader.prev(':not([hidden])');\r | |
560 | }\r | |
561 | \r | |
562 | // Navigate to the view and grid which the column header relates to.\r | |
563 | view = columnHeader.getView();\r | |
564 | grid = view.ownerCt;\r | |
565 | \r | |
566 | // Ensure the row we want to edit is in the rendered range if the view is buffer rendered\r | |
567 | grid.ensureVisible(record, {\r | |
568 | column : columnHeader\r | |
569 | });\r | |
570 | \r | |
571 | gridRow = view.getRow(record);\r | |
572 | \r | |
573 | // An intervening listener may have deleted the Record.\r | |
574 | if (!gridRow) {\r | |
575 | return;\r | |
576 | }\r | |
577 | \r | |
578 | // Column index must be relative to the View the Context is using.\r | |
579 | // It must be the real owning View, NOT the lockable pseudo view.\r | |
580 | colIdx = view.getVisibleColumnManager().indexOf(columnHeader);\r | |
581 | \r | |
582 | if (Ext.isNumber(record)) {\r | |
583 | // look up record if numeric row index was passed\r | |
584 | rowIdx = record;\r | |
585 | record = view.getRecord(gridRow);\r | |
586 | } else {\r | |
587 | rowIdx = view.indexOf(gridRow);\r | |
588 | }\r | |
589 | \r | |
590 | // The record may be removed from the store but the view\r | |
591 | // not yet updated, so check it exists\r | |
592 | if (!record) {\r | |
593 | return;\r | |
594 | }\r | |
595 | \r | |
596 | // Create a new CellContext\r | |
597 | result = new Ext.grid.CellContext(view).setAll(view, rowIdx, colIdx, record, columnHeader);\r | |
598 | \r | |
599 | // Add extra Editing information\r | |
600 | result.grid = grid;\r | |
601 | result.store = view.dataSource;\r | |
602 | result.field = columnHeader.dataIndex;\r | |
603 | result.value = result.originalValue = record.get(columnHeader.dataIndex);\r | |
604 | result.row = gridRow;\r | |
605 | result.node = view.getNode(record);\r | |
606 | result.cell = view.getCellByPosition(result, true);\r | |
607 | \r | |
608 | return result;\r | |
609 | },\r | |
610 | \r | |
611 | /**\r | |
612 | * Cancels any active edit that is in progress.\r | |
613 | */\r | |
614 | cancelEdit: function() {\r | |
615 | var me = this;\r | |
616 | \r | |
617 | me.editing = false;\r | |
618 | me.fireEvent('canceledit', me, me.context);\r | |
619 | },\r | |
620 | \r | |
621 | /**\r | |
622 | * Completes the edit if there is an active edit in progress.\r | |
623 | */\r | |
624 | completeEdit: function() {\r | |
625 | var me = this;\r | |
626 | \r | |
627 | if (me.editing && me.validateEdit()) {\r | |
628 | me.fireEvent('edit', me, me.context);\r | |
629 | }\r | |
630 | \r | |
631 | me.context = null;\r | |
632 | me.editing = false;\r | |
633 | },\r | |
634 | \r | |
635 | // @abstract\r | |
636 | validateEdit: function(context) {\r | |
637 | var me = this;\r | |
638 | \r | |
639 | return me.fireEvent('validateedit', me, context) !== false && !context.cancel;\r | |
640 | }\r | |
641 | }); |