[Commits] r673 - in sandbox/bartvde/legend/geoext: examples lib/GeoExt/widgets lib/GeoExt/widgets/tree tests/lib/GeoExt/widgets tests/lib/GeoExt/widgets/tree

commits at geoext.org commits at geoext.org
Mon May 11 09:06:23 CEST 2009


Author: bartvde
Date: 2009-05-11 09:06:22 +0200 (Mon, 11 May 2009)
New Revision: 673

Added:
   sandbox/bartvde/legend/geoext/examples/tree.html
   sandbox/bartvde/legend/geoext/examples/tree.js
   sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/
   sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/BaseLayerContainer.js
   sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerContainer.js
   sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerNode.js
   sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/OverlayLayerContainer.js
   sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/
   sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerContainer.html
   sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html
Log:
applying Andreas' treenodes patch

Added: sandbox/bartvde/legend/geoext/examples/tree.html
===================================================================
--- sandbox/bartvde/legend/geoext/examples/tree.html	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/examples/tree.html	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,23 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <link rel="stylesheet" type="text/css" href="../../ext/resources/css/ext-all.css"></link>
+        <link rel="stylesheet" type="text/css" href="../../ext/examples/shared/examples.css"></link>
+        <script type="text/javascript" src="../../openlayers/lib/OpenLayers.js"></script>
+        <script type="text/javascript" src="../../ext/adapter/ext/ext-base.js"></script>
+        <script type="text/javascript" src="../../ext/ext-all.js"></script>
+        <script type="text/javascript" src="../lib/GeoExt.js"></script>        
+        <script type="text/javascript" src="tree.js"></script>
+    </head>
+    <body>
+        <div id="desc">
+            <h1>GeoExt.tree Components</h1>
+            <p>This example shows how to work with layer tree.  The basic
+            component for building layer trees is the LayerNode, and there are
+            differnt types of containers for automatically adding a map's
+            layers to the tree. The tree configuration of this example is pure
+            JSON and can be edited live by clicking on the "Show/Edit Tree Config"
+            button below the layers panel.<p>
+            <p>The js is not minified so it is readable. See <a href="tree.js">tree.js</a>.</p>
+        </div>
+    </body>
+</html>

