Ext.GridSelectionModel = Ext.extend(Ext.AbstractStoreSelectionModel, { /** * @cfg {Boolean} enableKeyNav * Turns on/off keyboard navigation within the grid. Defaults to true. */ enableKeyNav: true, bindComponent: function(grid) { this.grid = grid; this.bind(grid.getStore(), true); grid.on({ rowmousedown: this.onRowMouseDown, scope: this }); var view = grid.getView(); view.on({ refresh: this.refresh, rowupdated: this.onRowUpdated, scope: this }); if (this.enableKeyNav) { this.initKeyNav(); } }, initKeyNav: function() { var grid = this.grid; if (!grid.rendered) { this.grid.on('render', this.initKeyNav, this, {single: true}); return; } var view = grid.view; // view.el has tabIndex -1 to allow for // keyboard events to be passed to it. this.keyNav = new Ext.util.KeyNav(view.el, { "up": this.onKeyUp, "down": this.onKeyDown, "pageDown": this.onKeyPageDown, "pageUp": this.onKeyPageUp, "home": this.onKeyHome, "end": this.onKeyEnd, scope: this }); view.el.on(Ext.EventManager.getKeyEvent(), this.onKeyPress, this); }, // Returns the number of rows currently visible on the screen or // false if there were no rows. This assumes that all rows are // of the same height. getRowsVisible: function() { var rowsVisible = false, view = this.grid.view, row = view.getRow(0); if (row) { var rowHeight = Ext.fly(row).getHeight(); var gridViewHeight = view.el.getHeight(); rowsVisible = Math.floor(gridViewHeight/rowHeight); } return rowsVisible; }, // go to last visible record in grid. onKeyEnd: function(e, t) { var last = this.store.getAt(this.store.getCount() - 1); if (last) { if (e.shiftKey) { this.selectRange(last, this.lastFocused || 0); this.setLastFocused(last); } else if (e.ctrlKey) { this.setLastFocused(last); } else { this.doSelect(last); } } }, // go to first visible record in grid. onKeyHome: function(e, t) { var first = this.store.getAt(0); if (first) { if (e.shiftKey) { this.selectRange(first, this.lastFocused || 0); this.setLastFocused(first); } else if (e.ctrlKey) { this.setLastFocused(first); } else { this.doSelect(first, false); } } }, // Go one page up from the lastFocused record in the grid. onKeyPageUp: function(e, t) { var rowsVisible = this.getRowsVisible(); if (rowsVisible) { var selIdx = this.lastFocused ? this.store.indexOf(this.lastFocused) : 0; var prevIdx = selIdx - rowsVisible; if (prevIdx < 0) { prevIdx = 0; } var prevRecord = this.store.getAt(prevIdx); if (e.shiftKey) { var currRec = this.store.getAt(selIdx); this.selectRange(prevRecord, currRec, e.ctrlKey, 'up'); this.setLastFocused(prevRecord); } else if (e.ctrlKey) { e.preventDefault(); this.setLastFocused(prevRecord); } else { this.doSelect(prevRecord); } } }, // Go one page down from the lastFocused record in the grid. onKeyPageDown: function(e, t) { var rowsVisible = this.getRowsVisible(); if (rowsVisible) { var selIdx = this.lastFocused ? this.store.indexOf(this.lastFocused) : 0; var nextIdx = selIdx + rowsVisible; if (nextIdx >= this.store.getCount()) { nextIdx = this.store.getCount() - 1; } var nextRecord = this.store.getAt(nextIdx); if (e.shiftKey) { var currRec = this.store.getAt(selIdx); this.selectRange(nextRecord, currRec, e.ctrlKey, 'down'); this.setLastFocused(nextRecord); } else if (e.ctrlKey) { // some browsers, this means go thru browser tabs // attempt to stop. e.preventDefault(); this.setLastFocused(nextRecord); } else { this.doSelect(nextRecord); } } }, // Select/Deselect based on pressing Spacebar. // Assumes a SIMPLE selectionmode style onKeyPress: function(e, t) { if (e.getKey() === e.SPACE) { var record = this.lastFocused; if (record) { if (this.isSelected(record)) { this.doDeselect(record, true); } else { this.doSelect(record, true); } } } }, // Navigate one record up. This could be a selection or // could be simply focusing a record for discontiguous // selection. Provides bounds checking. onKeyUp: function(e, t) { var view = this.grid.view; var idx = this.store.indexOf(this.lastFocused); if (idx > 0) { // needs to be the filtered count as thats what // will be visible. var record = this.store.getAt(idx - 1); if (e.shiftKey && this.lastFocused) { if (this.isSelected(this.lastFocused) && this.isSelected(record)) { this.doDeselect(this.lastFocused, true); this.setLastFocused(record); } else if (!this.isSelected(this.lastFocused)) { this.doSelect(this.lastFocused, true); this.doSelect(record, true); } else { this.doSelect(record, true); } } else if (e.ctrlKey) { this.setLastFocused(record); } else { this.doSelect(record); view.focusRow(idx - 1); } } else if (this.selected.getCount() == 0) { this.doSelect(record); view.focusRow(idx - 1); } }, // Navigate one record down. This could be a selection or // could be simply focusing a record for discontiguous // selection. Provides bounds checking. onKeyDown: function(e, t) { var view = this.grid.view; var idx = this.store.indexOf(this.lastFocused); // needs to be the filtered count as thats what // will be visible. if (idx + 1 < this.store.getCount()) { var record = this.store.getAt(idx + 1); if (this.selected.getCount() == 0) { this.doSelect(record); view.focusRow(idx + 1); } else if (e.shiftKey && this.lastFocused) { if (this.isSelected(this.lastFocused) && this.isSelected(record)) { this.doDeselect(this.lastFocused, true); this.setLastFocused(record); } else if (!this.isSelected(this.lastFocused)) { this.doSelect(this.lastFocused, true); this.doSelect(record, true); } else { this.doSelect(record, true); } } else if (e.ctrlKey) { this.setLastFocused(record); } else { this.doSelect(record); view.focusRow(idx + 1); } } }, // Select the record with the event included so that // we can take into account ctrlKey, shiftKey, etc onRowMouseDown: function(grid, rowIdx, e) { var record = grid.getStore().getAt(rowIdx); this.selectWithEvent(record, e); }, // row has been repainted, lets maintain selection onRowUpdated: function(view, rowIdx, record) { if (this.isSelected(record)) { view.onRowSelect(rowIdx); } }, // Allow the GridView to update the UI by // adding/removing a CSS class from the row. onSelectChange: function(record, isSelected) { var grid = this.grid, store = grid.getStore(), rowIdx = store.indexOf(record), view = grid.view; if (isSelected) { view.onRowSelect(rowIdx); } else { view.onRowDeselect(rowIdx); } }, // Provide indication of what row was last focused via // the gridview. onLastFocusChanged: function(oldFocused, newFocused) { var view = this.grid.view, idx; idx = this.store.indexOf(oldFocused); if (idx != -1) { view.onRowFocus(idx, false); } idx = this.store.indexOf(newFocused); if (idx != -1) { view.onRowFocus(idx, true); } } });