Roo/grid/CellSelectionModel.js
[roojs1] / Roo / grid / CellSelectionModel.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  * @class Roo.grid.CellSelectionModel
13  * @extends Roo.grid.AbstractSelectionModel
14  * This class provides the basic implementation for cell selection in a grid.
15  * @constructor
16  * @param {Object} config The object containing the configuration of this model.
17  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
18  */
19 Roo.grid.CellSelectionModel = function(config){
20     Roo.apply(this, config);
21
22     this.selection = null;
23
24     this.addEvents({
25         /**
26              * @event beforerowselect
27              * Fires before a cell is selected.
28              * @param {SelectionModel} this
29              * @param {Number} rowIndex The selected row index
30              * @param {Number} colIndex The selected cell index
31              */
32             "beforecellselect" : true,
33         /**
34              * @event cellselect
35              * Fires when a cell is selected.
36              * @param {SelectionModel} this
37              * @param {Number} rowIndex The selected row index
38              * @param {Number} colIndex The selected cell index
39              */
40             "cellselect" : true,
41         /**
42              * @event selectionchange
43              * Fires when the active selection changes.
44              * @param {SelectionModel} this
45              * @param {Object} selection null for no selection or an object (o) with two properties
46                 <ul>
47                 <li>o.record: the record object for the row the selection is in</li>
48                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49                 </ul>
50              */
51             "selectionchange" : true
52     });
53     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
54 };
55
56 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
57     
58     enter_is_tab: false,
59
60     /** @ignore */
61     initEvents : function(){
62         this.grid.on("mousedown", this.handleMouseDown, this);
63         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
64         var view = this.grid.view;
65         view.on("refresh", this.onViewChange, this);
66         view.on("rowupdated", this.onRowUpdated, this);
67         view.on("beforerowremoved", this.clearSelections, this);
68         view.on("beforerowsinserted", this.clearSelections, this);
69         if(this.grid.isEditor){
70             this.grid.on("beforeedit", this.beforeEdit,  this);
71         }
72     },
73
74         //private
75     beforeEdit : function(e){
76         this.select(e.row, e.column, false, true, e.record);
77     },
78
79         //private
80     onRowUpdated : function(v, index, r){
81         if(this.selection && this.selection.record == r){
82             v.onCellSelect(index, this.selection.cell[1]);
83         }
84     },
85
86         //private
87     onViewChange : function(){
88         this.clearSelections(true);
89     },
90
91         /**
92          * Returns the currently selected cell,.
93          * @return {Array} The selected cell (row, column) or null if none selected.
94          */
95     getSelectedCell : function(){
96         return this.selection ? this.selection.cell : null;
97     },
98
99     /**
100      * Clears all selections.
101      * @param {Boolean} true to prevent the gridview from being notified about the change.
102      */
103     clearSelections : function(preventNotify){
104         var s = this.selection;
105         if(s){
106             if(preventNotify !== true){
107                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
108             }
109             this.selection = null;
110             this.fireEvent("selectionchange", this, null);
111         }
112     },
113
114     /**
115      * Returns true if there is a selection.
116      * @return {Boolean}
117      */
118     hasSelection : function(){
119         return this.selection ? true : false;
120     },
121
122     /** @ignore */
123     handleMouseDown : function(e, t){
124         var v = this.grid.getView();
125         if(this.isLocked()){
126             return;
127         };
128         var row = v.findRowIndex(t);
129         var cell = v.findCellIndex(t);
130         if(row !== false && cell !== false){
131             this.select(row, cell);
132         }
133     },
134
135     /**
136      * Selects a cell.
137      * @param {Number} rowIndex
138      * @param {Number} collIndex
139      */
140     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
141         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
142             this.clearSelections();
143             r = r || this.grid.dataSource.getAt(rowIndex);
144             this.selection = {
145                 record : r,
146                 cell : [rowIndex, colIndex]
147             };
148             if(!preventViewNotify){
149                 var v = this.grid.getView();
150                 v.onCellSelect(rowIndex, colIndex);
151                 if(preventFocus !== true){
152                     v.focusCell(rowIndex, colIndex);
153                 }
154             }
155             this.fireEvent("cellselect", this, rowIndex, colIndex);
156             this.fireEvent("selectionchange", this, this.selection);
157         }
158     },
159
160         //private
161     isSelectable : function(rowIndex, colIndex, cm){
162         return !cm.isHidden(colIndex);
163     },
164
165     /** @ignore */
166     handleKeyDown : function(e){
167         //Roo.log('Cell Sel Model handleKeyDown');
168         if(!e.isNavKeyPress()){
169             return;
170         }
171         var g = this.grid, s = this.selection;
172         if(!s){
173             e.stopEvent();
174             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
175             if(cell){
176                 this.select(cell[0], cell[1]);
177             }
178             return;
179         }
180         var sm = this;
181         var walk = function(row, col, step){
182             return g.walkCells(row, col, step, sm.isSelectable,  sm);
183         };
184         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
185         var newCell, forward=false;
186
187         if (this.enter_is_tab && k == e.ENTER) {
188             k = e.TAB;
189         }
190
191         switch(k){
192             case e.TAB:
193                 // handled by onEditorKey
194                 if (g.isEditor && g.editing) {
195                     return;
196                 }
197                 if(e.shiftKey) {
198                     newCell = walk(r, c-1, -1);
199                 } else {
200                     newCell = walk(r, c+1, 1);
201                     forward = true;
202                 }
203                 break;
204             
205             case e.DOWN:
206                newCell = walk(r+1, c, 1);
207                 forward = true;
208                 break;
209             
210             case e.UP:
211                 newCell = walk(r-1, c, -1);
212                 break;
213             
214             case e.RIGHT:
215                 newCell = walk(r, c+1, 1);
216                 forward = true;
217                 break;
218             
219             case e.LEFT:
220                 newCell = walk(r, c-1, -1);
221                 break;
222             
223             case e.ENTER:
224                 
225                 if(g.isEditor && !g.editing){
226                    g.startEditing(r, c);
227                    e.stopEvent();
228                    return;
229                 }
230                 
231                 
232              break;
233         };
234         if(newCell){
235             this.select(newCell[0], newCell[1]);
236             e.stopEvent();
237             
238         } else if (forward) {
239             // tabbed past last
240             g.fireEvent('tabend');
241             
242         }
243     },
244
245     acceptsNav : function(row, col, cm){
246         return !cm.isHidden(col) && cm.isCellEditable(col, row);
247     },
248     /**
249      * Selects a cell.
250      * @param {Number} field (not used) - as it's normally used as a listener
251      * @param {Number} e - event - fake it by using
252      *
253      * var e = Roo.EventObjectImpl.prototype;
254      * e.keyCode = e.TAB
255      *
256      * 
257      */
258     onEditorKey : function(field, e){
259         
260         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
261         ///Roo.log('onEditorKey' + k);
262         if (!ed) {
263             
264             
265             
266         }
267         if(k == e.TAB){
268             if(e.shiftKey){
269                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
270             }else{
271                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
272             }
273             
274             e.stopEvent();
275             
276         }else if(k == e.ENTER &&  !e.ctrlKey){
277             ed.completeEdit();
278             e.stopEvent();
279             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
280         }else if(k == e.ESC){
281             ed.cancelEdit();
282         }
283         
284         
285         if(newCell){
286             //Roo.log('next cell after edit');
287             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
288         }
289     }
290 });