[Commits] r2515 - sandbox/mapgears/geoext.ux/ux/LayerTreeBuilder/lib/GeoExt.ux/widgets/tree

commits at geoext.org commits at geoext.org
Tue Dec 7 21:16:37 CET 2010


Author: adube
Date: 2010-12-07 21:16:37 +0100 (Tue, 07 Dec 2010)
New Revision: 2515

Modified:
   sandbox/mapgears/geoext.ux/ux/LayerTreeBuilder/lib/GeoExt.ux/widgets/tree/LayerTreeBuilder.js
Log:
LayerTreeBuilder - 'checkableGroupNodes' new property to have checkboxes for group nodes

Modified: sandbox/mapgears/geoext.ux/ux/LayerTreeBuilder/lib/GeoExt.ux/widgets/tree/LayerTreeBuilder.js
===================================================================
--- sandbox/mapgears/geoext.ux/ux/LayerTreeBuilder/lib/GeoExt.ux/widgets/tree/LayerTreeBuilder.js	2010-12-05 08:22:00 UTC (rev 2514)
+++ sandbox/mapgears/geoext.ux/ux/LayerTreeBuilder/lib/GeoExt.ux/widgets/tree/LayerTreeBuilder.js	2010-12-07 20:16:37 UTC (rev 2515)
@@ -27,6 +27,13 @@
      */
     vectorLegendNodes: true,
 