Added: sandbox/bartvde/legend/geoext/examples/tree.js
===================================================================
--- sandbox/bartvde/legend/geoext/examples/tree.js	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/examples/tree.js	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,175 @@
+var mapPanel;
+Ext.onReady(function() {
+    // using OpenLayers.Format.JSON to create a nice formatted string of the
+    // configuration for editing it in the UI
+    var treeConfig = new OpenLayers.Format.JSON().write([{
+        nodeType: "gx_baselayercontainer"
+    }, {
+        nodeType: "gx_overlaylayercontainer",
+        // render the nodes inside this container with a radio button,
+        // and assign them the group "foo"
+        defaults: {
+            radioGroup: "foo"
+        }
+    }, {
+        nodeType: "gx_layer",
+        layer: "Tasmania Roads"
+    }], true);
+
+    mapPanel = new GeoExt.MapPanel({
+        border: true,
+        region: "center",
+        // we do not want all overlays, to try the OverlayLayerContainer
+        map: new OpenLayers.Map({allOverlays: false}),
+        center: [146.1569825, -41.6109735],
+        zoom: 6,
+        layers: [
+            new OpenLayers.Layer.WMS("Blue Marble",
+                "http://sigma.openplans.org/geoserver/wms", {
+                    layers: "bluemarble"
+                }, {
+                    buffer: 0,
+                    visibility: false
+                }),
+            new OpenLayers.Layer.WMS("Tasmania State Boundaries",
+                "http://demo.opengeo.org/geoserver/wms", {
+                    layers: "topp:tasmania_state_boundaries"
+                }, {
+                    buffer: 0
+               }),
+            new OpenLayers.Layer.WMS("Water",
+                "http://demo.opengeo.org/geoserver/wms", {
+                    layers: "topp:tasmania_water_bodies",
+                    transparent: true,
+                    format: "image/gif"
+                }, {
+                    isBaseLayer: false,
+                    buffer: 0
+                }),
+            new OpenLayers.Layer.WMS("Cities",
+                "http://demo.opengeo.org/geoserver/wms", {
+                    layers: "topp:tasmania_cities",
+                    transparent: true,
+                    format: "image/gif"
+                }, {
+                    isBaseLayer: false,
+                    buffer: 0
+                }),
+            new OpenLayers.Layer.WMS("Tasmania Roads",
+                "http://demo.opengeo.org/geoserver/wms", {
+                    layers: "topp:tasmania_roads",
+                    transparent: true,
+                    format: "image/gif"
+                }, {
+                    isBaseLayer: false,
+                    buffer: 0,
+                    // exclude this layer from layer container nodes
+                    displayInLayerSwitcher: false
+                })
+        ]
+    });
+    
+    // dialog for editing the tree configuration
+    var treeConfigWin = new Ext.Window({
+        layout: "fit",
+        hideBorders: true,
+        closeAction: "hide",
+        width: 300,
+        height: 400,
+        title: "Tree Configuration",
+        items: [{
+            xtype: "form",
+            layout: "fit",
+            items: [{
+                id: "treeconfig",
+                xtype: "textarea"
+            }],
+            buttons: [{
+                text: "Save",
+                handler: function() {
+                    var value = Ext.getCmp("treeconfig").getValue()
+                    try {
+                        var root = tree.getRootNode();
+                        root.attributes.children = Ext.decode(value);
+                        tree.getLoader().load(root);
+                    } catch(e) {
+                        alert("Invalid JSON");
+                        return;
+                    }
+                    treeConfig = value;
+                    treeConfigWin.hide();
+                }
+            }, {
+                text: "Cancel",
+                handler: function() {
+                    treeConfigWin.hide();
+                }
+            }]
+        }]
+    });
+    
+    var toolbar = new Ext.Toolbar({
+        items: [{
+            text: "Show/Edit Tree Config",
+            handler: function() {
+                treeConfigWin.show();
+                Ext.getCmp("treeconfig").setValue(treeConfig);
+            }
+        }]
+    });
+    
+    var tree = new Ext.tree.TreePanel({
+        border: true,
+        region: "west",
+        title: "Layers",
+        width: 200,
+        split: true,
+        collapsible: true,
+        collapseMode: "mini",
+        autoScroll: true,
+        loader: new Ext.tree.TreeLoader({
+            clearOnLoad: true
+        }),
+        root: {
+            nodeType: "async",
+            children: Ext.decode(treeConfig)
+        },
+        rootVisible: false,
+        lines: false,
+        bbar: toolbar
+    });
+    
+    // the layer node's radio button with its radiochange event can be used
+    // to set an active layer.
+    var registerRadio = function(node){
+        if(!node.hasListener("radiochange")) {
+            node.on("radiochange", function(node){
+                alert(node.layer.name + " is now the the active layer.");
+            });
+        }
+    }
+    tree.on({
+        "insert": registerRadio,
+        "append": registerRadio,
+        scope: this
+    });
+    
+    new Ext.Viewport({
+        layout: "fit",
+        hideBorders: true,
+        items: {
+            layout: "border",
+            deferredRender: false,
+            items: [mapPanel, tree, {
+                contentEl: "desc",
+                region: "east",
+                bodyStyle: {"padding": "5px"},
+                collapsible: true,
+                collapseMode: "mini",
+                split: true,
+                width: 200,
+                title: "Description"
+            }]
+        }
+    });
+});

