allow string based values for comboboxarray
[roojs1] / Roo / BorderLayout.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.BorderLayout
13  * @extends Roo.LayoutManager
14  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
15  * please see: <br><br>
16  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
17  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
18  * Example:
19  <pre><code>
20  var layout = new Roo.BorderLayout(document.body, {
21     north: {
22         initialSize: 25,
23         titlebar: false
24     },
25     west: {
26         split:true,
27         initialSize: 200,
28         minSize: 175,
29         maxSize: 400,
30         titlebar: true,
31         collapsible: true
32     },
33     east: {
34         split:true,
35         initialSize: 202,
36         minSize: 175,
37         maxSize: 400,
38         titlebar: true,
39         collapsible: true
40     },
41     south: {
42         split:true,
43         initialSize: 100,
44         minSize: 100,
45         maxSize: 200,
46         titlebar: true,
47         collapsible: true
48     },
49     center: {
50         titlebar: true,
51         autoScroll:true,
52         resizeTabs: true,
53         minTabWidth: 50,
54         preferredTabWidth: 150
55     }
56 });
57
58 // shorthand
59 var CP = Roo.ContentPanel;
60
61 layout.beginUpdate();
62 layout.add("north", new CP("north", "North"));
63 layout.add("south", new CP("south", {title: "South", closable: true}));
64 layout.add("west", new CP("west", {title: "West"}));
65 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
66 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
67 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
68 layout.getRegion("center").showPanel("center1");
69 layout.endUpdate();
70 </code></pre>
71
72 <b>The container the layout is rendered into can be either the body element or any other element.
73 If it is not the body element, the container needs to either be an absolute positioned element,
74 or you will need to add "position:relative" to the css of the container.  You will also need to specify
75 the container size if it is not the body element.</b>
76
77 * @constructor
78 * Create a new BorderLayout
79 * @param {String/HTMLElement/Element} container The container this layout is bound to
80 * @param {Object} config Configuration options
81  */
82 Roo.BorderLayout = function(container, config){
83     config = config || {};
84     Roo.BorderLayout.superclass.constructor.call(this, container, config);
85     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
86     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
87         var target = this.factory.validRegions[i];
88         if(config[target]){
89             this.addRegion(target, config[target]);
90         }
91     }
92 };
93
94 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
95     /**
96      * Creates and adds a new region if it doesn't already exist.
97      * @param {String} target The target region key (north, south, east, west or center).
98      * @param {Object} config The regions config object
99      * @return {BorderLayoutRegion} The new region
100      */
101     addRegion : function(target, config){
102         if(!this.regions[target]){
103             var r = this.factory.create(target, this, config);
104             this.bindRegion(target, r);
105         }
106         return this.regions[target];
107     },
108
109     // private (kinda)
110     bindRegion : function(name, r){
111         this.regions[name] = r;
112         r.on("visibilitychange", this.layout, this);
113         r.on("paneladded", this.layout, this);
114         r.on("panelremoved", this.layout, this);
115         r.on("invalidated", this.layout, this);
116         r.on("resized", this.onRegionResized, this);
117         r.on("collapsed", this.onRegionCollapsed, this);
118         r.on("expanded", this.onRegionExpanded, this);
119     },
120
121     /**
122      * Performs a layout update.
123      */
124     layout : function(){
125         if(this.updating) {
126             return;
127         }
128         var size = this.getViewSize();
129         var w = size.width;
130         var h = size.height;
131         var centerW = w;
132         var centerH = h;
133         var centerY = 0;
134         var centerX = 0;
135         //var x = 0, y = 0;
136
137         var rs = this.regions;
138         var north = rs["north"];
139         var south = rs["south"]; 
140         var west = rs["west"];
141         var east = rs["east"];
142         var center = rs["center"];
143         //if(this.hideOnLayout){ // not supported anymore
144             //c.el.setStyle("display", "none");
145         //}
146         if(north && north.isVisible()){
147             var b = north.getBox();
148             var m = north.getMargins();
149             b.width = w - (m.left+m.right);
150             b.x = m.left;
151             b.y = m.top;
152             centerY = b.height + b.y + m.bottom;
153             centerH -= centerY;
154             north.updateBox(this.safeBox(b));
155         }
156         if(south && south.isVisible()){
157             var b = south.getBox();
158             var m = south.getMargins();
159             b.width = w - (m.left+m.right);
160             b.x = m.left;
161             var totalHeight = (b.height + m.top + m.bottom);
162             b.y = h - totalHeight + m.top;
163             centerH -= totalHeight;
164             south.updateBox(this.safeBox(b));
165         }
166         if(west && west.isVisible()){
167             var b = west.getBox();
168             var m = west.getMargins();
169             b.height = centerH - (m.top+m.bottom);
170             b.x = m.left;
171             b.y = centerY + m.top;
172             var totalWidth = (b.width + m.left + m.right);
173             centerX += totalWidth;
174             centerW -= totalWidth;
175             west.updateBox(this.safeBox(b));
176         }
177         if(east && east.isVisible()){
178             var b = east.getBox();
179             var m = east.getMargins();
180             b.height = centerH - (m.top+m.bottom);
181             var totalWidth = (b.width + m.left + m.right);
182             b.x = w - totalWidth + m.left;
183             b.y = centerY + m.top;
184             centerW -= totalWidth;
185             east.updateBox(this.safeBox(b));
186         }
187         if(center){
188             var m = center.getMargins();
189             var centerBox = {
190                 x: centerX + m.left,
191                 y: centerY + m.top,
192                 width: centerW - (m.left+m.right),
193                 height: centerH - (m.top+m.bottom)
194             };
195             //if(this.hideOnLayout){
196                 //center.el.setStyle("display", "block");
197             //}
198             center.updateBox(this.safeBox(centerBox));
199         }
200         this.el.repaint();
201         this.fireEvent("layout", this);
202     },
203
204     // private
205     safeBox : function(box){
206         box.width = Math.max(0, box.width);
207         box.height = Math.max(0, box.height);
208         return box;
209     },
210
211     /**
212      * Adds a ContentPanel (or subclass) to this layout.
213      * @param {String} target The target region key (north, south, east, west or center).
214      * @param {Roo.ContentPanel} panel The panel to add
215      * @return {Roo.ContentPanel} The added panel
216      */
217     add : function(target, panel){
218          
219         target = target.toLowerCase();
220         return this.regions[target].add(panel);
221     },
222
223     /**
224      * Remove a ContentPanel (or subclass) to this layout.
225      * @param {String} target The target region key (north, south, east, west or center).
226      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
227      * @return {Roo.ContentPanel} The removed panel
228      */
229     remove : function(target, panel){
230         target = target.toLowerCase();
231         return this.regions[target].remove(panel);
232     },
233
234     /**
235      * Searches all regions for a panel with the specified id
236      * @param {String} panelId
237      * @return {Roo.ContentPanel} The panel or null if it wasn't found
238      */
239     findPanel : function(panelId){
240         var rs = this.regions;
241         for(var target in rs){
242             if(typeof rs[target] != "function"){
243                 var p = rs[target].getPanel(panelId);
244                 if(p){
245                     return p;
246                 }
247             }
248         }
249         return null;
250     },
251
252     /**
253      * Searches all regions for a panel with the specified id and activates (shows) it.
254      * @param {String/ContentPanel} panelId The panels id or the panel itself
255      * @return {Roo.ContentPanel} The shown panel or null
256      */
257     showPanel : function(panelId) {
258       var rs = this.regions;
259       for(var target in rs){
260          var r = rs[target];
261          if(typeof r != "function"){
262             if(r.hasPanel(panelId)){
263                return r.showPanel(panelId);
264             }
265          }
266       }
267       return null;
268    },
269
270    /**
271      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
272      * @param {Roo.state.Provider} provider (optional) An alternate state provider
273      */
274     restoreState : function(provider){
275         if(!provider){
276             provider = Roo.state.Manager;
277         }
278         var sm = new Roo.LayoutStateManager();
279         sm.init(this, provider);
280     },
281
282     /**
283      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
284      * object should contain properties for each region to add ContentPanels to, and each property's value should be
285      * a valid ContentPanel config object.  Example:
286      * <pre><code>
287 // Create the main layout
288 var layout = new Roo.BorderLayout('main-ct', {
289     west: {
290         split:true,
291         minSize: 175,
292         titlebar: true
293     },
294     center: {
295         title:'Components'
296     }
297 }, 'main-ct');
298
299 // Create and add multiple ContentPanels at once via configs
300 layout.batchAdd({
301    west: {
302        id: 'source-files',
303        autoCreate:true,
304        title:'Ext Source Files',
305        autoScroll:true,
306        fitToFrame:true
307    },
308    center : {
309        el: cview,
310        autoScroll:true,
311        fitToFrame:true,
312        toolbar: tb,
313        resizeEl:'cbody'
314    }
315 });
316 </code></pre>
317      * @param {Object} regions An object containing ContentPanel configs by region name
318      */
319     batchAdd : function(regions){
320         this.beginUpdate();
321         for(var rname in regions){
322             var lr = this.regions[rname];
323             if(lr){
324                 this.addTypedPanels(lr, regions[rname]);
325             }
326         }
327         this.endUpdate();
328     },
329
330     // private
331     addTypedPanels : function(lr, ps){
332         if(typeof ps == 'string'){
333             lr.add(new Roo.ContentPanel(ps));
334         }
335         else if(ps instanceof Array){
336             for(var i =0, len = ps.length; i < len; i++){
337                 this.addTypedPanels(lr, ps[i]);
338             }
339         }
340         else if(!ps.events){ // raw config?
341             var el = ps.el;
342             delete ps.el; // prevent conflict
343             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
344         }
345         else {  // panel object assumed!
346             lr.add(ps);
347         }
348     },
349     /**
350      * Adds a xtype elements to the layout.
351      * <pre><code>
352
353 layout.addxtype({
354        xtype : 'ContentPanel',
355        region: 'west',
356        items: [ .... ]
357    }
358 );
359
360 layout.addxtype({
361         xtype : 'NestedLayoutPanel',
362         region: 'west',
363         layout: {
364            center: { },
365            west: { }   
366         },
367         items : [ ... list of content panels or nested layout panels.. ]
368    }
369 );
370 </code></pre>
371      * @param {Object} cfg Xtype definition of item to add.
372      */
373     addxtype : function(cfg)
374     {
375         // basically accepts a pannel...
376         // can accept a layout region..!?!?
377         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
378         
379         if (!cfg.xtype.match(/Panel$/)) {
380             return false;
381         }
382         var ret = false;
383         
384         if (typeof(cfg.region) == 'undefined') {
385             Roo.log("Failed to add Panel, region was not set");
386             Roo.log(cfg);
387             return false;
388         }
389         var region = cfg.region;
390         delete cfg.region;
391         
392           
393         var xitems = [];
394         if (cfg.items) {
395             xitems = cfg.items;
396             delete cfg.items;
397         }
398         var nb = false;
399         
400         switch(cfg.xtype) 
401         {
402             case 'ContentPanel':  // ContentPanel (el, cfg)
403             case 'ScrollPanel':  // ContentPanel (el, cfg)
404             case 'ViewPanel': 
405                 if(cfg.autoCreate) {
406                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
407                 } else {
408                     var el = this.el.createChild();
409                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
410                 }
411                 
412                 this.add(region, ret);
413                 break;
414             
415             
416             case 'TreePanel': // our new panel!
417                 cfg.el = this.el.createChild();
418                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
419                 this.add(region, ret);
420                 break;
421             
422             case 'NestedLayoutPanel': 
423                 // create a new Layout (which is  a Border Layout...
424                 var el = this.el.createChild();
425                 var clayout = cfg.layout;
426                 delete cfg.layout;
427                 clayout.items   = clayout.items  || [];
428                 // replace this exitems with the clayout ones..
429                 xitems = clayout.items;
430                  
431                 
432                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
433                     cfg.background = false;
434                 }
435                 var layout = new Roo.BorderLayout(el, clayout);
436                 
437                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
438                 //console.log('adding nested layout panel '  + cfg.toSource());
439                 this.add(region, ret);
440                 nb = {}; /// find first...
441                 break;
442                 
443             case 'GridPanel': 
444             
445                 // needs grid and region
446                 
447                 //var el = this.getRegion(region).el.createChild();
448                 var el = this.el.createChild();
449                 // create the grid first...
450                 
451                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
452                 delete cfg.grid;
453                 if (region == 'center' && this.active ) {
454                     cfg.background = false;
455                 }
456                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
457                 
458                 this.add(region, ret);
459                 if (cfg.background) {
460                     ret.on('activate', function(gp) {
461                         if (!gp.grid.rendered) {
462                             gp.grid.render();
463                         }
464                     });
465                 } else {
466                     grid.render();
467                 }
468                 break;
469            
470            
471            
472                 
473                 
474                 
475             default:
476                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
477                     
478                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
479                     this.add(region, ret);
480                 } else {
481                 
482                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
483                     return null;
484                 }
485                 
486              // GridPanel (grid, cfg)
487             
488         }
489         this.beginUpdate();
490         // add children..
491         var region = '';
492         var abn = {};
493         Roo.each(xitems, function(i)  {
494             region = nb && i.region ? i.region : false;
495             
496             var add = ret.addxtype(i);
497            
498             if (region) {
499                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
500                 if (!i.background) {
501                     abn[region] = nb[region] ;
502                 }
503             }
504             
505         });
506         this.endUpdate();
507
508         // make the last non-background panel active..
509         //if (nb) { Roo.log(abn); }
510         if (nb) {
511             
512             for(var r in abn) {
513                 region = this.getRegion(r);
514                 if (region) {
515                     // tried using nb[r], but it does not work..
516                      
517                     region.showPanel(abn[r]);
518                    
519                 }
520             }
521         }
522         return ret;
523         
524     }
525 });
526
527 /**
528  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
529  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
530  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
531  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
532  * <pre><code>
533 // shorthand
534 var CP = Roo.ContentPanel;
535
536 var layout = Roo.BorderLayout.create({
537     north: {
538         initialSize: 25,
539         titlebar: false,
540         panels: [new CP("north", "North")]
541     },
542     west: {
543         split:true,
544         initialSize: 200,
545         minSize: 175,
546         maxSize: 400,
547         titlebar: true,
548         collapsible: true,
549         panels: [new CP("west", {title: "West"})]
550     },
551     east: {
552         split:true,
553         initialSize: 202,
554         minSize: 175,
555         maxSize: 400,
556         titlebar: true,
557         collapsible: true,
558         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
559     },
560     south: {
561         split:true,
562         initialSize: 100,
563         minSize: 100,
564         maxSize: 200,
565         titlebar: true,
566         collapsible: true,
567         panels: [new CP("south", {title: "South", closable: true})]
568     },
569     center: {
570         titlebar: true,
571         autoScroll:true,
572         resizeTabs: true,
573         minTabWidth: 50,
574         preferredTabWidth: 150,
575         panels: [
576             new CP("center1", {title: "Close Me", closable: true}),
577             new CP("center2", {title: "Center Panel", closable: false})
578         ]
579     }
580 }, document.body);
581
582 layout.getRegion("center").showPanel("center1");
583 </code></pre>
584  * @param config
585  * @param targetEl
586  */
587 Roo.BorderLayout.create = function(config, targetEl){
588     var layout = new Roo.BorderLayout(targetEl || document.body, config);
589     layout.beginUpdate();
590     var regions = Roo.BorderLayout.RegionFactory.validRegions;
591     for(var j = 0, jlen = regions.length; j < jlen; j++){
592         var lr = regions[j];
593         if(layout.regions[lr] && config[lr].panels){
594             var r = layout.regions[lr];
595             var ps = config[lr].panels;
596             layout.addTypedPanels(r, ps);
597         }
598     }
599     layout.endUpdate();
600     return layout;
601 };
602
603 // private
604 Roo.BorderLayout.RegionFactory = {
605     // private
606     validRegions : ["north","south","east","west","center"],
607
608     // private
609     create : function(target, mgr, config){
610         target = target.toLowerCase();
611         if(config.lightweight || config.basic){
612             return new Roo.BasicLayoutRegion(mgr, config, target);
613         }
614         switch(target){
615             case "north":
616                 return new Roo.NorthLayoutRegion(mgr, config);
617             case "south":
618                 return new Roo.SouthLayoutRegion(mgr, config);
619             case "east":
620                 return new Roo.EastLayoutRegion(mgr, config);
621             case "west":
622                 return new Roo.WestLayoutRegion(mgr, config);
623             case "center":
624                 return new Roo.CenterLayoutRegion(mgr, config);
625         }
626         throw 'Layout region "'+target+'" not supported.';
627     }
628 };