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              * @event tabend
54              * Fires when the tab (or enter) was pressed on the last editable cell
55              * You can use this to trigger add new row.
56              * @param {SelectionModel} this
57              */
58             "tabend" : true,
59          /**
60              * @event beforeeditnext
61              * Fires before the next editable sell is made active
62              * You can use this to skip to another cell
63              * @param {Array} newCell the new [ row, col ] to be selected
64              */
65             "beforeeditnext" : true
66     });
67     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
68 };
69
70 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
71     
72     enter_is_tab: false,
73
74     /** @ignore */
75     initEvents : function(){
76         this.grid.on("mousedown", this.handleMouseDown, this);
77         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
78         var view = this.grid.view;
79         view.on("refresh", this.onViewChange, this);
80         view.on("rowupdated", this.onRowUpdated, this);
81         view.on("beforerowremoved", this.clearSelections, this);
82         view.on("beforerowsinserted", this.clearSelections, this);
83         if(this.grid.isEditor){
84             this.grid.on("beforeedit", this.beforeEdit,  this);
85         }
86     },
87
88         //private
89     beforeEdit : function(e){
90         this.select(e.row, e.column, false, true, e.record);
91     },
92
93         //private
94     onRowUpdated : function(v, index, r){
95         if(this.selection && this.selection.record == r){
96             v.onCellSelect(index, this.selection.cell[1]);
97         }
98     },
99
100         //private
101     onViewChange : function(){
102         this.clearSelections(true);
103     },
104
105         /**
106          * Returns the currently selected cell,.
107          * @return {Array} The selected cell (row, column) or null if none selected.
108          */
109     getSelectedCell : function(){
110         return this.selection ? this.selection.cell : null;
111     },
112
113     /**
114      * Clears all selections.
115      * @param {Boolean} true to prevent the gridview from being notified about the change.
116      */
117     clearSelections : function(preventNotify){
118         var s = this.selection;
119         if(s){
120             if(preventNotify !== true){
121                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
122             }
123             this.selection = null;
124             this.fireEvent("selectionchange", this, null);
125         }
126     },
127
128     /**
129      * Returns true if there is a selection.
130      * @return {Boolean}
131      */
132     hasSelection : function(){
133         return this.selection ? true : false;
134     },
135
136     /** @ignore */
137     handleMouseDown : function(e, t){
138         var v = this.grid.getView();
139         if(this.isLocked()){
140             return;
141         };
142         var row = v.findRowIndex(t);
143         var cell = v.findCellIndex(t);
144         if(row !== false && cell !== false){
145             this.select(row, cell);
146         }
147     },
148
149     /**
150      * Selects a cell.
151      * @param {Number} rowIndex
152      * @param {Number} collIndex
153      */
154     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
155         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
156             this.clearSelections();
157             r = r || this.grid.dataSource.getAt(rowIndex);
158             this.selection = {
159                 record : r,
160                 cell : [rowIndex, colIndex]
161             };
162             if(!preventViewNotify){
163                 var v = this.grid.getView();
164                 v.onCellSelect(rowIndex, colIndex);
165                 if(preventFocus !== true){
166                     v.focusCell(rowIndex, colIndex);
167                 }
168             }
169             this.fireEvent("cellselect", this, rowIndex, colIndex);
170             this.fireEvent("selectionchange", this, this.selection);
171         }
172     },
173
174         //private
175     isSelectable : function(rowIndex, colIndex, cm){
176         return !cm.isHidden(colIndex);
177     },
178
179     /** @ignore */
180     handleKeyDown : function(e){
181         //Roo.log('Cell Sel Model handleKeyDown');
182         if(!e.isNavKeyPress()){
183             return;
184         }
185         var g = this.grid, s = this.selection;
186         if(!s){
187             e.stopEvent();
188             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
189             if(cell){
190                 this.select(cell[0], cell[1]);
191             }
192             return;
193         }
194         var sm = this;
195         var walk = function(row, col, step){
196             return g.walkCells(row, col, step, sm.isSelectable,  sm);
197         };
198         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
199         var newCell;
200
201       
202
203         switch(k){
204             case e.TAB:
205                 // handled by onEditorKey
206                 if (g.isEditor && g.editing) {
207                     return;
208                 }
209                 if(e.shiftKey) {
210                     newCell = walk(r, c-1, -1);
211                 } else {
212                     newCell = walk(r, c+1, 1);
213                 }
214                 break;
215             
216             case e.DOWN:
217                newCell = walk(r+1, c, 1);
218                 break;
219             
220             case e.UP:
221                 newCell = walk(r-1, c, -1);
222                 break;
223             
224             case e.RIGHT:
225                 newCell = walk(r, c+1, 1);
226                 break;
227             
228             case e.LEFT:
229                 newCell = walk(r, c-1, -1);
230                 break;
231             
232             case e.ENTER:
233                 
234                 if(g.isEditor && !g.editing){
235                    g.startEditing(r, c);
236                    e.stopEvent();
237                    return;
238                 }
239                 
240                 
241              break;
242         };
243         if(newCell){
244             this.select(newCell[0], newCell[1]);
245             e.stopEvent();
246             
247         }
248     },
249
250     acceptsNav : function(row, col, cm){
251         return !cm.isHidden(col) && cm.isCellEditable(col, row);
252     },
253     /**
254      * Selects a cell.
255      * @param {Number} field (not used) - as it's normally used as a listener
256      * @param {Number} e - event - fake it by using
257      *
258      * var e = Roo.EventObjectImpl.prototype;
259      * e.keyCode = e.TAB
260      *
261      * 
262      */
263     onEditorKey : function(field, e){
264         
265         var k = e.getKey(),
266             newCell,
267             g = this.grid,
268             ed = g.activeEditor,
269             forward = false;
270         ///Roo.log('onEditorKey' + k);
271         
272         
273         if (this.enter_is_tab && k == e.ENTER) {
274             k = e.TAB;
275         }
276         
277         if(k == e.TAB){
278             if(e.shiftKey){
279                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
280             }else{
281                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
282                 forward = true;
283             }
284             
285             e.stopEvent();
286             
287         }else if(k == e.ENTER &&  !e.ctrlKey){
288             ed.completeEdit();
289             e.stopEvent();
290             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
291         }else if(k == e.ESC){
292             ed.cancelEdit();
293         }
294         if (newCell) {
295             var ecall = { cell : newCell } 
296             this.fireEvent('beforeeditnext', ecall );
297             newCell = ecall.newCell;
298         }
299         if(newCell){
300             // can modify new Cell
301
302             
303             //Roo.log('next cell after edit');
304             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
305         } else if (forward) {
306             // tabbed past last
307             this.fireEvent.defer(100, this, ['tabend',this]);
308         }
309     }
310 });