Added: sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/BaseLayerContainer.js
===================================================================
--- sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/BaseLayerContainer.js	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/BaseLayerContainer.js	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,65 @@
+/**
+ * Copyright (c) 2008 The Open Planning Project
+ */
+Ext.namespace("GeoExt.tree");
+
+/**
+ * Class: GeoExt.tree.BaseLayerContainer
+ * 
+ * A layer container that will collect all base layers of an OpenLayers map.
+ * Only layers that have displayInLayerSwitcher set to true will be included.
+ * 
+ * To use this node type in JSON config, set nodeType to
+ * "olBaseLayerContainer".
+ * 
+ * Inherits from:
+ * - <GeoExt.tree.LayerContainer>
+ */
+GeoExt.tree.BaseLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, {
+
+    /**
+     * Constructor: GeoExt.tree.BaseLayerContainer
+     * 
+     * Parameters:
+     * config - {Object}
+     */
+    constructor: function(config) {
+        config.text = config.text || "Base Layer";
+        GeoExt.tree.BaseLayerContainer.superclass.constructor.apply(this, arguments);
+    },
+
+    /**
+     * Method: addLayerNode
+     * Adds a child node representing a base layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to add a node for
+     */
+    addLayerNode: function(layerRecord) {
+        var layer = layerRecord.get("layer");
+        if (layer.isBaseLayer == true) {
+            GeoExt.tree.BaseLayerContainer.superclass.addLayerNode.call(this,
+                layerRecord);
+        }
+    },
+    
+    /**
+     * Method: removeLayerNode
+     * Removes a child node representing a base layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to remove the node for
+     */
+    removeLayerNode: function(layer) {
+        var layer = layerRecord.get("layer");
+        if (layer.isBaseLayer == true) {
+            GeoExt.tree.BaseLayerContainer.superclass.removeLayerNode.call(this,
+                layerRecord);
+    	}
+    }
+});
+
+/**
+ * NodeType: gx_baselayercontainer
+ */
+Ext.tree.TreePanel.nodeTypes.gx_baselayercontainer = GeoExt.tree.BaseLayerContainer;

