litera
8 Apr 2008, 8:30 AM
Single row selection model (SingleRowSelectionModel) works different than RowSelectionModel (singleSelect = true). My version fires following events:
beforerowselect
beforerowdeselect - new event
rowselect
rowdeselect
selectionchange
Regular RowSelectionModel downsides:
fires too many events when singleSelect is used, because it actually fires selects and deselect for every row when we do Shift+click
doesn't have the beforerowdeselect event that is added here
fires rowselected and rowdeselected every time there's a selection change (sequence being: deselect, change, select, change). This one just fires selectionchange
This selection model uses a different approach:
When you first select a row, rowselected(selModel, index, record) is fired
If you select a different row, selectionchange(selModel, newIndex, newRecord, oldIndex, oldRecord) is fired
If you Ctrl+click on a selected row, rowdeselected(selModel, index, record) is fired
Shift+click behaves as expected: it only fires either a rowselected (when nothing has been selected so far) or selectionchange (if there's a selection change)
So here's the code. Use it at your own risk ;)
Ext.ux.SingleRowSelectionModel = function(config){
Ext.apply(this, config);
this.selectedRow = null;
this.selectedIndex = -1;
this.addEvents(
// params: selectionModel, index, record
"beforerowselect",
// params: selectionModel, index, record
"beforerowdeselect",
// params: selectionModel, index, record
"rowselect",
// params: selectionModel, index, record
"rowdeselect",
//params: selectionModel, newIndex, newRecord, oldIndex, oldRecord
"selectionchange"
);
Ext.ux.SingleRowSelectionModel.superclass.constructor.call(this);
};
Ext.extend(Ext.ux.SingleRowSelectionModel, Ext.grid.AbstractSelectionModel, {
selectedRow: null,
selectedIndex: -1,
initEvents : function(){
if (this.grid.enableDragDrop || this.grid.enableDrag){
this.grid.enableDragDrop = false;
}
this.grid.on("rowmousedown", this.handleMouseDown, this);
this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
"up" : function(e){
if (this.selectedIndex > 0)
this.selectPrevious();
},
"down" : function(e){
if (this.selectedIndex < this.grid.store.getCount() - 1)
this.selectNext();
},
scope: this
});
var view = this.grid.view;
view.on("refresh", this.onViewRefresh, this);
view.on("rowupdated", this.onViewRowUpdated, this);
view.on("rowremoved", this.onViewRowRemove, this);
},
getCount: function(){
return this.selectedRow === null ? 0 : 1;
},
onViewRefresh : function(view){
var ds = this.grid.store;
var row = this.selectedRow;
var index = this.selectedIndex;
if (this.selectedIndex != -1 && this.selectedRow !== null){
this.deselectRow(index);
}
},
onViewRowRemove : function(view, index, record){
if (this.selectedIndex == index){
this.deselectRow(index);
}
},
onViewRowUpdated : function(view, index, record){
if(this.selectedIndex == index){
view.onRowSelect(index);
}
},
selectRow : function(index, preventViewNotify){
if(this.locked || (index < 0 || index >= this.grid.store.getCount())) return;
var record = this.grid.store.getAt(index);
// select row and fire appropriate events
if (this.selectedRow !== record){
if (record && this.fireEvent("beforerowselect", this, index, record) !== false)
{
if(!preventViewNotify){
if (this.selectedRow !== null){
this.grid.getView().onRowDeselect(this.selectedIndex);
}
this.grid.getView().onRowSelect(index);
}
// is it a selection change or an actual selection
if (this.selectedIndex != -1 && this.selectedRow !== null)
this.fireEvent("selectionchange", this, index, record, this.selectedIndex, this.selectedRow);
else
this.fireEvent("rowselect", this, index, record);
// store selection
this.selectedRow = record;
this.selectedIndex = index;
}
}
else{
if (record && this.fireEvent("beforerowselect", this, index, record) !== false){
this.fireEvent("rowselect", this, index, record);
}
}
},
deselectRow : function(index, preventViewNotify){
if(this.locked) return;
var record = this.grid.store.getAt(index);
if (record && this.fireEvent("beforerowdeselect", this, index, record) !== false){
if(!preventViewNotify){
this.grid.getView().onRowDeselect(index);
}
this.fireEvent("rowdeselect", this, index, record);
// forget selection
this.selectedRow = null;
this.selectedIndex = -1;
}
},
clearSelection : function(fast){
if (this.locked) return;
if (fast !== true){
if (this.selectedRow !== null && this.selectedIndex != -1)
this.deselectRow(this.selectedIndex);
}
else {
this.selectedRow = null;
this.selectedIndex = -1;
}
},
handleMouseDown : function(g, index, e){
if(e.button !== 0 || this.locked) return;
if(e.ctrlKey && this.selectedIndex == index){
this.deselectRow(index);
}
else {
this.selectRow(index);
this.grid.getView().focusRow(index);
}
},
selectFirstRow : function(){
this.selectRow(0);
},
selectLastRow : function(){
var lastIndex = this.grid.store.getCount() - 1;
this.selectRow(lastIndex);
},
selectNext : function(){
if(this.hasNext()){
this.selectRow(this.selectedIndex + 1);
this.grid.getView().focusRow(this.selectedIndex);
}
},
selectPrevious : function(){
if(this.hasPrevious()){
this.selectRow(this.selectedIndex - 1);
this.grid.getView().focusRow(this.selectedIndex);
}
},
hasNext : function(){
return this.selectedIndex !== -1 && (this.selectedIndex + 1) < this.grid.store.getCount();
},
hasPrevious : function(){
return this.selectedIndex > 0;
},
getSelected : function(){
return this.selectedRow;
},
hasSelection : function(){
return this.selectedRow !== null;
},
isSelected : function(index){
return (this.selectedIndex == index);
},
isIdSelected : function(id){
return (this.selectedRow !== null && this.selectedRow.id == id);
},
acceptsNav : function(row, col, cm){
return !cm.isHidden(col) && cm.isCellEditable(col, row);
},
onEditorKey : function(field, e){
var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
if(k == e.TAB){
e.stopEvent();
ed.completeEdit();
if(e.shiftKey){
newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
}else{
newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
}
}else if(k == e.ENTER){
e.stopEvent();
ed.completeEdit();
if(e.shiftKey){
newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
}else{
newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
}
}else if(k == e.ESC){
ed.cancelEdit();
}
if(newCell){
g.startEditing(newCell[0], newCell[1]);
}
}
});
beforerowselect
beforerowdeselect - new event
rowselect
rowdeselect
selectionchange
Regular RowSelectionModel downsides:
fires too many events when singleSelect is used, because it actually fires selects and deselect for every row when we do Shift+click
doesn't have the beforerowdeselect event that is added here
fires rowselected and rowdeselected every time there's a selection change (sequence being: deselect, change, select, change). This one just fires selectionchange
This selection model uses a different approach:
When you first select a row, rowselected(selModel, index, record) is fired
If you select a different row, selectionchange(selModel, newIndex, newRecord, oldIndex, oldRecord) is fired
If you Ctrl+click on a selected row, rowdeselected(selModel, index, record) is fired
Shift+click behaves as expected: it only fires either a rowselected (when nothing has been selected so far) or selectionchange (if there's a selection change)
So here's the code. Use it at your own risk ;)
Ext.ux.SingleRowSelectionModel = function(config){
Ext.apply(this, config);
this.selectedRow = null;
this.selectedIndex = -1;
this.addEvents(
// params: selectionModel, index, record
"beforerowselect",
// params: selectionModel, index, record
"beforerowdeselect",
// params: selectionModel, index, record
"rowselect",
// params: selectionModel, index, record
"rowdeselect",
//params: selectionModel, newIndex, newRecord, oldIndex, oldRecord
"selectionchange"
);
Ext.ux.SingleRowSelectionModel.superclass.constructor.call(this);
};
Ext.extend(Ext.ux.SingleRowSelectionModel, Ext.grid.AbstractSelectionModel, {
selectedRow: null,
selectedIndex: -1,
initEvents : function(){
if (this.grid.enableDragDrop || this.grid.enableDrag){
this.grid.enableDragDrop = false;
}
this.grid.on("rowmousedown", this.handleMouseDown, this);
this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
"up" : function(e){
if (this.selectedIndex > 0)
this.selectPrevious();
},
"down" : function(e){
if (this.selectedIndex < this.grid.store.getCount() - 1)
this.selectNext();
},
scope: this
});
var view = this.grid.view;
view.on("refresh", this.onViewRefresh, this);
view.on("rowupdated", this.onViewRowUpdated, this);
view.on("rowremoved", this.onViewRowRemove, this);
},
getCount: function(){
return this.selectedRow === null ? 0 : 1;
},
onViewRefresh : function(view){
var ds = this.grid.store;
var row = this.selectedRow;
var index = this.selectedIndex;
if (this.selectedIndex != -1 && this.selectedRow !== null){
this.deselectRow(index);
}
},
onViewRowRemove : function(view, index, record){
if (this.selectedIndex == index){
this.deselectRow(index);
}
},
onViewRowUpdated : function(view, index, record){
if(this.selectedIndex == index){
view.onRowSelect(index);
}
},
selectRow : function(index, preventViewNotify){
if(this.locked || (index < 0 || index >= this.grid.store.getCount())) return;
var record = this.grid.store.getAt(index);
// select row and fire appropriate events
if (this.selectedRow !== record){
if (record && this.fireEvent("beforerowselect", this, index, record) !== false)
{
if(!preventViewNotify){
if (this.selectedRow !== null){
this.grid.getView().onRowDeselect(this.selectedIndex);
}
this.grid.getView().onRowSelect(index);
}
// is it a selection change or an actual selection
if (this.selectedIndex != -1 && this.selectedRow !== null)
this.fireEvent("selectionchange", this, index, record, this.selectedIndex, this.selectedRow);
else
this.fireEvent("rowselect", this, index, record);
// store selection
this.selectedRow = record;
this.selectedIndex = index;
}
}
else{
if (record && this.fireEvent("beforerowselect", this, index, record) !== false){
this.fireEvent("rowselect", this, index, record);
}
}
},
deselectRow : function(index, preventViewNotify){
if(this.locked) return;
var record = this.grid.store.getAt(index);
if (record && this.fireEvent("beforerowdeselect", this, index, record) !== false){
if(!preventViewNotify){
this.grid.getView().onRowDeselect(index);
}
this.fireEvent("rowdeselect", this, index, record);
// forget selection
this.selectedRow = null;
this.selectedIndex = -1;
}
},
clearSelection : function(fast){
if (this.locked) return;
if (fast !== true){
if (this.selectedRow !== null && this.selectedIndex != -1)
this.deselectRow(this.selectedIndex);
}
else {
this.selectedRow = null;
this.selectedIndex = -1;
}
},
handleMouseDown : function(g, index, e){
if(e.button !== 0 || this.locked) return;
if(e.ctrlKey && this.selectedIndex == index){
this.deselectRow(index);
}
else {
this.selectRow(index);
this.grid.getView().focusRow(index);
}
},
selectFirstRow : function(){
this.selectRow(0);
},
selectLastRow : function(){
var lastIndex = this.grid.store.getCount() - 1;
this.selectRow(lastIndex);
},
selectNext : function(){
if(this.hasNext()){
this.selectRow(this.selectedIndex + 1);
this.grid.getView().focusRow(this.selectedIndex);
}
},
selectPrevious : function(){
if(this.hasPrevious()){
this.selectRow(this.selectedIndex - 1);
this.grid.getView().focusRow(this.selectedIndex);
}
},
hasNext : function(){
return this.selectedIndex !== -1 && (this.selectedIndex + 1) < this.grid.store.getCount();
},
hasPrevious : function(){
return this.selectedIndex > 0;
},
getSelected : function(){
return this.selectedRow;
},
hasSelection : function(){
return this.selectedRow !== null;
},
isSelected : function(index){
return (this.selectedIndex == index);
},
isIdSelected : function(id){
return (this.selectedRow !== null && this.selectedRow.id == id);
},
acceptsNav : function(row, col, cm){
return !cm.isHidden(col) && cm.isCellEditable(col, row);
},
onEditorKey : function(field, e){
var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
if(k == e.TAB){
e.stopEvent();
ed.completeEdit();
if(e.shiftKey){
newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
}else{
newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
}
}else if(k == e.ENTER){
e.stopEvent();
ed.completeEdit();
if(e.shiftKey){
newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
}else{
newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
}
}else if(k == e.ESC){
ed.cancelEdit();
}
if(newCell){
g.startEditing(newCell[0], newCell[1]);
}
}
});