X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/selection/RowModel.js diff --git a/src/selection/RowModel.js b/src/selection/RowModel.js new file mode 100644 index 00000000..4541a6d3 --- /dev/null +++ b/src/selection/RowModel.js @@ -0,0 +1,397 @@ +/** + * @class Ext.selection.RowModel + * @extends Ext.selection.Model + * + * Implement row based navigation via keyboard. + * + * Must synchronize across grid sections + */ +Ext.define('Ext.selection.RowModel', { + extend: 'Ext.selection.Model', + alias: 'selection.rowmodel', + requires: ['Ext.util.KeyNav'], + + /** + * @private + * Number of pixels to scroll to the left/right when pressing + * left/right keys. + */ + deltaScroll: 5, + + /** + * @cfg {Boolean} enableKeyNav + * + * Turns on/off keyboard navigation within the grid. Defaults to true. + */ + enableKeyNav: true, + + constructor: function(){ + this.addEvents( + /** + * @event deselect + * Fired after a record is deselected + * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.data.Model} record The deselected record + * @param {Number} index The row index deselected + */ + 'deselect', + + /** + * @event select + * Fired after a record is selected + * @param {Ext.selection.RowSelectionModel} this + * @param {Ext.data.Model} record The selected record + * @param {Number} index The row index selected + */ + 'select' + ); + this.callParent(arguments); + }, + + bindComponent: function(view) { + var me = this; + + me.views = me.views || []; + me.views.push(view); + me.bind(view.getStore(), true); + + view.on({ + itemmousedown: me.onRowMouseDown, + scope: me + }); + + if (me.enableKeyNav) { + me.initKeyNav(view); + } + }, + + initKeyNav: function(view) { + var me = this; + + if (!view.rendered) { + view.on('render', Ext.Function.bind(me.initKeyNav, me, [view], 0), me, {single: true}); + return; + } + + view.el.set({ + tabIndex: -1 + }); + + // view.el has tabIndex -1 to allow for + // keyboard events to be passed to it. + me.keyNav = new Ext.util.KeyNav(view.el, { + up: me.onKeyUp, + down: me.onKeyDown, + right: me.onKeyRight, + left: me.onKeyLeft, + pageDown: me.onKeyPageDown, + pageUp: me.onKeyPageUp, + home: me.onKeyHome, + end: me.onKeyEnd, + scope: me + }); + view.el.on(Ext.EventManager.getKeyEvent(), me.onKeyPress, me); + }, + + // 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 and the first view is accurate. + getRowsVisible: function() { + var rowsVisible = false, + view = this.views[0], + row = view.getNode(0), + rowHeight, gridViewHeight; + + if (row) { + rowHeight = Ext.fly(row).getHeight(); + gridViewHeight = view.el.getHeight(); + rowsVisible = Math.floor(gridViewHeight / rowHeight); + } + + return rowsVisible; + }, + + // go to last visible record in grid. + onKeyEnd: function(e, t) { + var me = this, + last = me.store.getAt(me.store.getCount() - 1); + + if (last) { + if (e.shiftKey) { + me.selectRange(last, me.lastFocused || 0); + me.setLastFocused(last); + } else if (e.ctrlKey) { + me.setLastFocused(last); + } else { + me.doSelect(last); + } + } + }, + + // go to first visible record in grid. + onKeyHome: function(e, t) { + var me = this, + first = me.store.getAt(0); + + if (first) { + if (e.shiftKey) { + me.selectRange(first, me.lastFocused || 0); + me.setLastFocused(first); + } else if (e.ctrlKey) { + me.setLastFocused(first); + } else { + me.doSelect(first, false); + } + } + }, + + // Go one page up from the lastFocused record in the grid. + onKeyPageUp: function(e, t) { + var me = this, + rowsVisible = me.getRowsVisible(), + selIdx, + prevIdx, + prevRecord, + currRec; + + if (rowsVisible) { + selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0; + prevIdx = selIdx - rowsVisible; + if (prevIdx < 0) { + prevIdx = 0; + } + prevRecord = me.store.getAt(prevIdx); + if (e.shiftKey) { + currRec = me.store.getAt(selIdx); + me.selectRange(prevRecord, currRec, e.ctrlKey, 'up'); + me.setLastFocused(prevRecord); + } else if (e.ctrlKey) { + e.preventDefault(); + me.setLastFocused(prevRecord); + } else { + me.doSelect(prevRecord); + } + + } + }, + + // Go one page down from the lastFocused record in the grid. + onKeyPageDown: function(e, t) { + var me = this, + rowsVisible = me.getRowsVisible(), + selIdx, + nextIdx, + nextRecord, + currRec; + + if (rowsVisible) { + selIdx = me.lastFocused ? me.store.indexOf(me.lastFocused) : 0; + nextIdx = selIdx + rowsVisible; + if (nextIdx >= me.store.getCount()) { + nextIdx = me.store.getCount() - 1; + } + nextRecord = me.store.getAt(nextIdx); + if (e.shiftKey) { + currRec = me.store.getAt(selIdx); + me.selectRange(nextRecord, currRec, e.ctrlKey, 'down'); + me.setLastFocused(nextRecord); + } else if (e.ctrlKey) { + // some browsers, this means go thru browser tabs + // attempt to stop. + e.preventDefault(); + me.setLastFocused(nextRecord); + } else { + me.doSelect(nextRecord); + } + } + }, + + // Select/Deselect based on pressing Spacebar. + // Assumes a SIMPLE selectionmode style + onKeyPress: function(e, t) { + if (e.getKey() === e.SPACE) { + e.stopEvent(); + var me = this, + record = me.lastFocused; + + if (record) { + if (me.isSelected(record)) { + me.doDeselect(record, false); + } else { + me.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 me = this, + view = me.views[0], + idx = me.store.indexOf(me.lastFocused), + record; + + if (idx > 0) { + // needs to be the filtered count as thats what + // will be visible. + record = me.store.getAt(idx - 1); + if (e.shiftKey && me.lastFocused) { + if (me.isSelected(me.lastFocused) && me.isSelected(record)) { + me.doDeselect(me.lastFocused, true); + me.setLastFocused(record); + } else if (!me.isSelected(me.lastFocused)) { + me.doSelect(me.lastFocused, true); + me.doSelect(record, true); + } else { + me.doSelect(record, true); + } + } else if (e.ctrlKey) { + me.setLastFocused(record); + } else { + me.doSelect(record); + //view.focusRow(idx - 1); + } + } + // There was no lastFocused record, and the user has pressed up + // Ignore?? + //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 me = this, + view = me.views[0], + idx = me.store.indexOf(me.lastFocused), + record; + + // needs to be the filtered count as thats what + // will be visible. + if (idx + 1 < me.store.getCount()) { + record = me.store.getAt(idx + 1); + if (me.selected.getCount() === 0) { + me.doSelect(record); + //view.focusRow(idx + 1); + } else if (e.shiftKey && me.lastFocused) { + if (me.isSelected(me.lastFocused) && me.isSelected(record)) { + me.doDeselect(me.lastFocused, true); + me.setLastFocused(record); + } else if (!me.isSelected(me.lastFocused)) { + me.doSelect(me.lastFocused, true); + me.doSelect(record, true); + } else { + me.doSelect(record, true); + } + } else if (e.ctrlKey) { + me.setLastFocused(record); + } else { + me.doSelect(record); + //view.focusRow(idx + 1); + } + } + }, + + scrollByDeltaX: function(delta) { + var view = this.views[0], + section = view.up(), + hScroll = section.horizontalScroller; + + if (hScroll) { + hScroll.scrollByDeltaX(delta); + } + }, + + onKeyLeft: function(e, t) { + this.scrollByDeltaX(-this.deltaScroll); + }, + + onKeyRight: function(e, t) { + this.scrollByDeltaX(this.deltaScroll); + }, + + // Select the record with the event included so that + // we can take into account ctrlKey, shiftKey, etc + onRowMouseDown: function(view, record, item, index, e) { + view.el.focus(); + this.selectWithEvent(record, e); + }, + + // Allow the GridView to update the UI by + // adding/removing a CSS class from the row. + onSelectChange: function(record, isSelected, suppressEvent) { + var me = this, + views = me.views, + viewsLn = views.length, + store = me.store, + rowIdx = store.indexOf(record), + i = 0; + + for (; i < viewsLn; i++) { + if (isSelected) { + views[i].onRowSelect(rowIdx, suppressEvent); + if (!suppressEvent) { + me.fireEvent('select', me, record, rowIdx); + } + } else { + views[i].onRowDeselect(rowIdx, suppressEvent); + if (!suppressEvent) { + me.fireEvent('deselect', me, record, rowIdx); + } + } + } + }, + + // Provide indication of what row was last focused via + // the gridview. + onLastFocusChanged: function(oldFocused, newFocused, supressFocus) { + var views = this.views, + viewsLn = views.length, + store = this.store, + rowIdx, + i = 0; + + if (oldFocused) { + rowIdx = store.indexOf(oldFocused); + if (rowIdx != -1) { + for (; i < viewsLn; i++) { + views[i].onRowFocus(rowIdx, false); + } + } + } + + if (newFocused) { + rowIdx = store.indexOf(newFocused); + if (rowIdx != -1) { + for (i = 0; i < viewsLn; i++) { + views[i].onRowFocus(rowIdx, true, supressFocus); + } + } + } + }, + + onEditorTab: function(editingPlugin, e) { + var me = this, + view = me.views[0], + record = editingPlugin.getActiveRecord(), + header = editingPlugin.getActiveColumn(), + position = view.getPosition(record, header), + direction = e.shiftKey ? 'left' : 'right', + newPosition = view.walkCells(position, direction, e, this.preventWrap); + + if (newPosition) { + editingPlugin.startEditByPosition(newPosition); + } + }, + + selectByPosition: function(position) { + var record = this.store.getAt(position.row); + this.select(record); + } +}); \ No newline at end of file