Added: sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerContainer.js
===================================================================
--- sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerContainer.js	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerContainer.js	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,244 @@
+/**
+ * Copyright (c) 2008 The Open Planning Project
+ */
+Ext.namespace("GeoExt.tree");
+
+/**
+ * Class: GeoExt.tree.LayerContainer
+ * 
+ * A subclass of {Ext.tree.TreeNode} that will collect all layers of an
+ * OpenLayers map. Only layers that have displayInLayerSwitcher set to true
+ * will be included. The childrens' iconCls will be set to "baselayer-icon"
+ * for base layers, and to "layer-icon" for overlay layers.
+ * 
+ * To use this node type in JSON config, set nodeType to "olLayerContainer".
+ * 
+ * Inherits from:
+ * - <Ext.tree.TreeNode>
+ */
+GeoExt.tree.LayerContainer = Ext.extend(Ext.tree.TreeNode, {
+    
+    /**
+     * APIProperty: layerStore
+     * {<GeoExt.data.LayerStore>} The layer store containing layers to be
+     *     displayed in the container.
+     */
+    layerStore: null,
+    
+    /**
+     * APIProperty: defaults
+     * {Object} a configuration object passed to all nodes that this
+     *     LayerContainer creates.
+     */
+    defaults: null,
+
+    /**
+     * Constructor: GeoExt.tree.LayerContainer
+     * 
+     * Parameters:
+     * config - {Object}
+     */
+    constructor: function(config) {
+        this.layerStore = config.layerStore;
+        this.defaults = config.defaults;
+        GeoExt.tree.LayerContainer.superclass.constructor.apply(this, arguments);
+    },
+
+    /**
+     * Method: render
+     * 
+     * Parameters:
+     * bulkRender - {Boolean}
+     */
+    render: function(bulkRender) {
+        if (!this.rendered) {
+            if(!this.layerStore) {
+                this.layerStore = GeoExt.MapPanel.guess().layers;
+            }
+            this.layerStore.each(function(record) {
+                this.addLayerNode(record);
+            }, this);
+            this.layerStore.on({
+                "add": this.onStoreAdd,
+                "remove": this.onStoreRemove,
+                scope: this
+            });
+        }
+        GeoExt.tree.LayerContainer.superclass.render.call(this, bulkRender);
+    },
+    
+    /**
+     * Method: onStoreAdd
+     * Listener for the store's add event.
+     *
+     * Parameters:
+     * store - {Ext.data.Store}
+     * records - {Array(Ext.data.Record)}
+     * index - {Number}
+     */
+    onStoreAdd: function(store, records, index) {
+        if(!this._reordering) {
+            var nodeIndex = this.recordIndexToNodeIndex(index);
+            for(var i=0; i<records.length; ++i) {
+                this.addLayerNode(records[i], nodeIndex);
+            }
+        }
+    },
+    
+    /**
+     * Method: onStoreRemove
+     * Listener for the store's remove event.
+     *
+     * Parameters:
+     * store - {Ext.data.Store}
+     * record - {Ext.data.Record}
+     * index - {Number}
+     */
+    onStoreRemove: function(store, record, index) {
+        if(!this._reordering) {
+            this.removeLayerNode(record);
+        }
+    },
+
+    /**
+     * Method: onDestroy
+     */
+    onDestroy: function() {
+        if(this.layerStore) {
+            this.layerStore.un("add", this.onStoreAdd, this);
+            this.layerStore.un("remove", this.onStoreRemove, this);
+        }
+        GeoExt.tree.LayerContainer.superclass.onDestroy.apply(this, arguments);
+    },
+    
+    /**
+     * Method: recordIndexToNodeIndex
+     * Convert a record index into a child node index.
+     *
+     * Parameters:
+     * index - {Number} The record index in the layer store.
+     *
+     * Returns:
+     * {Number} The appropriate child node index for the record.
+     */
+    recordIndexToNodeIndex: function(index) {
+        var store = this.layerStore;
+        var count = store.getCount();
+        var nodeIndex = -1;
+        for(var i=count-1; i>=0; --i) {
+            if(store.getAt(i).get("layer").displayInLayerSwitcher) {
+                ++nodeIndex;
+                if(index === i) {
+                    break;
+                }
+            }
+        };
+        return nodeIndex;
+    },
+    
+    /**
+     * Method: nodeIndexToRecordIndex
+     * Convert a child node index to a record index.
+     *
+     * Parameters:
+     * index - {Number} The child node index.
+     *
+     * Returns:
+     * {Number} The appropriate record index for the node.
+     */
+    nodeIndexToRecordIndex: function(index) {
+        var store = this.layerStore;
+        var count = store.getCount();
+        var nodeIndex = -1;
+        for(var i=count-1; i>=0; --i) {
+            if(store.getAt(i).get("layer").displayInLayerSwitcher) {
+                ++nodeIndex;
+                if(index === nodeIndex) {
+                    break;
+                }
+            }
+        }
+        return i;
+    },
+    
+    /**
+     * Method: addLayerNode
+     * Adds a child node representing a layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to add the layer for
+     * index - {Number} Optional index for the new layer.  Default is 0.
+     */
+    addLayerNode: function(layerRecord, index) {
+        index = index || 0;
+        var layer = layerRecord.get("layer");
+        if (layer.displayInLayerSwitcher === true) {
+            var node = new GeoExt.tree.LayerNode(Ext.applyIf({
+                iconCls: layer.isBayeLayer ? 'baselayer-icon' : 'layer-icon',
+                layer: layer,
+                layerStore: this.layerStore
+            }, this.defaults));
+            var sibling = this.item(index);
+            if(sibling) {
+                this.insertBefore(node, sibling);
+            } else {
+                this.appendChild(node);
+            }
+            node.on("move", this.onChildMove, this);
+        }
+    },
+    
+    /**
+     * Method: removeLayerNode
+     * Removes a child node representing a layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to remove the node for
+     */
+    removeLayerNode: function(layerRecord) {
+        var layer = layerRecord.get("layer");
+        if (layer.displayInLayerSwitcher == true) {
+            var node = this.findChildBy(function(node) {
+                return node.layer == layer;
+            });
+            if(node) {
+                node.un("move", this.onChildMove, this);
+                node.remove();
+            }
+    	}
+    },
+    
+    /**
+     * Method: onChildMove
+     * Listener for child node "move" events.  This updates the order of
+     *     records in the store based on new node order if the node has not
+     *     changed parents.
+     *
+     * Parameters:
+     * tree - {Ext.data.Tree}
+     * node - {Ext.tree.TreeNode}
+     * oldParent - {Ext.tree.TreeNode}
+     * newParent - {Ext.tree.TreeNode}
+     * index {Number}
+     */
+    onChildMove: function(tree, node, oldParent, newParent, index) {
+        if(oldParent === newParent) {
+            var newRecordIndex = this.nodeIndexToRecordIndex(index);
+            var oldRecordIndex = this.layerStore.findBy(function(record) {
+                return record.get("layer") === node.layer;
+            });
+            // remove the record and re-insert it at the correct index
+            var record = this.layerStore.getAt(oldRecordIndex);
+            this._reordering = true;
+            this.layerStore.remove(record);
+            this.layerStore.insert(newRecordIndex, [record]);
+            delete this._reordering;
+        }
+    }
+    
+});
+
+/**
+ * NodeType: gx_layercontainer
+ */
+Ext.tree.TreePanel.nodeTypes.gx_layercontainer = GeoExt.tree.LayerContainer;

