Roo/bootstrap/Table/RowSelectionModel.js
[roojs1] / Roo / bootstrap / Table / RowSelectionModel.js
1
2 /**
3  * @extends Roo.bootstrap.Table.AbstractSelectionModel
4  * @class Roo.bootstrap.Table.RowSelectionModel
5  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
6  * It supports multiple selections and keyboard selection/navigation. 
7  * @constructor
8  * @param {Object} config
9  */
10
11 Roo.bootstrap.Table.RowSelectionModel = function(config){
12     Roo.apply(this, config);
13     this.selections = new Roo.util.MixedCollection(false, function(o){
14         return o.id;
15     });
16
17     this.last = false;
18     this.lastActive = false;
19
20     this.addEvents({
21         /**
22              * @event selectionchange
23              * Fires when the selection changes
24              * @param {SelectionModel} this
25              */
26             "selectionchange" : true,
27         /**
28              * @event afterselectionchange
29              * Fires after the selection changes (eg. by key press or clicking)
30              * @param {SelectionModel} this
31              */
32             "afterselectionchange" : true,
33         /**
34              * @event beforerowselect
35              * Fires when a row is selected being selected, return false to cancel.
36              * @param {SelectionModel} this
37              * @param {Number} rowIndex The selected index
38              * @param {Boolean} keepExisting False if other selections will be cleared
39              */
40             "beforerowselect" : true,
41         /**
42              * @event rowselect
43              * Fires when a row is selected.
44              * @param {SelectionModel} this
45              * @param {Number} rowIndex The selected index
46              * @param {Roo.data.Record} r The record
47              */
48             "rowselect" : true,
49         /**
50              * @event rowdeselect
51              * Fires when a row is deselected.
52              * @param {SelectionModel} this
53              * @param {Number} rowIndex The selected index
54              */
55         "rowdeselect" : true
56     });
57     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
58     this.locked = false;
59  };
60
61 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
62     /**
63      * @cfg {Boolean} singleSelect
64      * True to allow selection of only one row at a time (defaults to false)
65      */
66     singleSelect : false,
67
68     // private
69     initEvents : function()
70     {
71
72         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
73         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
74         //}else{ // allow click to work like normal
75          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
76         //}
77         this.grid.on("mousedown", this.handleMouseDown, this);
78         
79         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
80             "up" : function(e){
81                 if(!e.shiftKey){
82                     this.selectPrevious(e.shiftKey);
83                 }else if(this.last !== false && this.lastActive !== false){
84                     var last = this.last;
85                     this.selectRange(this.last,  this.lastActive-1);
86                     this.grid.getView().focusRow(this.lastActive);
87                     if(last !== false){
88                         this.last = last;
89                     }
90                 }else{
91                     this.selectFirstRow();
92                 }
93                 this.fireEvent("afterselectionchange", this);
94             },
95             "down" : function(e){
96                 if(!e.shiftKey){
97                     this.selectNext(e.shiftKey);
98                 }else if(this.last !== false && this.lastActive !== false){
99                     var last = this.last;
100                     this.selectRange(this.last,  this.lastActive+1);
101                     this.grid.getView().focusRow(this.lastActive);
102                     if(last !== false){
103                         this.last = last;
104                     }
105                 }else{
106                     this.selectFirstRow();
107                 }
108                 this.fireEvent("afterselectionchange", this);
109             },
110             scope: this
111         });
112         /*
113         var view = this.grid.view;
114         view.on("refresh", this.onRefresh, this);
115         view.on("rowupdated", this.onRowUpdated, this);
116         view.on("rowremoved", this.onRemove, this);
117         */
118     },
119
120     // private
121     onRefresh : function(){
122         var ds = this.grid.dataSource, i, v = this.grid.view;
123         var s = this.selections;
124         s.each(function(r){
125             if((i = ds.indexOfId(r.id)) != -1){
126                 v.onRowSelect(i);
127             }else{
128                 s.remove(r);
129             }
130         });
131     },
132
133     // private
134     onRemove : function(v, index, r){
135         this.selections.remove(r);
136     },
137
138     // private
139     onRowUpdated : function(v, index, r){
140         if(this.isSelected(r)){
141             v.onRowSelect(index);
142         }
143     },
144
145     /**
146      * Select records.
147      * @param {Array} records The records to select
148      * @param {Boolean} keepExisting (optional) True to keep existing selections
149      */
150     selectRecords : function(records, keepExisting){
151         if(!keepExisting){
152             this.clearSelections();
153         }
154         var ds = this.grid.dataSource;
155         for(var i = 0, len = records.length; i < len; i++){
156             this.selectRow(ds.indexOf(records[i]), true);
157         }
158     },
159
160     /**
161      * Gets the number of selected rows.
162      * @return {Number}
163      */
164     getCount : function(){
165         return this.selections.length;
166     },
167
168     /**
169      * Selects the first row in the grid.
170      */
171     selectFirstRow : function(){
172         this.selectRow(0);
173     },
174
175     /**
176      * Select the last row.
177      * @param {Boolean} keepExisting (optional) True to keep existing selections
178      */
179     selectLastRow : function(keepExisting){
180         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
181     },
182
183     /**
184      * Selects the row immediately following the last selected row.
185      * @param {Boolean} keepExisting (optional) True to keep existing selections
186      */
187     selectNext : function(keepExisting){
188         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
189             this.selectRow(this.last+1, keepExisting);
190             this.grid.getView().focusRow(this.last);
191         }
192     },
193
194     /**
195      * Selects the row that precedes the last selected row.
196      * @param {Boolean} keepExisting (optional) True to keep existing selections
197      */
198     selectPrevious : function(keepExisting){
199         if(this.last){
200             this.selectRow(this.last-1, keepExisting);
201             this.grid.getView().focusRow(this.last);
202         }
203     },
204
205     /**
206      * Returns the selected records
207      * @return {Array} Array of selected records
208      */
209     getSelections : function(){
210         return [].concat(this.selections.items);
211     },
212
213     /**
214      * Returns the first selected record.
215      * @return {Record}
216      */
217     getSelected : function(){
218         return this.selections.itemAt(0);
219     },
220
221
222     /**
223      * Clears all selections.
224      */
225     clearSelections : function(fast){
226         if(this.locked) {
227             return;
228         }
229         if(fast !== true){
230             var ds = this.grid.dataSource;
231             var s = this.selections;
232             s.each(function(r){
233                 this.deselectRow(ds.indexOfId(r.id));
234             }, this);
235             s.clear();
236         }else{
237             this.selections.clear();
238         }
239         this.last = false;
240     },
241
242
243     /**
244      * Selects all rows.
245      */
246     selectAll : function(){
247         if(this.locked) {
248             return;
249         }
250         this.selections.clear();
251         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
252             this.selectRow(i, true);
253         }
254     },
255
256     /**
257      * Returns True if there is a selection.
258      * @return {Boolean}
259      */
260     hasSelection : function(){
261         return this.selections.length > 0;
262     },
263
264     /**
265      * Returns True if the specified row is selected.
266      * @param {Number/Record} record The record or index of the record to check
267      * @return {Boolean}
268      */
269     isSelected : function(index){
270         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
271         return (r && this.selections.key(r.id) ? true : false);
272     },
273
274     /**
275      * Returns True if the specified record id is selected.
276      * @param {String} id The id of record to check
277      * @return {Boolean}
278      */
279     isIdSelected : function(id){
280         return (this.selections.key(id) ? true : false);
281     },
282
283     // private
284     handleMouseDown : function(e, t){
285         var view = this.grid.getView(), rowIndex;
286         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
287             return;
288         };
289         if(e.shiftKey && this.last !== false){
290             var last = this.last;
291             this.selectRange(last, rowIndex, e.ctrlKey);
292             this.last = last; // reset the last
293             view.focusRow(rowIndex);
294         }else{
295             var isSelected = this.isSelected(rowIndex);
296             if(e.button !== 0 && isSelected){
297                 view.focusRow(rowIndex);
298             }else if(e.ctrlKey && isSelected){
299                 this.deselectRow(rowIndex);
300             }else if(!isSelected){
301                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
302                 view.focusRow(rowIndex);
303             }
304         }
305         this.fireEvent("afterselectionchange", this);
306     },
307     // private
308     handleDragableRowClick :  function(grid, rowIndex, e) 
309     {
310         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
311             this.selectRow(rowIndex, false);
312             grid.view.focusRow(rowIndex);
313              this.fireEvent("afterselectionchange", this);
314         }
315     },
316     
317     /**
318      * Selects multiple rows.
319      * @param {Array} rows Array of the indexes of the row to select
320      * @param {Boolean} keepExisting (optional) True to keep existing selections
321      */
322     selectRows : function(rows, keepExisting){
323         if(!keepExisting){
324             this.clearSelections();
325         }
326         for(var i = 0, len = rows.length; i < len; i++){
327             this.selectRow(rows[i], true);
328         }
329     },
330
331     /**
332      * Selects a range of rows. All rows in between startRow and endRow are also selected.
333      * @param {Number} startRow The index of the first row in the range
334      * @param {Number} endRow The index of the last row in the range
335      * @param {Boolean} keepExisting (optional) True to retain existing selections
336      */
337     selectRange : function(startRow, endRow, keepExisting){
338         if(this.locked) {
339             return;
340         }
341         if(!keepExisting){
342             this.clearSelections();
343         }
344         if(startRow <= endRow){
345             for(var i = startRow; i <= endRow; i++){
346                 this.selectRow(i, true);
347             }
348         }else{
349             for(var i = startRow; i >= endRow; i--){
350                 this.selectRow(i, true);
351             }
352         }
353     },
354
355     /**
356      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
357      * @param {Number} startRow The index of the first row in the range
358      * @param {Number} endRow The index of the last row in the range
359      */
360     deselectRange : function(startRow, endRow, preventViewNotify){
361         if(this.locked) {
362             return;
363         }
364         for(var i = startRow; i <= endRow; i++){
365             this.deselectRow(i, preventViewNotify);
366         }
367     },
368
369     /**
370      * Selects a row.
371      * @param {Number} row The index of the row to select
372      * @param {Boolean} keepExisting (optional) True to keep existing selections
373      */
374     selectRow : function(index, keepExisting, preventViewNotify){
375         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
376             return;
377         }
378         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
379             if(!keepExisting || this.singleSelect){
380                 this.clearSelections();
381             }
382             var r = this.grid.dataSource.getAt(index);
383             this.selections.add(r);
384             this.last = this.lastActive = index;
385             if(!preventViewNotify){
386                 this.grid.getView().onRowSelect(index);
387             }
388             this.fireEvent("rowselect", this, index, r);
389             this.fireEvent("selectionchange", this);
390         }
391     },
392
393     /**
394      * Deselects a row.
395      * @param {Number} row The index of the row to deselect
396      */
397     deselectRow : function(index, preventViewNotify){
398         if(this.locked) {
399             return;
400         }
401         if(this.last == index){
402             this.last = false;
403         }
404         if(this.lastActive == index){
405             this.lastActive = false;
406         }
407         var r = this.grid.dataSource.getAt(index);
408         this.selections.remove(r);
409         if(!preventViewNotify){
410             this.grid.getView().onRowDeselect(index);
411         }
412         this.fireEvent("rowdeselect", this, index);
413         this.fireEvent("selectionchange", this);
414     },
415
416     // private
417     restoreLast : function(){
418         if(this._last){
419             this.last = this._last;
420         }
421     },
422
423     // private
424     acceptsNav : function(row, col, cm){
425         return !cm.isHidden(col) && cm.isCellEditable(col, row);
426     },
427
428     // private
429     onEditorKey : function(field, e){
430         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
431         if(k == e.TAB){
432             e.stopEvent();
433             ed.completeEdit();
434             if(e.shiftKey){
435                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
436             }else{
437                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
438             }
439         }else if(k == e.ENTER && !e.ctrlKey){
440             e.stopEvent();
441             ed.completeEdit();
442             if(e.shiftKey){
443                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
444             }else{
445                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
446             }
447         }else if(k == e.ESC){
448             ed.cancelEdit();
449         }
450         if(newCell){
451             g.startEditing(newCell[0], newCell[1]);
452         }
453     }
454 });