2 * Ext JS Library 2.2.1
\r
3 * Copyright(c) 2006-2009, Ext JS, LLC.
\r
4 * licensing@extjs.com
\r
6 * http://extjs.com/license
\r
10 @class Ext.grid.RowSelectionModel
\r
11 * @extends Ext.grid.AbstractSelectionModel
\r
12 * The default SelectionModel used by {@link Ext.grid.GridPanel}.
\r
13 * It supports multiple selections and keyboard selection/navigation. The objects stored
\r
14 * as selections and returned by {@link #getSelected}, and {@link #getSelections} are
\r
15 * the {@link Ext.data.Record Record}s which provide the data for the selected rows.
\r
17 * @param {Object} config
\r
19 Ext.grid.RowSelectionModel = function(config){
\r
20 Ext.apply(this, config);
\r
21 this.selections = new Ext.util.MixedCollection(false, function(o){
\r
26 this.lastActive = false;
\r
30 * @event selectionchange
\r
31 * Fires when the selection changes
\r
32 * @param {SelectionModel} this
\r
36 * @event beforerowselect
\r
37 * Fires when a row is being selected, return false to cancel.
\r
38 * @param {SelectionModel} this
\r
39 * @param {Number} rowIndex The index to be selected
\r
40 * @param {Boolean} keepExisting False if other selections will be cleared
\r
41 * @param {Record} record The record to be selected
\r
46 * Fires when a row is selected.
\r
47 * @param {SelectionModel} this
\r
48 * @param {Number} rowIndex The selected index
\r
49 * @param {Ext.data.Record} r The selected record
\r
53 * @event rowdeselect
\r
54 * Fires when a row is deselected.
\r
55 * @param {SelectionModel} this
\r
56 * @param {Number} rowIndex
\r
57 * @param {Record} record
\r
62 Ext.grid.RowSelectionModel.superclass.constructor.call(this);
\r
65 Ext.extend(Ext.grid.RowSelectionModel, Ext.grid.AbstractSelectionModel, {
\r
67 * @cfg {Boolean} singleSelect
\r
68 * True to allow selection of only one row at a time (defaults to false)
\r
70 singleSelect : false,
\r
73 * @cfg {Boolean} moveEditorOnEnter
\r
74 * False to turn off moving the editor to the next cell when the enter key is pressed
\r
77 initEvents : function(){
\r
79 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
\r
80 this.grid.on("rowmousedown", this.handleMouseDown, this);
\r
81 }else{ // allow click to work like normal
\r
82 this.grid.on("rowclick", function(grid, rowIndex, e) {
\r
83 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
\r
84 this.selectRow(rowIndex, false);
\r
85 grid.view.focusRow(rowIndex);
\r
90 this.rowNav = new Ext.KeyNav(this.grid.getGridEl(), {
\r
92 if(!e.shiftKey || this.singleSelect){
\r
93 this.selectPrevious(false);
\r
94 }else if(this.last !== false && this.lastActive !== false){
\r
95 var last = this.last;
\r
96 this.selectRange(this.last, this.lastActive-1);
\r
97 this.grid.getView().focusRow(this.lastActive);
\r
102 this.selectFirstRow();
\r
105 "down" : function(e){
\r
106 if(!e.shiftKey || this.singleSelect){
\r
107 this.selectNext(false);
\r
108 }else if(this.last !== false && this.lastActive !== false){
\r
109 var last = this.last;
\r
110 this.selectRange(this.last, this.lastActive+1);
\r
111 this.grid.getView().focusRow(this.lastActive);
\r
112 if(last !== false){
\r
116 this.selectFirstRow();
\r
122 var view = this.grid.view;
\r
123 view.on("refresh", this.onRefresh, this);
\r
124 view.on("rowupdated", this.onRowUpdated, this);
\r
125 view.on("rowremoved", this.onRemove, this);
\r
129 onRefresh : function(){
\r
130 var ds = this.grid.store, index;
\r
131 var s = this.getSelections();
\r
132 this.clearSelections(true);
\r
133 for(var i = 0, len = s.length; i < len; i++){
\r
135 if((index = ds.indexOfId(r.id)) != -1){
\r
136 this.selectRow(index, true);
\r
139 if(s.length != this.selections.getCount()){
\r
140 this.fireEvent("selectionchange", this);
\r
145 onRemove : function(v, index, r){
\r
146 if(this.selections.remove(r) !== false){
\r
147 this.fireEvent('selectionchange', this);
\r
152 onRowUpdated : function(v, index, r){
\r
153 if(this.isSelected(r)){
\r
154 v.onRowSelect(index);
\r
160 * @param {Array} records The records to select
\r
161 * @param {Boolean} keepExisting (optional) True to keep existing selections
\r
163 selectRecords : function(records, keepExisting){
\r
165 this.clearSelections();
\r
167 var ds = this.grid.store;
\r
168 for(var i = 0, len = records.length; i < len; i++){
\r
169 this.selectRow(ds.indexOf(records[i]), true);
\r
174 * Gets the number of selected rows.
\r
177 getCount : function(){
\r
178 return this.selections.length;
\r
182 * Selects the first row in the grid.
\r
184 selectFirstRow : function(){
\r
189 * Select the last row.
\r
190 * @param {Boolean} keepExisting (optional) True to keep existing selections
\r
192 selectLastRow : function(keepExisting){
\r
193 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
\r
197 * Selects the row immediately following the last selected row.
\r
198 * @param {Boolean} keepExisting (optional) True to keep existing selections
\r
199 * @return {Boolean} True if there is a next row, else false
\r
201 selectNext : function(keepExisting){
\r
202 if(this.hasNext()){
\r
203 this.selectRow(this.last+1, keepExisting);
\r
204 this.grid.getView().focusRow(this.last);
\r
211 * Selects the row that precedes the last selected row.
\r
212 * @param {Boolean} keepExisting (optional) True to keep existing selections
\r
213 * @return {Boolean} True if there is a previous row, else false
\r
215 selectPrevious : function(keepExisting){
\r
216 if(this.hasPrevious()){
\r
217 this.selectRow(this.last-1, keepExisting);
\r
218 this.grid.getView().focusRow(this.last);
\r
225 * Returns true if there is a next record to select
\r
226 * @return {Boolean}
\r
228 hasNext : function(){
\r
229 return this.last !== false && (this.last+1) < this.grid.store.getCount();
\r
233 * Returns true if there is a previous record to select
\r
234 * @return {Boolean}
\r
236 hasPrevious : function(){
\r
237 return !!this.last;
\r
242 * Returns the selected records
\r
243 * @return {Array} Array of selected records
\r
245 getSelections : function(){
\r
246 return [].concat(this.selections.items);
\r
250 * Returns the first selected record.
\r
253 getSelected : function(){
\r
254 return this.selections.itemAt(0);
\r
258 * Calls the passed function with each selection. If the function returns false, iteration is
\r
259 * stopped and this function returns false. Otherwise it returns true.
\r
260 * @param {Function} fn
\r
261 * @param {Object} scope (optional)
\r
262 * @return {Boolean} true if all selections were iterated
\r
264 each : function(fn, scope){
\r
265 var s = this.getSelections();
\r
266 for(var i = 0, len = s.length; i < len; i++){
\r
267 if(fn.call(scope || this, s[i], i) === false){
\r
275 * Clears all selections.
\r
277 clearSelections : function(fast){
\r
278 if(this.isLocked()) return;
\r
280 var ds = this.grid.store;
\r
281 var s = this.selections;
\r
282 s.each(function(r){
\r
283 this.deselectRow(ds.indexOfId(r.id));
\r
287 this.selections.clear();
\r
294 * Selects all rows.
\r
296 selectAll : function(){
\r
297 if(this.isLocked()) return;
\r
298 this.selections.clear();
\r
299 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
\r
300 this.selectRow(i, true);
\r
305 * Returns True if there is a selection.
\r
306 * @return {Boolean}
\r
308 hasSelection : function(){
\r
309 return this.selections.length > 0;
\r
313 * Returns True if the specified row is selected.
\r
314 * @param {Number/Record} record The record or index of the record to check
\r
315 * @return {Boolean}
\r
317 isSelected : function(index){
\r
318 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
\r
319 return (r && this.selections.key(r.id) ? true : false);
\r
323 * Returns True if the specified record id is selected.
\r
324 * @param {String} id The id of record to check
\r
325 * @return {Boolean}
\r
327 isIdSelected : function(id){
\r
328 return (this.selections.key(id) ? true : false);
\r
332 handleMouseDown : function(g, rowIndex, e){
\r
333 if(e.button !== 0 || this.isLocked()){
\r
336 var view = this.grid.getView();
\r
337 if(e.shiftKey && !this.singleSelect && this.last !== false){
\r
338 var last = this.last;
\r
339 this.selectRange(last, rowIndex, e.ctrlKey);
\r
340 this.last = last; // reset the last
\r
341 view.focusRow(rowIndex);
\r
343 var isSelected = this.isSelected(rowIndex);
\r
344 if(e.ctrlKey && isSelected){
\r
345 this.deselectRow(rowIndex);
\r
346 }else if(!isSelected || this.getCount() > 1){
\r
347 this.selectRow(rowIndex, e.ctrlKey || e.shiftKey);
\r
348 view.focusRow(rowIndex);
\r
354 * Selects multiple rows.
\r
355 * @param {Array} rows Array of the indexes of the row to select
\r
356 * @param {Boolean} keepExisting (optional) True to keep existing selections (defaults to false)
\r
358 selectRows : function(rows, keepExisting){
\r
360 this.clearSelections();
\r
362 for(var i = 0, len = rows.length; i < len; i++){
\r
363 this.selectRow(rows[i], true);
\r
368 * Selects a range of rows. All rows in between startRow and endRow are also selected.
\r
369 * @param {Number} startRow The index of the first row in the range
\r
370 * @param {Number} endRow The index of the last row in the range
\r
371 * @param {Boolean} keepExisting (optional) True to retain existing selections
\r
373 selectRange : function(startRow, endRow, keepExisting){
\r
374 if(this.isLocked()) return;
\r
376 this.clearSelections();
\r
378 if(startRow <= endRow){
\r
379 for(var i = startRow; i <= endRow; i++){
\r
380 this.selectRow(i, true);
\r
383 for(var i = startRow; i >= endRow; i--){
\r
384 this.selectRow(i, true);
\r
390 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
\r
391 * @param {Number} startRow The index of the first row in the range
\r
392 * @param {Number} endRow The index of the last row in the range
\r
394 deselectRange : function(startRow, endRow, preventViewNotify){
\r
395 if(this.isLocked()) return;
\r
396 for(var i = startRow; i <= endRow; i++){
\r
397 this.deselectRow(i, preventViewNotify);
\r
403 * @param {Number} row The index of the row to select
\r
404 * @param {Boolean} keepExisting (optional) True to keep existing selections
\r
406 selectRow : function(index, keepExisting, preventViewNotify){
\r
407 if(this.isLocked() || (index < 0 || index >= this.grid.store.getCount()) || this.isSelected(index)) return;
\r
408 var r = this.grid.store.getAt(index);
\r
409 if(r && this.fireEvent("beforerowselect", this, index, keepExisting, r) !== false){
\r
410 if(!keepExisting || this.singleSelect){
\r
411 this.clearSelections();
\r
413 this.selections.add(r);
\r
414 this.last = this.lastActive = index;
\r
415 if(!preventViewNotify){
\r
416 this.grid.getView().onRowSelect(index);
\r
418 this.fireEvent("rowselect", this, index, r);
\r
419 this.fireEvent("selectionchange", this);
\r
425 * @param {Number} row The index of the row to deselect
\r
427 deselectRow : function(index, preventViewNotify){
\r
428 if(this.isLocked()) return;
\r
429 if(this.last == index){
\r
432 if(this.lastActive == index){
\r
433 this.lastActive = false;
\r
435 var r = this.grid.store.getAt(index);
\r
437 this.selections.remove(r);
\r
438 if(!preventViewNotify){
\r
439 this.grid.getView().onRowDeselect(index);
\r
441 this.fireEvent("rowdeselect", this, index, r);
\r
442 this.fireEvent("selectionchange", this);
\r
447 restoreLast : function(){
\r
449 this.last = this._last;
\r
454 acceptsNav : function(row, col, cm){
\r
455 return !cm.isHidden(col) && cm.isCellEditable(col, row);
\r
459 onEditorKey : function(field, e){
\r
460 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
\r
461 var shift = e.shiftKey;
\r
466 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
\r
468 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
\r
470 }else if(k == e.ENTER){
\r
473 if(this.moveEditorOnEnter !== false){
\r
475 newCell = g.walkCells(ed.row - 1, ed.col, -1, this.acceptsNav, this);
\r
477 newCell = g.walkCells(ed.row + 1, ed.col, 1, this.acceptsNav, this);
\r
480 }else if(k == e.ESC){
\r
484 g.startEditing(newCell[0], newCell[1]);
\r