Added: sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerNode.js
===================================================================
--- sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerNode.js	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/LayerNode.js	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,307 @@
+/**
+ * Copyright (c) 2008 The Open Planning Project
+ */
+Ext.namespace("GeoExt.tree");
+
+/**
+ * Class: GeoExt.tree.LayerNodeUI
+ * 
+ * Inherits from:
+ * - <GeoExt.tree.TristateCheckboxNodeUI>
+ */
+GeoExt.tree.LayerNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {
+    
+    /**
+     * Property: radio
+     * {Ext.Element}
+     */
+    radio: null,
+    
+    /**
+     * Constructor: GeoExt.tree.LayerNodeUI
+     * 
+     * Parameters:
+     * config - {Object}
+     */
+    constructor: function(config) {
+        GeoExt.tree.LayerNodeUI.superclass.constructor.apply(this, arguments);
+    },
+    
+    /**
+     * Method: render
+     * 
+     * Parameters:
+     * bulkRender - {Boolean}
+     */
+    render: function(bulkRender) {
+        GeoExt.tree.LayerNodeUI.superclass.render.call(this, bulkRender);
+        var a = this.node.attributes;
+        if (a.radioGroup && this.radio !== undefined) {
+            this.radio = Ext.DomHelper.insertAfter(this.checkbox,
+                ['<input type="radio" class="x-tree-node-radio" name="',
+                a.radioGroup, '_radio"></input>'].join(""));
+        }
+    },
+    
+    /**
+     * Method: onClick
+     * 
+     * Parameters:
+     * e - {Object}
+     */
+    onClick: function(e) {
+        if (e.getTarget('input[type=radio]', 1)) {
+            this.fireEvent("radiochange", this.node);
+        } else {
+            GeoExt.tree.LayerNodeUI.superclass.onClick.call(this, e);
+        }
+    },
+    
+    /**
+     * Method: toggleCheck
+     * 
+     * Parameters:
+     * value - {Boolean}
+     */
+    toggleCheck: function(value) {
+        GeoExt.tree.LayerNodeUI.superclass.toggleCheck.call(this, value);
+        var node = this.node;
+        var layer = this.node.layer;
+        node.visibilityChanging = true;
+        if(this.checkbox && (layer.getVisibility() != this.isChecked())) {
+            layer.setVisibility(this.isChecked());
+        }
+        node.visibilityChanging = false;
+    },
+    
+    /**
+     * Method: onDestroy
+     */
+    onDestroy: function() {
+        delete this.radio;
+        GeoExt.tree.LayerNodeUI.superclass.onDestroy.call(this);
+    }
+});
+
+
+/**
+ * Class: GeoExt.tree.LayerNode
+ * 
+ * A subclass of {<GeoExt.tree.TristateCheckboxNode>} that is connected to an
+ * {OpenLayers.Layer} by setting the node's layer property. Checking or
+ * unchecking the checkbox of this node will directly affect the layer and
+ * vice versa. The default iconCls for this node's icon is "layer-icon",
+ * unless it has children.
+ * 
+ * Setting the node's layer property to a layer name instead of an object
+ * will also work. As soon as a layer is found, it will be stored as layer
+ * property in the attributes hash.
+ * 
+ * The node's text property defaults to the layer name.
+ * 
+ * If the node has a radioGroup attribute configured, the node will be
+ * rendered with a radio button. This works like the checkbox with the
+ * checked attribute, but radioGroup is a string that identifies the options
+ * group. Clicking the radio button will fire a radioChange event.
+ * 
+ * To use this node type in a JSON config, set nodeType to "gx_layer".
+ * 
+ * Inherits from:
+ * - Ext.tree.TreeNode
+ */
+GeoExt.tree.LayerNode = Ext.extend(Ext.tree.TreeNode, {
+    
+    /**
+     * APIProperty: layer
+     * {OpenLayers.Layer|String} The layer that this layer node will
+     *     be bound to, or the name of the layer (has to match the layer's
+     *     name property). If a layer name is provided, <layerStore> also has
+     *     to be provided.
+     */
+    layer: null,
+    
+    /**
+     * APIProperty: layerStore
+     * {<GeoExt.data.LayerStore|"auto"} The layer store containing the layer
+     *     that this node represents. If set to "auto", the node will query
+     *     the ComponentManager for a <GeoExt.MapPanel>, take the first one it
+     *     finds and take its layer store. This property is only required
+     *     if <layer> is provided as a string.
+     */
+    layerStore: null,
+    
+    /**
+     * APIProperty: childNodeType
+     * {Ext.tree.Node|String} node class or nodeType of childnodes for this
+     *     node. A node type provided here needs to have an add method, with
+     *     a scope argument. This method will be run by this node in the
+     *     context of this node, to create child nodes.
+     */
+    childNodeType: null,
+    
+    /**
+     * Property: visibilityChanging
+     * {Boolean} private property indicating layer visibility being changed
+     *     by this node in order to prevent visibilitychanged events bouncing
+     *     back and forth
+     */
+    visibilityChanging: false,
+    
+    /**
+     * Constructor: GeoExt.tree.LayerNode
+     * 
+     * Parameters:
+     * config - {Object}
+     */
+    constructor: function(config) {
+        config.leaf = config.leaf || !config.children;
+        
+        config.iconCls = typeof config.iconCls == "undefined" &&
+            !config.children ? "layer-icon" : config.iconCls;
+        // checked status will be set by layer event, so setting it to false
+        // to always get the checkbox rendered
+        config.checked = false;
+        
+        this.defaultUI = this.defaultUI || GeoExt.tree.LayerNodeUI;
+        this.addEvents(
+            /**
+             * Event: radiochange
+             * Notifies listener when a differnt radio button was selected.
+             * Will be called with the currently selected node as argument.
+             */
+            "radiochange"
+        );
+        
+        Ext.apply(this, {
+            layer: config.layer,
+            layerStore: config.layerStore,
+            childNodeType: config.childNodeType
+        });
+        GeoExt.tree.LayerNode.superclass.constructor.apply(this, arguments);
+    },
+
+    /**
+     * Method: render
+     * 
+     * Properties:
+     * bulkRender {Boolean} - optional
+     * layer {<OpenLayers.Layer>} - optional
+     */
+    render: function(bulkRender) {
+        var layer = this.layer instanceof OpenLayers.Layer && this.layer;
+        if(!layer) {
+            // guess the store if not provided
+            if(!this.layerStore || this.layerStore == "auto") {
+                this.layerStore = GeoExt.MapPanel.guess().layers;
+            }
+            // now we try to find the layer by its name in the layer store
+            var i = this.layerStore.findBy(function(o) {
+                return o.get("title") == this.layer;
+            }, this);
+            if(i != -1) {
+                // if we found the layer, we can assign it and everything
+                // will be fine
+                layer = this.layerStore.getAt(i).get("layer");
+            }
+        }
+        if (!this.rendered || !layer) {
+            var ui = this.getUI();
+            
+            if(layer) {
+                this.layer = layer;
+                if(!this.text) {
+                    this.text = layer.name;
+                }
+                
+                if(this.childNodeType) {
+                    this.addChildNodes();
+                }
+                
+                ui.show();
+                ui.toggleCheck(layer.getVisibility());
+                this.addVisibilityEventHandlers();
+                // set initial checked status
+                this.attributes.checked = layer.getVisibility();
+            } else {
+                ui.hide();
+            }
+            
+            if(this.layerStore instanceof GeoExt.data.LayerStore) {
+                this.addStoreEventHandlers(layer);
+            }            
+        }
+        GeoExt.tree.LayerNode.superclass.render.call(this, bulkRender);
+    },
+    
+    /**
+     * Method: addVisibilityHandlers
+     * Adds handlers that sync the checkbox state with the layer's visibility
+     * state
+     */
+    addVisibilityEventHandlers: function() {
+        this.layer.events.register("visibilitychanged", this, function() {
+            if(!this.visibilityChanging &&
+                    this.attributes.checked != this.layer.getVisibility()) {
+                this.getUI().toggleCheck(this.layer.getVisibility());
+            }
+        });
+        this.on({
+            "checkchange": function(node, checked) {
+                if (checked && this.layer.isBaseLayer && this.layer.map) {
+                    this.layer.map.setBaseLayer(this.layer);
+                }
+                this.layer.setVisibility(checked);
+            },
+            scope: this
+        });
+    },
+    
+    /**
+     * Method: addStoreEventHandlers
+     * Adds handlers that make sure the node disappeares when the layer is
+     * removed from the store, and appears when it is re-added.
+     */
+    addStoreEventHandlers: function() {
+        this.layerStore.on({
+            "add": function(store, records, index) {
+                var l;
+                for(var i=0; i<records.length; ++i) {
+                    l = records[i].get("layer");
+                    if(this.layer == l) {
+                        this.getUI().show();
+                    } else if (this.layer == l.name) {
+                        // layer is a string, which means the node has not yet
+                        // been rendered because the layer was not found. But
+                        // now we have the layer and can render.
+                        this.render(bulkRender);
+                        return;
+                    }
+                }
+            },
+            "remove": function(store, record, index) {
+                if(this.layer == record.get("layer")) {
+                    this.getUI().hide();
+                }
+            },
+            scope: this
+        });
+    },
+    
+    /**
+     * Method: addChildNodes
+     * Calls the add method of a node type configured as <childNodeType>
+     * to add children.
+     */
+    addChildNodes: function() {
+        if(typeof this.childNodeType == "string") {
+            Ext.tree.TreePanel.nodeTypes[this.childNodeType].add(this);
+        } else if(typeof this.childNodeType.add === "function") {
+            this.childNodeType.add(this);
+        }
+    }
+});
+
+/**
+ * NodeType: gx_layer
+ */
+Ext.tree.TreePanel.nodeTypes.gx_layer = GeoExt.tree.LayerNode;

