[mapguide-commits] r7017 - in trunk/Tools/Maestro: Maestro.Editors/MapDefinition Maestro.MapViewer

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Fri Sep 14 05:56:48 PDT 2012


Author: jng
Date: 2012-09-14 05:56:47 -0700 (Fri, 14 Sep 2012)
New Revision: 7017

Added:
   trunk/Tools/Maestro/Maestro.MapViewer/LegendPresenter.cs
Modified:
   trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapDefinitionEditorCtrl.cs
   trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapEditorLegend.cs
   trunk/Tools/Maestro/Maestro.MapViewer/Legend.Designer.cs
   trunk/Tools/Maestro/Maestro.MapViewer/Legend.cs
   trunk/Tools/Maestro/Maestro.MapViewer/Legend.resx
   trunk/Tools/Maestro/Maestro.MapViewer/Maestro.MapViewer.csproj
Log:
#2018: Port over mg-desktop viewer enhancements to the Maestro MapViewer component. This includes the layerdef/icon caching and background construction of TreeNode objects.

Modified: trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapDefinitionEditorCtrl.cs
===================================================================
--- trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapDefinitionEditorCtrl.cs	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapDefinitionEditorCtrl.cs	2012-09-14 12:56:47 UTC (rev 7017)
@@ -31,6 +31,7 @@
 using OSGeo.MapGuide.MaestroAPI;
 using OSGeo.MapGuide.ObjectModels.LayerDefinition;
 using OSGeo.MapGuide.MaestroAPI.Resource;
