]>
git.proxmox.com Git - extjs.git/blob - extjs/classic/classic/src/selection/CellModel.js
2 * A selection model for {@link Ext.grid.Panel grid panels} which allows selection of a single cell at a time.
4 * Implements cell based navigation via keyboard.
7 * var store = Ext.create('Ext.data.Store', {
8 * fields: ['name', 'email', 'phone'],
10 * { name: 'Lisa', email: 'lisa@simpsons.com', phone: '555-111-1224' },
11 * { name: 'Bart', email: 'bart@simpsons.com', phone: '555-222-1234' },
12 * { name: 'Homer', email: 'homer@simpsons.com', phone: '555-222-1244' },
13 * { name: 'Marge', email: 'marge@simpsons.com', phone: '555-222-1254' }
17 * Ext.create('Ext.grid.Panel', {
21 * renderTo: Ext.getBody(),
23 * { text: 'Name', dataIndex: 'name' },
24 * { text: 'Email', dataIndex: 'email', flex: 1 },
25 * { text: 'Phone', dataIndex: 'phone' }
27 * selModel: 'cellmodel'
30 Ext
.define('Ext.selection.CellModel', {
31 extend
: 'Ext.selection.DataViewModel',
32 alias
: 'selection.cellmodel',
34 'Ext.grid.CellContext'
38 * @cfg {"SINGLE"} mode
39 * Mode of selection. Valid values are:
41 * - **"SINGLE"** - Only allows selecting one item at a time. This is the default.
50 deselectOnContainerClick
: false,
53 * @cfg {Boolean} enableKeyNav
54 * Turns on/off keyboard navigation within the grid.
59 * @cfg {Boolean} preventWrap
60 * Set this configuration to true to prevent wrapping around of selection as
61 * a user navigates to the first or last column.
67 * Fired after a cell is deselected
68 * @param {Ext.selection.CellModel} this
69 * @param {Ext.data.Model} record The record of the deselected cell
70 * @param {Number} row The row index deselected
71 * @param {Number} column The column index deselected
76 * Fired after a cell is selected
77 * @param {Ext.selection.CellModel} this
78 * @param {Ext.data.Model} record The record of the selected cell
79 * @param {Number} row The row index selected
80 * @param {Number} column The column index selected
83 bindComponent: function(view
) {
88 if (me
.view
&& me
.gridListeners
) {
89 me
.gridListeners
.destroy();
92 // DataViewModel's bindComponent
93 me
.callParent([view
]);
96 // view.grid is present during View construction, before the view has been
97 // added as a child of the Panel, and an upward link it still needed.
98 grid
= view
.grid
|| view
.ownerCt
;
100 if (grid
.optimizedColumnMove
!== false) {
101 me
.gridListeners
= grid
.on({
102 columnmove
: me
.onColumnMove
,
110 getViewListeners: function() {
111 var result
= this.callParent();
112 result
.refresh
= this.onViewRefresh
;
116 getHeaderCt: function() {
117 var selection
= this.navigationModel
.getPosition(),
118 view
= selection
? selection
.view
: this.primaryView
;
120 return view
.headerCt
;
123 // Selection blindly follows focus. For now.
124 onNavigate: function(e
) {
125 // It was a navigate out event.
126 // Or stopSelection was stamped into the event by an upstream handler.
127 // This is used by ActionColumn and CheckColumn to implement their stopSelection config
128 if (!e
.record
|| e
.keyEvent
.stopSelection
) {
132 this.setPosition(e
.position
);
135 selectWithEvent: function(record
, e
) {
139 * Selects a cell by row / column.
141 * var grid = Ext.create('Ext.grid.Panel', {
144 * fields: ['name', 'email', 'phone'],
147 * email: "lisa@simpsons.com",
148 * phone: "555-111-1224"
156 * dataIndex: 'email',
160 * dataIndex: 'phone',
165 * renderTo: Ext.getBody(),
166 * selType: 'cellmodel',
168 * text: 'Select position Object',
169 * handler: function() {
170 * grid.getSelectionModel().select({
171 * row: grid.getStore().getAt(0),
172 * column: grid.down('gridcolumn[dataIndex=name]')
176 * text: 'Select position by Number',
177 * handler: function() {
178 * grid.getSelectionModel().select({
186 * @param {Object} pos An object with row and column properties
187 * @param {Ext.data.Model/Number} pos.row
188 * A record or index of the record (starting at 0)
189 * @param {Ext.grid.column.Column/Number} pos.column
190 * A column or index of the column (starting at 0). Includes visible columns only.
192 select: function(pos
, /* private */ keepExisting
, suppressEvent
) {
195 oldPos
= me
.getPosition(),
196 store
= me
.view
.store
;
198 if (pos
|| pos
=== 0) {
200 row
= store
.indexOf(pos
);
204 column
: oldPos
? oldPos
.column
: 0
209 } else if (typeof pos
=== 'number') {
218 me
.selectByPosition(pos
, suppressEvent
);
225 * Returns the current position in the format {row: row, column: column}
226 * @deprecated 5.0.1 This API uses column indices which include hidden columns in the count. Use {@link #getPosition} instead.
228 getCurrentPosition: function() {
229 // If it's during a select, return nextSelection since we buffer
230 // the real selection until after the event fires
231 var position
= this.selecting
? this.nextSelection
: this.selection
;
233 // This is the previous Format of the private CellContext class which was used here.
234 // Do not return a CellContext so that if this object is passed into setCurrentPosition, it will be
235 // read in the legacy (including hidden columns) way.
238 record
: position
.record
,
239 row
: position
.rowIdx
,
240 columnHeader
: position
.column
,
241 // IMPORTANT: The historic API for columns has been to include hidden columns
242 // in the index. So we must report the index of the column in the "all" ColumnManager.
243 column
: position
.view
.getColumnManager().indexOf(position
.column
)
248 * Returns the current position in the format {row: row, column: column}
249 * @return {Ext.grid.CellContext} A CellContext object describing the current cell.
251 getPosition: function() {
252 return (this.selecting
? this.nextSelection
: this.selection
) || null;
256 * Sets the current position.
257 * @deprecated 5.0.1 This API uses column indices which include hidden columns in the count. Use {@link #setPosition} instead.
258 * @param {Ext.grid.CellContext/Object} position The position to set. May be an object of the form `{row:1, column:2}`
259 * @param {Boolean} suppressEvent True to suppress selection events
261 setCurrentPosition: function(pos
, suppressEvent
, /* private */ preventCheck
) {
262 if (pos
&& !pos
.isCellContext
) {
263 pos
= new Ext
.grid
.CellContext(this.view
).setPosition({
265 // IMPORTANT: The historic API for columns has been to include hidden columns
266 // in the index. So we must index into the "all" ColumnManager.
267 column
: typeof pos
.column
=== 'number' ? this.view
.getColumnManager().getColumns()[pos
.column
] : pos
.column
270 return this.setPosition(pos
, suppressEvent
, preventCheck
);
274 * Sets the current position.
276 * Note that if passing a column index, it is the index within the *visible* column set.
278 * @param {Ext.grid.CellContext/Object} position The position to set. May be an object of the form `{row:1, column:2}`
279 * @param {Boolean} suppressEvent True to suppress selection events
281 setPosition: function(pos
, suppressEvent
, /* private */ preventCheck
) {
285 // Normalize it into an Ext.grid.CellContext if necessary
287 pos
= pos
.isCellContext
? pos
.clone() : new Ext
.grid
.CellContext(me
.view
).setPosition(pos
);
289 if (!preventCheck
&& last
) {
290 // If the position is the same, jump out & don't fire the event
291 if (pos
&& (pos
.record
=== last
.record
&& pos
.column
=== last
.column
&& pos
.view
=== last
.view
)) {
294 me
.onCellDeselect(me
.selection
, suppressEvent
);
299 me
.nextSelection
= pos
;
300 // set this flag here so we know to use nextSelection
301 // if the node is updated during a select
303 me
.onCellSelect(me
.nextSelection
, suppressEvent
);
304 me
.selecting
= false;
305 // Deselect triggered by new selection will kill the selection property, so restore it here.
306 return (me
.selection
= pos
);
309 // Enforce code correctness in unbuilt source.
314 isCellSelected: function(view
, row
, column
) {
317 pos
= me
.getPosition();
319 if (pos
&& pos
.view
=== view
) {
320 testPos
= new Ext
.grid
.CellContext(view
).setPosition({
322 // IMPORTANT: The historic API for columns has been to include hidden columns
323 // in the index. So we must index into the "all" ColumnManager.
324 column
: typeof column
=== 'number' ? view
.getColumnManager().getColumns()[column
] : column
326 return (testPos
.record
=== pos
.record
) && (testPos
.column
=== pos
.column
);
330 // Keep selection model in consistent state upon record deletion.
331 onStoreRemove: function(store
, records
, indices
) {
333 pos
= me
.getPosition();
335 me
.callParent(arguments
);
336 if (pos
&& store
.isMoving(pos
.record
)) {
340 if (pos
&& store
.getCount() && store
.indexOf(pos
.record
) !== -1) {
341 pos
.setRow(pos
.record
);
347 onStoreClear: function() {
348 this.callParent(arguments
);
349 this.selection
= null;
352 onStoreAdd: function() {
354 pos
= me
.getPosition();
356 me
.callParent(arguments
);
358 pos
.setRow(pos
.record
);
365 * Set the current position based on where the user clicks.
367 * IMPORTANT* Due to V4.0.0 history, the cellIndex here is the index within ALL columns, including hidden.
369 onCellClick: function(view
, cell
, cellIndex
, record
, row
, recordIndex
, e
) {
370 // Record index will be -1 if the clicked record is a metadata record and not selectable
371 if (recordIndex
!== -1) {
372 this.setPosition(e
.position
);
376 // notify the view that the cell has been selected to update the ui
377 // appropriately and bring the cell into focus
378 onCellSelect: function(position
, supressEvent
) {
379 if (position
&& position
.rowIdx
!== undefined && position
.rowIdx
> -1) {
380 this.doSelect(position
.record
, /*keepExisting*/false, supressEvent
);
384 // notify view that the cell has been deselected to update the ui
386 onCellDeselect: function(position
, supressEvent
) {
387 if (position
&& position
.rowIdx
!== undefined) {
388 this.doDeselect(position
.record
, supressEvent
);
392 onSelectChange: function(record
, isSelected
, suppressEvent
, commitFn
) {
394 pos
, eventName
, view
;
397 pos
= me
.nextSelection
;
398 eventName
= 'select';
401 eventName
= 'deselect';
404 // CellModel may be shared between two sides of a Lockable.
405 // The position must include a reference to the view in which the selection is current.
406 // Ensure we use the view specified by the position.
407 view
= pos
.view
|| me
.primaryView
;
409 if ((suppressEvent
|| me
.fireEvent('before' + eventName
, me
, record
, pos
.rowIdx
, pos
.colIdx
)) !== false &&
410 commitFn() !== false) {
413 view
.onCellSelect(pos
);
415 view
.onCellDeselect(pos
);
419 if (!suppressEvent
) {
420 me
.fireEvent(eventName
, me
, record
, pos
.rowIdx
, pos
.colIdx
);
425 refresh: function() {
426 var pos
= this.getPosition(),
429 // Synchronize the current position's row with the row of the last selected record.
430 if (pos
&& (selRowIdx
= this.store
.indexOf(this.selected
.last())) !== -1) {
431 pos
.rowIdx
= selRowIdx
;
437 * When grid uses {@link Ext.panel.Table#optimizedColumnMove optimizedColumnMove} (the default), this is added as a
438 * {@link Ext.panel.Table#columnmove columnmove} handler to correctly maintain the
439 * selected column using the same column header.
441 * If optimizedColumnMove === false, (which some grid Features set) then the view is refreshed,
442 * so this is not added as a handler because the selected column.
444 onColumnMove: function(headerCt
, header
, fromIdx
, toIdx
) {
445 var grid
= headerCt
.up('tablepanel');
447 this.onViewRefresh(grid
.view
);
451 onUpdate: function(record
) {
455 if (me
.isSelected(record
)) {
456 pos
= me
.selecting
? me
.nextSelection
: me
.selection
;
457 me
.view
.onCellSelect(pos
);
461 onViewRefresh: function(view
) {
463 pos
= me
.getPosition(),
465 headerCt
= view
.headerCt
,
468 // Re-establish selection of the same cell coordinate.
469 // DO NOT fire events because the selected
470 if (pos
&& pos
.view
=== view
) {
474 // After a refresh, recreate the selection using the same record and grid column as before
475 if (!column
.isDescendantOf(headerCt
)) {
476 // column header is not a child of the header container
477 // this happens when the grid is reconfigured with new columns
478 // make a best effor to select something by matching on id, then text, then dataIndex
479 column
= headerCt
.queryById(column
.id
) ||
480 headerCt
.down('[text="' + column
.text
+ '"]') ||
481 headerCt
.down('[dataIndex="' + column
.dataIndex
+ '"]');
484 // If we have a columnHeader (either the column header that already exists in
485 // the headerCt, or a suitable match that was found after reconfiguration)
486 // AND the record still exists in the store (or a record matching the id of
487 // the previously selected record) We are ok to go ahead and set the selection
489 if (column
&& (view
.store
.indexOfId(record
.getId()) !== -1)) {
490 newPos
= new Ext
.grid
.CellContext(view
).setPosition({
494 me
.setPosition(newPos
);
504 * Used internally by CellEditing
506 selectByPosition: function(position
, suppressEvent
) {
507 this.setPosition(position
, suppressEvent
);