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;
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                 }
207                 break;
208             
209             case e.DOWN:
210                newCell = walk(r+1, c, 1);
211                 break;
212             
213             case e.UP:
214                 newCell = walk(r-1, c, -1);
215                 break;
216             
217             case e.RIGHT:
218                 newCell = walk(r, c+1, 1);
219                 break;
220             
221             case e.LEFT:
222                 newCell = walk(r, c-1, -1);
223                 break;
224             
225             case e.ENTER:
226                 
227                 if(g.isEditor && !g.editing){
228                    g.startEditing(r, c);
229                    e.stopEvent();
230                    return;
231                 }
232                 
233                 
234              break;
235         };
236         if(newCell){
237             this.select(newCell[0], newCell[1]);
238             e.stopEvent();
239             
240         }
241     },
242
243     acceptsNav : function(row, col, cm){
244         return !cm.isHidden(col) && cm.isCellEditable(col, row);
245     },
246     /**
247      * Selects a cell.
248      * @param {Number} field (not used) - as it's normally used as a listener
249      * @param {Number} e - event - fake it by using
250      *
251      * var e = Roo.EventObjectImpl.prototype;
252      * e.keyCode = e.TAB
253      *
254      * 
255      */
256     onEditorKey : function(field, e){
257         
258         var k = e.getKey(),
259             newCell,
260             g = this.grid,
261             ed = g.activeEditor,
262             forward = false;
263         ///Roo.log('onEditorKey' + k);
264         
265         
266         if (this.enter_is_tab && k == e.ENTER) {
267             k = e.TAB;
268         }
269         
270         if(k == e.TAB){
271             if(e.shiftKey){
272                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
273             }else{
274                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
275                 forward = true;
276             }
277             
278             e.stopEvent();
279             
280         }else if(k == e.ENTER &&  !e.ctrlKey){
281             ed.completeEdit();
282             e.stopEvent();
283             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
284         }else if(k == e.ESC){
285             ed.cancelEdit();
286         }
287         
288         
289         if(newCell){
290             //Roo.log('next cell after edit');
291             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
292         } else if (forward) {
293             // tabbed past last
294             this.fireEvent.defer(100, this, ['tabend',this]);
295         }
296     }
297 });