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