initial import
[roojs1] / Roo / grid / Grid.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 /**
13  * @class Roo.grid.Grid
14  * @extends Roo.util.Observable
15  * This class represents the primary interface of a component based grid control.
16  * <br><br>Usage:<pre><code>
17  var grid = new Roo.grid.Grid("my-container-id", {
18      ds: myDataStore,
19      cm: myColModel,
20      selModel: mySelectionModel,
21      autoSizeColumns: true,
22      monitorWindowResize: false,
23      trackMouseOver: true
24  });
25  // set any options
26  grid.render();
27  * </code></pre>
28  * <b>Common Problems:</b><br/>
29  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
30  * element will correct this<br/>
31  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33  * are unpredictable.<br/>
34  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
35  * grid to calculate dimensions/offsets.<br/>
36   * @constructor
37  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38  * The container MUST have some type of size defined for the grid to fill. The container will be
39  * automatically set to position relative if it isn't already.
40  * @param {Object} config A config object that sets properties on this grid.
41  */
42 Roo.grid.Grid = function(container, config){
43         // initialize the container
44         this.container = Roo.get(container);
45         this.container.update("");
46         this.container.setStyle("overflow", "hidden");
47     this.container.addClass('x-grid-container');
48
49     this.id = this.container.id;
50
51     Roo.apply(this, config);
52     // check and correct shorthanded configs
53     if(this.ds){
54         this.dataSource = this.ds;
55         delete this.ds;
56     }
57     if(this.cm){
58         this.colModel = this.cm;
59         delete this.cm;
60     }
61     if(this.sm){
62         this.selModel = this.sm;
63         delete this.sm;
64     }
65
66     if (this.selModel) {
67         this.selModel = Roo.factory(this.selModel, Roo.grid);
68     }
69     if (typeof(this.colModel.config) == 'undefined') {
70         this.colModel = new Roo.grid.ColumnModel(this.colModel);
71     }
72     if (this.dataSource) {
73         this.dataSource= Roo.factory(this.dataSource, Roo.data);
74     }
75     
76     
77     
78     if(this.width){
79         this.container.setWidth(this.width);
80     }
81
82     if(this.height){
83         this.container.setHeight(this.height);
84     }
85     /** @private */
86         this.addEvents({
87             // raw events
88             /**
89              * @event click
90              * The raw click event for the entire grid.
91              * @param {Roo.EventObject} e
92              */
93             "click" : true,
94             /**
95              * @event dblclick
96              * The raw dblclick event for the entire grid.
97              * @param {Roo.EventObject} e
98              */
99             "dblclick" : true,
100             /**
101              * @event contextmenu
102              * The raw contextmenu event for the entire grid.
103              * @param {Roo.EventObject} e
104              */
105             "contextmenu" : true,
106             /**
107              * @event mousedown
108              * The raw mousedown event for the entire grid.
109              * @param {Roo.EventObject} e
110              */
111             "mousedown" : true,
112             /**
113              * @event mouseup
114              * The raw mouseup event for the entire grid.
115              * @param {Roo.EventObject} e
116              */
117             "mouseup" : true,
118             /**
119              * @event mouseover
120              * The raw mouseover event for the entire grid.
121              * @param {Roo.EventObject} e
122              */
123             "mouseover" : true,
124             /**
125              * @event mouseout
126              * The raw mouseout event for the entire grid.
127              * @param {Roo.EventObject} e
128              */
129             "mouseout" : true,
130             /**
131              * @event keypress
132              * The raw keypress event for the entire grid.
133              * @param {Roo.EventObject} e
134              */
135             "keypress" : true,
136             /**
137              * @event keydown
138              * The raw keydown event for the entire grid.
139              * @param {Roo.EventObject} e
140              */
141             "keydown" : true,
142
143             // custom events
144
145             /**
146              * @event cellclick
147              * Fires when a cell is clicked
148              * @param {Grid} this
149              * @param {Number} rowIndex
150              * @param {Number} columnIndex
151              * @param {Roo.EventObject} e
152              */
153             "cellclick" : true,
154             /**
155              * @event celldblclick
156              * Fires when a cell is double clicked
157              * @param {Grid} this
158              * @param {Number} rowIndex
159              * @param {Number} columnIndex
160              * @param {Roo.EventObject} e
161              */
162             "celldblclick" : true,
163             /**
164              * @event rowclick
165              * Fires when a row is clicked
166              * @param {Grid} this
167              * @param {Number} rowIndex
168              * @param {Roo.EventObject} e
169              */
170             "rowclick" : true,
171             /**
172              * @event rowdblclick
173              * Fires when a row is double clicked
174              * @param {Grid} this
175              * @param {Number} rowIndex
176              * @param {Roo.EventObject} e
177              */
178             "rowdblclick" : true,
179             /**
180              * @event headerclick
181              * Fires when a header is clicked
182              * @param {Grid} this
183              * @param {Number} columnIndex
184              * @param {Roo.EventObject} e
185              */
186             "headerclick" : true,
187             /**
188              * @event headerdblclick
189              * Fires when a header cell is double clicked
190              * @param {Grid} this
191              * @param {Number} columnIndex
192              * @param {Roo.EventObject} e
193              */
194             "headerdblclick" : true,
195             /**
196              * @event rowcontextmenu
197              * Fires when a row is right clicked
198              * @param {Grid} this
199              * @param {Number} rowIndex
200              * @param {Roo.EventObject} e
201              */
202             "rowcontextmenu" : true,
203             /**
204          * @event cellcontextmenu
205          * Fires when a cell is right clicked
206          * @param {Grid} this
207          * @param {Number} rowIndex
208          * @param {Number} cellIndex
209          * @param {Roo.EventObject} e
210          */
211          "cellcontextmenu" : true,
212             /**
213              * @event headercontextmenu
214              * Fires when a header is right clicked
215              * @param {Grid} this
216              * @param {Number} columnIndex
217              * @param {Roo.EventObject} e
218              */
219             "headercontextmenu" : true,
220             /**
221              * @event bodyscroll
222              * Fires when the body element is scrolled
223              * @param {Number} scrollLeft
224              * @param {Number} scrollTop
225              */
226             "bodyscroll" : true,
227             /**
228              * @event columnresize
229              * Fires when the user resizes a column
230              * @param {Number} columnIndex
231              * @param {Number} newSize
232              */
233             "columnresize" : true,
234             /**
235              * @event columnmove
236              * Fires when the user moves a column
237              * @param {Number} oldIndex
238              * @param {Number} newIndex
239              */
240             "columnmove" : true,
241             /**
242              * @event startdrag
243              * Fires when row(s) start being dragged
244              * @param {Grid} this
245              * @param {Roo.GridDD} dd The drag drop object
246              * @param {event} e The raw browser event
247              */
248             "startdrag" : true,
249             /**
250              * @event enddrag
251              * Fires when a drag operation is complete
252              * @param {Grid} this
253              * @param {Roo.GridDD} dd The drag drop object
254              * @param {event} e The raw browser event
255              */
256             "enddrag" : true,
257             /**
258              * @event dragdrop
259              * Fires when dragged row(s) are dropped on a valid DD target
260              * @param {Grid} this
261              * @param {Roo.GridDD} dd The drag drop object
262              * @param {String} targetId The target drag drop object
263              * @param {event} e The raw browser event
264              */
265             "dragdrop" : true,
266             /**
267              * @event dragover
268              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
269              * @param {Grid} this
270              * @param {Roo.GridDD} dd The drag drop object
271              * @param {String} targetId The target drag drop object
272              * @param {event} e The raw browser event
273              */
274             "dragover" : true,
275             /**
276              * @event dragenter
277              *  Fires when the dragged row(s) first cross another DD target while being dragged
278              * @param {Grid} this
279              * @param {Roo.GridDD} dd The drag drop object
280              * @param {String} targetId The target drag drop object
281              * @param {event} e The raw browser event
282              */
283             "dragenter" : true,
284             /**
285              * @event dragout
286              * Fires when the dragged row(s) leave another DD target while being dragged
287              * @param {Grid} this
288              * @param {Roo.GridDD} dd The drag drop object
289              * @param {String} targetId The target drag drop object
290              * @param {event} e The raw browser event
291              */
292             "dragout" : true,
293         /**
294          * @event render
295          * Fires when the grid is rendered
296          * @param {Grid} grid
297          */
298         render : true
299     });
300
301     Roo.grid.Grid.superclass.constructor.call(this);
302 };
303 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
304     /**
305      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
306          */
307         minColumnWidth : 25,
308
309     /**
310          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
311          * <b>on initial render.</b> It is more efficient to explicitly size the columns
312          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
313          */
314         autoSizeColumns : false,
315
316         /**
317          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
318          */
319         autoSizeHeaders : true,
320
321         /**
322          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
323          */
324         monitorWindowResize : true,
325
326         /**
327          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
328          * rows measured to get a columns size. Default is 0 (all rows).
329          */
330         maxRowsToMeasure : 0,
331
332         /**
333          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
334          */
335         trackMouseOver : true,
336
337         /**
338          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
339          */
340         enableDragDrop : false,
341
342         /**
343          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
344          */
345         enableColumnMove : true,
346
347         /**
348          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
349          */
350         enableColumnHide : true,
351
352         /**
353          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
354          */
355         enableRowHeightSync : false,
356
357         /**
358          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
359          */
360         stripeRows : true,
361
362         /**
363          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
364          */
365         autoHeight : false,
366
367     /**
368      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
369      */
370     autoExpandColumn : false,
371
372     /**
373     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
374     * Default is 50.
375     */
376     autoExpandMin : 50,
377
378     /**
379     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
380     */
381     autoExpandMax : 1000,
382
383     /**
384          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
385          */
386         view : null,
387
388         /**
389      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
390          */
391         loadMask : false,
392
393     // private
394     rendered : false,
395
396     /**
397     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
398     * of a fixed width. Default is false.
399     */
400     /**
401     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
402     */
403     /**
404      * Called once after all setup has been completed and the grid is ready to be rendered.
405      * @return {Roo.grid.Grid} this
406      */
407     render : function(){
408         var c = this.container;
409         // try to detect autoHeight/width mode
410         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
411             this.autoHeight = true;
412         }
413         var view = this.getView();
414         view.init(this);
415
416         c.on("click", this.onClick, this);
417         c.on("dblclick", this.onDblClick, this);
418         c.on("contextmenu", this.onContextMenu, this);
419         c.on("keydown", this.onKeyDown, this);
420
421         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
422
423         this.getSelectionModel().init(this);
424
425         view.render();
426
427         if(this.loadMask){
428             this.loadMask = new Roo.LoadMask(this.container,
429                     Roo.apply({store:this.dataSource}, this.loadMask));
430         }
431         
432         
433         if (this.toolbar && this.toolbar.xtype) {
434             this.toolbar.container = this.getView().getHeaderPanel(true);
435             this.toolbar = new Ext.Toolbar(this.toolbar);
436         }
437         if (this.footer && this.footer.xtype) {
438             this.footer.dataSource = this.getDataSource();
439             this.footer.container = this.getView().getFooterPanel(true);
440             this.footer = Roo.factory(this.footer, Roo);
441         }
442         this.rendered = true;
443         this.fireEvent('render', this);
444         return this;
445     },
446
447         /**
448          * Reconfigures the grid to use a different Store and Column Model.
449          * The View will be bound to the new objects and refreshed.
450          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
451          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
452          */
453     reconfigure : function(dataSource, colModel){
454         if(this.loadMask){
455             this.loadMask.destroy();
456             this.loadMask = new Roo.LoadMask(this.container,
457                     Roo.apply({store:dataSource}, this.loadMask));
458         }
459         this.view.bind(dataSource, colModel);
460         this.dataSource = dataSource;
461         this.colModel = colModel;
462         this.view.refresh(true);
463     },
464
465     // private
466     onKeyDown : function(e){
467         this.fireEvent("keydown", e);
468     },
469
470     /**
471      * Destroy this grid.
472      * @param {Boolean} removeEl True to remove the element
473      */
474     destroy : function(removeEl, keepListeners){
475         if(this.loadMask){
476             this.loadMask.destroy();
477         }
478         var c = this.container;
479         c.removeAllListeners();
480         this.view.destroy();
481         this.colModel.purgeListeners();
482         if(!keepListeners){
483             this.purgeListeners();
484         }
485         c.update("");
486         if(removeEl === true){
487             c.remove();
488         }
489     },
490
491     // private
492     processEvent : function(name, e){
493         this.fireEvent(name, e);
494         var t = e.getTarget();
495         var v = this.view;
496         var header = v.findHeaderIndex(t);
497         if(header !== false){
498             this.fireEvent("header" + name, this, header, e);
499         }else{
500             var row = v.findRowIndex(t);
501             var cell = v.findCellIndex(t);
502             if(row !== false){
503                 this.fireEvent("row" + name, this, row, e);
504                 if(cell !== false){
505                     this.fireEvent("cell" + name, this, row, cell, e);
506                 }
507             }
508         }
509     },
510
511     // private
512     onClick : function(e){
513         this.processEvent("click", e);
514     },
515
516     // private
517     onContextMenu : function(e, t){
518         this.processEvent("contextmenu", e);
519     },
520
521     // private
522     onDblClick : function(e){
523         this.processEvent("dblclick", e);
524     },
525
526     // private
527     walkCells : function(row, col, step, fn, scope){
528         var cm = this.colModel, clen = cm.getColumnCount();
529         var ds = this.dataSource, rlen = ds.getCount(), first = true;
530         if(step < 0){
531             if(col < 0){
532                 row--;
533                 first = false;
534             }
535             while(row >= 0){
536                 if(!first){
537                     col = clen-1;
538                 }
539                 first = false;
540                 while(col >= 0){
541                     if(fn.call(scope || this, row, col, cm) === true){
542                         return [row, col];
543                     }
544                     col--;
545                 }
546                 row--;
547             }
548         } else {
549             if(col >= clen){
550                 row++;
551                 first = false;
552             }
553             while(row < rlen){
554                 if(!first){
555                     col = 0;
556                 }
557                 first = false;
558                 while(col < clen){
559                     if(fn.call(scope || this, row, col, cm) === true){
560                         return [row, col];
561                     }
562                     col++;
563                 }
564                 row++;
565             }
566         }
567         return null;
568     },
569
570     // private
571     getSelections : function(){
572         return this.selModel.getSelections();
573     },
574
575     /**
576      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
577      * but if manual update is required this method will initiate it.
578      */
579     autoSize : function(){
580         if(this.rendered){
581             this.view.layout();
582             if(this.view.adjustForScroll){
583                 this.view.adjustForScroll();
584             }
585         }
586     },
587
588     /**
589      * Returns the grid's underlying element.
590      * @return {Element} The element
591      */
592     getGridEl : function(){
593         return this.container;
594     },
595
596     // private for compatibility, overridden by editor grid
597     stopEditing : function(){},
598
599     /**
600      * Returns the grid's SelectionModel.
601      * @return {SelectionModel}
602      */
603     getSelectionModel : function(){
604         if(!this.selModel){
605             this.selModel = new Roo.grid.RowSelectionModel();
606         }
607         return this.selModel;
608     },
609
610     /**
611      * Returns the grid's DataSource.
612      * @return {DataSource}
613      */
614     getDataSource : function(){
615         return this.dataSource;
616     },
617
618     /**
619      * Returns the grid's ColumnModel.
620      * @return {ColumnModel}
621      */
622     getColumnModel : function(){
623         return this.colModel;
624     },
625
626     /**
627      * Returns the grid's GridView object.
628      * @return {GridView}
629      */
630     getView : function(){
631         if(!this.view){
632             this.view = new Roo.grid.GridView(this.viewConfig);
633         }
634         return this.view;
635     },
636     /**
637      * Called to get grid's drag proxy text, by default returns this.ddText.
638      * @return {String}
639      */
640     getDragDropText : function(){
641         var count = this.selModel.getCount();
642         return String.format(this.ddText, count, count == 1 ? '' : 's');
643     }
644 });
645 /**
646  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
647  * %0 is replaced with the number of selected rows.
648  * @type String
649  */
650 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";