/// <summary> /// Add a new topic file to the project and the editor /// </summary> /// <param name="filename">The filename of the topic to add</param> /// <param name="addAsChild">True to add as a child of the selected /// topic or false to add it as a sibling.</param> /// <returns>The topic that was just added</returns> private Topic AddTopicFile(string filename, bool addAsChild) { Topic newTopic, currentTopic = ucContentLayoutEditor.CurrentTopic; string newPath = filename, projectPath = Path.GetDirectoryName( contentLayoutFile.Project.Filename); // The file must reside under the project path if(!Path.GetDirectoryName(filename).StartsWith(projectPath, StringComparison.OrdinalIgnoreCase)) newPath = Path.Combine(projectPath, Path.GetFileName(filename)); // Add the file to the project if not already there FileItem newItem = contentLayoutFile.Project.AddFileToProject(filename, newPath); // Add the topic to the editor's collection newTopic = new Topic { TopicFile = new TopicFile(newItem.ToContentFile()) }; if(addAsChild && currentTopic != null) { currentTopic.Subtopics.Add(newTopic); currentTopic.IsExpanded = true; } else if(currentTopic == null) ucContentLayoutEditor.Topics.Add(newTopic); else currentTopic.Parent.Insert(currentTopic.Parent.IndexOf(currentTopic) + 1, newTopic); newTopic.IsSelected = true; return newTopic; }
//===================================================================== /// <summary> /// This is used to load the topic information from the project /// file. /// </summary> /// <param name="xr">The XML text reader from which the information /// is loaded.</param> internal void ReadXml(XmlReader xr) { Topic newTopic; string guid, parentMode; bool visible, attrValue; guid = xr.GetAttribute("id"); if(guid != null && guid.Trim().Length != 0) contentId = guid; else contentId = Guid.NewGuid().ToString(); if(!Boolean.TryParse(xr.GetAttribute("noFile"), out noFile)) noFile = false; if(!Boolean.TryParse(xr.GetAttribute("visible"), out visible)) visible = true; if(Boolean.TryParse(xr.GetAttribute("isDefault"), out attrValue)) this.IsDefaultTopic = attrValue; if(Boolean.TryParse(xr.GetAttribute("isMSHVRoot"), out attrValue)) this.IsMSHVRootContentContainer = attrValue; parentMode = xr.GetAttribute("apiParentMode"); if(!String.IsNullOrEmpty(parentMode)) this.ApiParentMode = (ApiParentMode)Enum.Parse(typeof(ApiParentMode), parentMode, true); this.Visible = visible; this.Title = xr.GetAttribute("title"); this.TocTitle = xr.GetAttribute("tocTitle"); this.LinkText = xr.GetAttribute("linkText"); if(!xr.IsEmptyElement) while(!xr.EOF) { xr.Read(); if(xr.NodeType == XmlNodeType.EndElement && xr.Name == "Topic") break; if(xr.NodeType == XmlNodeType.Element) if(xr.Name == "HelpAttributes") helpAttributes.ReadXml(xr); else if(xr.Name == "HelpKeywords") keywords.ReadXml(xr); else if(xr.Name == "Topic") { newTopic = new Topic(); newTopic.ReadXml(xr); this.Subtopics.Add(newTopic); } } }
/// <summary> /// Load the tree view with the topics and set the form up to edit them /// </summary> /// <param name="selectedEntry">If not null, the node containing the /// specified entry is set as the selected node. If null, the first /// node is selected.</param> private void LoadTopics(Topic selectedEntry) { TreeNode node; Topic defTopic = topics.DefaultTopic, apiTopic = topics.ApiContentInsertionPoint, rootContainer = topics.MSHVRootContentContainer; try { tvContent.SuspendLayout(); tvContent.Nodes.Clear(); defaultNode = apiInsertionNode = rootContainerNode = firstNode = null; firstSelection = selectedEntry; if(topics.Count != 0) { foreach(Topic t in topics) { node = tvContent.Nodes.Add(t.DisplayTitle); node.Name = t.Id; node.Tag = t; if(t == defTopic) defaultNode = node; if(t == apiTopic) apiInsertionNode = node; if(t == rootContainer) rootContainerNode = node; if(t == firstSelection) firstNode = node; if(t.Subtopics.Count != 0) this.AddChildren(t.Subtopics, node); } this.UpdateDefaultAndApiNodeImages(); tvContent.ExpandAll(); if(firstNode != null) { tvContent.SelectedNode = firstNode; firstNode.EnsureVisible(); } else { tvContent.SelectedNode = tvContent.Nodes[0]; tvContent.SelectedNode.EnsureVisible(); } } else this.UpdateControlStatus(); } finally { tvContent.ResumeLayout(); } }
/// <summary> /// Add an empty container node that is not associated with any topic /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void tsbAddTopic_ButtonClick(object sender, EventArgs e) { ToolStripItem tiAdd = (ToolStripItem)sender; Topic topic, parentTopic; TreeNode tnNew, tnParent; topic = new Topic(); topic.Title = "Table of Contents Container"; topic.TopicFile = null; tnNew = new TreeNode(topic.DisplayTitle); tnNew.Name = topic.Id; tnNew.Tag = topic; if(tiAdd == tsbAddChildTopic || tiAdd.Owner == cmsNewChildTopic) { tvContent.SelectedNode.Nodes.Add(tnNew); parentTopic = (Topic)tvContent.SelectedNode.Tag; parentTopic.Subtopics.Add(topic); } else if(tvContent.SelectedNode == null) { tvContent.Nodes.Add(tnNew); topics.Add(topic); } else { if(tvContent.SelectedNode.Parent == null) tvContent.Nodes.Insert(tvContent.Nodes.IndexOf( tvContent.SelectedNode) + 1, tnNew); else { tnParent = tvContent.SelectedNode.Parent; tnParent.Nodes.Insert(tnParent.Nodes.IndexOf( tvContent.SelectedNode) + 1, tnNew); } parentTopic = (Topic)tvContent.SelectedNode.Tag; parentTopic.Parent.Insert( parentTopic.Parent.IndexOf(parentTopic) + 1, topic); } tvContent.SelectedNode = tnNew; }
/// <summary> /// Paste the selected topic as a sibling or child of the selected topic /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdPaste_Executed(object sender, ExecutedRoutedEventArgs e) { Topic targetTopic = this.CurrentTopic, newTopic = clipboardTopic; if(newTopic != null) { // Don't allow pasting multiple copies of the same item in here as the IDs must be unique clipboardTopic = null; if(targetTopic == null) topics.Add(newTopic); else { if(e.Command == EditorCommands.PasteAsChild) { targetTopic.Subtopics.Add(newTopic); targetTopic.IsExpanded = true; } else targetTopic.Parent.Insert(targetTopic.Parent.IndexOf(targetTopic) + 1, newTopic); } newTopic.IsSelected = true; tvContent.Focus(); } }
/// <summary> /// Add a new topic file /// </summary> /// <param name="filename">The filename of the topic to add</param> /// <param name="addAsChild">True to add as a child of the selected /// node or false to add it as a sibling.</param> private void AddTopicFile(string filename, bool addAsChild) { Topic topic, parentTopic; TreeNode tnNew, tnParent; string newPath = filename, projectPath = Path.GetDirectoryName( topics.FileItem.ProjectElement.Project.Filename); // The file must reside under the project path if(!Path.GetDirectoryName(filename).StartsWith(projectPath, StringComparison.OrdinalIgnoreCase)) newPath = Path.Combine(projectPath, Path.GetFileName(filename)); // Add the file to the project FileItem newItem = topics.FileItem.ProjectElement.Project.AddFileToProject( filename, newPath); topic = new Topic(); topic.TopicFile = new TopicFile(newItem); tnNew = new TreeNode(topic.DisplayTitle); tnNew.Name = topic.Id; tnNew.Tag = topic; if(addAsChild) { tvContent.SelectedNode.Nodes.Add(tnNew); parentTopic = (Topic)tvContent.SelectedNode.Tag; parentTopic.Subtopics.Add(topic); } else if(tvContent.SelectedNode == null) { tvContent.Nodes.Add(tnNew); topics.Add(topic); } else { if(tvContent.SelectedNode.Parent == null) tvContent.Nodes.Insert(tvContent.Nodes.IndexOf( tvContent.SelectedNode) + 1, tnNew); else { tnParent = tvContent.SelectedNode.Parent; tnParent.Nodes.Insert(tnParent.Nodes.IndexOf( tvContent.SelectedNode) + 1, tnNew); } parentTopic = (Topic)tvContent.SelectedNode.Tag; parentTopic.Parent.Insert( parentTopic.Parent.IndexOf(parentTopic) + 1, topic); } tvContent.SelectedNode = tnNew; }
/// <summary> /// Cut the selected topic to the internal clipboard /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdCut_Executed(object sender, ExecutedRoutedEventArgs e) { clipboardTopic = this.CurrentTopic; if(clipboardTopic != null) { clipboardTopic.Parent.Remove(clipboardTopic); tvContent.Focus(); } }
//===================================================================== /// <summary> /// Add an empty container topic to the collection that is not associated with any file /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdAddItem_Executed(object sender, ExecutedRoutedEventArgs e) { Topic currentTopic = this.CurrentTopic, newTopic = new Topic { Title = "Table of Contents Container", TopicFile = null, // Assign a default GUID }; // If the command parameter is null, add it as a sibling. If not, add it as a child. if(e.Parameter == null || currentTopic == null) { if(currentTopic == null || topics.Count == 0) topics.Add(newTopic); else currentTopic.Parent.Insert(currentTopic.Parent.IndexOf(currentTopic) + 1, newTopic); } else { currentTopic.Subtopics.Add(newTopic); currentTopic.IsExpanded = true; } newTopic.IsSelected = true; }
/// <summary> /// Import attribute information from a companion file /// </summary> /// <param name="filename">The companion filename</param> private void ImportCompanionFileInfo(string filename) { XPathDocument info = new XPathDocument(filename); XPathNavigator navTopics = info.CreateNavigator(); Topic t; foreach(XPathNavigator topic in navTopics.Select("metadata/topic")) { if(!topicSettings.TryGetValue(topic.GetAttribute("id", String.Empty), out t)) { t = new Topic(); topicSettings.Add(topic.GetAttribute("id", String.Empty), t); } foreach(XPathNavigator attr in topic.Select("*")) switch(attr.Name) { case "title": t.Title = attr.Value; break; case "tableOfContentsTitle": t.TocTitle = attr.Value; break; case "attribute": t.HelpAttributes.Add(attr.GetAttribute("name", String.Empty), attr.Value); break; default: break; } } }
/// <summary> /// Topic settings were found but no content layout file. In such /// cases, this is called to create a default content layout file /// based on the settings alone. /// </summary> private void CreateDefaultContentLayoutFile() { string filename = Path.Combine(base.ProjectFolder, "ContentLayout.content"); Topic commonSettings, t; if(!topicSettings.TryGetValue("*", out commonSettings)) commonSettings = new Topic(); else topicSettings.Remove("*"); if(topicSettings.Count == 0) return; XmlWriterSettings settings = new XmlWriterSettings(); XmlWriter writer = null; try { settings.Indent = true; settings.CloseOutput = true; writer = XmlWriter.Create(filename, settings); writer.WriteStartDocument(); writer.WriteStartElement("Topics"); foreach(string key in topicSettings.Keys) { t = topicSettings[key]; foreach(MSHelpKeyword kw in commonSettings.Keywords) t.Keywords.Add(kw); writer.WriteStartElement("Topic"); writer.WriteAttributeString("id", key); writer.WriteAttributeString("visible", "true"); if(!String.IsNullOrEmpty(t.Title)) writer.WriteAttributeString("title", t.Title); if(!String.IsNullOrEmpty(t.TocTitle) && t.TocTitle != t.Title) writer.WriteAttributeString("tocTitle", t.TocTitle); if(!String.IsNullOrEmpty(t.LinkText)) writer.WriteAttributeString("linkText", t.LinkText); if(t.HelpAttributes.Count != 0) t.HelpAttributes.WriteXml(writer, true); if(t.Keywords.Count != 0) t.Keywords.WriteXml(writer); writer.WriteEndElement(); } writer.WriteEndElement(); writer.WriteEndDocument(); } finally { if(writer != null) writer.Close(); } project.AddFileToProject(filename, filename); }
/// <summary> /// Insert a link to a topic /// </summary> /// <param name="extension">The extension of the file in which the /// link is being inserted.</param> /// <param name="topic">The topic for which to create a link</param> /// <remarks>If dropped inside some selected text, the link will /// wrap the selected text.</remarks> private void InsertTopicLink(string extension, Topic topic) { TextArea textArea = editor.ActiveTextAreaControl.TextArea; int offset = textArea.Caret.Offset; string selectedText; if(textArea.SelectionManager.HasSomethingSelected && textArea.SelectionManager.SelectionCollection[0].ContainsOffset(offset)) selectedText = textArea.SelectionManager.SelectionCollection[0].SelectedText; else selectedText = String.Empty; if(extension == ".htm" || extension == ".html") ContentEditorControl.InsertString(textArea, topic.ToAnchor(selectedText)); else ContentEditorControl.InsertString(textArea, topic.ToLink(selectedText)); }