Ticket #22: treenodes.3.patch

File treenodes.3.patch, 42.7 kB (added by dwins, 1 year ago)
  • tests/lib/GeoExt/widgets/MapPanel.html

    old new  
    3535        } 
    3636 
    3737        function test_mappanel(t) { 
    38             t.plan(2
     38            t.plan(3
    3939             
    4040            loadMapPanel(); 
    4141            t.eq(mapPanel.map.getCenter().toString(), "lon=5,lat=45", "Map center set correctly"); 
    4242            t.eq(mapPanel.map.getZoom(), 4, "Zoom set correctly"); 
     43            t.eq(GeoExt.MapPanel.guess().id, mapPanel.id, "MapPanel guessed correctly"); 
    4344        } 
    4445         
    4546        function test_allOverlays(t) { 
  • lib/GeoExt/widgets/tree/LayerParamsNode.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.LayerParamsNode 
     8 *  
     9 * A subclass of GeoExt.tree.TristateCheckboxNode that provides a filter for a 
     10 * parent node (usually {<GeoExt.tree.LayerParamsNode>} or 
     11 * {<GeoExt.tree.FilteredLayerNode>}). 
     12 *  
     13 * The default node class (cls property) for this node type is 
     14 * "cql-filter-node", except for nodes that have no filter property set. 
     15 * Those will have the "cql-filter-group-node"  class. LayerParamsNodes without 
     16 * a filter property set are used for filter grouping. 
     17 *  
     18 * To use this node type in a JSON config, set nodeType to "olCqlFilter". 
     19 *  
     20 * Inherits from: 
     21 * - <GeoExt.tree.TristateCheckboxNode> 
     22 */ 
     23GeoExt.tree.LayerParamsNode = Ext.extend(GeoExt.tree.TristateCheckboxNode, { 
     24     
     25    /** 
     26     * ConfigProperty: params 
     27     * {Object} an object of layer params to apply to a parent layer if this node 
     28     *     is checked. 
     29     */ 
     30    params: null, 
     31     
     32    /** 
     33     * Constructor: GeoExt.tree.LayerParamsNode 
     34     *  
     35     * Parameters: 
     36     * config - {Object} 
     37     */ 
     38    constructor: function(config) { 
     39        this.params = config.params; 
     40         
     41        this.defaultUI = this.defaultUI || GeoExt.tree.TristateCheckboxNodeUI; 
     42         
     43        config.cls = config.cls || "layerparams-node"; 
     44        config.leaf = config.leaf || !config.children; 
     45        GeoExt.tree.LayerParamsNode.superclass.constructor.apply(this, arguments); 
     46    }, 
     47 
     48    /** 
     49     * Method: updateParams 
     50     *  
     51     * Returns: 
     52     * {Object} 
     53     */ 
     54    updateParams: function() { 
     55        var params = this.attributes.checked ? Ext.applyIf({}, this.params) : {}; 
     56        var node, childParams; 
     57        for(var i=0; i<this.childNodes.length; ++i) { 
     58            node = this.childNodes[i]; 
     59            childParams = node.updateParams(); 
     60            for(var p in childParams) { 
     61                params[p] = (params[p] || []).concat(childParams[p]); 
     62            } 
     63        } 
     64        return params; 
     65    }     
     66}); 
     67 
     68GeoExt.tree.LayerParamsNode.add = function(parent) { 
     69    if(!parent.loaded) { 
     70        var subLayers = []; 
     71        var layer = parent.layer; 
     72        var parentAttrs = parent.attributes; 
     73        if(layer.params[parent.attributes.param] instanceof Array) { 
     74            subLayers = layer.params[parentAttrs.param]; 
     75            parent.loaded = true; 
     76            parent.leaf = false; 
     77        } 
     78        for(var i=0; i<subLayers.length; i++) { 
     79            subLayer = subLayers[i]; 
     80            var params = {}; 
     81            params[parentAttrs.param] = subLayer; 
     82            parent.appendChild( 
     83                new GeoExt.tree.LayerParamsNode({ 
     84                    params: params, 
     85                    text: parentAttrs.texts ? parentAttrs.texts[i] : subLayer, 
     86                    checked: true 
     87                }) 
     88            ); 
     89        } 
     90        parent.on("childcheckchange", function() { 
     91            var params = GeoExt.tree.LayerParamsNode.prototype.updateParams.call(parent) || {}; 
     92            var delimiter = parent.attributes.paramsDelimiter; 
     93            var join; 
     94            for(var p in params) { 
     95                join = delimiter && delimiter[p]; 
     96                params[p] = join ? params[p].join(join) : params[p]; 
     97            } 
     98             
     99            parent.layer.mergeNewParams(params); 
     100             
     101            return params; 
     102        }, parent); 
     103    } 
     104}; 
     105 
     106/** 
     107 * NodeType: gx_layerparams 
     108 */ 
     109Ext.tree.TreePanel.nodeTypes.gx_layerparams = GeoExt.tree.LayerParamsNode; 
  • lib/GeoExt/widgets/tree/BaseLayerContainer.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.BaseLayerContainer 
     8 *  
     9 * A layer container that will collect all base layers of an OpenLayers map. 
     10 * Only layers that have displayInLayerSwitcher set to true will be included. 
     11 *  
     12 * To use this node type in JSON config, set nodeType to 
     13 * "olBaseLayerContainer". 
     14 *  
     15 * Inherits from: 
     16 * - <GeoExt.tree.LayerContainer> 
     17 */ 
     18GeoExt.tree.BaseLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, { 
     19 
     20    /** 
     21     * Constructor: GeoExt.tree.BaseLayerContainer 
     22     *  
     23     * Parameters: 
     24     * config - {Object} 
     25     */ 
     26    constructor: function(config) { 
     27        config.text = config.text || "Base Layer"; 
     28        GeoExt.tree.BaseLayerContainer.superclass.constructor.apply(this, arguments); 
     29    }, 
     30 
     31    /** 
     32     * Method: addLayerNode 
     33     * Adds a child node representing a base layer of the map 
     34     *  
     35     * Parameters: 
     36     * layerRecord - {Ext.data.Record} the layer record to add a node for 
     37     */ 
     38    addLayerNode: function(layerRecord) { 
     39        var layer = layerRecord.get("layer"); 
     40        if (layer.isBaseLayer == true) { 
     41            GeoExt.tree.BaseLayerContainer.superclass.addLayerNode.call(this, 
     42                layerRecord); 
     43        } 
     44    }, 
     45     
     46    /** 
     47     * Method: removeLayerNode 
     48     * Removes a child node representing a base layer of the map 
     49     *  
     50     * Parameters: 
     51     * layerRecord - {Ext.data.Record} the layer record to remove the node for 
     52     */ 
     53    removeLayerNode: function(layer) { 
     54        var layer = layerRecord.get("layer"); 
     55        if (layer.isBaseLayer == true) { 
     56            GeoExt.tree.BaseLayerContainer.superclass.removeLayerNode.call(this, 
     57                layerRecord); 
     58        } 
     59    } 
     60}); 
     61 
     62/** 
     63 * NodeType: gx_baselayercontainer 
     64 */ 
     65Ext.tree.TreePanel.nodeTypes.gx_baselayercontainer = GeoExt.tree.BaseLayerContainer; 
  • lib/GeoExt/widgets/tree/TristateCheckboxNode.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.TristateCheckboxNodeUI 
     8 *  
     9 * Inherits from: 
     10 * - <Ext.tree.TreeNodeUI> 
     11 */ 
     12GeoExt.tree.TristateCheckboxNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { 
     13     
     14    /** 
     15     * Constructor: GeoExt.tree.TristateCheckbosNodeUI 
     16     *  
     17     * Parameters: 
     18     * config - {Object} 
     19     */ 
     20    constructor: function(config) { 
     21        GeoExt.tree.TristateCheckboxNodeUI.superclass.constructor.apply(this, arguments); 
     22    }, 
     23     
     24    /** 
     25     * Method: toggleCheck 
     26     *  
     27     * Parameters: 
     28     * value - {Boolean} checked status 
     29     * thirdState - {Boolean} 
     30     * options - {Object} Hash of options for this method 
     31     *  
     32     * Currently supported options: 
     33     * silent - {Boolean} set to true if no checkchange event should be 
     34     *     fired 
     35     */ 
     36    toggleCheck: function(value, thirdState, options) { 
     37        options = options || {} 
     38        var cb = this.checkbox; 
     39        if(thirdState == true) { 
     40            if(cb) { 
     41                Ext.get(cb).setOpacity(0.5); 
     42            } 
     43            this.node.attributes.thirdState = true; 
     44        } else { 
     45            if(cb) { 
     46                Ext.get(cb).clearOpacity(); 
     47            } 
     48            delete this.node.attributes.thirdState; 
     49        } 
     50 
     51        options.silent && this.node.suspendEvents(); 
     52        GeoExt.tree.TristateCheckboxNodeUI.superclass.toggleCheck.call(this, 
     53            value); 
     54        options.silent && this.node.resumeEvents(); 
     55    } 
     56}); 
     57 
     58/** 
     59 * Class: GeoExt.tree.TristateCheckboxNode 
     60 *  
     61 * Provides a tree node that will have a third state (stored in 
     62 * attributes.thirdState) to have a proper semi-checked state for 
     63 * nodes with only *some* children checked. attributes.thirdState will be 
     64 * undefined if the node is checked or unchecked, and true if the node is 
     65 * semi-checked. 
     66 *  
     67 * This node has also a childcheckchange event that will be triggered with a 
     68 * child node and it's checked state to notify listeners when a the checked 
     69 * state of a child's node has changed. 
     70 *  
     71 * Applications using this class should not rely on the checkchange event to 
     72 * determine the checked state of non-leaf nodes. Instead, applications should 
     73 * also listen to the childcheckchange event and read out attributes.checked 
     74 * and attributes.thirdState to get the node's checked state. 
     75 *  
     76 * To use this node type in a JSON config, set nodeType to "tristateCheckbox". 
     77 *  
     78 * Inherits from: 
     79 * - <Ext.tree.AsyncTreeNode> 
     80 */ 
     81GeoExt.tree.TristateCheckboxNode = Ext.extend(Ext.tree.AsyncTreeNode, { 
     82     
     83    /** 
     84     * Property: checkedChildNodes 
     85     * {Object} Hash of 0.1 for thirdState nodes and 1 for fully checked 
     86     *     nodes, keyed by node ids. In combination with <checkedCount>, 
     87     *     this provides an efficient way of keeping track of the childnodes' 
     88     *     checked state. 
     89     */ 
     90    checkedChildNodes: null, 
     91     
     92    /** 
     93     * Property: checkedCount 
     94     * {Number} A cache for the sum of checkedChildNodes' values. 
     95     */ 
     96    checkedCount: null, 
     97     
     98    /** 
     99     * Constructor: GeoExt.tree.TristateCheckboxNode 
     100     *  
     101     * Parameters: 
     102     * config - {Object} 
     103     */ 
     104    constructor: function(config) { 
     105        this.checkedChildNodes = {}; 
     106        this.checkedCount = 0; 
     107         
     108        this.defaultUI = this.defaultUI || GeoExt.tree.TristateCheckboxNodeUI; 
     109        this.addEvents.apply(this, GeoExt.tree.TristateCheckboxNode.EVENT_TYPES); 
     110         
     111        GeoExt.tree.TristateCheckboxNode.superclass.constructor.apply(this, arguments); 
     112 
     113        this.on("childcheckchange", this.updateCheckedChildNodes, this); 
     114    }, 
     115     
     116    /** 
     117     * Method: render 
     118     *  
     119     * Parameters: 
     120     * bulkRender - {Boolean} 
     121     */ 
     122    render: function(bulkRender) { 
     123        var rendered = this.rendered; 
     124        var checked = this.attributes.checked; 
     125        this.attributes.checked = 
     126            typeof this.attributes.checked == "undefined" ? false : 
     127            this.attributes.checked; 
     128        GeoExt.tree.TristateCheckboxNode.superclass.render.call(this, bulkRender); 
     129        var ui = this.getUI(); 
     130        if(!rendered) { 
     131            if(typeof checked == "undefined" && this.parentNode.ui.checkbox) { 
     132                ui.toggleCheck(this.parentNode.ui.checkbox.checked); 
     133            } 
     134            this.parentNode.on("checkchange", function(node, checked) { 
     135                ui.toggleCheck(checked); 
     136            }, this); 
     137        } 
     138    }, 
     139     
     140    /** 
     141     * Method: updateCheckedChildNodes 
     142     * Updates the status cache of checked child nodes. 
     143     *  
     144     * Parameters: 
     145     * node - {Ext.tree.Node} child node that has changed 
     146     * checked - {Boolean} new checked status of the changed child node 
     147     */ 
     148    updateCheckedChildNodes: function(node, checked) { 
     149        if(checked) { 
     150            this.addChecked(node, node.attributes.thirdState); 
     151        } else { 
     152            this.removeChecked(node); 
     153        } 
     154 
     155        var childrenChecked, childrenThirdState; 
     156        if(this.checkedCount.toFixed() == this.childNodes.length) { 
     157            childrenChecked = true; 
     158            childrenThirdState = false; 
     159        } else if(this.checkedCount.toFixed(1) == 0) { 
     160            childrenChecked = false; 
     161            childrenThirdState = false; 
     162        } else { 
     163            childrenChecked = true; 
     164            childrenThirdState = true; 
     165        } 
     166        // do a special silent toggleCheck to avoid checkchange events being 
     167        // triggered 
     168        this.getUI().toggleCheck(childrenChecked, childrenThirdState, 
     169            {silent: true}); 
     170        if(this.parentNode) { 
     171            this.parentNode.fireEvent("childcheckchange", this, 
     172                childrenChecked); 
     173        } 
     174    }, 
     175     
     176    /** 
     177     * Method: appendChild 
     178     *  
     179     * Parameters: 
     180     * node - {Ext.tree.Node} 
     181     */ 
     182    appendChild: function(node) { 
     183        GeoExt.tree.TristateCheckboxNode.superclass.appendChild.call(this, node); 
     184        if(this.attributes.checked || node.attributes.checked) { 
     185            this.addChecked(node); 
     186        } 
     187        // We do not want this event handler to trigger checkchange events on 
     188        // parent nodes, because this would cause bouncing between this 
     189        // handler and the handler for (un-)checking children on a parent's 
     190        // checkchange event. So we introduce a special childcheckchange 
     191        // event with a handler that will also trigger this event on the 
     192        // parent. 
     193        node.on("checkchange", function(node, checked) { 
     194            if(this.childrenRendered) { 
     195                this.fireEvent("childcheckchange", node, checked); 
     196            } 
     197        }, this); 
     198    }, 
     199     
     200    /** 
     201     * Method: addChecked 
     202     * Adds a child node to the checkedChildNodes hash. Adds 1 for fully 
     203     * checked nodes, 0.1 for third state checked nodes. 
     204     *  
     205     * Parameters: 
     206     * node - {Ext.tree.Node} 
     207     * thirdState - {Boolean} 
     208     */ 
     209    addChecked: function(node, thirdState) { 
     210        // subtract current value (if any). This is needed to change from a 
     211        // tristate to a fully checked state and vice versa. 
     212        this.checkedCount -= (this.checkedChildNodes[node.id] || 0); 
     213         
     214        var add = thirdState ? 0.1 : 1; 
     215        this.checkedChildNodes[node.id] = add; 
     216        this.checkedCount += add; 
     217    }, 
     218     
     219    /** 
     220     * Method: removeChecked 
     221     * Removes a child node from the checkedChildNodes hash. 
     222     *  
     223     * Parameters: 
     224     * node - {Ext.tree.Node} 
     225     */ 
     226    removeChecked: function(node) { 
     227        var remove = this.checkedChildNodes[node.id] 
     228        if(remove) { 
     229            delete this.checkedChildNodes[node.id]; 
     230            this.checkedCount -= remove; 
     231        } 
     232    } 
     233}); 
     234 
     235/** 
     236 * Constant: EVENT_TYPES 
     237 * {Array(String)} - supported event types 
     238 *  
     239 * Event types supported for this class, in additon to the ones inherited 
     240 * from {<Ext.tree.AsyncTreeNode>}: 
     241 * - *childcheckchange* fired to notify a parent node that the status of 
     242 *     its checked child nodes has changed 
     243 */ 
     244GeoExt.tree.TristateCheckboxNode.EVENT_TYPES = ["childcheckchange"]; 
     245 
     246/** 
     247 * NodeType: gx_tristatecheckbox 
     248 */ 
     249Ext.tree.TreePanel.nodeTypes.gx_tristatecheckbox = GeoExt.tree.TristateCheckboxNode; 
  • lib/GeoExt/widgets/tree/LayerContainer.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.LayerContainer 
     8 *  
     9 * A subclass of {Ext.tree.TreeNode} that will collect all layers of an 
     10 * OpenLayers map. Only layers that have displayInLayerSwitcher set to true 
     11 * will be included. The childrens' iconCls will be set to "baselayer-icon" 
     12 * for base layers, and to "layer-icon" for overlay layers. 
     13 *  
     14 * To use this node type in JSON config, set nodeType to "olLayerContainer". 
     15 *  
     16 * Inherits from: 
     17 * - <Ext.tree.TreeNode> 
     18 */ 
     19GeoExt.tree.LayerContainer = Ext.extend(Ext.tree.TreeNode, { 
     20     
     21    /** 
     22     * ConfigProperty: layerStore 
     23     * {<GeoExt.data.LayerStore>} The layer store containing layers to be 
     24     *     displayed in the container. 
     25     */ 
     26    layerStore: null, 
     27     
     28    /** 
     29     * ConfigProperty: defaults 
     30     * {Object} a configuration object passed to all nodes that this 
     31     *     LayerContainer creates. 
     32     */ 
     33    defaults: null, 
     34 
     35    /** 
     36     * Constructor: GeoExt.tree.LayerContainer 
     37     *  
     38     * Parameters: 
     39     * config - {Object} 
     40     */ 
     41    constructor: function(config) { 
     42        this.layerStore = config.layerStore; 
     43        this.defaults = config.defaults; 
     44        GeoExt.tree.LayerContainer.superclass.constructor.apply(this, arguments); 
     45    }, 
     46 
     47    /** 
     48     * Method: render 
     49     *  
     50     * Parameters: 
     51     * bulkRender - {Boolean} 
     52     */ 
     53    render: function(bulkRender) { 
     54        if (!this.rendered) { 
     55            if(!this.layerStore) { 
     56                this.layerStore = GeoExt.MapPanel.guess().layers; 
     57            } 
     58            this.layerStore.each(function(record) { 
     59                this.addLayerNode(record); 
     60            }, this); 
     61            this.layerStore.on({ 
     62                "add": function(store, records, index) { 
     63                    var nodeIndex = this.recordIndexToNodeIndex(index); 
     64                    console.log(index, nodeIndex); 
     65                    for(var i=0; i<records.length; ++i) { 
     66                        this.addLayerNode(records[i], nodeIndex); 
     67                    } 
     68                }, 
     69                "remove": function(store, record, index) { 
     70                    this.removeLayerNode(record); 
     71                }, 
     72                scope: this 
     73            }); 
     74        } 
     75        GeoExt.tree.LayerContainer.superclass.render.call(this, bulkRender); 
     76    }, 
     77     
     78    /** 
     79     * Method: recordIndexToNodeIndex 
     80     * Convert a record index into a child node index. 
     81     * 
     82     * Parameters: 
     83     * index - {Number} The record index in the layer store. 
     84     * 
     85     * Returns: 
     86     * {Number} The appropriate child node index for the record. 
     87     */ 
     88    recordIndexToNodeIndex: function(index) { 
     89        var store = this.layerStore; 
     90        var count = store.getCount(); 
     91        var nodeIndex = -1; 
     92        for(var i=count-1; i>=0; --i) { 
     93            if(store.getAt(i).get("layer").displayInLayerSwitcher) { 
     94                ++nodeIndex; 
     95                if(index === i) { 
     96                    break; 
     97                } 
     98            } 
     99        }; 
     100        return nodeIndex; 
     101    }, 
     102     
     103    /** 
     104     * Method: addLayerNode 
     105     * Adds a child node representing a layer of the map 
     106     *  
     107     * Parameters: 
     108     * layerRecord - {Ext.data.Record} the layer record to add the layer for 
     109     * index - {Number} Optional index for the new layer.  Default is 0. 
     110     */ 
     111    addLayerNode: function(layerRecord, index) { 
     112        index = index || 0; 
     113        var layer = layerRecord.get("layer"); 
     114        if (layer.displayInLayerSwitcher == true) { 
     115            var node = new GeoExt.tree.LayerNode(Ext.applyIf({ 
     116                iconCls: layer.isBayeLayer ? 'baselayer-icon' : 'layer-icon', 
     117                layer: layer, 
     118                layerStore: this.layerStore 
     119            }, this.defaults)); 
     120            var sibling = this.item(index); 
     121            if(sibling) { 
     122                this.insertBefore(node, sibling); 
     123            } else { 
     124                this.appendChild(node); 
     125            } 
     126        } 
     127    }, 
     128     
     129    /** 
     130     * Method: removeLayerNode 
     131     * Removes a child node representing a layer of the map 
     132     *  
     133     * Parameters: 
     134     * layerRecord - {Ext.data.Record} the layer record to remove the node for 
     135     */ 
     136    removeLayerNode: function(layerRecord) { 
     137        var layer = layerRecord.get("layer"); 
     138        if (layer.displayInLayerSwitcher == true) { 
     139            var node = this.findChildBy(function(node) { 
     140                return node.layer == layer; 
     141            }); 
     142            if(node) { 
     143                node.remove(); 
     144            } 
     145        } 
     146    } 
     147}); 
     148 
     149/** 
     150 * NodeType: gx_layercontainer 
     151 */ 
     152Ext.tree.TreePanel.nodeTypes.gx_layercontainer = GeoExt.tree.LayerContainer; 
  • lib/GeoExt/widgets/tree/OverlayLayerContainer.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.OverlayLayerContainer 
     8 *  
     9 * A layer container that will collect all overlay layers of an OpenLayers map. 
     10 * Only layers that have displayInLayerSwitcher set to true will be included. 
     11 *  
     12 * To use this node type in JSON config, set nodeType to 
     13 * "olOverlayLayerContainer". 
     14 *  
     15 * Inherits from: 
     16 * - <GeoExt.tree.LayerContainer> 
     17 */ 
     18GeoExt.tree.OverlayLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, { 
     19 
     20    /** 
     21     * Constructor: GeoExt.tree.OverlayLayerContainer 
     22     *  
     23     * Parameters: 
     24     * config - {Object} 
     25     */ 
     26    constructor: function(config) { 
     27        config.text = config.text || "Overlays"; 
     28        GeoExt.tree.OverlayLayerContainer.superclass.constructor.apply(this, 
     29            arguments); 
     30    }, 
     31 
     32    /** 
     33     * Method: addLayerNode 
     34     * Adds a child node representing a overlay layer of the map 
     35     *  
     36     * Parameters: 
     37     * layerRecord - {Ext.data.Record} the layer record to add a node for 
     38     */ 
     39    addLayerNode: function(layerRecord) { 
     40        var layer = layerRecord.get("layer"); 
     41        if (layer.isBaseLayer == false) { 
     42            GeoExt.tree.OverlayLayerContainer.superclass.addLayerNode.call(this, 
     43                layerRecord); 
     44        } 
     45    }, 
     46     
     47    /** 
     48     * Method: removeLayerNode 
     49     * Removes a child node representing an overlay layer of the map 
     50     *  
     51     * Parameters: 
     52     * layerRecord - {Ext.data.Record} the layer record to remove the node for 
     53     */ 
     54    removeLayerNode: function(layerRecord) { 
     55        var layer = layerRecord.get("layer"); 
     56        if (layer.isBaseLayer == false) { 
     57            GeoExt.tree.OverlayLayerContainer.superclass.removeLayerNode.call( 
     58                this, layerRecord); 
     59        } 
     60    } 
     61}); 
     62 
     63/** 
     64 * NodeType: gx_overlaylayercontainer 
     65 */ 
     66Ext.tree.TreePanel.nodeTypes.gx_overlaylayercontainer = GeoExt.tree.OverlayLayerContainer; 
  • lib/GeoExt/widgets/tree/LayerNode.js

    old new  
     1/** 
     2 * Copyright (c) 2008 The Open Planning Project 
     3 */ 
     4Ext.namespace("GeoExt.tree"); 
     5 
     6/** 
     7 * Class: GeoExt.tree.LayerNodeUI 
     8 *  
     9 * Inherits from: 
     10 * - <GeoExt.tree.TristateCheckboxNodeUI> 
     11 */ 
     12GeoExt.tree.LayerNodeUI = Ext.extend(GeoExt.tree.TristateCheckboxNodeUI, { 
     13     
     14    /** 
     15     * Property: radio 
     16     * {Ext.Element} 
     17     */ 
     18    radio: null, 
     19     
     20    /** 
     21     * Constructor: GeoExt.tree.LayerNodeUI 
     22     *  
     23     * Parameters: 
     24     * config - {Object} 
     25     */ 
     26    constructor: function(config) { 
     27        GeoExt.tree.LayerNodeUI.superclass.constructor.apply(this, arguments); 
     28    }, 
     29     
     30    /** 
     31     * Method: render 
     32     *  
     33     * Parameters: 
     34     * bulkRender - {Boolean} 
     35     */ 
     36    render: function(bulkRender) { 
     37        GeoExt.tree.LayerNodeUI.superclass.render.call(this, bulkRender); 
     38        var a = this.node.attributes; 
     39        if (a.radioGroup && !this.radio) { 
     40            this.radio = Ext.DomHelper.insertAfter(this.checkbox, 
     41                ['<input type="radio" class="x-tree-node-cb" name="', 
     42                a.radioGroup, '_querylayer"></input>'].join("")); 
     43        } 
     44    }, 
     45     
     46    /** 
     47     * Method: onClick 
     48     *  
     49     * Parameters: 
     50     * e - {Object} 
     51     */ 
     52    onClick: function(e) { 
     53        if (e.getTarget('input[type=radio]', 1)) { 
     54            this.fireEvent("querylayerchange", this.node.layer); 
     55        } else { 
     56            GeoExt.tree.LayerNodeUI.superclass.onClick.call(this, e); 
     57        } 
     58    }, 
     59     
     60    /** 
     61     * Method: toggleCheck 
     62     *  
     63     * Parameters: 
     64     * value - {Boolean} 
     65     */ 
     66    toggleCheck: function(value) { 
     67        GeoExt.tree.LayerNodeUI.superclass.toggleCheck.apply(this, arguments); 
     68        var node = this.node; 
     69        var layer = this.node.layer; 
     70        node.visibilityChanging = true; 
     71        if(this.checkbox && (layer.getVisibility() != this.isChecked())) { 
     72            layer.setVisibility(this.isChecked()); 
     73        } 
     74        node.visibilityChanging = false; 
     75    }, 
     76     
     77    /** 
     78     * Method: destroy 
     79     */ 
     80    destroy: function() { 
     81        GeoExt.tree.LayerNodeUI.superclass.destroy.call(this); 
     82        delete this.radio; 
     83    } 
     84}); 
     85 
     86 
     87/** 
     88 * Class: GeoExt.tree.LayerNode 
     89 *  
     90 * A subclass of {<GeoExt.tree.TristateCheckboxNode>} that is connected to an 
     91 * {OpenLayers.Layer} by setting the node's layer property. Checking or 
     92 * unchecking the checkbox of this node will directly affect the layer and 
     93 * vice versa. The default iconCls for this node's icon is "layer-icon", 
     94 * unless it has children. 
     95 *  
     96 * This node can contain children, e.g. filter nodes. 
     97 *  
     98 * Setting the node's layer property to a layer name instead of an object 
     99 * will also work. As soon as a layer is found, it will be stored as layer 
     100 * property in the attributes hash. 
     101 *  
     102 * The node's text property defaults to the layer name. 
     103 *  
     104 * If the layer has a queryable property set to true, the node will render a 
     105 * radio button to select the query layer. Clicking on the radio button will 
     106 * fire the querychange event, with the layer as argument. A queryGroup 
     107 * attribute, set to the map's id, will be added to the attributes hash. 
     108 *  
     109 * To use this node type in a JSON config, set nodeType to "olLayer". 
     110 *  
     111 * Inherits from: 
     112 * - <GeoExt.tree.TristateCheckboxNode> 
     113 */ 
     114GeoExt.tree.LayerNode = Ext.extend(GeoExt.tree.TristateCheckboxNode, { 
     115     
     116    /** 
     117     * ConfigProperty: layer 
     118     * {OpenLayers.Layer|String} The layer that this layer node will 
     119     *     be bound to, or the name of the layer (has to match the layer's 
     120     *     name property). If a layer name is provided, <layerStore> also has 
     121     *     to be provided. 
     122     */ 
     123    layer: null, 
     124     
     125    /** 
     126     * ConfigProperty: layerStore 
     127     * {<GeoExt.data.LayerStore|"auto"} The layer store containing the layer 
     128     *     that this node represents. If set to "auto", the node will query 
     129     *     the ComponentManager for a <GeoExt.MapPanel>, take the first one it 
     130     *     finds and takes its layer store. This property is only required 
     131     *     if <layer> is provided as a string. 
     132     */ 
     133    layerStore: null, 
     134     
     135    /** 
     136     * ConfigProperty: childNodeType 
     137     * {Ext.tree.Node|String} node class or nodeType of childnodes for this 
     138     *     node. A node type provided here needs to have an add method, with 
     139     *     a scope argument. This method will be run by this node in the 
     140     *     context of this node, to create child nodes. See 
     141     *     {<GeoExt.tree.LayerParamsNode>} for an example implementation that 
     142     *     adds nodes based on a layer's params object.  
     143     */ 
     144    childNodeType: null, 
     145     
     146    /** 
     147     * Property: visibilityChanging 
     148     * {Boolean} private property indicating layer visibility being changed 
     149     *     by this node in order to prevent visibilitychanged events bouncing 
     150     *     back and forth 
     151     */ 
     152    visibilityChanging: false, 
     153     
     154    /** 
     155     * Constructor: GeoExt.tree.LayerNode 
     156     *  
     157     * Parameters: 
     158     * config - {Object} 
     159     */ 
     160    constructor: function(config) { 
     161        config.leaf = config.leaf || !config.children; 
     162         
     163        config.iconCls = typeof config.iconCls == "undefined" && 
     164            !config.children ? "layer-icon" : config.iconCls; 
     165        // checked status will be set by layer event, so setting it to false 
     166        // to always get the checkbox rendered 
     167        config.checked = false; 
     168         
     169        this.defaultUI = this.defaultUI || GeoExt.tree.LayerNodeUI; 
     170        this.addEvents.apply(this, GeoExt.tree.LayerNode.EVENT_TYPES); 
     171         
     172        Ext.apply(this, { 
     173            layer: config.layer, 
     174            layerStore: config.layerStore, 
     175            childNodeType: config.childNodeType 
     176        }); 
     177        GeoExt.tree.LayerNode.superclass.constructor.apply(this, arguments); 
     178    }, 
     179 
     180    /** 
     181     * Method: render 
     182     *  
     183     * Properties: 
     184     * bulkRender {Boolean} - optional 
     185     * layer {<OpenLayers.Layer>} - optional 
     186     */ 
     187    render: function(bulkRender) { 
     188        var layer = this.layer instanceof OpenLayers.Layer && this.layer; 
     189        if(!layer) { 
     190            // guess the store if not provided 
     191            if(!this.layerStore || this.layerStore == "auto") { 
     192                this.layerStore = GeoExt.MapPanel.guess().layers; 
     193            } 
     194            // now we try to find the layer by its name in the layer store 
     195            var i = this.layerStore.findBy(function(o) { 
     196                return o.get("title") == this.layer; 
     197            }, this); 
     198            if(i != -1) { 
     199                // if we found the layer, we can assign it and everything 
     200                // will be fine 
     201                layer = this.layerStore.getAt(i).get("layer"); 
     202            } 
     203        } 
     204        if (!this.rendered || !layer) { 
     205            var ui = this.getUI(); 
     206             
     207            if(layer) { 
     208                this.layer = layer; 
     209                if(!this.text) { 
     210                    this.text = layer.name; 
     211                } 
     212                 
     213                if(this.childNodeType) { 
     214                    this.addChildNodes(); 
     215                } 
     216                 
     217                ui.show(); 
     218                ui.toggleCheck(layer.getVisibility()); 
     219                this.addVisibilityEventHandlers(); 
     220                // set initial checked status 
     221                this.attributes.checked = layer.getVisibility(); 
     222            } else { 
     223                ui.hide(); 
     224            } 
     225             
     226            if(this.layerStore instanceof GeoExt.data.LayerStore) { 
     227                this.addStoreEventHandlers(layer); 
     228            }             
     229        } 
     230        GeoExt.tree.LayerNode.superclass.render.call(this, bulkRender); 
     231    }, 
     232     
     233    /** 
     234     * Method: addVisibilityHandlers 
     235     * Adds handlers that sync the checkbox state with the layer's visibility 
     236     * state 
     237     */ 
     238    addVisibilityEventHandlers: function() { 
     239        this.layer.events.register("visibilitychanged", this, function() { 
     240            if(!this.visibilityChanging && 
     241                    this.attributes.checked != this.layer.getVisibility()) { 
     242                this.getUI().toggleCheck(this.layer.getVisibility()); 
     243            } 
     244        }); 
     245        this.on({ 
     246            "checkchange": function(node, checked) { 
     247                if (checked && this.layer.isBaseLayer) { 
     248                    this.layer.map.setBaseLayer(this.layer); 
     249                } 
     250                this.layer.setVisibility(checked); 
     251            }, 
     252            scope: this 
     253        }); 
     254    }, 
     255     
     256    /** 
     257     * Method: addStoreEventHandlers 
     258     * Adds handlers that make sure the node disappeares when the layer is 
     259     * removed from the store, and appears when it is re-added. 
     260     */ 
     261    addStoreEventHandlers: function() { 
     262        this.layerStore.on({ 
     263            "add": function(store, records, index) { 
     264                var l; 
     265                for(var i=0; i<records.length; ++i) { 
     266                    l = records[i].get("layer"); 
     267                    if(this.layer == l) { 
     268                        this.getUI().show(); 
     269                    } else if (this.layer == l.name) { 
     270                        // layer is a string, which means the node has not yet 
     271                        // been rendered because the layer was not found. But 
     272                        // now we have the layer and can render. 
     273                        this.render(bulkRender); 
     274                        return; 
     275                    } 
     276                } 
     277            }, 
     278            "remove": function(store, record, index) { 
     279                if(this.layer == record.get("layer")) { 
     280                    this.getUI().hide(); 
     281                } 
     282            }, 
     283            scope: this 
     284        }); 
     285    }, 
     286     
     287    /** 
     288     * Method: addChildNodes 
     289     * If the layer's layers param is an array of layer names, a subnode for 
     290     * each name will be created. 
     291     */ 
     292    addChildNodes: function() { 
     293        if(typeof this.childNodeType == "string") { 
     294            Ext.tree.TreePanel.nodeTypes[this.childNodeType].add(this); 
     295        } else if(this.childNodeType.add) { 
     296            this.childNodeType.add(this); 
     297        } 
     298    }, 
     299 
     300    /** 
     301     * Method: updateCheckedChildNodes 
     302     *  
     303     * Parameters: 
     304     * node - {Ext.tree.node} 
     305     * checked - {Boolean} 
     306     */ 
     307    updateCheckedChildNodes: function(node, checked) { 
     308        GeoExt.tree.LayerNode.superclass.updateCheckedChildNodes.call(this, 
     309            node, checked); 
     310    }     
     311}); 
     312 
     313/** 
     314 * Constant: GeoExt.tree.LayerNode.EVENT_TYPES 
     315 * {Array(String)} - supported event types 
     316 *  
     317 * Event types supported for this class, in additon to the ones inherited 
     318 * from {<GeoExt.tree.TristateCheckboxNode>}: 
     319 * - *querylayerchange* notifies listener when the query layer has 
     320 *     changed. Will be called with the new query layer as argument. 
     321 */ 
     322GeoExt.tree.LayerNode.EVENT_TYPES = ["querylayerchange"]; 
     323 
     324/** 
     325 * NodeType: gx_layer 
     326 */ 
     327Ext.tree.TreePanel.nodeTypes.gx_layer = GeoExt.tree.LayerNode; 
  • lib/GeoExt/widgets/MapPanel.js

    old new  
    193193}); 
    194194 
    195195/** 
     196 * APIFunction GeoExt.MapPanel.guess 
     197 * Convenience function for guessing the map panel of an application. This can 
     198 *     reliably be used for all applications that just have one map panel in the 
     199 *     viewport. 
     200 *  
     201 * Returns: 
     202 * {<GeoExt.MapPanel>} The first map panel found by the Ext component manager. 
     203 */ 
     204GeoExt.MapPanel.guess = function() { 
     205    return Ext.ComponentMgr.all.find(function(o) {  
     206        return o instanceof GeoExt.MapPanel;  
     207    });  
     208} 
     209 
     210 
     211/** 
    196212 * XType: gx_mappanel 
    197213 */ 
    198214Ext.reg('gx_mappanel', GeoExt.MapPanel);  
  • examples/tree.html

    old new  
     1<html xmlns="http://www.w3.org/1999/xhtml"> 
     2  <head> 
     3    <link rel="stylesheet" type="text/css" href="../../../ext/2.2/resources/css/ext-all.css"></link> 
     4    <script type="text/javascript" src="../../../openlayers/lib/OpenLayers.js"></script> 
     5    <script type="text/javascript" src="../../../ext/2.2/adapter/ext/ext-base.js"></script> 
     6    <script type="text/javascript" src="../../../ext/2.2/ext-all-debug.js"></script> 
     7    <script type="text/javascript" src="../lib/GeoExt.js"></script> 
     8    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/TristateCheckboxNode.js"></script> 
     9    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerNode.js"></script> 
     10    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerParamsNode.js"></script> 
     11    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerContainer.js"></script> 
     12    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/BaseLayerContainer.js"></script> 
     13    <script type="text/javascript" src="../lib/GeoExt/widgets/tree/OverlayLayerContainer.js"></script> 
     14    <script> 
     15        var mapPanel; 
     16        Ext.onReady(function() { 
     17            var treeConfig = new OpenLayers.Format.JSON().write([{ 
     18                nodeType: "gx_baselayercontainer" 
     19            }, { 
     20                nodeType: "gx_overlaylayercontainer", 
     21                defaults: { 
     22                    childNodeType: "gx_layerparams", 
     23                    param: "LAYERS", 
     24                    texts: ["Water", "Cities"] 
     25                } 
     26            }, { 
     27                nodeType: "gx_layer", 
     28                layer: "Tasmania Roads", 
     29                childNodeType: "gx_layerparams", 
     30                paramsDelimiter: { 
     31                    "CQL_FILTER": " or " 
     32                }, 
     33                children: [{ 
     34                    nodeType: "gx_layerparams", 
     35                    text: "Highway", 
     36                    params: {CQL_FILTER: "TYPE='highway'"} 
     37                }, { 
     38                    nodeType: "gx_layerparams", 
     39                    text: "Lane", 
     40                    params: {CQL_FILTER: "TYPE='lane'"} 
     41                }, { 
     42                    nodeType: "gx_layerparams", 
     43                    text: "Road", 
     44                    params: {CQL_FILTER: "TYPE='road'"} 
     45                }, { 
     46                    nodeType: "gx_layerparams", 
     47                    text: "Alley", 
     48                    params: {CQL_FILTER: "TYPE='alley'"} 
     49                }, { 
     50                    nodeType: "gx_layerparams", 
     51                    text: "Gravel", 
     52                    params: {CQL_FILTER: "TYPE='gravel'"} 
     53                }, { 
     54                    nodeType: "gx_layerparams", 
     55                    text: "Logging", 
     56                    params: {CQL_FILTER: "TYPE='logging'"} 
     57                }] 
     58            }], true); 
     59 
     60            mapPanel = new GeoExt.MapPanel({ 
     61                border: true, 
     62                region: "center", 
     63                map: new OpenLayers.Map({ 
     64                    maxExtent: new OpenLayers.Bounds( 
     65                        143.60260815000004, -43.851764249999995, 
     66                        148.71135685000002, -39.370182750000005 
     67                    ), 
     68                    maxResolution: 0.0199560496093749, 
     69                }), 
     70            }); 
     71             
     72            mapPanel.map.addLayers([ 
     73                new OpenLayers.Layer.WMS("Tasmania State Boundaries", 
     74                    "http://sigma.openplans.org/geoserver/wms", { 
     75                        layers: "topp:tasmania_state_boundaries" 
     76                    }, { 
     77                        buffer: 0 
     78                   }), 
     79                new OpenLayers.Layer.WMS("Blue Marble", 
     80                    "http://sigma.openplans.org/geoserver/wms", { 
     81                        layers: "bluemarble" 
     82                    }, { 
     83                        buffer: 0, 
     84                        displayOutsideMaxExtent: true 
     85                    }), 
     86                new OpenLayers.Layer.WMS("Water, Cities", 
     87                    "http://sigma.openplans.org/geoserver/wms", { 
     88                        layers: ["topp:tasmania_water_bodies","topp:tasmania_cities"], 
     89                        transparent: "true", 
     90                        format: "image/gif" 
     91                    }, { 
     92                        queryable: true, 
     93                        isBaseLayer: false, 
     94                        buffer: 0 
     95                    }), 
     96                new OpenLayers.Layer.WMS("Tasmania Roads", 
     97                    "http://sigma.openplans.org/geoserver/wms", { 
     98                        layers: "topp:tasmania_roads", 
     99                        transparent: "true", 
     100                        format: "image/gif", 
     101                        cql_filter: "TYPE='highway' or TYPE='lane' or TYPE='road' or TYPE='alley' or TYPE='gravel' or TYPE='logging'" 
     102                    }, { 
     103                        queryable: true, 
     104                        isBaseLayer: false, 
     105                        displayInLayerSwitcher: false, 
     106                        singleTile: true, 
     107                        ratio: 1 
     108                    }) 
     109            ]); 
     110 
     111            var treeConfigWin = new Ext.Window({ 
     112                layout: "fit", 
     113                hideBorders: true, 
     114                closeAction: "hide", 
     115                width: 300, 
     116                height: 400, 
     117                title: "Tree Configuration", 
     118                items: [{ 
     119                    xtype: "form", 
     120                    layout: "fit", 
     121                    items: [{ 
     122                        id: "treeconfig", 
     123                        xtype: "textarea" 
     124                    }], 
     125                    buttons: [{ 
     126                        text: "Save", 
     127                        handler: function() { 
     128                            var value = Ext.getCmp("treeconfig").getValue() 
     129                            try { 
     130                                var root = tree.getRootNode(); 
     131                                root.attributes.children = Ext.decode(value); 
     132                                tree.getLoader().load(root); 
     133                            } catch(e) { 
     134                                alert("Invalid JSON"); 
     135                                return; 
     136                            } 
     137                            treeConfig = value; 
     138                            treeConfigWin.hide(); 
     139                        } 
     140                    }, { 
     141                        text: "Cancel", 
     142                        handler: function() { 
     143                            treeConfigWin.hide(); 
     144                        } 
     145                    }, { 
     146                        text: "Help", 
     147                        tooltipType: "title", 
     148                        tooltip: "Opens a window with a list of nodeTypes provided by GeoExt", 
     149                        handler: function() { 
     150                            window.open("../doc/index/NodeTypes.html"); 
     151                        } 
     152                    }] 
     153                }] 
     154            }); 
     155             
     156            var toolbar = new Ext.Toolbar({ 
     157                items: [{ 
     158                    text: "Show/Edit Tree Config", 
     159                    handler: function() { 
     160                        treeConfigWin.show(); 
     161                        Ext.getCmp("treeconfig").setValue(treeConfig); 
     162                    } 
     163                }] 
     164            }); 
     165             
     166            var tree = new Ext.tree.TreePanel({ 
     167                border: true, 
     168                region: "west", 
     169                title: "Layers and Filters", 
     170                width: 200, 
     171                split: true, 
     172                collapsible: true, 
     173                collapseMode: "mini", 
     174                autoScroll: true, 
     175                loader: new Ext.tree.TreeLoader({ 
     176                    clearOnLoad: true 
     177                }), 
     178                root: { 
     179                    nodeType: "async", 
     180                    children: Ext.decode(treeConfig) 
     181                }, 
     182                rootVisible: false, 
     183                lines: false, 
     184                bbar: toolbar 
     185            }); 
     186             
     187            tree.on("append", function(tree, parent, node) { 
     188                node.on("querylayerchange", function(layer) { 
     189                    alert(layer.name+" is now the query layer."); 
     190                }) 
     191            }); 
     192             
     193            new Ext.Viewport({ 
     194                layout: "fit", 
     195                hideBorders: true, 
     196                items: { 
     197                    layout: "border", 
     198                    deferredRender: false, 
     199                    items: [mapPanel, tree] 
     200                } 
     201            }); 
     202        }); 
     203    </script> 
     204  </head> 
     205  <body> 
     206  </body> 
     207</html>