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