Merge branch 'master' of http://git.roojs.com/roojs1
[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     
30     
31     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32         if(config[region]){
33             config[region].region = region;
34             this.addRegion(config[region]);
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     {
76         if(this.updating) {
77             return;
78         }
79         var size = this.getViewSize();
80         var w = size.width;
81         var h = size.height;
82         var centerW = w;
83         var centerH = h;
84         var centerY = 0;
85         var centerX = 0;
86         //var x = 0, y = 0;
87
88         var rs = this.regions;
89         var north = rs["north"];
90         var south = rs["south"]; 
91         var west = rs["west"];
92         var east = rs["east"];
93         var center = rs["center"];
94         //if(this.hideOnLayout){ // not supported anymore
95             //c.el.setStyle("display", "none");
96         //}
97         if(north && north.isVisible()){
98             var b = north.getBox();
99             var m = north.getMargins();
100             b.width = w - (m.left+m.right);
101             b.x = m.left;
102             b.y = m.top;
103             centerY = b.height + b.y + m.bottom;
104             centerH -= centerY;
105             north.updateBox(this.safeBox(b));
106         }
107         if(south && south.isVisible()){
108             var b = south.getBox();
109             var m = south.getMargins();
110             b.width = w - (m.left+m.right);
111             b.x = m.left;
112             var totalHeight = (b.height + m.top + m.bottom);
113             b.y = h - totalHeight + m.top;
114             centerH -= totalHeight;
115             south.updateBox(this.safeBox(b));
116         }
117         if(west && west.isVisible()){
118             var b = west.getBox();
119             var m = west.getMargins();
120             b.height = centerH - (m.top+m.bottom);
121             b.x = m.left;
122             b.y = centerY + m.top;
123             var totalWidth = (b.width + m.left + m.right);
124             centerX += totalWidth;
125             centerW -= totalWidth;
126             west.updateBox(this.safeBox(b));
127         }
128         if(east && east.isVisible()){
129             var b = east.getBox();
130             var m = east.getMargins();
131             b.height = centerH - (m.top+m.bottom);
132             var totalWidth = (b.width + m.left + m.right);
133             b.x = w - totalWidth + m.left;
134             b.y = centerY + m.top;
135             centerW -= totalWidth;
136             east.updateBox(this.safeBox(b));
137         }
138         if(center){
139             var m = center.getMargins();
140             var centerBox = {
141                 x: centerX + m.left,
142                 y: centerY + m.top,
143                 width: centerW - (m.left+m.right),
144                 height: centerH - (m.top+m.bottom)
145             };
146             //if(this.hideOnLayout){
147                 //center.el.setStyle("display", "block");
148             //}
149             center.updateBox(this.safeBox(centerBox));
150         }
151         this.el.repaint();
152         this.fireEvent("layout", this);
153     },
154
155     // private
156     safeBox : function(box){
157         box.width = Math.max(0, box.width);
158         box.height = Math.max(0, box.height);
159         return box;
160     },
161
162     /**
163      * Adds a ContentPanel (or subclass) to this layout.
164      * @param {String} target The target region key (north, south, east, west or center).
165      * @param {Roo.ContentPanel} panel The panel to add
166      * @return {Roo.ContentPanel} The added panel
167      */
168     add : function(target, panel){
169          
170         target = target.toLowerCase();
171         return this.regions[target].add(panel);
172     },
173
174     /**
175      * Remove a ContentPanel (or subclass) to this layout.
176      * @param {String} target The target region key (north, south, east, west or center).
177      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
178      * @return {Roo.ContentPanel} The removed panel
179      */
180     remove : function(target, panel){
181         target = target.toLowerCase();
182         return this.regions[target].remove(panel);
183     },
184
185     /**
186      * Searches all regions for a panel with the specified id
187      * @param {String} panelId
188      * @return {Roo.ContentPanel} The panel or null if it wasn't found
189      */
190     findPanel : function(panelId){
191         var rs = this.regions;
192         for(var target in rs){
193             if(typeof rs[target] != "function"){
194                 var p = rs[target].getPanel(panelId);
195                 if(p){
196                     return p;
197                 }
198             }
199         }
200         return null;
201     },
202
203     /**
204      * Searches all regions for a panel with the specified id and activates (shows) it.
205      * @param {String/ContentPanel} panelId The panels id or the panel itself
206      * @return {Roo.ContentPanel} The shown panel or null
207      */
208     showPanel : function(panelId) {
209       var rs = this.regions;
210       for(var target in rs){
211          var r = rs[target];
212          if(typeof r != "function"){
213             if(r.hasPanel(panelId)){
214                return r.showPanel(panelId);
215             }
216          }
217       }
218       return null;
219    },
220
221    /**
222      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
223      * @param {Roo.state.Provider} provider (optional) An alternate state provider
224      */
225    /*
226     restoreState : function(provider){
227         if(!provider){
228             provider = Roo.state.Manager;
229         }
230         var sm = new Roo.LayoutStateManager();
231         sm.init(this, provider);
232     },
233 */
234  
235  
236     /**
237      * Adds a xtype elements to the layout.
238      * <pre><code>
239
240 layout.addxtype({
241        xtype : 'ContentPanel',
242        region: 'west',
243        items: [ .... ]
244    }
245 );
246
247 layout.addxtype({
248         xtype : 'NestedLayoutPanel',
249         region: 'west',
250         layout: {
251            center: { },
252            west: { }   
253         },
254         items : [ ... list of content panels or nested layout panels.. ]
255    }
256 );
257 </code></pre>
258      * @param {Object} cfg Xtype definition of item to add.
259      */
260     addxtype : function(cfg)
261     {
262         // basically accepts a pannel...
263         // can accept a layout region..!?!?
264         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
265         
266         
267         // theory?  children can only be panels??
268         
269         //if (!cfg.xtype.match(/Panel$/)) {
270         //    return false;
271         //}
272         var ret = false;
273         
274         if (typeof(cfg.region) == 'undefined') {
275             Roo.log("Failed to add Panel, region was not set");
276             Roo.log(cfg);
277             return false;
278         }
279         var region = cfg.region;
280         delete cfg.region;
281         
282           
283         var xitems = [];
284         if (cfg.items) {
285             xitems = cfg.items;
286             delete cfg.items;
287         }
288         var nb = false;
289         
290         switch(cfg.xtype) 
291         {
292             case 'Content':  // ContentPanel (el, cfg)
293             case 'Scroll':  // ContentPanel (el, cfg)
294             case 'View': 
295                 cfg.autoCreate = true;
296                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
297                 //} else {
298                 //    var el = this.el.createChild();
299                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
300                 //}
301                 
302                 this.add(region, ret);
303                 break;
304             
305             /*
306             case 'TreePanel': // our new panel!
307                 cfg.el = this.el.createChild();
308                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
309                 this.add(region, ret);
310                 break;
311             */
312             
313             case 'Nest': 
314                 // create a new Layout (which is  a Border Layout...
315                 
316                 var clayout = cfg.layout;
317                 clayout.el  = this.el.createChild();
318                 clayout.items   = clayout.items  || [];
319                 
320                 delete cfg.layout;
321                 
322                 // replace this exitems with the clayout ones..
323                 xitems = clayout.items;
324                  
325                 // force background off if it's in center...
326                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
327                     cfg.background = false;
328                 }
329                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
330                 
331                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
332                 //console.log('adding nested layout panel '  + cfg.toSource());
333                 this.add(region, ret);
334                 nb = {}; /// find first...
335                 break;
336                 /*
337             case 'GridPanel': 
338             
339                 // needs grid and region
340                 
341                 //var el = this.getRegion(region).el.createChild();
342                 var el = this.el.createChild();
343                 // create the grid first...
344                 
345                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
346                 delete cfg.grid;
347                 if (region == 'center' && this.active ) {
348                     cfg.background = false;
349                 }
350                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
351                 
352                 this.add(region, ret);
353                 if (cfg.background) {
354                     ret.on('activate', function(gp) {
355                         if (!gp.grid.rendered) {
356                             gp.grid.render();
357                         }
358                     });
359                 } else {
360                     grid.render();
361                 }
362                 break;
363            
364            */
365            
366                 
367                 
368                 
369             default:
370                 /*
371                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
372                     
373                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
374                     this.add(region, ret);
375                 } else {
376                 */
377                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
378                     return null;
379              
380                                 
381              // GridPanel (grid, cfg)
382             
383         }
384         this.beginUpdate();
385         // add children..
386         var region = '';
387         var abn = {};
388         Roo.each(xitems, function(i)  {
389             region = nb && i.region ? i.region : false;
390             
391             var add = ret.addxtype(i);
392            
393             if (region) {
394                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
395                 if (!i.background) {
396                     abn[region] = nb[region] ;
397                 }
398             }
399             
400         });
401         this.endUpdate();
402
403         // make the last non-background panel active..
404         //if (nb) { Roo.log(abn); }
405         if (nb) {
406             
407             for(var r in abn) {
408                 region = this.getRegion(r);
409                 if (region) {
410                     // tried using nb[r], but it does not work..
411                      
412                     region.showPanel(abn[r]);
413                    
414                 }
415             }
416         }
417         return ret;
418         
419     },
420     
421     
422 // private
423     factory : function(cfg)
424     {
425         
426         var validRegions = Roo.bootstrap.layout.Border.regions;
427
428         var target = cfg.region;
429         cfg.manager = this;
430         
431         var r = Roo.bootstrap.layout;
432         
433         switch(target){
434             case "north":
435                 return new r.North(cfg);
436             case "south":
437                 return new r.South(cfg);
438             case "east":
439                 return new r.East(cfg);
440             case "west":
441                 return new r.West(cfg);
442             case "center":
443                 return new r.Center(cfg);
444         }
445         throw 'Layout region "'+target+'" not supported.';
446     }
447     
448     
449 });
450