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