/// <summary> /// Checks if a given element in the tree view needs updating /// </summary> /// <param name="node">The node to check for</param> /// <param name="browser">The browser instance to check in</param> /// <returns>a tree view node based on the new data.</returns> private static KmlTreeViewNode UpdateCheck(KmlTreeViewNode node, GEWebBrowser browser) { dynamic liveObject = null; try { liveObject = browser.Plugin.getElementByUrl(node.Name); } catch (COMException) { } if (liveObject != null) { KmlTreeViewNode newNode = new KmlTreeViewNode(liveObject) { Name = node.Name, BaseUrl = node.BaseUrl }; if (node.Parent != null) { // update the tree node.Parent.Nodes.Insert(node.Index, newNode); node.Parent.Nodes.Remove(node); } return(newNode); } return(node); }
/// <summary> /// Sets the StateImageIndex of each child node of the given <paramref name="node">parent tree node</paramref> /// </summary> /// <param name="node">The parent tree node</param> private void SetChildStateImageIndex(KmlTreeViewNode node) { foreach (KmlTreeViewNode child in node.Nodes) { if (child.StateImageIndex == -1) { child.StateImageIndex = child.Checked ? 1 : 0; } } this.Refresh(); }
/// <summary> /// Returns the index of the first occurrence of a tree node with the specified object ID. /// As the node key is automatically set from the kmlObject ID the IDs should always correspond and be unique. /// </summary> /// <param name="id">The API object id</param> /// <returns>The tree node for the object from the given ID (or an empty tree node if the ID isn't found)</returns> public KmlTreeViewNode GetNodeById(string id) { TreeNode[] nodes = this.Nodes.Find(id, true); KmlTreeViewNode node = new KmlTreeViewNode(); if (nodes.Length == 1) { node = (KmlTreeViewNode)nodes[0]; } return(node); }
/// <summary> /// Adds children to a KmlTreeViewNode. /// </summary> /// <param name="sender">The object that raised the event.</param> /// <param name="e">Event arguments.</param> private void NodeBuilderRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Result == null) { return; } // the result object from NodeBuilder_DoWork object[] result = (object[])e.Result; KmlTreeViewNode baseNode = (KmlTreeViewNode)result[0]; Stack <KmlTreeViewNode> children = (Stack <KmlTreeViewNode>)result[1]; bool linkFailed = Convert.ToBoolean(result[2], CultureInfo.InvariantCulture); // clear the node of all children baseNode.Nodes.Clear(); // add the children to the node // (is just a new placeholder if there was error loading a network link) while (children.Count > 0) { KmlTreeViewNode child = children.Pop(); baseNode.Nodes.Add(child); // If the parent has a BaseUrl then we need to use this to generate the // correct ids for the child, we also set the `baseurl` on the child for its children ... // we also do an update check as it is possible the node needs to be updated... string url = child.Parent.BaseUrl; if (!string.IsNullOrEmpty(url) && child.ApiObject != null) { child.Name = GenerateId(url, child.ApiObject.getId()); child.BaseUrl = url; UpdateCheck(child, this.browser); } } if (linkFailed) { baseNode.Collapse(); baseNode.ImageKey = baseNode.SelectedImageKey = "linkFolderClosedDisconected"; } else { this.SetChildStateImageIndex(baseNode); } }
/// <summary> /// Raised when a KmlTreeView node is double clicked /// </summary> /// <param name="e">Event arguments</param> protected override void OnNodeMouseDoubleClick(TreeNodeMouseClickEventArgs e) { base.OnNodeMouseDoubleClick(e); // check if the node needs updating... // ideally this should be event driven rather than via user interaction // but as yet the API does not expose network link events... KmlTreeViewNode node = UpdateCheck((KmlTreeViewNode)e.Node, this.browser); node.Refresh(); if (node.IsLoading) { return; } switch (node.ApiType) { case ApiType.KmlPlacemark: case ApiType.KmlFolder: case ApiType.KmlDocument: case ApiType.KmlGroundOverlay: { GEHelpers.OpenFeatureBalloon(this.browser.Plugin, node.ApiObject, setBalloon: node.Checked); } break; case ApiType.KmlTour: case ApiType.KmlPhotoOverlay: { GEHelpers.ToggleMediaPlayer(this.browser.Plugin, node.ApiObject, node.Checked); } return; // exit here as the media player handles the view update case ApiType.None: return; } GEHelpers.FlyToObject(this.browser.Plugin, node.ApiObject); }
/// <summary> /// Refreshes and updates the layout of the control /// </summary> public override void Refresh() { base.Refresh(); this.BeginUpdate(); if (!this.CheckBoxes) { return; } base.CheckBoxes = false; Stack <KmlTreeViewNode> stack = new Stack <KmlTreeViewNode>(this.Nodes.Count); foreach (KmlTreeViewNode node in this.Nodes) { // Refresh internal properties of each node // http://code.google.com/p/winforms-geplugin-control-library/issues/detail?id=66 node.Refresh(); stack.Push(node); } while (stack.Count > 0) { KmlTreeViewNode node = stack.Pop(); if (node.StateImageIndex == -1) { node.StateImageIndex = node.Checked ? 1 : 0; } for (int i = 0; i < node.Nodes.Count; i++) { stack.Push((KmlTreeViewNode)node.Nodes[i]); } } this.EndUpdate(); }
/// <summary> /// Raised after a KmlTreeViewNode is expanded /// </summary> /// <param name="e">Event arguments</param> protected override void OnAfterExpand(TreeViewEventArgs e) { base.OnAfterExpand(e); KmlTreeViewNode eventNode = (KmlTreeViewNode)e.Node; eventNode.SetStyle(); // If there is a place-holder node if (eventNode.Nodes.ContainsKey(ApiType.None.ToString())) { if (eventNode.IsLoading) { return; } // animate the place holder int index = eventNode.Nodes.IndexOfKey(ApiType.None.ToString()); ((KmlTreeViewNode)eventNode.Nodes[index]).Animate(); // set up the background worker // ...using named delegates to stop code clutter here using (BackgroundWorker nodeBuilder = new BackgroundWorker()) { nodeBuilder.DoWork += this.NodeBuilderDoWork; nodeBuilder.RunWorkerCompleted += this.NodeBuilderRunWorkerCompleted; nodeBuilder.RunWorkerAsync(eventNode); } } else { // There is no place-holder node... // so just set the check state of the children this.SetChildStateImageIndex(eventNode); } }
/// <summary> /// Creates a KmlTreeViewNode from an KML feature /// </summary> /// <param name="feature">The KML feature to base the node on</param> /// <returns>A KmlTreeViewNode based on the feature</returns> public static KmlTreeViewNode CreateNode(dynamic feature) { // create the node from the feature KmlTreeViewNode treeNode = new KmlTreeViewNode(feature); // if the children are hidden just return the empty node... if (treeNode.KmlListStyle == ListItemStyle.CheckHideChildren) { return(treeNode); } // if the node is a network link or a document or folder // and it has children... if (treeNode.ApiType == ApiType.KmlNetworkLink || ((treeNode.ApiType == ApiType.KmlDocument || treeNode.ApiType == ApiType.KmlFolder) && KmlHelpers.HasChildNodes(feature))) { // add a place holder for the children... treeNode.Nodes.Add(new KmlTreeViewNode()); } return(treeNode); }
/// <summary> /// Asynchronous method for building a list of nodes /// </summary> /// <param name="sender">The object that raised the event.</param> /// <param name="e">Event arguments.</param> private void NodeBuilderDoWork(object sender, DoWorkEventArgs e) { // node passed in from RunWorkerAsync KmlTreeViewNode baseNode = (KmlTreeViewNode)e.Argument; baseNode.IsLoading = true; // create a stack to hold the children we will create Stack <KmlTreeViewNode> children = new Stack <KmlTreeViewNode>(); bool linkFailed = false; if (baseNode.ApiType == ApiType.KmlNetworkLink) { // fetch the network link data string url = KmlHelpers.GetUrl(baseNode.ApiObject).ToString(); dynamic data; bool rebuild = false; // can't use the new FetchAndParse with archive files... if (url.EndsWith("kmz", StringComparison.OrdinalIgnoreCase) || url.EndsWith("dae", StringComparison.OrdinalIgnoreCase)) { data = this.browser.FetchKmlSynchronous(url); } else { data = this.browser.FetchAndParse(url); rebuild = true; } if (data != null) { KmlTreeViewNode link = CreateNode(data); if (rebuild) { // The KmlObjects are created via parseKml so they need their id's need to be rebuilt // so that the Url is still present (e.g. http://foo.com/#id vs. #id) // the `baseurl` of the node is set so that any child nodes can also have there id's rebuilt // when they are created. link.Name = GenerateId(url, data.getId()); link.BaseUrl = url; } // create a new tree node from the data and push it on to the stack children.Push(link); } else { // no data, so push a new place holder node in and set the loadError flag baseNode.IsLoading = false; children.Push(new KmlTreeViewNode()); linkFailed = true; } } else { // the feature must be a KmlFolder or a KmlDocument (KmlContainer) // ...so get the child nodes from it dynamic kmlChildNodes = KmlHelpers.GetChildNodes(baseNode.ApiObject); if (kmlChildNodes != null) { int count = kmlChildNodes.getLength(); for (int i = 0; i < count; i++) { // create a new KmlTreeViewNode from each feature in the KmlContainer // and push it on to the stack. try { children.Push(CreateNode(kmlChildNodes.item(i))); } catch (COMException) { children.Clear(); return; } } } } // pass the base node, child stack and error flag as the result. e.Result = new object[] { baseNode, children, linkFailed }; }
/// <summary> /// Raised when the mouse is clicked on the KmlTreeView /// </summary> /// <param name="e">The event arguments</param> protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e) { base.OnNodeMouseClick(e); this.preventChecking = true; int space = this.ImageList == null ? 0 : 18; if ((e.X > e.Node.Bounds.Left - space || e.X < e.Node.Bounds.Left - (space + 16)) && e.Button != MouseButtons.None) { return; } KmlTreeViewNode treeNode = (KmlTreeViewNode)e.Node; if (e.Button == MouseButtons.Left) { // toggle the check state treeNode.Checked = !treeNode.Checked; treeNode.ApiObjectVisible = treeNode.Checked; if (!treeNode.Checked) { // Turn off the media player for the node if it was unchecked // For example, un-checking a Tour object in the tree exits the tour player GEHelpers.ToggleMediaPlayer(this.browser.Plugin, treeNode.ApiObject, false); } } treeNode.StateImageIndex = treeNode.Checked ? 1 : treeNode.StateImageIndex; this.OnAfterCheck(new TreeViewEventArgs(treeNode, TreeViewAction.ByMouse)); Stack <KmlTreeViewNode> nodes = new Stack <KmlTreeViewNode>(treeNode.Nodes.Count); nodes.Push(treeNode); do { treeNode = nodes.Pop(); treeNode.Checked = e.Node.Checked; treeNode.ApiObjectVisible = treeNode.Checked; for (int i = 0; i < treeNode.Nodes.Count; i++) { nodes.Push((KmlTreeViewNode)treeNode.Nodes[i]); } }while (nodes.Count > 0); bool state = false; treeNode = (KmlTreeViewNode)e.Node; while (treeNode.Parent != null) { foreach (KmlTreeViewNode child in treeNode.Parent.Nodes) { state |= child.Checked != treeNode.Checked | child.StateImageIndex == 2; } int index = (int)Convert.ToUInt32(treeNode.Checked); treeNode.Parent.Checked = state || (index > 0); if (state) { treeNode.Parent.ApiObjectVisible = true; treeNode.Parent.StateImageIndex = 2; } else { treeNode.Parent.StateImageIndex = index; } treeNode = treeNode.Parent; } this.preventChecking = false; }
/// <summary> /// Checks if a given element in the tree view needs updating /// </summary> /// <param name="node">The node to check for</param> /// <param name="browser">The browser instance to check in</param> /// <returns>a tree view node based on the new data.</returns> private static KmlTreeViewNode UpdateCheck(KmlTreeViewNode node, GEWebBrowser browser) { dynamic liveObject = null; try { liveObject = browser.Plugin.getElementByUrl(node.Name); } catch (COMException) { } if (liveObject != null) { KmlTreeViewNode newNode = new KmlTreeViewNode(liveObject) { Name = node.Name, BaseUrl = node.BaseUrl }; if (node.Parent != null) { // update the tree node.Parent.Nodes.Insert(node.Index, newNode); node.Parent.Nodes.Remove(node); } return newNode; } return node; }
/// <summary> /// Returns the index of the first occurrence of a tree node with the specified object ID. /// As the node key is automatically set from the kmlObject ID the IDs should always correspond and be unique. /// </summary> /// <param name="id">The API object id</param> /// <returns>The tree node for the object from the given ID (or an empty tree node if the ID isn't found)</returns> public KmlTreeViewNode GetNodeById(string id) { TreeNode[] nodes = this.Nodes.Find(id, true); KmlTreeViewNode node = new KmlTreeViewNode(); if (nodes.Length == 1) { node = (KmlTreeViewNode)nodes[0]; } return node; }
/// <summary> /// Creates a KmlTreeViewNode from an KML feature /// </summary> /// <param name="feature">The KML feature to base the node on</param> /// <returns>A KmlTreeViewNode based on the feature</returns> public static KmlTreeViewNode CreateNode(dynamic feature) { // create the node from the feature KmlTreeViewNode treeNode = new KmlTreeViewNode(feature); // if the children are hidden just return the empty node... if (treeNode.KmlListStyle == ListItemStyle.CheckHideChildren) { return treeNode; } // if the node is a network link or a document or folder // and it has children... if (treeNode.ApiType == ApiType.KmlNetworkLink || ((treeNode.ApiType == ApiType.KmlDocument || treeNode.ApiType == ApiType.KmlFolder) && KmlHelpers.HasChildNodes(feature))) { // add a place holder for the children... treeNode.Nodes.Add(new KmlTreeViewNode()); } return treeNode; }
/// <summary> /// Creates a KmlTreeViewNode from an kml feature /// </summary> /// <param name="feature">The kml feature to base the node on</param> /// <returns>A KmlTreeViewNode based on the feature</returns> public KmlTreeViewNode CreateNode(dynamic feature) { // create the node from the feature KmlTreeViewNode treeNode = new KmlTreeViewNode(feature); // if the node is a networlink, or // a document or folder with children if (treeNode.ApiType == ApiType.KmlNetworkLink || ((treeNode.ApiType == ApiType.KmlDocument || treeNode.ApiType == ApiType.KmlFolder) && KmlHelpers.HasChildNodes(feature))) { // add a place holder treeNode.Nodes.Add(new KmlTreeViewNode()); } return treeNode; }