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.
16 * @class Ext.selection.RowModel
17 * @extends Ext.selection.Model
19 * Implement row based navigation via keyboard.
21 * Must synchronize across grid sections
23 Ext.define('Ext.selection.RowModel', {
24 extend: 'Ext.selection.Model',
25 alias: 'selection.rowmodel',
26 requires: ['Ext.util.KeyNav'],
30 * Number of pixels to scroll to the left/right when pressing
36 * @cfg {Boolean} enableKeyNav
38 * Turns on/off keyboard navigation within the grid. Defaults to true.
42 constructor: function(){
45 * @event beforedeselect
46 * Fired before a record is deselected. If any listener returns false, the
47 * deselection is cancelled.
48 * @param {Ext.selection.RowSelectionModel} this
49 * @param {Ext.data.Model} record The deselected record
50 * @param {Number} index The row index deselected
56 * Fired before a record is selected. If any listener returns false, the
57 * selection is cancelled.
58 * @param {Ext.selection.RowSelectionModel} this
59 * @param {Ext.data.Model} record The selected record
60 * @param {Number} index The row index selected
66 * Fired after a record is deselected
67 * @param {Ext.selection.RowSelectionModel} this
68 * @param {Ext.data.Model} record The deselected record
69 * @param {Number} index The row index deselected
75 * Fired after a record is selected
76 * @param {Ext.selection.RowSelectionModel} this
77 * @param {Ext.data.Model} record The selected record
78 * @param {Number} index The row index selected
82 this.callParent(arguments);
85 bindComponent: function(view) {
88 me.views = me.views || [];
90 me.bind(view.getStore(), true);
93 itemmousedown: me.onRowMouseDown,
97 if (me.enableKeyNav) {
102 initKeyNav: function(view) {
105 if (!view.rendered) {
106 view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true});
114 // view.el has tabIndex -1 to allow for
115 // keyboard events to be passed to it.
116 me.keyNav = new Ext.util.KeyNav(view.el, {
119 right: me.onKeyRight,
121 pageDown: me.onKeyPageDown,
122 pageUp: me.onKeyPageUp,
127 view.el.on(Ext.EventManager.getKeyEvent(), me.onKeyPress, me);
130 // Returns the number of rows currently visible on the screen or
131 // false if there were no rows. This assumes that all rows are
132 // of the same height and the first view is accurate.
133 getRowsVisible: function() {
134 var rowsVisible = false,
135 view = this.views[0],
136 row = view.getNode(0),
137 rowHeight, gridViewHeight;
140 rowHeight = Ext.fly(row).getHeight();
141 gridViewHeight = view.el.getHeight();
142 rowsVisible = Math.floor(gridViewHeight / rowHeight);
148 // go to last visible record in grid.
149 onKeyEnd: function(e, t) {
151 last = me.store.getAt(me.store.getCount() - 1);
155 me.selectRange(last, me.lastFocused || 0);
156 me.setLastFocused(last);
157 } else if (e.ctrlKey) {
158 me.setLastFocused(last);
165 // go to first visible record in grid.
166 onKeyHome: function(e, t) {
168 first = me.store.getAt(0);
172 me.selectRange(first, me.lastFocused || 0);
173 me.setLastFocused(first);
174 } else if (e.ctrlKey) {
175 me.setLastFocused(first);
177 me.doSelect(first, false);
182 // Go one page up from the lastFocused record in the grid.
183 onKeyPageUp: function(e, t) {
185 rowsVisible = me.getRowsVisible(),
192 selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0;
193 prevIdx = selIdx - rowsVisible;
197 prevRecord = me.store.getAt(prevIdx);
199 currRec = me.store.getAt(selIdx);
200 me.selectRange(prevRecord, currRec, e.ctrlKey, 'up');
201 me.setLastFocused(prevRecord);
202 } else if (e.ctrlKey) {
204 me.setLastFocused(prevRecord);
206 me.doSelect(prevRecord);
212 // Go one page down from the lastFocused record in the grid.
213 onKeyPageDown: function(e, t) {
215 rowsVisible = me.getRowsVisible(),
222 selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0;
223 nextIdx = selIdx + rowsVisible;
224 if (nextIdx >= me.store.getCount()) {
225 nextIdx = me.store.getCount() - 1;
227 nextRecord = me.store.getAt(nextIdx);
229 currRec = me.store.getAt(selIdx);
230 me.selectRange(nextRecord, currRec, e.ctrlKey, 'down');
231 me.setLastFocused(nextRecord);
232 } else if (e.ctrlKey) {
233 // some browsers, this means go thru browser tabs
236 me.setLastFocused(nextRecord);
238 me.doSelect(nextRecord);
243 // Select/Deselect based on pressing Spacebar.
244 // Assumes a SIMPLE selectionmode style
245 onKeyPress: function(e, t) {
246 if (e.getKey() === e.SPACE) {
249 record = me.lastFocused;
252 if (me.isSelected(record)) {
253 me.doDeselect(record, false);
255 me.doSelect(record, true);
261 // Navigate one record up. This could be a selection or
262 // could be simply focusing a record for discontiguous
263 // selection. Provides bounds checking.
264 onKeyUp: function(e, t) {
267 idx = me.store.indexOf(me.lastFocused),
271 // needs to be the filtered count as thats what
273 record = me.store.getAt(idx - 1);
274 if (e.shiftKey && me.lastFocused) {
275 if (me.isSelected(me.lastFocused) && me.isSelected(record)) {
276 me.doDeselect(me.lastFocused, true);
277 me.setLastFocused(record);
278 } else if (!me.isSelected(me.lastFocused)) {
279 me.doSelect(me.lastFocused, true);
280 me.doSelect(record, true);
282 me.doSelect(record, true);
284 } else if (e.ctrlKey) {
285 me.setLastFocused(record);
288 //view.focusRow(idx - 1);
291 // There was no lastFocused record, and the user has pressed up
293 //else if (this.selected.getCount() == 0) {
295 // this.doSelect(record);
296 // //view.focusRow(idx - 1);
300 // Navigate one record down. This could be a selection or
301 // could be simply focusing a record for discontiguous
302 // selection. Provides bounds checking.
303 onKeyDown: function(e, t) {
306 idx = me.store.indexOf(me.lastFocused),
309 // needs to be the filtered count as thats what
311 if (idx + 1 < me.store.getCount()) {
312 record = me.store.getAt(idx + 1);
313 if (me.selected.getCount() === 0) {
315 //view.focusRow(idx + 1);
316 } else if (e.shiftKey && me.lastFocused) {
317 if (me.isSelected(me.lastFocused) && me.isSelected(record)) {
318 me.doDeselect(me.lastFocused, true);
319 me.setLastFocused(record);
320 } else if (!me.isSelected(me.lastFocused)) {
321 me.doSelect(me.lastFocused, true);
322 me.doSelect(record, true);
324 me.doSelect(record, true);
326 } else if (e.ctrlKey) {
327 me.setLastFocused(record);
330 //view.focusRow(idx + 1);
335 scrollByDeltaX: function(delta) {
336 var view = this.views[0],
338 hScroll = section.horizontalScroller;
341 hScroll.scrollByDeltaX(delta);
345 onKeyLeft: function(e, t) {
346 this.scrollByDeltaX(-this.deltaScroll);
349 onKeyRight: function(e, t) {
350 this.scrollByDeltaX(this.deltaScroll);
353 // Select the record with the event included so that
354 // we can take into account ctrlKey, shiftKey, etc
355 onRowMouseDown: function(view, record, item, index, e) {
357 this.selectWithEvent(record, e);
360 // Allow the GridView to update the UI by
361 // adding/removing a CSS class from the row.
362 onSelectChange: function(record, isSelected, suppressEvent, commitFn) {
365 viewsLn = views.length,
367 rowIdx = store.indexOf(record),
368 eventName = isSelected ? 'select' : 'deselect',
371 if ((suppressEvent || me.fireEvent('before' + eventName, me, record, rowIdx)) !== false &&
372 commitFn() !== false) {
374 for (; i < viewsLn; i++) {
376 views[i].onRowSelect(rowIdx, suppressEvent);
378 views[i].onRowDeselect(rowIdx, suppressEvent);
382 if (!suppressEvent) {
383 me.fireEvent(eventName, me, record, rowIdx);
388 // Provide indication of what row was last focused via
390 onLastFocusChanged: function(oldFocused, newFocused, supressFocus) {
391 var views = this.views,
392 viewsLn = views.length,
398 rowIdx = store.indexOf(oldFocused);
400 for (; i < viewsLn; i++) {
401 views[i].onRowFocus(rowIdx, false);
407 rowIdx = store.indexOf(newFocused);
409 for (i = 0; i < viewsLn; i++) {
410 views[i].onRowFocus(rowIdx, true, supressFocus);
416 onEditorTab: function(editingPlugin, e) {
419 record = editingPlugin.getActiveRecord(),
420 header = editingPlugin.getActiveColumn(),
421 position = view.getPosition(record, header),
422 direction = e.shiftKey ? 'left' : 'right',
423 newPosition = view.walkCells(position, direction, e, this.preventWrap);
426 editingPlugin.startEditByPosition(newPosition);
430 selectByPosition: function(position) {
431 var record = this.store.getAt(position.row);