Uncommited changes synced
[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("rowdblclick", this.handleMouseDBClick, this);
78         this.grid.on("rowclick", this.handleMouseDown, this);
79         
80         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
81             "up" : function(e){
82                 if(!e.shiftKey){
83                     this.selectPrevious(e.shiftKey);
84                 }else if(this.last !== false && this.lastActive !== false){
85                     var last = this.last;
86                     this.selectRange(this.last,  this.lastActive-1);
87                     this.grid.getView().focusRow(this.lastActive);
88                     if(last !== false){
89                         this.last = last;
90                     }
91                 }else{
92                     this.selectFirstRow();
93                 }
94                 this.fireEvent("afterselectionchange", this);
95             },
96             "down" : function(e){
97                 if(!e.shiftKey){
98                     this.selectNext(e.shiftKey);
99                 }else if(this.last !== false && this.lastActive !== false){
100                     var last = this.last;
101                     this.selectRange(this.last,  this.lastActive+1);
102                     this.grid.getView().focusRow(this.lastActive);
103                     if(last !== false){
104                         this.last = last;
105                     }
106                 }else{
107                     this.selectFirstRow();
108                 }
109                 this.fireEvent("afterselectionchange", this);
110             },
111             scope: this
112         });
113         this.grid.store.on('load', function(){
114             this.selections.clear();
115         },this);
116         /*
117         var view = this.grid.view;
118         view.on("refresh", this.onRefresh, this);
119         view.on("rowupdated", this.onRowUpdated, this);
120         view.on("rowremoved", this.onRemove, this);
121         */
122     },
123
124     // private
125     onRefresh : function()
126     {
127         var ds = this.grid.store, i, v = this.grid.view;
128         var s = this.selections;
129         s.each(function(r){
130             if((i = ds.indexOfId(r.id)) != -1){
131                 v.onRowSelect(i);
132             }else{
133                 s.remove(r);
134             }
135         });
136     },
137
138     // private
139     onRemove : function(v, index, r){
140         this.selections.remove(r);
141     },
142
143     // private
144     onRowUpdated : function(v, index, r){
145         if(this.isSelected(r)){
146             v.onRowSelect(index);
147         }
148     },
149
150     /**
151      * Select records.
152      * @param {Array} records The records to select
153      * @param {Boolean} keepExisting (optional) True to keep existing selections
154      */
155     selectRecords : function(records, keepExisting)
156     {
157         if(!keepExisting){
158             this.clearSelections();
159         }
160             var ds = this.grid.store;
161         for(var i = 0, len = records.length; i < len; i++){
162             this.selectRow(ds.indexOf(records[i]), true);
163         }
164     },
165
166     /**
167      * Gets the number of selected rows.
168      * @return {Number}
169      */
170     getCount : function(){
171         return this.selections.length;
172     },
173
174     /**
175      * Selects the first row in the grid.
176      */
177     selectFirstRow : function(){
178         this.selectRow(0);
179     },
180
181     /**
182      * Select the last row.
183      * @param {Boolean} keepExisting (optional) True to keep existing selections
184      */
185     selectLastRow : function(keepExisting){
186         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
187         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
188     },
189
190     /**
191      * Selects the row immediately following the last selected row.
192      * @param {Boolean} keepExisting (optional) True to keep existing selections
193      */
194     selectNext : function(keepExisting)
195     {
196             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
197             this.selectRow(this.last+1, keepExisting);
198             this.grid.getView().focusRow(this.last);
199         }
200     },
201
202     /**
203      * Selects the row that precedes the last selected row.
204      * @param {Boolean} keepExisting (optional) True to keep existing selections
205      */
206     selectPrevious : function(keepExisting){
207         if(this.last){
208             this.selectRow(this.last-1, keepExisting);
209             this.grid.getView().focusRow(this.last);
210         }
211     },
212
213     /**
214      * Returns the selected records
215      * @return {Array} Array of selected records
216      */
217     getSelections : function(){
218         return [].concat(this.selections.items);
219     },
220
221     /**
222      * Returns the first selected record.
223      * @return {Record}
224      */
225     getSelected : function(){
226         return this.selections.itemAt(0);
227     },
228
229
230     /**
231      * Clears all selections.
232      */
233     clearSelections : function(fast)
234     {
235         if(this.locked) {
236             return;
237         }
238         if(fast !== true){
239                 var ds = this.grid.store;
240             var s = this.selections;
241             s.each(function(r){
242                 this.deselectRow(ds.indexOfId(r.id));
243             }, this);
244             s.clear();
245         }else{
246             this.selections.clear();
247         }
248         this.last = false;
249     },
250
251
252     /**
253      * Selects all rows.
254      */
255     selectAll : function(){
256         if(this.locked) {
257             return;
258         }
259         this.selections.clear();
260         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
261             this.selectRow(i, true);
262         }
263     },
264
265     /**
266      * Returns True if there is a selection.
267      * @return {Boolean}
268      */
269     hasSelection : function(){
270         return this.selections.length > 0;
271     },
272
273     /**
274      * Returns True if the specified row is selected.
275      * @param {Number/Record} record The record or index of the record to check
276      * @return {Boolean}
277      */
278     isSelected : function(index){
279             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
280         return (r && this.selections.key(r.id) ? true : false);
281     },
282
283     /**
284      * Returns True if the specified record id is selected.
285      * @param {String} id The id of record to check
286      * @return {Boolean}
287      */
288     isIdSelected : function(id){
289         return (this.selections.key(id) ? true : false);
290     },
291
292
293     // private
294     handleMouseDBClick : function(e, t){
295         
296     },
297     // private
298     handleMouseDown : function(e, t)
299     {
300             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
301         if(this.isLocked() || rowIndex < 0 ){
302             return;
303         };
304         if(e.shiftKey && this.last !== false){
305             var last = this.last;
306             this.selectRange(last, rowIndex, e.ctrlKey);
307             this.last = last; // reset the last
308             t.focus();
309     
310         }else{
311             var isSelected = this.isSelected(rowIndex);
312             //Roo.log("select row:" + rowIndex);
313             if(isSelected){
314                 this.deselectRow(rowIndex);
315             } else {
316                         this.selectRow(rowIndex, true);
317             }
318     
319             /*
320                 if(e.button !== 0 && isSelected){
321                 alert('rowIndex 2: ' + rowIndex);
322                     view.focusRow(rowIndex);
323                 }else if(e.ctrlKey && isSelected){
324                     this.deselectRow(rowIndex);
325                 }else if(!isSelected){
326                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
327                     view.focusRow(rowIndex);
328                 }
329             */
330         }
331         this.fireEvent("afterselectionchange", this);
332     },
333     // private
334     handleDragableRowClick :  function(grid, rowIndex, e) 
335     {
336         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
337             this.selectRow(rowIndex, false);
338             grid.view.focusRow(rowIndex);
339              this.fireEvent("afterselectionchange", this);
340         }
341     },
342     
343     /**
344      * Selects multiple rows.
345      * @param {Array} rows Array of the indexes of the row to select
346      * @param {Boolean} keepExisting (optional) True to keep existing selections
347      */
348     selectRows : function(rows, keepExisting){
349         if(!keepExisting){
350             this.clearSelections();
351         }
352         for(var i = 0, len = rows.length; i < len; i++){
353             this.selectRow(rows[i], true);
354         }
355     },
356
357     /**
358      * Selects a range of rows. All rows in between startRow and endRow are also selected.
359      * @param {Number} startRow The index of the first row in the range
360      * @param {Number} endRow The index of the last row in the range
361      * @param {Boolean} keepExisting (optional) True to retain existing selections
362      */
363     selectRange : function(startRow, endRow, keepExisting){
364         if(this.locked) {
365             return;
366         }
367         if(!keepExisting){
368             this.clearSelections();
369         }
370         if(startRow <= endRow){
371             for(var i = startRow; i <= endRow; i++){
372                 this.selectRow(i, true);
373             }
374         }else{
375             for(var i = startRow; i >= endRow; i--){
376                 this.selectRow(i, true);
377             }
378         }
379     },
380
381     /**
382      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
383      * @param {Number} startRow The index of the first row in the range
384      * @param {Number} endRow The index of the last row in the range
385      */
386     deselectRange : function(startRow, endRow, preventViewNotify){
387         if(this.locked) {
388             return;
389         }
390         for(var i = startRow; i <= endRow; i++){
391             this.deselectRow(i, preventViewNotify);
392         }
393     },
394
395     /**
396      * Selects a row.
397      * @param {Number} row The index of the row to select
398      * @param {Boolean} keepExisting (optional) True to keep existing selections
399      */
400     selectRow : function(index, keepExisting, preventViewNotify)
401     {
402             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
403             return;
404         }
405         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
406             if(!keepExisting || this.singleSelect){
407                 this.clearSelections();
408             }
409             
410             var r = this.grid.store.getAt(index);
411             //console.log('selectRow - record id :' + r.id);
412             
413             this.selections.add(r);
414             this.last = this.lastActive = index;
415             if(!preventViewNotify){
416                 var proxy = new Roo.Element(
417                                 this.grid.getRowDom(index)
418                 );
419                 proxy.addClass('bg-info info');
420             }
421             this.fireEvent("rowselect", this, index, r);
422             this.fireEvent("selectionchange", this);
423         }
424     },
425
426     /**
427      * Deselects a row.
428      * @param {Number} row The index of the row to deselect
429      */
430     deselectRow : function(index, preventViewNotify)
431     {
432         if(this.locked) {
433             return;
434         }
435         if(this.last == index){
436             this.last = false;
437         }
438         if(this.lastActive == index){
439             this.lastActive = false;
440         }
441         
442         var r = this.grid.store.getAt(index);
443         if (!r) {
444             return;
445         }
446         
447         this.selections.remove(r);
448         //.console.log('deselectRow - record id :' + r.id);
449         if(!preventViewNotify){
450         
451             var proxy = new Roo.Element(
452                 this.grid.getRowDom(index)
453             );
454             proxy.removeClass('bg-info info');
455         }
456         this.fireEvent("rowdeselect", this, index);
457         this.fireEvent("selectionchange", this);
458     },
459
460     // private
461     restoreLast : function(){
462         if(this._last){
463             this.last = this._last;
464         }
465     },
466
467     // private
468     acceptsNav : function(row, col, cm){
469         return !cm.isHidden(col) && cm.isCellEditable(col, row);
470     },
471
472     // private
473     onEditorKey : function(field, e){
474         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
475         if(k == e.TAB){
476             e.stopEvent();
477             ed.completeEdit();
478             if(e.shiftKey){
479                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
480             }else{
481                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
482             }
483         }else if(k == e.ENTER && !e.ctrlKey){
484             e.stopEvent();
485             ed.completeEdit();
486             if(e.shiftKey){
487                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
488             }else{
489                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
490             }
491         }else if(k == e.ESC){
492             ed.cancelEdit();
493         }
494         if(newCell){
495             g.startEditing(newCell[0], newCell[1]);
496         }
497     }
498 });