+    /** api: config[checkableGroupNodes]
+     * ``Boolean``
+     * Defaults to true.  Whether LayerContainer and TreeNode nodes used as
+     * group directories should be checkable or not.
+     */
+    checkableGroupNodes: true,
+
     /** api: config[layerStore]
      *  ``GeoExt.data.LayerStore``
      *  The layer store containing layers to be displayed in the tree. 
@@ -60,51 +67,42 @@
             "remove": this.onLayerRemoved,
             scope: this
         });
+
+        this.layerStore.treeBuilder = this;
     },
 
     onLayerRemoved: function(store, records, index){
         //todo: remove empty groups
     },
 
-    onLayerAdded: function(store, records, index){
+    onLayerAdded: function(store, records, index) {
+        // first, validate all 'group' options
         Ext.each(records, function(record, index) {
             var layer = record.getLayer();
-            var group;
-            var groupString;
 
-            // do not add layers that have 'displayInLayerSwitcher' property
-            // set to false.  Remove its 'group' property and option as well.
-            if(layer.displayInLayerSwitcher === false)
-            {
-                if(layer.group && layer.options && layer.options.group)
-                {
+            if(layer.displayInLayerSwitcher === false) {
+                if(layer.group && layer.options && layer.options.group) {
                     delete layer.group;
                     delete layer.options.group;
                 }
                 return;
-            }
-
-            // get group array and group string
-            if(layer.options && layer.options.group !== undefined)
-            {
-                group = layer.options.group.split('/');
-                groupString = layer.options.group;
-            }
-            else
-            {
-                groupString = (layer.isBaseLayer)
+            } else if(layer.options && layer.options.group === undefined) {
+                layer.options.group = (layer.isBaseLayer)
                     ? this.baseLayersText : this.otherLayersText;
-                group = [groupString];
-                layer.options.group = groupString;
             }
+        }, this);
 
-            // get layer title
-            var layerTitle = record.data.title
-            if(layer.options && layer.options.title)
-            {
-                layerTitle = layer.options.title;
+        // then, create the nodes according to the records
+        Ext.each(records, function(record, index) {
+            var layer = record.getLayer();
+
+            if(layer.displayInLayerSwitcher === false) {
+                return;
             }
 
+            var group = layer.options.group.split('/');
+            var groupString = layer.options.group;
+
             // if layer has no GROUP
             if (groupString === "") {
                 var layerNode = {
@@ -127,6 +125,7 @@
     addGroupNodes: function(groups, parentNode, groupString, layerRecord){
         var group = groups.shift();
         var childNode = this.getNodeByText(parentNode, group);
+        var layer = layerRecord.getLayer();
 
         // if the childNode doesn't exist, we need to create and append it
         if (!childNode) {
@@ -134,10 +133,19 @@
             // 'LayerContainer'
             if (groups.length == 0) {
                 var createNode;
-                var layer = layerRecord.getLayer();
+
+                // default 'baseLayers' and 'otherLayers' groups don't have
+                // checkboxes
+                if (group == this.baseLayersText ||
+                    group == this.otherLayersText)
+                {
+                    createNode = function(attr) {
+                        return GeoExt.tree.LayerLoader.prototype.createNode.call(this, attr);
+                    }
+                }
                 // WMS and Vector layers can have legend nodes if according
                 // property is enabled
-                if (layer instanceof OpenLayers.Layer.WMS
+                else if (layer instanceof OpenLayers.Layer.WMS
                     && this.wmsLegendNodes)
                 {
                     createNode = function(attr) {
@@ -148,8 +156,16 @@
                           layerRecord: layerRecord,
                           showTitle: false,
                           hidden: !layer.visibility,
-                          cls: "gx-layertreebuilder-legend"
+                          cls: "gx-layertreebuilder-legend",
                         };
+                        if (this.store.treeBuilder.checkableGroupNodes && 
+                            !layer.isBaseLayer) {
+                            Ext.apply(attr, {
+                                listeners: {
+                                    checkchange: this.store.treeBuilder.checkChange
+                                }
+                            });
+                        }
                         return GeoExt.tree.LayerLoader.prototype.createNode.call(this, attr);
                     }
                 } else if (layer instanceof OpenLayers.Layer.Vector
@@ -165,20 +181,41 @@
                           hidden: !layer.visibility,
                           cls: "gx-layertreebuilder-legend"
                         };
+                        if (this.store.treeBuilder.checkableGroupNodes && 
+                            !layer.isBaseLayer) {
+                            Ext.apply(attr, {
+                                listeners: {
+                                    checkchange: this.store.treeBuilder.checkChange
+                                }
+                            });
+                        }
                         return GeoExt.tree.LayerLoader.prototype.createNode.call(this, attr);
                     }
                 } else {
                     createNode = function(attr) {
+                        if (this.store.treeBuilder.checkableGroupNodes && 
+                            !layer.isBaseLayer) {
+                            Ext.apply(attr, {
+                                listeners: {
+                                    checkchange: this.store.treeBuilder.checkChange
+                                }
+                            });
+                        }
                         return GeoExt.tree.LayerLoader.prototype.createNode.call(this, attr);
                     }
                 }
-                
-                parentNode.appendChild({
+
+                childNode = {
                     text: group,
                     layerStore: this.layerStore,
                     allowDrag: false,
                     nodeType: 'gx_layercontainer',
                     leaf: false,
+                    listeners: {
+                      insert: this.onLayerContainerNodeInsert,
+                      append: this.onLayerContainerNodeAppend,
+                      scope: this
+                    },
                     loader: {
                         filter: function(record) {
                             return record.getLayer().options.group == groupString;
@@ -188,24 +225,61 @@
                         },
                         createNode: createNode
                     }
-                });
+                };
             } else {
                 // else, create and append a simple node...
-                parentNode.appendChild({
+                childNode = {
                     text: group,
                     leaf: false,
+                    listeners: {
+                      append: this.onTreeNodeAppend,
+                      scope: this
+                    },
                     allowDrag: false,
                     nodeType: "node"
-                });
+                };
             }
 
+            // apply checkbox if option is set
+            if (this.checkableGroupNodes && group != this.baseLayersText &&
+                group != this.otherLayersText && (!layer || !layer.isBaseLayer))
+            {
+                Ext.apply(childNode, {checked: false});
+                Ext.apply(childNode.listeners, {
+                    'checkchange' : function(node, checked) {
+                        // If a parent node is unchecked, uncheck all
+                        // the children
+                        if (node.getUI().isChecked()) {
+                            node.expand();
+                            node.eachChild(function(child){
+                               child.ui.toggleCheck(true);
+                            });
+                        }
+                        if (!node.getUI().isChecked())
+                        {
+                            node.expand();
+                            node.eachChild(function(child) {
+                                child.ui.toggleCheck(false);
+                            });
+                        }
+                    }
+                });                    
+            }
+
+            parentNode.appendChild(childNode);
+
             childNode = this.getNodeByText(parentNode, group);
         }
 
+        // if node contains any child or grand-child with a visible layer,
+        // expand it
+        if (layer && layer.visibility) {
+            childNode.expand();
+        }
+
         if (groups.length != 0){
             this.addGroupNodes(groups, childNode, groupString, layerRecord);
         }
-
     },
 
     getNodeByText: function(node, text){
@@ -217,5 +291,161 @@
             }
         }
         return false;
+    },
+
+    checkChange: function(node, checked) {
+        // Map of all the node ids not yet visited by updateNodeCheckbox
+        var unvisitedNodeIds = {};
+        var tree = node.getOwnerTree();
+
+        //
+        // This function updates the node checkbox according to the status of
+        // the descendants. It must be called on a node checkbox nodes only.
+        //
+        // It is called recursively and returns a boolean:
+        // - If the node has no children checkboxes, the status of the checkbox
+        //   is returned
+        // - Otherwise, it returns true if all the children witch checkbox are
+        //   checked or false in the other case.
+        //
+        // As a side effect, it will update the checkbox state of the node, and
+        //  remove visited node ids from the unvisitedNodeIds variable, to
+        //  prevent visiting nodes multiple times.
+
+        tree.setNodeChecked=  function(nodeOrId, checked, fireEvent) {
+            var node = (nodeOrId instanceof Ext.data.Node) ?
+            nodeOrId : this.getNodeById(nodeOrId);
+
+            if (!node || typeof(node.attributes.checked) != "boolean") {
+                return;
+            }
+
+            if (checked === undefined) {
+                checked = !node.attributes.checked;
+            }
+
+            // update model
+            node.attributes.checked = checked;
+
+            // sync ui
+            if (node.ui && node.ui.checkbox) {
+                node.ui.checkbox.checked = checked;
+            }
+
+            // fire event if required
+            if (fireEvent || (fireEvent === undefined))  {
+                node.fireEvent('checkchange', node, checked);
+            }
+        }
+
+        function updateNodeCheckbox(node) {
+            if (typeof(node.attributes.checked) != "boolean") {
+                throw new Error(arguments.callee.name +
+                                " should only be called on checkbox nodes");
+            }
+
+            var checkboxChildren = [];
+            node.eachChild(function(child) {
+                if (typeof(child.attributes.checked) == "boolean")
+                    checkboxChildren.push(child);
+            }, this);
+
+            // If this node has no children with checkbox, its checked state
+            // will be returned.
+            if (checkboxChildren.length == 0) {
+                return node.attributes.checked;
+            }
+
+            var allChecked = true;
+            Ext.each(checkboxChildren, function(child) {
+                    if (!updateNodeCheckbox(child)) {
+                        allChecked = false;
+                        return false;
+                    }
+                }, this);
+
+            tree.setNodeChecked(node, allChecked, false);
+            delete unvisitedNodeIds[node.id];
+
+            return allChecked;
+        }
+
+        var checkboxNodes = [];
+
+        tree.getRootNode().cascade(function(node) {
+                if (typeof(node.attributes.checked) == "boolean") {
+                    checkboxNodes.push(node);
+                    unvisitedNodeIds[node.id] = true;
+                }
+            }, this);
+
+        // taking node from the tree order (using shift) should be more
+        // efficient
+        var node;
+        while (node = checkboxNodes.shift()) {
+            if (unvisitedNodeIds[node.id])
+                updateNodeCheckbox(node);
+        }
+    },
+
+    onLayerContainerNodeInsert: function(tree, parentNode, childNode, refNode) {
+        this.validateLayerContainerStatus(parentNode);
+    },
+
+    onLayerContainerNodeAppend: function(tree, parentNode, childNode, index) {
+        this.validateLayerContainerStatus(parentNode);
+    },
+
+    validateLayerContainerStatus: function(node) {
+        var show;
+        Ext.each(node.childNodes, function(childNode, index) {
+            show = true;
+
+            visibility = childNode.layer.visibility;
+            if (!childNode.layer.visibility) {
+                show = false;
+                return false;
+            }
+        });
+
+        // check the checkbox (if any)
+        var checkbox = node.getUI().checkbox;
+        if (checkbox) {
+            checkbox.checked = (show) ? true : false;
+        }
+
+        // expand this node and all its parents
+        show && node.ensureVisible();
+
+        node.parentNode && this.validateTreeNodeStatus(node.parentNode);
+    },
+
+    onTreeNodeAppend: function(tree, parentNode, childNode, index) {
+        this.validateTreeNodeStatus(parentNode);
+    },
+
+    validateTreeNodeStatus: function(node) {
+        var show;
+
+        if (!this.checkableGroupNodes || node.isRoot) {
+            return;
+        }
+
+        Ext.each(node.childNodes, function(childNode, index) {
+            show = true;
+            var checkbox = childNode.getUI().checkbox;
+            if (checkbox && !checkbox.checked) {
+                show = false;
+                return false;
+            }
+        });
+
+        var checkbox = node.getUI().checkbox;
+        if (checkbox) {
+            checkbox.checked = (show) ? true : false;
+        }
+
+        node.parentNode && this.validateTreeNodeStatus(node.parentNode);
     }
+
 });



More information about the Commits mailing list