+using Maestro.MapViewer.Model;
 
 namespace Maestro.Editors.MapDefinition
 {
@@ -123,11 +124,13 @@
 
         private void legendCtrl_NodeSelected(object sender, TreeNode node)
         {
-            var layer = node.Tag as Maestro.MapViewer.Legend.LayerNodeMetadata;
+            var layer = node.Tag as LayerNodeMetadata;
+            var group = node.Tag as GroupNodeMetadata;
             //Nothing to edit for theme rule nodes
-            if (layer != null && layer.IsThemeRule)
-                return;
-            propGrid.SelectedObject = node.Tag;
+            if (layer == null && group == null)
+                propGrid.SelectedObject = null;
+            else
+                propGrid.SelectedObject = node.Tag;
         }
 
         private void drawOrderCtrl_LayerChanged(object sender, RuntimeMapLayer layer)
@@ -143,7 +146,7 @@
 
         private void legendCtrl_NodeDeleted(object sender, TreeNode node)
         {
-            var layer = node.Tag as Maestro.MapViewer.Legend.LayerNodeMetadata;
+            var layer = node.Tag as LayerNodeMetadata;
             //Nothing to edit for theme rule nodes
             if (layer != null && layer == propGrid.SelectedObject)
                 propGrid.SelectedObject = null;

Modified: trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapEditorLegend.cs
===================================================================
--- trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapEditorLegend.cs	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.Editors/MapDefinition/LiveMapEditorLegend.cs	2012-09-14 12:56:47 UTC (rev 7017)
@@ -29,6 +29,7 @@
 using OSGeo.MapGuide.MaestroAPI.Mapping;
 using OSGeo.MapGuide.MaestroAPI.Resource;
 using OSGeo.MapGuide.ObjectModels.LayerDefinition;
+using Maestro.MapViewer.Model;
 
 namespace Maestro.Editors.MapDefinition
 {
@@ -133,7 +134,7 @@
                 var node = legendCtrl.SelectedNode;
                 if (node != null)
                 {
-                    var grp = node.Tag as Legend.GroupNodeMetadata;
+                    var grp = node.Tag as GroupNodeMetadata;
                     if (grp != null)
                     {
                         using (var picker = new ResourcePicker(map.CurrentConnection.ResourceService, ResourceTypes.LayerDefinition, ResourcePickerMode.OpenResource))
@@ -164,10 +165,10 @@
                 var node = legendCtrl.SelectedNode;
                 if (node != null)
                 {
-                    var grp = node.Tag as Legend.GroupNodeMetadata;
+                    var grp = node.Tag as GroupNodeMetadata;
                     if (grp != null)
                     {
-                        var group = map.Groups[grp.Group];
+                        var group = map.Groups[grp.Name];
                         if (group != null)
                         {
                             map.Groups.Remove(group);
@@ -186,8 +187,8 @@
                 var node = legendCtrl.SelectedNode;
                 if (node != null)
                 {
-                    var lyr = node.Tag as Legend.LayerNodeMetadata;
-                    if (lyr != null && !lyr.IsThemeRule)
+                    var lyr = node.Tag as LayerNodeMetadata;
+                    if (lyr != null)
                     {
                         var layer = map.Layers[lyr.Name];
                         if (layer != null)

Modified: trunk/Tools/Maestro/Maestro.MapViewer/Legend.Designer.cs
===================================================================
--- trunk/Tools/Maestro/Maestro.MapViewer/Legend.Designer.cs	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.MapViewer/Legend.Designer.cs	2012-09-14 12:56:47 UTC (rev 7017)
@@ -31,6 +31,7 @@
             this.components = new System.ComponentModel.Container();
             this.trvLegend = new System.Windows.Forms.TreeView();
             this.imgLegend = new System.Windows.Forms.ImageList(this.components);
+            this.bgLegendUpdate = new System.ComponentModel.BackgroundWorker();
             this.SuspendLayout();
             // 
             // trvLegend
@@ -61,6 +62,11 @@
             this.imgLegend.ImageSize = new System.Drawing.Size(16, 16);
             this.imgLegend.TransparentColor = System.Drawing.Color.Transparent;
             // 
+            // bgLegendUpdate
+            // 
+            this.bgLegendUpdate.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bgLegendUpdate_DoWork);
+            this.bgLegendUpdate.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bgLegendUpdate_RunWorkerCompleted);
+            // 
             // Legend
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -76,5 +82,6 @@
 
         private System.Windows.Forms.TreeView trvLegend;
         private System.Windows.Forms.ImageList imgLegend;
+        private System.ComponentModel.BackgroundWorker bgLegendUpdate;
     }
 }

Modified: trunk/Tools/Maestro/Maestro.MapViewer/Legend.cs
===================================================================
--- trunk/Tools/Maestro/Maestro.MapViewer/Legend.cs	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.MapViewer/Legend.cs	2012-09-14 12:56:47 UTC (rev 7017)
@@ -31,12 +31,13 @@
 using OSGeo.MapGuide.MaestroAPI.Services;
 using System.Xml;
 using System.IO;
+using Maestro.MapViewer.Model;
 
 namespace Maestro.MapViewer
 {
     public delegate void NodeEventHandler(object sender, TreeNode node);
 
-    public partial class Legend : UserControl
+    public partial class Legend : UserControl, INotifyPropertyChanged
     {
         const string IMG_BROKEN = "lc_broken";
         const string IMG_DWF = "lc_dwf";
@@ -90,6 +91,7 @@
         void OnMapLoaded(object sender, EventArgs e)
         {
             _map = _viewer.GetMap();
+            _presenter = new LegendControlPresenter(this, _map);
         }
 
         void OnMapRefreshed(object sender, EventArgs e)
@@ -101,16 +103,18 @@
         private Dictionary<string, RuntimeMapGroup> _groups = new Dictionary<string, RuntimeMapGroup>();
         private Dictionary<string, string> _layerDefinitionContents = new Dictionary<string, string>();
 
-        private bool GetVisibilityFlag(RuntimeMapGroup group)
+        internal bool GetVisibilityFlag(RuntimeMapGroup group)
         {
             return this.ShowAllLayersAndGroups;
         }
 
-        private bool GetVisibilityFlag(RuntimeMapLayer layer)
+        internal bool GetVisibilityFlag(RuntimeMapLayer layer)
         {
             return layer.IsVisibleAtScale(_map.ViewScale);
         }
 
+        private LegendControlPresenter _presenter;
+
         /// <summary>
         /// Refreshes this component
         /// </summary>
@@ -119,174 +123,82 @@
             if (_noUpdate)
                 return;
 
-            if (_map == null)
+            if (_presenter == null)
                 return;
 
-            //System.Diagnostics.Trace.TraceInformation("MgLegend.RefreshLegend()");
-            var scale = _map.ViewScale;
-            var groups = _map.Groups;
-            var layers = _map.Layers;
+            if (IsBusy)
+                return;
 
             ResetTreeView();
+            trvLegend.BeginUpdate();
+            _legendUpdateStopwatch.Start();
+            this.IsBusy = true;
+            bgLegendUpdate.RunWorkerAsync();
+        }
 
-            _layerDefinitionContents.Clear();
-            _layers.Clear();
-            _groups.Clear();
+        private Stopwatch _legendUpdateStopwatch = new Stopwatch();
 
-            IResourceService resSvc = _map.ResourceService;
+        private bool _busy = false;
 
-            trvLegend.BeginUpdate();
-            try
+        [Browsable(false)]
+        public bool IsBusy
+        {
+            get { return _busy; }
+            private set
             {
-                //Process groups first
-                List<RuntimeMapGroup> remainingNodes = new List<RuntimeMapGroup>();
-                for (int i = 0; i < groups.Count; i++)
-                {
-                    var group = groups[i];
-                    _groups.Add(group.ObjectId, group);
-                    if (!this.ShowAllLayersAndGroups && !group.ShowInLegend)
-                        continue;
+                if (_busy.Equals(value))
+                    return;
 
-                    //Add ones without parents first.
-                    if (!string.IsNullOrEmpty(group.Group))
-                    {
-                        remainingNodes.Add(group);
-                    }
-                    else
-                    {
-                        var node = CreateGroupNode(group, GetVisibilityFlag(group));
-                        trvLegend.Nodes.Add(node);
-                    }
+                _busy = value;
+                Trace.TraceInformation("Legend IsBusy: {0}", this.IsBusy);
+                OnPropertyChanged("IsBusy");
+            }
+        }
 
-                    while (remainingNodes.Count > 0)
-                    {
-                        List<RuntimeMapGroup> toRemove = new List<RuntimeMapGroup>();
-                        for (int j = 0; j < remainingNodes.Count; j++)
-                        {
-                            var parentGroupName = remainingNodes[j].Group;
-                            var parentGroup = groups[parentGroupName];
-                            var parentId = parentGroup.ObjectId;
+        public event PropertyChangedEventHandler PropertyChanged;
 
-                            var nodes = trvLegend.Nodes.Find(parentId, false);
-                            if (nodes.Length == 1)
-                            {
-                                var node = CreateGroupNode(remainingNodes[j], GetVisibilityFlag(remainingNodes[j]));
-                                nodes[0].Nodes.Add(node);
-                                toRemove.Add(remainingNodes[j]);
-                            }
-                        }
-                        //Whittle down this list
-                        if (toRemove.Count > 0)
-                        {
-                            foreach (var g in toRemove)
-                            {
-                                remainingNodes.Remove(g);
-                            }
-                        }
-                    }
-                }
+        private void OnPropertyChanged(string propertyName)
+        {
+            var h = this.PropertyChanged;
+            if (h != null)
+                h(this, new PropertyChangedEventArgs(propertyName));
+        }
 
-                //Now process layers
-                for (int i = 0; i < layers.Count; i++)
-                {
-                    var lyr = layers[i];
-                    var ldfId = lyr.LayerDefinitionID;
+        private void bgLegendUpdate_DoWork(object sender, DoWorkEventArgs e)
+        {
+            e.Result = _presenter.CreateNodes();
+        }
 
-                    if (!_layerDefinitionContents.ContainsKey(ldfId.ToString()))
-                    {
-                        _layerDefinitionContents[ldfId.ToString()] = string.Empty;
-                    }
-                }
-
-                //TODO: Surely we can optimize this better
-                var keys = new List<string>(_layerDefinitionContents.Keys);
-                foreach (var lid in keys)
+        private void bgLegendUpdate_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+        {
+            this.IsBusy = bgLegendUpdate.IsBusy;
+            var nodes = e.Result as TreeNode[];
+            if (nodes != null)
+            {
+                //Attach relevant context menus based on attached metadata
+                foreach (var n in nodes)
                 {
-                    using (var sr = new StreamReader(resSvc.GetResourceXmlData(lid)))
+                    var lm = n.Tag as LegendNodeMetadata;
+                    if (lm != null)
                     {
-                        _layerDefinitionContents[lid] = sr.ReadToEnd();
-                    }
-                }
-
-                List<RuntimeMapLayer> remainingLayers = new List<RuntimeMapLayer>();
-                for (int i = 0; i < layers.Count; i++)
-                {
-                    var layer = layers[i];
-                    _layers.Add(layer.ObjectId, layer);
-
-                    bool display = layer.ShowInLegend;
-                    bool visible = layer.IsVisibleAtScale(_map.ViewScale);
-                    if (!this.ShowAllLayersAndGroups && !display)
-                        continue;
-
-                    if (!this.ShowAllLayersAndGroups && !visible)
-                        continue;
-
-                    //Add ones without parents first.
-                    if (!string.IsNullOrEmpty(layer.Group))
-                    {
-                        remainingLayers.Add(layer);
-                    }
-                    else
-                    {
-                        var node = CreateLayerNode(layer, GetVisibilityFlag(layer));
-                        if (node != null)
+                        if (lm.IsGroup)
                         {
-                            trvLegend.Nodes.Add(node);
-                            if (layer.ExpandInLegend)
-                                node.Expand();
+                            n.ContextMenuStrip = this.GroupContextMenu;
                         }
-                    }
-
-                    while (remainingLayers.Count > 0)
-                    {
-                        List<RuntimeMapLayer> toRemove = new List<RuntimeMapLayer>();
-                        for (int j = 0; j < remainingLayers.Count; j++)
+                        else
                         {
-                            var parentGroup = remainingLayers[j].GetParentGroup();
-                            var parentId = parentGroup.ObjectId;
-                            var nodes = trvLegend.Nodes.Find(parentId, false);
-                            if (nodes.Length == 1)
-                            {
-                                var node = CreateLayerNode(remainingLayers[j], GetVisibilityFlag(remainingLayers[j]));
-                                if (node != null)
-                                {
-                                    nodes[0].Nodes.Add(node);
-                                    if (remainingLayers[j].ExpandInLegend)
-                                        node.Expand();
-                                }
-                                toRemove.Add(remainingLayers[j]);
-                            }
+                            var lyrm = n.Tag as LayerNodeMetadata;
+                            if (lyrm != null)
+                                n.ContextMenuStrip = this.LayerContextMenu;
                         }
-                        //Whittle down this list
-                        if (toRemove.Count > 0)
-                        {
-                            foreach (var g in toRemove)
-                            {
-                                remainingLayers.Remove(g);
-                            }
-                        }
                     }
                 }
-
-                //Now expand any relevant groups
-                for (int i = 0; i < groups.Count; i++)
-                {
-                    var group = groups[i];
-                    if (group.ExpandInLegend)
-                    {
-                        var nodes = trvLegend.Nodes.Find(group.ObjectId, false);
-                        if (nodes.Length == 1)
-                        {
-                            nodes[0].Expand();
-                        }
-                    }
-                }
+                trvLegend.Nodes.AddRange(nodes);
             }
-            finally
-            {
-                trvLegend.EndUpdate();
-            }
+            trvLegend.EndUpdate();
+            _legendUpdateStopwatch.Stop();
+            Trace.TraceInformation("RefreshLegend: Completed in {0}ms", _legendUpdateStopwatch.ElapsedMilliseconds);
+            _legendUpdateStopwatch.Reset();
         }
 
         public TreeNode SelectedNode { get { return trvLegend.SelectedNode; } }
@@ -297,14 +209,6 @@
             {
                 if (node.Nodes.Count > 0)
                     ClearNodes(node.Nodes);
-
-                var layerMeta = node.Tag as LayerNodeMetadata;
-                if (layerMeta != null && layerMeta.ThemeIcon != null)
-                {
-                    layerMeta.Layer = null;
-                    layerMeta.ThemeIcon.Dispose();
-                    layerMeta.ThemeIcon = null;
-                }
             }
             nodes.Clear();
         }
@@ -324,226 +228,6 @@
             imgLegend.Images.Add(IMG_OTHER, Properties.Resources.icon_etc);
         }
 
-        private TreeNode CreateLayerNode(RuntimeMapLayer layer, bool visibilityFlag)
-        {
-            var node = new TreeNode();
-            node.Name = layer.ObjectId;
-            node.Text = layer.LegendLabel;
-            node.Checked = layer.Visible;
-            node.ContextMenuStrip = this.LayerContextMenu;
-            var lt = layer.Type;
-            var fsId = layer.FeatureSourceID;
-
-            if (fsId.EndsWith("DrawingSource"))
-            {
-                node.SelectedImageKey = node.ImageKey = IMG_DWF;
-                node.Tag = new LayerNodeMetadata(layer, visibilityFlag);
-                node.ToolTipText = string.Format(Properties.Resources.DrawingLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID);
-            }
-            else
-            {
-                string layerData = null;
-                var ldfId = layer.LayerDefinitionID;
-                if (_layerDefinitionContents.ContainsKey(ldfId.ToString()))
-                    layerData = _layerDefinitionContents[ldfId.ToString()];
-
-                if (layerData == null)
-                    return null;
-
-                XmlDocument doc = new XmlDocument();
-                doc.LoadXml(layerData);
-                int type = 0;
-                XmlNodeList scaleRanges = doc.GetElementsByTagName("VectorScaleRange");
-                if (scaleRanges.Count == 0)
-                {
-                    scaleRanges = doc.GetElementsByTagName("GridScaleRange");
-                    if (scaleRanges.Count == 0)
-                    {
-                        scaleRanges = doc.GetElementsByTagName("DrawingLayerDefinition");
-                        if (scaleRanges.Count == 0)
-                            return null;
-                        type = 2;
-                    }
-                    else
-                        type = 1;
-                }
-
-                String[] typeStyles = new String[] { "PointTypeStyle", "LineTypeStyle", "AreaTypeStyle", "CompositeTypeStyle" };
-                String[] ruleNames = new String[] { "PointRule", "LineRule", "AreaRule", "CompositeRule" };
-
-                try
-                {
-                    Image layerIcon = _map.GetLegendImage(layer.LayerDefinitionID,
-                                                          _map.ViewScale,
-                                                          16,
-                                                          16,
-                                                          "PNG",
-                                                          -1,
-                                                          -1);
-                    if (layerIcon != null)
-                    {
-                        string id = Guid.NewGuid().ToString();
-                        imgLegend.Images.Add(id, layerIcon);
-                        node.SelectedImageKey = node.ImageKey = id;
-                        node.Tag = new LayerNodeMetadata(layer, visibilityFlag)
-                        {
-                            ThemeIcon = layerIcon
-                        };
-                        node.ToolTipText = string.Format(Properties.Resources.DefaultLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID, layer.QualifiedClassName);
-                    }
-                    else
-                    {
-                        node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
-                        node.Tag = new LayerNodeMetadata(layer, visibilityFlag)
-                        {
-                            ThemeIcon = imgLegend.Images[IMG_BROKEN]
-                        };
-                    }
-                }
-                catch
-                {
-                    node.SelectedImageKey = node.ImageKey = IMG_BROKEN;
-                    node.Tag = new LayerNodeMetadata(layer, visibilityFlag)
-                    {
-                        ThemeIcon = imgLegend.Images[IMG_BROKEN]
-                    };
-                }
-
-                for (int sc = 0; sc < scaleRanges.Count; sc++)
-                {
-                    XmlElement scaleRange = (XmlElement)scaleRanges[sc];
-                    XmlNodeList minElt = scaleRange.GetElementsByTagName("MinScale");
-                    XmlNodeList maxElt = scaleRange.GetElementsByTagName("MaxScale");
-                    String minScale, maxScale;
-                    minScale = "0";
-                    maxScale = "1000000000000.0";   // as MDF's VectorScaleRange::MAX_MAP_SCALE
-                    if (minElt.Count > 0)
-                        minScale = minElt[0].ChildNodes[0].Value;
-                    if (maxElt.Count > 0)
-                        maxScale = maxElt[0].ChildNodes[0].Value;
-                    
-                    if (type != 0)
-                        break;
-
-                    for (int geomType = 0; geomType < typeStyles.Length; geomType++)
-                    {
-                        int catIndex = 0;
-                        XmlNodeList typeStyle = scaleRange.GetElementsByTagName(typeStyles[geomType]);
-                        for (int st = 0; st < typeStyle.Count; st++)
-                        {
-                            // We will check if this typestyle is going to be shown in the legend
-                            XmlNodeList showInLegend = ((XmlElement)typeStyle[st]).GetElementsByTagName("ShowInLegend");
-                            if (showInLegend.Count > 0)
-                                if (!bool.Parse(showInLegend[0].ChildNodes[0].Value))
-                                    continue;   // This typestyle does not need to be shown in the legend
-
-                            XmlNodeList rules = ((XmlElement)typeStyle[st]).GetElementsByTagName(ruleNames[geomType]);
-                            if (rules.Count > 1)
-                            {
-                                node.SelectedImageKey = node.ImageKey = IMG_THEME;
-                                var layerMeta = node.Tag as LayerNodeMetadata;
-                                if (layerMeta != null)
-                                {
-                                    layerMeta.ThemeIcon = Properties.Resources.lc_theme;
-                                    node.ToolTipText = string.Format(Properties.Resources.ThemedLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID, layer.QualifiedClassName, rules.Count);
-                                }
-                                if (this.ThemeCompressionLimit > 0 && rules.Count > this.ThemeCompressionLimit)
-                                {
-                                    AddThemeRuleNode(layer, node, geomType, 0, rules, 0, visibilityFlag);
-                                    node.Nodes.Add(CreateCompressedThemeNode(rules.Count - 2, visibilityFlag));
-                                    AddThemeRuleNode(layer, node, geomType, rules.Count - 1, rules, rules.Count - 1, visibilityFlag);
-                                }
-                                else
-                                {
-                                    for (int r = 0; r < rules.Count; r++)
-                                    {
-                                        AddThemeRuleNode(layer, node, geomType, catIndex++, rules, r, visibilityFlag);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            
-            return node;
-        }
-
-        private void AddThemeRuleNode(RuntimeMapLayer layer, TreeNode node, int geomType, int catIndex, XmlNodeList rules, int r, bool visibilityFlag)
-        {
-            XmlElement rule = (XmlElement)rules[r];
-            XmlNodeList label = rule.GetElementsByTagName("LegendLabel");
-            XmlNodeList filter = rule.GetElementsByTagName("Filter");
-
-            String labelText = "";
-            if (label != null && label.Count > 0 && label[0].ChildNodes.Count > 0)
-                labelText = label[0].ChildNodes[0].Value;
-            //String filterText = "";
-            //if (filter != null && filter.Count > 0 && filter[0].ChildNodes.Count > 0)
-            //    filterText = filter[0].ChildNodes[0].Value;
-
-            var child = CreateThemeRuleNode(layer.LayerDefinitionID, _map.ViewScale, labelText, (geomType + 1), catIndex, visibilityFlag);
-            node.Nodes.Add(child);
-        }
-
-        private TreeNode CreateCompressedThemeNode(int count, bool visibilityFlag)
-        {
-            TreeNode node = new TreeNode();
-            node.Text = (count + " other styles");
-            node.ImageKey = node.SelectedImageKey = IMG_OTHER;
-            node.Tag = new LayerNodeMetadata(null, visibilityFlag) {
-                IsBaseLayer = false,
-                ThemeIcon = Properties.Resources.icon_etc,
-                IsThemeRule = true
-            };
-            return node;
-        }
-
-        private TreeNode CreateThemeRuleNode(string layerDefId, double viewScale, string labelText, int geomType, int categoryIndex, bool visibilityFlag)
-        {
-            Image layerIcon = null;
-            try
-            {
-                layerIcon = _map.GetLegendImage(layerDefId,
-                                                      viewScale,
-                                                      16,
-                                                      16,
-                                                      "PNG",
-                                                      geomType,
-                                                      categoryIndex);
-            }
-            catch
-            {
-                layerIcon = Properties.Resources.lc_broken;
-            }
-            TreeNode node = new TreeNode();
-            node.Text = labelText;
-            if (layerIcon != null)
-            {
-                var tag = new LayerNodeMetadata(null, visibilityFlag)
-                {
-                    IsBaseLayer = false,
-                    IsThemeRule = true
-                };
-                string id = Guid.NewGuid().ToString();
-                tag.ThemeIcon = layerIcon;
-                node.Tag = tag;
-            }
-            return node;
-        }
-
-        private TreeNode CreateGroupNode(RuntimeMapGroup group, bool visibilityFlag)
-        {
-            var node = new TreeNode();
-            node.Name = group.ObjectId;
-            node.Text = group.LegendLabel;
-            node.Checked = group.Visible;
-            node.SelectedImageKey = node.ImageKey = IMG_GROUP;
-            node.Tag = new GroupNodeMetadata(group, visibilityFlag);
-            node.ContextMenuStrip = this.GroupContextMenu;
-            return node;
-        }
-
         private double _scale;
 
         /// <summary>
@@ -556,140 +240,6 @@
             RefreshLegend();
         }
 
-        public class LegendNodeMetadata
-        {
-            [Browsable(false)]
-            public bool IsGroup { get; protected set; }
-        }
-
-        public class GroupNodeMetadata : LegendNodeMetadata
-        {
-            [Browsable(false)]
-            internal RuntimeMapGroup WrappedGroupObject { get; set; }
-
-            public GroupNodeMetadata(RuntimeMapGroup group, bool visibilityFlag) 
-            { 
-                base.IsGroup = true;
-                this.WrappedGroupObject = group;
-                this.VisbilityFlag = visibilityFlag;
-            }
-
-            [Browsable(false)]
-            internal bool VisbilityFlag { get; private set; }
-
-            public bool Visible
-            {
-                get { return this.WrappedGroupObject.Visible; }
-                set { this.WrappedGroupObject.Visible = value; }
-            }
-
-            public string Group
-            {
-                get { return this.WrappedGroupObject.Group; }
-                set { this.WrappedGroupObject.Group = value; }
-            }
-
-            public string Name
-            {
-                get { return this.WrappedGroupObject.Name; }
-                set { this.WrappedGroupObject.Name = value; }
-            }
-
-            public bool ShowInLegend
-            {
-                get { return this.WrappedGroupObject.ShowInLegend; }
-                set { this.WrappedGroupObject.ShowInLegend = value; }
-            }
-
-            public string LegendLabel
-            {
-                get { return this.WrappedGroupObject.LegendLabel; }
-                set { this.WrappedGroupObject.LegendLabel = value; }
-            }
-
-            public bool ExpandInLegend
-            {
-                get { return this.WrappedGroupObject.ExpandInLegend; }
-                set { this.WrappedGroupObject.ExpandInLegend = value; }
-            }
-        }
-
-        public class LayerNodeMetadata : LegendNodeMetadata
-        {
-            public LayerNodeMetadata(RuntimeMapLayer layer, bool visibilityFlag) 
-            { 
-                base.IsGroup = false;
-                this.Layer = layer;
-                this.IsSelectable = (layer != null) ? layer.Selectable : false;
-                this.DrawSelectabilityIcon = (layer != null);
-                this.IsThemeRule = false;
-                this.VisibilityFlag = visibilityFlag;
-            }
-
-            [Browsable(false)]
-            internal bool VisibilityFlag { get; private set; }
-
-            [Browsable(false)]
-            internal RuntimeMapLayer Layer { get; set; }
-
-            [Browsable(false)]
-            public bool DrawSelectabilityIcon { get; set; }
-
-            [Browsable(false)]
-            public bool IsSelectable { get; set; }
-
-            [Browsable(false)]
-            public bool IsThemeRule { get; set; }
-
-            [Browsable(false)]
-            public bool IsBaseLayer { get; set; }
-
-            [Browsable(false)]
-            public Image ThemeIcon { get; set; }
-
-            public string Group
-            {
-                get { return this.Layer.Group; }
-                set { this.Layer.Group = value; }
-            }
-
-            public bool Selectable
-            {
-                get { return this.Layer.Selectable; }
-                set { this.Layer.Selectable = this.IsSelectable = value; }
-            }
-
-            public string Name
-            {
-                get { return this.Layer.Name; }
-                set { this.Layer.Name = value; }
-            }
-
-            public bool ShowInLegend
-            {
-                get { return this.Layer.ShowInLegend; }
-                set { this.Layer.ShowInLegend = value; }
-            }
-
-            public string LegendLabel
-            {
-                get { return this.Layer.LegendLabel; }
-                set { this.Layer.LegendLabel = value; }
-            }
-
-            public bool ExpandInLegend
-            {
-                get { return this.Layer.ExpandInLegend; }
-                set { this.Layer.ExpandInLegend = value; }
-            }
-
-            public string LayerDefinition
-            {
-                get { return this.Layer.LayerDefinitionID; }
-                set { this.Layer.LayerDefinitionID = value; }
-            }
-        }
-
         private bool HasVisibleParent(RuntimeMapGroup grp)
         {
             if (string.IsNullOrEmpty(grp.Group))
@@ -795,133 +345,15 @@
 
         private bool _noUpdate = false;
 
-        private void OnRequestRefresh()
+        internal void OnRequestRefresh()
         {
             if (this.Viewer != null)
                 this.Viewer.RefreshMap();
         }
 
-        private static bool IsThemeLayerNode(TreeNode node)
-        {
-            var meta = node.Tag as LayerNodeMetadata;
-            if (meta != null)
-                return meta.ThemeIcon != null || meta.IsBaseLayer;
-
-            return false;
-        }
-
-        private static bool IsLayerNode(TreeNode node)
-        {
-            var meta = node.Tag as LayerNodeMetadata;
-            return meta != null;
-        }
-
         private void trvLegend_DrawNode(object sender, DrawTreeNodeEventArgs e)
         {
-            if (IsLayerNode(e.Node) && !e.Bounds.IsEmpty)
-            {
-                //TODO: Render +/- for nodes with children (ie. Themed layers)
-                Color backColor, foreColor;
-
-                //For some reason, the default bounds are way off from what you would
-                //expect it to be. So we apply this offset for any text/image draw operations
-                int xoffset = -36;
-                var tag = e.Node.Tag as LayerNodeMetadata;
-
-                bool bDrawSelection = false;
-                if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
-                {
-                    backColor = SystemColors.Highlight;
-                    foreColor = SystemColors.HighlightText;
-                    bDrawSelection = true;
-                }
-                else if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
-                {
-                    backColor = SystemColors.HotTrack;
-                    foreColor = SystemColors.HighlightText;
-                }
-                else
-                {
-                    backColor = e.Node.BackColor;
-                    foreColor = (tag != null && !tag.VisibilityFlag) ? SystemColors.InactiveCaptionText : Color.Black; //e.Node.ForeColor;
-                }
-
-                var checkBoxOffset = xoffset;
-                var selectabilityOffset = xoffset + 16;
-                var iconOffsetNoSelect = xoffset + 16;
-                if (tag != null && tag.IsThemeRule) //No checkbox for theme rule nodes
-                {
-                    selectabilityOffset = xoffset;
-                    iconOffsetNoSelect = xoffset;
-                }
-                var iconOffset = selectabilityOffset + 20;
-                var textOffset = iconOffset + 20;
-                var textOffsetNoSelect = iconOffsetNoSelect + 20;
-
-                //Uncomment if you need to "see" the bounds of the node
-                //e.Graphics.DrawRectangle(Pens.Black, e.Node.Bounds);
-
-                if (tag != null && !tag.IsThemeRule) //No checkbox for theme rule nodes
-                {
-                    CheckBoxRenderer.DrawCheckBox(
-                        e.Graphics,
-                        new Point(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y),
-                        e.Node.Checked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal);
-                }
-                if (tag != null)
-                {
-                    if (tag.DrawSelectabilityIcon)
-                    {
-                        var icon = tag.IsSelectable ? _selectableIcon : _unselectableIcon;
-                        e.Graphics.DrawImage(icon, e.Node.Bounds.X + selectabilityOffset, e.Node.Bounds.Y);
-                        Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                    }
-                    if (tag.ThemeIcon != null)
-                    {
-                        if (tag.DrawSelectabilityIcon)
-                        {
-                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffset, e.Node.Bounds.Y);
-                            Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                        }
-                        else
-                        {
-                            e.Graphics.DrawImage(tag.ThemeIcon, e.Node.Bounds.X + iconOffsetNoSelect, e.Node.Bounds.Y);
-                            Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
-                        }
-                    }
-
-                    if (bDrawSelection)
-                    {
-                        var size = e.Graphics.MeasureString(e.Node.Text, trvLegend.Font);
-                        using (var brush = new SolidBrush(backColor))
-                        {
-                            e.Graphics.FillRectangle(brush,
-                                                     e.Node.Bounds.X + (tag.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect),
-                                                     e.Node.Bounds.Y,
-                                                     size.Width,
-                                                     size.Height);
-                        }
-                    }
-
-                    using (SolidBrush brush = new SolidBrush(tag.VisibilityFlag ? foreColor : Color.Gray))
-                    {
-                        e.Graphics.DrawString(e.Node.Text, trvLegend.Font, brush, e.Node.Bounds.X + (tag.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect), e.Node.Bounds.Y);
-                    }
-                }
-                else
-                {
-                    using (SolidBrush brush = new SolidBrush(Color.Black))
-                    {
-                        e.Graphics.DrawString(e.Node.Text, trvLegend.Font, brush, e.Node.Bounds.X + 17.0f + xoffset, e.Node.Bounds.Y);
-                    }
-                }
-
-                e.DrawDefault = false;
-            }
-            else
-            {
-                e.DrawDefault = true;
-            }
+            _presenter.DrawNode(e, trvLegend.ShowPlusMinus, trvLegend.Font);
         }
 
         private ContextMenuStrip _grpContextMenu;
@@ -1050,9 +482,11 @@
             if (trvLegend.SelectedNode == null)
                 return null;
 
-            var grp = trvLegend.SelectedNode.Tag as GroupNodeMetadata;
-            if (grp != null)
-                return grp.WrappedGroupObject;
+            var grpMeta = trvLegend.SelectedNode.Tag as GroupNodeMetadata;
+            if (grpMeta != null)
+            {
+                return grpMeta.Group;
+            }
 
             return null;
         }

Modified: trunk/Tools/Maestro/Maestro.MapViewer/Legend.resx
===================================================================
--- trunk/Tools/Maestro/Maestro.MapViewer/Legend.resx	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.MapViewer/Legend.resx	2012-09-14 12:56:47 UTC (rev 7017)
@@ -120,4 +120,7 @@
   <metadata name="imgLegend.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
     <value>17, 17</value>
   </metadata>
+  <metadata name="bgLegendUpdate.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>130, 17</value>
+  </metadata>
 </root>
\ No newline at end of file

Added: trunk/Tools/Maestro/Maestro.MapViewer/LegendPresenter.cs
===================================================================
--- trunk/Tools/Maestro/Maestro.MapViewer/LegendPresenter.cs	                        (rev 0)
+++ trunk/Tools/Maestro/Maestro.MapViewer/LegendPresenter.cs	2012-09-14 12:56:47 UTC (rev 7017)
@@ -0,0 +1,1092 @@
+#region Disclaimer / License
+// Copyright (C) 2012, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at gmail.com
+// 
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+// 
+#endregion
+using Maestro.MapViewer.Model;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.MaestroAPI.Mapping;
+using OSGeo.MapGuide.MaestroAPI.Services;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Windows.Forms.VisualStyles;
+using System.Xml;
+
+namespace Maestro.MapViewer
+{
+    class LegendControlPresenter
+    {
+        const string IMG_BROKEN = "lc_broken";
+        const string IMG_DWF = "lc_dwf";
+        const string IMG_GROUP = "lc_group";
+        const string IMG_RASTER = "lc_raster";
+        const string IMG_SELECT = "lc_select";
+        const string IMG_THEME = "lc_theme";
+        const string IMG_UNSELECT = "lc_unselect";
+        const string IMG_OTHER = "icon_etc";
+
+        private IResourceService _resSvc;
+        private IServerConnection _provider;
+        private RuntimeMap _map;
+
+        private Image _selectableIcon;
+        private Image _unselectableIcon;
+
+        private Legend _legend;
+
+        private Dictionary<string, LayerNodeMetadata> _layers = new Dictionary<string, LayerNodeMetadata>();
+        private Dictionary<string, GroupNodeMetadata> _groups = new Dictionary<string, GroupNodeMetadata>();
+
+        public LegendControlPresenter(Legend legend, RuntimeMap map)
+        {
+            _legend = legend;
+            _map = map;
+            _provider = _map.CurrentConnection;
+            _resSvc = _provider.ResourceService;
+            InitInitialSelectabilityStates();
+            _selectableIcon = Properties.Resources.lc_select;
+            _unselectableIcon = Properties.Resources.lc_unselect;
+        }
+
+        private void InitInitialSelectabilityStates()
+        {
+            if (_map != null)
+            {
+                _layers.Clear();
+                var layers = _map.Layers;
+                for (int i = 0; i < layers.Count; i++)
+                {
+                    var layer = layers[i];
+                    _layers[layer.ObjectId] = new LayerNodeMetadata(layer, layer.Selectable);
+                }
+            }
+        }
+
+        private bool HasVisibleParent(RuntimeMapGroup grp)
+        {
+            var current = grp.Group;
+            if (current != null)
+            {
+                var parent = _map.Groups[current];
+                if (parent != null)
+                {
+                    return parent.Visible;
+                }
+            }
+            return true;
+        }
+
+        private bool HasVisibleParent(RuntimeMapLayer layer)
+        {
+            var current = layer.Group;
+            if (current != null)
+            {
+                var parent = _map.Groups[current];
+                if (parent != null)
+                {
+                    return parent.Visible;
+                }
+            }
+            return true;
+        }
+
+        private TreeNode CreateLayerNode(RuntimeMapLayer layer)
+        {
+            var node = new TreeNode();
+            node.Name = layer.ObjectId;
+            node.Text = layer.LegendLabel;
+            node.Checked = layer.Visible;
+            //node.ContextMenuStrip = _legend.LayerContextMenu;
+            var lt = layer.Type;
+            var fsId = layer.FeatureSourceID;
+
+            LayerNodeMetadata layerMeta = null;
+            if (fsId.EndsWith("DrawingSource"))
+            {
+                node.SelectedImageKey = node.ImageKey = IMG_DWF;
+                bool bInitiallySelectable = layer.Selectable;
+                if (_layers.ContainsKey(layer.ObjectId))
+                {
+                    layerMeta = _layers[layer.ObjectId];
+                    bInitiallySelectable = layerMeta.WasInitiallySelectable;
+                }
+                else //If not in the dictionary, assume it is a dynamically added layer
+                {
+                    layerMeta = new LayerNodeMetadata(layer, bInitiallySelectable);
+                    _layers[layer.ObjectId] = layerMeta;
+                }
+                node.Tag = layerMeta;
+                node.ToolTipText = string.Format(Properties.Resources.DrawingLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID);
+            }
+            else
+            {
+                var ldfId = layer.LayerDefinitionID;
+                if (_layers.ContainsKey(layer.ObjectId))
+                {
+                    layerMeta = _layers[layer.ObjectId];
+                }
+                else
+                {
+                    layerMeta = new LayerNodeMetadata(layer, layer.Selectable);
+                    _layers[layer.ObjectId] = layerMeta;
+                }
+                if (string.IsNullOrEmpty(layerMeta.LayerDefinitionContent))
+                    return null;
+
+                node.Tag = layerMeta;
+
+                XmlDocument doc = new XmlDocument();
+                doc.LoadXml(layerMeta.LayerDefinitionContent);
+                int type = 0;
+                XmlNodeList scaleRanges = doc.GetElementsByTagName("VectorScaleRange");
+                if (scaleRanges.Count == 0)
+                {
+                    scaleRanges = doc.GetElementsByTagName("GridScaleRange");
+                    if (scaleRanges.Count == 0)
+                    {
+                        scaleRanges = doc.GetElementsByTagName("DrawingLayerDefinition");
+                        if (scaleRanges.Count == 0)
+                            return null;
+                        type = 2;
+                    }
+                    else
+                        type = 1;
+                }
+
+                String[] typeStyles = new String[] { "PointTypeStyle", "LineTypeStyle", "AreaTypeStyle", "CompositeTypeStyle" };
+                String[] ruleNames = new String[] { "PointRule", "LineRule", "AreaRule", "CompositeRule" };
+
+                node.ToolTipText = string.Format(Properties.Resources.DefaultLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID, layer.QualifiedClassName);
+                if (!layerMeta.HasTheme() || !layerMeta.HasDefaultIcons())
+                {
+                    for (int sc = 0; sc < scaleRanges.Count; sc++)
+                    {
+                        XmlElement scaleRange = (XmlElement)scaleRanges[sc];
+                        XmlNodeList minElt = scaleRange.GetElementsByTagName("MinScale");
+                        XmlNodeList maxElt = scaleRange.GetElementsByTagName("MaxScale");
+                        String minScale, maxScale;
+                        minScale = "0";
+                        maxScale = "1000000000000.0";   // as MDF's VectorScaleRange::MAX_MAP_SCALE
+                        if (minElt.Count > 0)
+                            minScale = minElt[0].ChildNodes[0].Value;
+                        if (maxElt.Count > 0)
+                            maxScale = maxElt[0].ChildNodes[0].Value;
+
+                        if (type != 0)
+                            break;
+
+                        for (int geomType = 0; geomType < typeStyles.Length; geomType++)
+                        {
+                            ThemeCategory themeCat = new ThemeCategory()
+                            {
+                                MinScale = minScale,
+                                MaxScale = maxScale,
+                                GeometryType = geomType
+                            };
+
+                            int catIndex = 0;
+                            XmlNodeList typeStyle = scaleRange.GetElementsByTagName(typeStyles[geomType]);
+                            for (int st = 0; st < typeStyle.Count; st++)
+                            {
+                                // We will check if this typestyle is going to be shown in the legend
+                                XmlNodeList showInLegend = ((XmlElement)typeStyle[st]).GetElementsByTagName("ShowInLegend");
+                                if (showInLegend.Count > 0)
+                                    if (!bool.Parse(showInLegend[0].ChildNodes[0].Value))
+                                        continue;   // This typestyle does not need to be shown in the legend
+
+                                XmlNodeList rules = ((XmlElement)typeStyle[st]).GetElementsByTagName(ruleNames[geomType]);
+                                if (rules.Count > 1)
+                                {
+                                    layerMeta.SetDefaultIcon(themeCat, Properties.Resources.lc_theme);
+                                    node.ToolTipText = string.Format(Properties.Resources.ThemedLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID, layer.QualifiedClassName, rules.Count);
+
+                                    if (_legend.ThemeCompressionLimit > 0 && rules.Count > _legend.ThemeCompressionLimit)
+                                    {
+                                        AddThemeRuleNode(layerMeta, themeCat, node, geomType, 0, rules, 0);
+                                        node.Nodes.Add(CreateCompressedThemeNode(layerMeta, themeCat, rules.Count - 2));
+                                        AddThemeRuleNode(layerMeta, themeCat, node, geomType, rules.Count - 1, rules, rules.Count - 1);
+                                    }
+                                    else
+                                    {
+                                        for (int r = 0; r < rules.Count; r++)
+                                        {
+                                            AddThemeRuleNode(layerMeta, themeCat, node, geomType, catIndex++, rules, r);
+                                        }
+                                    }
+                                }
+                                else if (!layerMeta.HasDefaultIconsAt(_map.ViewScale))
+                                {
+                                    try
+                                    {
+                                        var img = _map.GetLegendImage(layer.LayerDefinitionID,
+                                                                                _map.ViewScale,
+                                                                                16,
+                                                                                16,
+                                                                                "PNG",
+                                                                                -1,
+                                                                                -1);
+                                        legendCallCount++;
+                                        layerMeta.SetDefaultIcon(themeCat, img);
+                                        node.ToolTipText = string.Format(Properties.Resources.DefaultLayerTooltip, Environment.NewLine, layer.Name, layer.FeatureSourceID, layer.QualifiedClassName);
+                                    }
+                                    catch
+                                    {
+                                        //layerMeta.SetDefaultIcon(themeCat, Properties.Resources.lc_broken);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                else //Already cached
+                {
+                    Trace.TraceInformation("Icons already cached for: " + layer.Name);
+                    node.Nodes.AddRange(layerMeta.CreateThemeNodesFromCachedMetadata(_map.ViewScale));
+                }
+            }
+
+            return node;
+        }
+
+        private void AddThemeRuleNode(LayerNodeMetadata layerMeta, ThemeCategory themeCat, TreeNode node, int geomType, int catIndex, XmlNodeList rules, int r)
+        {
+            XmlElement rule = (XmlElement)rules[r];
+            XmlNodeList label = rule.GetElementsByTagName("LegendLabel");
+            XmlNodeList filter = rule.GetElementsByTagName("Filter");
+
+            String labelText = "";
+            if (label != null && label.Count > 0 && label[0].ChildNodes.Count > 0)
+                labelText = label[0].ChildNodes[0].Value;
+            //String filterText = "";
+            //if (filter != null && filter.Count > 0 && filter[0].ChildNodes.Count > 0)
+            //    filterText = filter[0].ChildNodes[0].Value;
+
+            if (LayerNodeMetadata.ScaleIsApplicable(_map.ViewScale, themeCat))
+            {
+                var child = CreateThemeRuleNode(layerMeta, themeCat, _map.ViewScale, labelText, (geomType + 1), catIndex);
+                node.Nodes.Add(child);
+            }
+            else
+            {
+
+            }
+        }
+
+        private TreeNode CreateCompressedThemeNode(LayerNodeMetadata layer, ThemeCategory cat, int count)
+        {
+            TreeNode node = new TreeNode();
+            node.Text = (count + " other styles");
+            node.ImageKey = node.SelectedImageKey = IMG_OTHER;
+            var meta = new LayerThemeNodeMetadata(true, Properties.Resources.icon_etc, node.Text);
+            node.Tag = meta;
+            layer.AddThemeNode(cat, meta);
+            return node;
+        }
+
+        private TreeNode CreateThemeRuleNode(LayerNodeMetadata layer, ThemeCategory themeCat, double viewScale, string labelText, int geomType, int categoryIndex)
+        {
+            var lyr = layer.Layer;
+            TreeNode node = new TreeNode();
+            node.Text = labelText;
+
+            Image icon = _map.GetLegendImage(lyr.LayerDefinitionID,
+                                             viewScale,
+                                             16,
+                                             16,
+                                             "PNG",
+                                             geomType,
+                                             categoryIndex);
+            legendCallCount++;
+
+            var tag = new LayerThemeNodeMetadata(false, icon, labelText);
+            layer.AddThemeNode(themeCat, tag);
+            node.Tag = tag;
+
+            return node;
+        }
+
+        private TreeNode CreateGroupNode(RuntimeMapGroup group)
+        {
+            var node = new TreeNode();
+            node.Name = group.ObjectId;
+            node.Text = group.LegendLabel;
+            node.Checked = group.Visible;
+            node.SelectedImageKey = node.ImageKey = IMG_GROUP;
+            var meta = new GroupNodeMetadata(group);
+            node.Tag = meta;
+            _groups[group.ObjectId] = meta;
+            //node.ContextMenuStrip = _legend.GroupContextMenu;
+            return node;
+        }
+
+        private int legendCallCount = 0;
+
+        public TreeNode[] CreateNodes()
+        {
+            List<TreeNode> output = new List<TreeNode>();
+            var nodesById = new Dictionary<string, TreeNode>();
+
+            var scale = _map.ViewScale;
+            if (scale < 10.0)
+                return output.ToArray();
+
+            var groups = _map.Groups;
+            var layers = _map.Layers;
+
+            legendCallCount = 0;
+
+            //Process groups first
+            List<RuntimeMapGroup> remainingNodes = new List<RuntimeMapGroup>();
+            for (int i = 0; i < groups.Count; i++)
+            {
+                var group = groups[i];
+                if (!_legend.ShowAllLayersAndGroups && !group.ShowInLegend)
+                    continue;
+
+                //Add ones without parents first.
+                if (!string.IsNullOrEmpty(group.Group))
+                {
+                    remainingNodes.Add(group);
+                }
+                else
+                {
+                    var node = CreateGroupNode(group);
+                    output.Add(node);
+                    nodesById.Add(group.ObjectId, node);
+                }
+
+                while (remainingNodes.Count > 0)
+                {
+                    List<RuntimeMapGroup> toRemove = new List<RuntimeMapGroup>();
+                    for (int j = 0; j < remainingNodes.Count; j++)
+                    {
+                        var grpName = remainingNodes[j].Group;
+                        var parentGroup = _map.Groups[grpName];
+                        if (parentGroup != null && nodesById.ContainsKey(parentGroup.ObjectId))
+                        {
+                            var node = CreateGroupNode(remainingNodes[j]);
+                            nodesById[parentGroup.ObjectId].Nodes.Add(node);
+                            toRemove.Add(remainingNodes[j]);
+                        }
+                    }
+                    //Whittle down this list
+                    if (toRemove.Count > 0)
+                    {
+                        foreach (var g in toRemove)
+                        {
+                            remainingNodes.Remove(g);
+                        }
+                    }
+                }
+            }
+
+            //Collect all resource contents in a batch
+            var layerIds = new List<string>();
+            //Also collect the layer metadata nodes to create or update
+            var layerMetaNodesToUpdate = new Dictionary<string, RuntimeMapLayer>();
+            //Now process layers. Layers without metadata nodes or without layer definition content
+            //are added to the list
+            for (int i = 0; i < layers.Count; i++)
+            {
+                var lyr = layers[i];
+                bool display = lyr.ShowInLegend;
+                bool visible = lyr.IsVisibleAtScale(_map.ViewScale);
+                if (!_legend.ShowAllLayersAndGroups && !display)
+                    continue;
+
+                if (!_legend.ShowAllLayersAndGroups && !visible)
+                    continue;
+
+                if (_layers.ContainsKey(lyr.ObjectId))
+                {
+                    if (string.IsNullOrEmpty(_layers[lyr.ObjectId].LayerDefinitionContent))
+                    {
+                        var ldfId = lyr.LayerDefinitionID;
+                        layerIds.Add(ldfId.ToString());
+                        layerMetaNodesToUpdate[ldfId.ToString()] = lyr;
+                    }
+                }
+                else
+                {
+                    var ldfId = lyr.LayerDefinitionID;
+                    layerIds.Add(ldfId.ToString());
+                    layerMetaNodesToUpdate[ldfId.ToString()] = lyr;
+                }
+            }
+
+            if (layerIds.Count > 0)
+            {
+                int added = 0;
+                int updated = 0;
+                //Fetch the contents and create/update the required layer metadata nodes
+                //TODO: Surely we can optimize this better
+                foreach (var lid in layerIds)
+                {
+                    using (var sr = new StreamReader(_resSvc.GetResourceXmlData(lid)))
+                    {
+                        string content = sr.ReadToEnd();
+
+                        var lyr = layerMetaNodesToUpdate[lid];
+                        var objId = lyr.ObjectId;
+                        LayerNodeMetadata meta = null;
+                        if (_layers.ContainsKey(objId))
+                        {
+                            meta = _layers[objId];
+                            updated++;
+                        }
+                        else
+                        {
+                            meta = new LayerNodeMetadata(lyr, lyr.Selectable);
+                            _layers[objId] = meta;
+                            added++;
+                        }
+                        meta.LayerDefinitionContent = content;
+                    }
+                }
+                Trace.TraceInformation("CreateNodes: {0} layer contents added, {1} layer contents updated", added, updated);
+            }
+
+            List<RuntimeMapLayer> remainingLayers = new List<RuntimeMapLayer>();
+            for (int i = 0; i < layers.Count; i++)
+            {
+                var layer = layers[i];
+
+                bool display = layer.ShowInLegend;
+                bool visible = layer.IsVisibleAtScale(_map.ViewScale);
+                if (!_legend.ShowAllLayersAndGroups && !display)
+                    continue;
+
+                if (!_legend.ShowAllLayersAndGroups && !visible)
+                    continue;
+
+                //Add ones without parents first.
+                if (!string.IsNullOrEmpty(layer.Group))
+                {
+                    remainingLayers.Add(layer);
+                }
+                else
+                {
+                    var node = CreateLayerNode(layer);
+                    if (node != null)
+                    {
+                        output.Add(node);
+                        nodesById.Add(layer.ObjectId, node);
+                        if (layer.ExpandInLegend)
+                            node.Expand();
+                    }
+                }
+
+                while (remainingLayers.Count > 0)
+                {
+                    List<RuntimeMapLayer> toRemove = new List<RuntimeMapLayer>();
+                    for (int j = 0; j < remainingLayers.Count; j++)
+                    {
+                        var grpName = remainingLayers[j].Group;
+                        var parentGroup = _map.Groups[grpName];
+                        if (parentGroup != null && nodesById.ContainsKey(parentGroup.ObjectId))
+                        {
+                            var node = CreateLayerNode(remainingLayers[j]);
+                            if (node != null)
+                            {
+                                nodesById[parentGroup.ObjectId].Nodes.Add(node);
+                                if (remainingLayers[j].ExpandInLegend)
+                                    node.Expand();
+                            }
+                            toRemove.Add(remainingLayers[j]);
+                        }
+                    }
+                    //Whittle down this list
+                    if (toRemove.Count > 0)
+                    {
+                        foreach (var g in toRemove)
+                        {
+                            remainingLayers.Remove(g);
+                        }
+                    }
+                }
+            }
+
+            //Now expand any relevant groups
+            for (int i = 0; i < groups.Count; i++)
+            {
+                var group = groups[i];
+                if (group.ExpandInLegend)
+                {
+                    var groupId = group.ObjectId;
+                    if (nodesById.ContainsKey(groupId))
+                    {
+                        nodesById[groupId].Expand();
+                    }
+                }
+            }
+            Trace.TraceInformation("{0} calls made to GenerateLegendImage", legendCallCount);
+            return output.ToArray();
+        }
+
+        private static bool IsThemeLayerNode(TreeNode node)
+        {
+            var meta = node.Tag as LayerThemeNodeMetadata;
+            if (meta != null)
+                return true;
+
+            return false;
+        }
+
+        private static bool IsLayerNode(TreeNode node)
+        {
+            var meta = node.Tag as LayerNodeMetadata;
+            return meta != null;
+        }
+
+        internal void DrawNode(DrawTreeNodeEventArgs e, bool showPlusMinus, Font font)
+        {
+            var currentScale = _map.ViewScale;
+            var layerMeta = e.Node.Tag as LayerNodeMetadata;
+            var themeMeta = e.Node.Tag as LayerThemeNodeMetadata;
+            if (!e.Bounds.IsEmpty && (layerMeta != null || themeMeta != null))
+            {
+                Color backColor, foreColor;
+
+                //For some reason, the default bounds are way off from what you would
+                //expect it to be. So we apply this offset for any text/image draw operations
+                int xoffset = -36;
+                if (showPlusMinus && e.Node.Nodes.Count > 0)
+                {
+                    // Use the VisualStyles renderer to use the proper OS-defined glyphs
+                    Rectangle glyphRect = new Rectangle(e.Node.Bounds.X - 52, e.Node.Bounds.Y, 16, 16);
+                    if (Application.RenderWithVisualStyles)
+                    {
+                        VisualStyleElement element = (e.Node.IsExpanded) ?
+                            VisualStyleElement.TreeView.Glyph.Opened : VisualStyleElement.TreeView.Glyph.Closed;
+
+                        VisualStyleRenderer renderer = new VisualStyleRenderer(element);
+                        renderer.DrawBackground(e.Graphics, glyphRect);
+                    }
+                    else //Visual Styles disabled, fallback to drawing the +/- using geometric primitives
+                    {
+                        int h = 8;
+                        int w = 8;
+                        int x = glyphRect.X;
+                        int y = glyphRect.Y + (glyphRect.Height / 2) - 4;
+
+                        //Draw the -
+                        e.Graphics.DrawRectangle(new Pen(SystemBrushes.ControlDark), x, y, w, h);
+                        e.Graphics.FillRectangle(new SolidBrush(Color.White), x + 1, y + 1, w - 1, h - 1);
+                        e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 2, y + 4, x + w - 2, y + 4);
+
+                        //Draw the |
+                        if (!e.Node.IsExpanded)
+                            e.Graphics.DrawLine(new Pen(new SolidBrush(Color.Black)), x + 4, y + 2, x + 4, y + h - 2);
+                    }
+                }
+
+                bool bDrawSelection = false;
+                if ((e.State & TreeNodeStates.Selected) == TreeNodeStates.Selected)
+                {
+                    backColor = SystemColors.Highlight;
+                    foreColor = SystemColors.HighlightText;
+                    bDrawSelection = true;
+                }
+                else if ((e.State & TreeNodeStates.Hot) == TreeNodeStates.Hot)
+                {
+                    backColor = SystemColors.HotTrack;
+                    foreColor = SystemColors.HighlightText;
+                }
+                else
+                {
+                    backColor = e.Node.BackColor;
+                    foreColor = (layerMeta != null && !layerMeta.Layer.IsVisibleAtScale(_map.ViewScale)) ? SystemColors.InactiveCaptionText : Color.Black;
+                }
+
+                var checkBoxOffset = xoffset;
+                var selectabilityOffset = xoffset + 16;
+                var iconOffsetNoSelect = xoffset + 16;
+                if (themeMeta != null) //No checkbox for theme rule nodes
+                {
+                    selectabilityOffset = xoffset;
+                    iconOffsetNoSelect = xoffset;
+                }
+                var iconOffset = selectabilityOffset + 20;
+                var textOffset = iconOffset + 20;
+                var textOffsetNoSelect = iconOffsetNoSelect + 20;
+
+                //Uncomment if you need to "see" the bounds of the node
+                //e.Graphics.DrawRectangle(Pens.Black, e.Node.Bounds);
+
+                if (layerMeta != null) //No checkbox for theme rule nodes
+                {
+                    if (Application.RenderWithVisualStyles)
+                    {
+                        CheckBoxRenderer.DrawCheckBox(
+                            e.Graphics,
+                            new Point(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y),
+                            e.Node.Checked ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal);
+                    }
+                    else
+                    {
+                        //We don't have to do this, but with Visual Styles disabled, there is a noticeable jarring visual difference from DrawDefault'd checkboxes
+                        //So we might as well emulate that style for the sake of consistency
+                        var rect = new Rectangle(e.Node.Bounds.X + checkBoxOffset, e.Node.Bounds.Y, 16, 16);
+                        ControlPaint.DrawCheckBox(e.Graphics, rect, e.Node.Checked ? ButtonState.Checked | ButtonState.Flat : ButtonState.Flat);
+                        rect.Inflate(-2, -2);
+                        e.Graphics.DrawRectangle(new Pen(Brushes.Black, 2.0f), rect);
+                    }
+                }
+               
+                if (layerMeta != null)
+                {
+                    if (bDrawSelection)
+                    {
+                        var size = e.Graphics.MeasureString(e.Node.Text, font);
+                        using (var brush = new SolidBrush(backColor))
+                        {
+                            e.Graphics.FillRectangle(brush,
+                                                     e.Node.Bounds.X + (layerMeta.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect),
+                                                     e.Node.Bounds.Y,
+                                                     size.Width,
+                                                     size.Height);
+                        }
+                    }
+
+                    if (layerMeta.DrawSelectabilityIcon)
+                    {
+                        var icon = layerMeta.IsSelectable ? _selectableIcon : _unselectableIcon;
+                        e.Graphics.DrawImage(icon, e.Node.Bounds.X + selectabilityOffset, e.Node.Bounds.Y);
+                        //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                    }
+
+                    Image layerIcon = null;
+                    if (layerMeta.IsRaster)
+                    {
+                        layerIcon = Properties.Resources.lc_raster;
+                    }
+                    else if (layerMeta.IsDwf)
+                    {
+                        layerIcon = Properties.Resources.lc_dwf;
+                    }
+                    else
+                    {
+                        layerIcon = layerMeta.GetDefaultIcon(currentScale);
+                        if (layerIcon == null &&_legend.ShowAllLayersAndGroups)
+                            layerIcon = Properties.Resources.lc_broken;
+                    }
+                    if (layerIcon != null)
+                    {
+                        if (layerMeta.DrawSelectabilityIcon)
+                        {
+                            e.Graphics.DrawImage(layerIcon, e.Node.Bounds.X + iconOffset, e.Node.Bounds.Y);
+                            //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                        }
+                        else
+                        {
+                            e.Graphics.DrawImage(layerIcon, e.Node.Bounds.X + iconOffsetNoSelect, e.Node.Bounds.Y);
+                            //Trace.TraceInformation("Painted icon at ({0},{1})", e.Node.Bounds.X, e.Node.Bounds.Y);
+                        }
+                    }
+
+                    using (SolidBrush brush = new SolidBrush(foreColor))
+                    {
+                        e.Graphics.DrawString(e.Node.Text, font, brush, e.Node.Bounds.X + (layerMeta.DrawSelectabilityIcon ? textOffset : textOffsetNoSelect), e.Node.Bounds.Y);
+                    }
+                }
+                else if (themeMeta != null)
+                {
+                    if (bDrawSelection)
+                    {
+                        var size = e.Graphics.MeasureString(e.Node.Text, font);
+                        using (var brush = new SolidBrush(backColor))
+                        {
+                            e.Graphics.FillRectangle(brush,
+                                                     e.Node.Bounds.X + textOffsetNoSelect,
+                                                     e.Node.Bounds.Y,
+                                                     size.Width,
+                                                     size.Height);
+                        }
+                    }
+
+                    if (themeMeta.ThemeIcon != null)
+                    {
+                        e.Graphics.DrawImage(themeMeta.ThemeIcon, e.Node.Bounds.X + iconOffsetNoSelect, e.Node.Bounds.Y);
+                    }
+
+                    using (SolidBrush brush = new SolidBrush(foreColor))
+                    {
+                        e.Graphics.DrawString(e.Node.Text, font, brush, e.Node.Bounds.X + textOffsetNoSelect, e.Node.Bounds.Y);
+                    }
+                }
+                else
+                {
+                    using (SolidBrush brush = new SolidBrush(foreColor))
+                    {
+                        e.Graphics.DrawString(e.Node.Text, font, brush, e.Node.Bounds.X + 17.0f + xoffset, e.Node.Bounds.Y);
+                    }
+                }
+
+                e.DrawDefault = false;
+            }
+            else
+            {
+                e.DrawDefault = true;
+            }
+        }
+
+        internal void SetGroupExpandInLegend(string objectId, bool expand)
+        {
+            if (_groups.ContainsKey(objectId))
+            {
+                var grp = _groups[objectId].Group;
+                grp.ExpandInLegend = expand;
+            }
+        }
+
+        internal void SetLayerExpandInLegend(string objectId, bool expand)
+        {
+            if (_layers.ContainsKey(objectId))
+            {
+                var lyr = _layers[objectId].Layer;
+                lyr.ExpandInLegend = expand;
+            }
+        }
+
+        internal void SetGroupVisible(string objectId, bool bChecked)
+        {
+            if (_groups.ContainsKey(objectId))
+            {
+                var grp = _groups[objectId].Group;
+                grp.Visible = bChecked;
+                var bVis = HasVisibleParent(grp);
+                if (bVis)
+                    _legend.OnRequestRefresh();
+            }
+        }
+
+        internal void SetLayerVisible(string objectId, bool bChecked)
+        {
+            if (_layers.ContainsKey(objectId))
+            {
+                var layer = _layers[objectId].Layer;
+                layer.Visible = bChecked;
+                var bVis = HasVisibleParent(layer);
+                if (bVis)
+                {
+                    layer.ForceRefresh();
+                    _legend.OnRequestRefresh();
+                }
+            }
+        }
+    }
+
+    namespace Model
+    {
+        public abstract class LegendNodeMetadata
+        {
+            public bool IsGroup { get; protected set; }
+
+            public abstract string ObjectId { get; }
+        }
+
+        [DebuggerDisplay("Name = {GroupName}, Label = {LegendLabel}")]
+        public class GroupNodeMetadata : LegendNodeMetadata
+        {
+            internal RuntimeMapGroup Group { get; private set; }
+
+            public RuntimeMapGroup GetGroup() { return this.Group; }
+
+            public GroupNodeMetadata(RuntimeMapGroup group)
+            {
+                base.IsGroup = true;
+                this.Group = group;
+            }
+
+            public string LegendLabel { get { return this.Group.LegendLabel; } }
+
+            public string Name { get { return this.Group.Name; } }
+
+            public override string ObjectId
+            {
+                get { return this.Group.ObjectId; }
+            }
+        }
+
+        [DebuggerDisplay("Layer Theme Node")]
+        public class LayerThemeNodeMetadata : LegendNodeMetadata
+        {
+            public LayerThemeNodeMetadata(bool bPlaceholder, Image themeIcon, string labelText)
+            {
+                this.IsPlaceholder = bPlaceholder;
+                this.ThemeIcon = themeIcon;
+                this.Label = labelText;
+            }
+
+            public bool IsPlaceholder { get; private set; }
+
+            public Image ThemeIcon { get; set; }
+
+            public string Label { get; private set; }
+
+            public override string ObjectId
+            {
+                get { return ""; }
+            }
+        }
+
+        public class ThemeCategory
+        {
+            public string MinScale { get; set; }
+
+            public string MaxScale { get; set; }
+
+            public int GeometryType { get; set; }
+
+            public override int GetHashCode()
+            {
+                unchecked // Overflow is fine, just wrap
+                {
+                    int hash = 17;
+                    // Suitable nullity checks etc, of course :)
+                    if (MinScale != null)
+                        hash = hash * 23 + MinScale.GetHashCode();
+                    if (MaxScale != null)
+                        hash = hash * 23 + MaxScale.GetHashCode();
+                    hash = hash * 23 + GeometryType.GetHashCode();
+                    return hash;
+                }
+            }
+
+            public override bool Equals(object obj)
+            {
+                var cat = obj as ThemeCategory;
+                if (cat == null)
+                    return false;
+
+                return this.MaxScale == cat.MaxScale &&
+                       this.MinScale == cat.MinScale &&
+                       this.GeometryType == cat.GeometryType;
+            }
+        }
+
+        [DebuggerDisplay("Name = {Layer.Name}, Label = {Layer.LegendLabel}")]
+        public class LayerNodeMetadata : LegendNodeMetadata
+        {
+            public LayerNodeMetadata(RuntimeMapLayer layer, bool bInitiallySelectable)
+            {
+                base.IsGroup = false;
+                this.Layer = layer;
+                this.IsSelectable = (layer != null) ? layer.Selectable : false;
+                this.DrawSelectabilityIcon = (layer != null && bInitiallySelectable);
+                this.WasInitiallySelectable = bInitiallySelectable;
+                this.LayerDefinitionContent = null;
+                _themeNodes = new Dictionary<ThemeCategory, List<LayerThemeNodeMetadata>>();
+                _defaultIcons = new Dictionary<ThemeCategory, Image>();
+            }
+
+            public string Name { get { return this.Layer.Name; } }
+
+            public override string ObjectId
+            {
+                get { return this.Layer.ObjectId; }
+            }
+
+            private bool? _isRaster;
+
+            public bool IsRaster
+            {
+                get
+                {
+                    if (_isRaster.HasValue)
+                        return _isRaster.Value;
+
+                    if (!string.IsNullOrEmpty(this.LayerDefinitionContent))
+                        _isRaster = this.LayerDefinitionContent.Contains("<GridLayerDefinition");
+
+                    if (_isRaster.HasValue)
+                        return _isRaster.Value;
+
+                    throw new Exception("Layer metadata not fully initialized"); //Shouldn't get to here
+                }
+            }
+
+            private bool? _isDwf;
+
+            public bool IsDwf
+            {
+                get
+                {
+                    if (_isDwf.HasValue)
+                        return _isRaster.Value;
+
+                    if (!string.IsNullOrEmpty(this.LayerDefinitionContent))
+                        _isDwf = this.LayerDefinitionContent.Contains("<DrawingLayerDefinition");
+
+                    if (_isDwf.HasValue)
+                        return _isRaster.Value;
+
+                    throw new Exception("Layer metadata not fully initialized"); //Shouldn't get to here
+                }
+            }
+
+            private Dictionary<ThemeCategory, Image> _defaultIcons;
+
+            public void SetDefaultIcon(ThemeCategory cat, Image image)
+            {
+                _defaultIcons[cat] = image;
+            }
+
+            public Image GetDefaultIcon(double scale)
+            {
+                foreach (var cat in _defaultIcons.Keys)
+                {
+                    if (ScaleIsApplicable(scale, cat))
+                        return _defaultIcons[cat];
+                }
+                return null;
+            }
+
+            //public Image Icon { get; set; }
+
+            internal RuntimeMapLayer Layer { get; private set; }
+
+            public bool DrawSelectabilityIcon { get; set; }
+
+            public bool IsSelectable { get; set; }
+
+            public bool WasInitiallySelectable { get; private set; }
+
+            public bool IsBaseLayer { get; set; }
+
+            public string LayerDefinitionContent { get; set; }
+
+            private Dictionary<ThemeCategory, List<LayerThemeNodeMetadata>> _themeNodes;
+
+            public List<LayerThemeNodeMetadata> GetThemeNodes(ThemeCategory category)
+            {
+                if (_themeNodes.ContainsKey(category))
+                    return _themeNodes[category];
+                return null;
+            }
+
+            public void AddThemeNode(ThemeCategory category, LayerThemeNodeMetadata themeMeta)
+            {
+                if (!_themeNodes.ContainsKey(category))
+                    _themeNodes[category] = new List<LayerThemeNodeMetadata>();
+
+                _themeNodes[category].Add(themeMeta);
+            }
+
+            internal bool HasDefaultIcons()
+            {
+                return (_defaultIcons.Count > 0);
+            }
+
+            internal bool HasTheme()
+            {
+                if (_themeNodes.Count == 0)
+                    return false;
+
+                foreach (var coll in _themeNodes.Values)
+                    if (coll.Count > 0)
+                        return true;
+
+                return false;
+            }
+
+            internal TreeNode[] CreateThemeNodesFromCachedMetadata(double scale)
+            {
+                var nodes = new List<TreeNode>();
+
+                //Find the applicable scale range(s)
+                foreach (var cat in _themeNodes.Keys)
+                {
+                    bool bApplicable = ScaleIsApplicable(scale, cat);
+
+                    if (bApplicable)
+                    {
+                        var metadata = _themeNodes[cat];
+                        nodes.AddRange(CreateThemeNodes(metadata));
+                    }
+                }
+
+                return nodes.ToArray();
+            }
+
+            internal static bool ScaleIsApplicable(double scale, ThemeCategory cat)
+            {
+                bool bApplicable = false;
+
+                bool bHasMin = !string.IsNullOrEmpty(cat.MinScale);
+                bool bHasMax = !string.IsNullOrEmpty(cat.MaxScale);
+
+                if (bHasMin)
+                {
+                    double minVal = double.Parse(cat.MinScale);
+                    if (bHasMax) //bHasMin = true, bHasMax = true
+                    {
+                        double maxVal = double.Parse(cat.MaxScale);
+                        if (scale >= minVal && scale < maxVal)
+                            bApplicable = true;
+                    }
+                    else         //bHasMin = true, bHasMax = false
+                    {
+                        if (scale >= minVal)
+                            bApplicable = true;
+                    }
+                }
+                else
+                {
+                    if (bHasMax) //bHasMin = false, bHasMax = true
+                    {
+                        double maxVal = double.Parse(cat.MaxScale);
+                        if (scale < maxVal)
+                            bApplicable = true;
+                    }
+                    else         //bHasMin = false, bHasMax = false
+                    {
+                        bApplicable = true;
+                    }
+                }
+                return bApplicable;
+            }
+
+            private IEnumerable<TreeNode> CreateThemeNodes(List<LayerThemeNodeMetadata> metadata)
+            {
+                foreach (var meta in metadata)
+                {
+                    var node = new TreeNode();
+                    node.Text = meta.Label;
+                    node.Tag = meta;
+                    yield return node;
+                }
+            }
+
+            internal bool HasDefaultIconsAt(double scale)
+            {
+                foreach (var cat in _defaultIcons.Keys)
+                {
+                    if (ScaleIsApplicable(scale, cat))
+                        return true;
+                }
+                return false;
+            }
+        }
+    }
+}

Modified: trunk/Tools/Maestro/Maestro.MapViewer/Maestro.MapViewer.csproj
===================================================================
--- trunk/Tools/Maestro/Maestro.MapViewer/Maestro.MapViewer.csproj	2012-09-13 18:11:48 UTC (rev 7016)
+++ trunk/Tools/Maestro/Maestro.MapViewer/Maestro.MapViewer.csproj	2012-09-14 12:56:47 UTC (rev 7017)
@@ -60,6 +60,7 @@
     <Compile Include="Legend.Designer.cs">
       <DependentUpon>Legend.cs</DependentUpon>
     </Compile>
+    <Compile Include="LegendPresenter.cs" />
     <Compile Include="MapStatusTracker.cs">
       <SubType>Component</SubType>
     </Compile>



More information about the mapguide-commits mailing list