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