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