Added: sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/OverlayLayerContainer.js
===================================================================
--- sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/OverlayLayerContainer.js	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/lib/GeoExt/widgets/tree/OverlayLayerContainer.js	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2008 The Open Planning Project
+ */
+Ext.namespace("GeoExt.tree");
+
+/**
+ * Class: GeoExt.tree.OverlayLayerContainer
+ * 
+ * A layer container that will collect all overlay layers of an OpenLayers map.
+ * Only layers that have displayInLayerSwitcher set to true will be included.
+ * 
+ * To use this node type in JSON config, set nodeType to
+ * "olOverlayLayerContainer".
+ * 
+ * Inherits from:
+ * - <GeoExt.tree.LayerContainer>
+ */
+GeoExt.tree.OverlayLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, {
+
+    /**
+     * Constructor: GeoExt.tree.OverlayLayerContainer
+     * 
+     * Parameters:
+     * config - {Object}
+     */
+    constructor: function(config) {
+        config.text = config.text || "Overlays";
+        GeoExt.tree.OverlayLayerContainer.superclass.constructor.apply(this,
+            arguments);
+    },
+
+    /**
+     * Method: addLayerNode
+     * Adds a child node representing a overlay layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to add a node for
+     */
+    addLayerNode: function(layerRecord) {
+        var layer = layerRecord.get("layer");
+        if (layer.isBaseLayer == false) {
+            GeoExt.tree.OverlayLayerContainer.superclass.addLayerNode.call(this,
+                layerRecord);
+        }
+    },
+    
+    /**
+     * Method: removeLayerNode
+     * Removes a child node representing an overlay layer of the map
+     * 
+     * Parameters:
+     * layerRecord - {Ext.data.Record} the layer record to remove the node for
+     */
+    removeLayerNode: function(layerRecord) {
+        var layer = layerRecord.get("layer");
+        if (layer.isBaseLayer == false) {
+            GeoExt.tree.OverlayLayerContainer.superclass.removeLayerNode.call(
+                this, layerRecord);
+    	}
+    }
+});
+
+/**
+ * NodeType: gx_overlaylayercontainer
+ */
+Ext.tree.TreePanel.nodeTypes.gx_overlaylayercontainer = GeoExt.tree.OverlayLayerContainer;

