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