Added: sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerContainer.html
===================================================================
--- sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerContainer.html	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerContainer.html	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,65 @@
+<html>
+    <head>
+        <script src="../../../../../../openlayers/lib/OpenLayers.js"></script>
+        <script src="../../../../../../ext/adapter/ext/ext-base.js"></script>
+        <script src="../../../../../../ext/ext-all-debug.js"></script>
+        <script src="../../../../../lib/GeoExt.js"></script>
+    
+        <script>
+        
+        function test_constructor(t) {
+            
+            t.plan(2);
+            
+            var store = new GeoExt.data.LayerStore();
+            var defaults = {};
+            
+            var node = new GeoExt.tree.LayerContainer({
+                layerStore: store,
+                defaults: defaults
+            });
+            
+            t.ok(node.layerStore === store, "layerStore set");
+            t.ok(node.defaults === defaults, "defaults set");
+            
+            node.destroy();
+            
+        }
+        
+        function test_render(t) {
+            
+            t.plan(2);
+            
+            var map = new OpenLayers.Map({
+                div: "map",
+                allOverlays: true
+            });
+            var layer = new OpenLayers.Layer();
+            map.addLayer(layer);
+            
+            var store = new GeoExt.data.LayerStore({
+                map: map
+            });
+            
+            var node = new GeoExt.tree.LayerContainer({
+                layerStore: store
+            });
+            
+            var panel = new Ext.tree.TreePanel({
+                renderTo: document.body,
+                root: node
+            });
+            
+            t.eq(node.childNodes && node.childNodes.length, 1, "container has one child");
+            t.ok(node.firstChild.layer === layer, "child layer is correct");
+
+            node.destroy();
+            
+        }
+        
+        </script>
+    </head>
+    <body>
+        <div id="map" style="width: 100px; height: 100px;"></div>
+    </body>
+</html>
\ No newline at end of file

Added: sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html
===================================================================
--- sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html	                        (rev 0)
+++ sandbox/bartvde/legend/geoext/tests/lib/GeoExt/widgets/tree/LayerNode.html	2009-05-11 07:06:22 UTC (rev 673)
@@ -0,0 +1,78 @@
+<html>
+    <head>
+        <script src="../../../../../../openlayers/lib/OpenLayers.js"></script>
+        <script src="../../../../../../ext/adapter/ext/ext-base.js"></script>
+        <script src="../../../../../../ext/ext-all-debug.js"></script>
+        <script src="../../../../../lib/GeoExt.js"></script>
+    
+        <script>
+        
+        function test_constructor(t) {
+            
+            t.plan(1);
+            
+            var store = new GeoExt.data.LayerStore();
+            
+            var node = new GeoExt.tree.LayerNode({
+                layer: "foo",
+                layerStore: store
+            });
+            
+            t.ok(node.layerStore === store, "layerStore set");
+            
+            node.destroy();
+            
+        }
+        
+        function test_render(t) {
+            
+            t.plan(5);
+            
+            var layer = new OpenLayers.Layer("foo");
+            
+            var mapPanel = new GeoExt.MapPanel({
+                layers: [layer],
+                allOverlays: true
+            });
+            
+            var node = new GeoExt.tree.LayerNode({
+                layer: "foo",
+                radioGroup: "group",
+                childNodeType: {
+                    add: function() {
+                        t.ok(true, "add function of childNodeType called");
+                    }
+                }
+            });
+            
+            node.on("radiochange", function() {
+                t.ok(arguments[0] === node, "radiochange event triggered with the selected node as first argument");
+            });
+                        
+            var panel = new Ext.tree.TreePanel({
+                renderTo: "tree",
+                root: node
+            });
+            
+            mapPanel.on("render", function() {
+                t.ok(node.layer === layer, "layer found on detected map panel");
+                
+                t.ok(node.ui.radio, "node has a radio button");
+                // simulate a click event for testing the radiochange event
+                node.ui.onClick({getTarget: function() {return true}});
+                
+                node.ui.toggleCheck();
+                t.eq(layer.visibility, false, "unchecking node hides layer");
+            });
+
+            mapPanel.render("map");
+            
+        }
+        
+        </script>
+    </head>
+    <body>
+        <div id="map" style="width: 100px; height: 100px;"></div>
+        <div id="tree" style="width: 100px; height: 100px;"></div>
+    </body>
+</html>
\ No newline at end of file



More information about the Commits mailing list