/// <summary> /// Open the selected file for editing /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdEdit_Executed(object sender, ExecutedRoutedEventArgs e) { TocEntry t = ucTopicPreviewer.CurrentTopic; if (t.SourceFile.Exists) { string fullName = t.SourceFile; // If the document is already open, just activate it foreach (IDockContent content in this.DockPanel.Documents) { if (String.Equals(content.DockHandler.ToolTipText, fullName, StringComparison.OrdinalIgnoreCase)) { content.DockHandler.Activate(); return; } } if (File.Exists(fullName)) { TopicEditorWindow editor = new TopicEditorWindow(fullName); editor.Show(this.DockPanel); } else { WinFormsMessageBox.Show("File does not exist: " + fullName, Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); } } else { WinFormsMessageBox.Show("No file is associated with this topic", Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// This is used to copy site map files to the help format output folders including those for any child /// site map entries. /// </summary> /// <param name="site">The site entry containing the files to copy</param> private void CopySiteMapFiles(TocEntry site) { if (site.SourceFile.Path.Length != 0) { // Set the destination filename which will always match the source filename for site map files. site.DestinationFile = site.SourceFile.PersistablePath; foreach (string baseFolder in this.HelpFormatOutputFolders) { if (!File.Exists(baseFolder + site.DestinationFile)) { this.ReportProgress("{0} -> {1}{2}", site.SourceFile, baseFolder, site.DestinationFile); // All attributes are turned off so that we can delete it later File.Copy(site.SourceFile, baseFolder + site.DestinationFile, true); File.SetAttributes(baseFolder + site.DestinationFile, FileAttributes.Normal); } } } if (site.Children.Count != 0) { foreach (TocEntry entry in site.Children) { this.CopySiteMapFiles(entry); } } }
/// <summary> /// Edit the selected file for editing /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdEdit_Executed(object sender, ExecutedRoutedEventArgs e) { // If the sender is a topic, use that instead. Due to the way the WPF tree view works, the // selected topic isn't always the one we just added when it's the first child of a parent topic. TocEntry t = sender as TocEntry; if (t == null) { t = base.UIControl.CurrentTopic; } if (t.SourceFile.Path.Length != 0) { string fullName = t.SourceFile; if (File.Exists(fullName)) { VsShellUtilities.OpenDocument(this, fullName); } else { Utility.ShowMessageBox(OLEMSGICON.OLEMSGICON_INFO, "File does not exist: " + fullName); } } else { Utility.ShowMessageBox(OLEMSGICON.OLEMSGICON_INFO, "No file is associated with this topic"); } }
/// <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) { TocEntry 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.Children.Add(newTopic); targetTopic.IsExpanded = true; } else { targetTopic.Parent.Insert(targetTopic.Parent.IndexOf(targetTopic) + 1, newTopic); } } newTopic.IsSelected = true; 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) { TocEntry currentTopic = this.CurrentTopic, newTopic = new TocEntry(topics.ContentLayoutFile.BasePathProvider) { Title = "Table of Contents Container", UniqueId = Guid.NewGuid() }; // 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.Children.Add(newTopic); currentTopic.IsExpanded = true; } newTopic.IsSelected = true; }
internal RTUpdate GetRtUpdate(Guid tocKey) { if (this.toc.ContainsKey(tocKey)) { TocEntry entry = (TocEntry)toc[tocKey]; if (entry.DeckType == DeckTypeEnum.QuickPoll) { Debug.WriteLine(""); } if (!UpdateAssociation(entry)) { //If we failed to map a student submission to a deck it could be because the SS is on // a whiteboard page, or it could be that we don't yet have a TOC entry for the // original slide. For now, just treat both cases as if // they were whiteboard. // P2: Make a way to mark the message as SS and leave the deck association empty, and // have that be understood as a SS on a WB. I think this requires an update to WebViewer? RTUpdate rtu = entry.ToRtUpdate(); rtu.DeckType = (int)DeckTypeEnum.Whiteboard; return(rtu); } return(entry.ToRtUpdate()); } return(null); }
internal object UpdateZoomAndColorForSlide(CP3Msgs.SlideInformationMessage sim, out string warning) { Guid slideId = (Guid)sim.TargetId; float zoom = sim.Zoom; Color color = sim.SlideBackgroundColor; warning = ""; if (!this.tocBySlideId.ContainsKey(slideId)) { return(null); } TocEntry entry = (TocEntry)this.tocBySlideId[slideId]; if ((entry.SlideSize != zoom) || (!entry.BackgroundColor.Equals(color))) { if (!entry.BackgroundColor.Equals(color)) { Debug.WriteLine("*****Update to slide background color."); } entry.SlideSize = zoom; entry.BackgroundColor = color; if (!UpdateAssociation(entry)) { return(null); } return(entry.ToRtUpdate()); } return(null); }
/// <summary> /// for the given Toc key, find the entry and if the entry is for a Student Submission slide, and /// the slide and deck association are missing, attempt to resolve and update them. /// Return false if the assocation needs updating, but the update failed. Return true in all /// other cases. /// </summary> /// <param name="tocKey"></param> /// <returns></returns> private bool UpdateAssociation(TocEntry entry) { /// StudentSubmission and QuickPoll entries should have valid deck associations. /// A valid association includes a deck Guid, a slide index and a association deck typ /// that is a presentation or whiteboard. if (((entry.DeckType == DeckTypeEnum.StudentSubmission) || (entry.DeckType == DeckTypeEnum.QuickPoll)) && ((entry.DeckAssociation == Guid.Empty) || (entry.SlideAssociation < 0) || (entry.DeckTypeAssociation == DeckTypeEnum.QuickPoll) || (entry.DeckTypeAssociation == DeckTypeEnum.StudentSubmission) || (entry.DeckTypeAssociation == DeckTypeEnum.Undefined))) { TocEntry associationEntry = FindAssociation(entry); if (associationEntry != null) { entry.DeckAssociation = associationEntry.DeckId; entry.SlideAssociation = associationEntry.SlideIndex; entry.DeckTypeAssociation = associationEntry.DeckType; } else { return(false); } } return(true); }
/// <summary> /// Add a new topic from an existing file /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdAddExistingFile_Executed(object sender, ExecutedRoutedEventArgs e) { FileNode thisNode = this.FileNode; TocEntry t = base.UIControl.CurrentTopic; string projectPath = Path.GetDirectoryName(siteMapFile.ProjectElement.Project.Filename); using (WinFormsOpenFileDialog dlg = new WinFormsOpenFileDialog()) { dlg.Title = "Select the conceptual topic file(s)"; dlg.Filter = "HTML Files (*.htm, *.html)|*.htm;*.html|All files (*.*)|*.*"; dlg.DefaultExt = "html"; dlg.InitialDirectory = (t != null && t.SourceFile.Path.Length != 0) ? Path.GetDirectoryName(t.SourceFile) : projectPath; dlg.Multiselect = true; // If selected, add the new file(s). Filenames that are already in the collection are ignored. if (dlg.ShowDialog() == WinFormsDialogResult.OK) { foreach (string filename in dlg.FileNames) { this.AddTopicFile(filename, e.Parameter != null); if (t != null) { t.IsSelected = true; } } if (thisNode != null) { thisNode.ProjectManager.RefreshProject(); } } } }
/// <summary> /// This is used to recursively add child TOC entries to the entity reference collection /// </summary> /// <param name="t">The TOC entry</param> /// <param name="er">The parent entity reference</param> /// <param name="hasSelectedItem">The selected item state. Only first selected item found is /// marked as the selected item.</param> private bool AddChildTocEntries(TocEntry t, EntityReference er, bool hasSelectedItem) { EntityReference subEnt; foreach (var child in t.Children) { subEnt = new EntityReference { EntityType = EntityType.TocEntry, Id = child.Id, Label = (child.Title ?? child.Id ?? "(No title)"), ToolTip = String.Format(CultureInfo.CurrentCulture, "ID: {0}\nFile: {1}", (child.Id ?? child.Title ?? "(No ID)"), child.SourceFile), Tag = child, IsExpanded = child.IsExpanded, IsSelected = (child.IsSelected && !hasSelectedItem) }; if (subEnt.IsSelected) { hasSelectedItem = true; } er.SubEntities.Add(subEnt); if (child.Children.Count != 0) { hasSelectedItem = this.AddChildTocEntries(child, subEnt, hasSelectedItem); } } return(hasSelectedItem); }
/// <summary> /// Insert a link to a site map table of contents entry (HTML only) /// </summary> /// <param name="extension">The extension of the file in which the /// link is being inserted.</param> /// <param name="tocEntry">The TOC entry for which to create a link</param> /// <remarks>If dropped inside some selected text, the link will /// wrap the selected text.</remarks> private void InsertTocLink(string extension, TocEntry tocEntry) { 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, tocEntry.ToAnchor(selectedText)); } else { ContentEditorControl.InsertString(textArea, tocEntry.Title); // Not supported in MAML topics } }
//===================================================================== /// <summary> /// This is used to mark the file as dirty when the collection changes /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> void topics_ListChanged(object sender, ListChangedEventArgs e) { TocEntry selectedTopic = tvContent.SelectedItem as TocEntry; if (e.PropertyDescriptor != null) { switch (e.PropertyDescriptor.Name) { case "IsExpanded": case "IsSelected": // We don't care about changes to these properties as they are for the // editor and don't affect the state of the topic collection. return; case "ApiParentMode": // There can be only one API content parent if (selectedTopic != null && selectedTopic.ApiParentMode != ApiParentMode.None) { foreach (var match in topics.Find( t => t.ApiParentMode != ApiParentMode.None && t != selectedTopic, false)) { match.ApiParentMode = ApiParentMode.None; } } break; case "IsDefaultTopic": // There can be only one default topic if (selectedTopic != null && selectedTopic.IsDefaultTopic) { foreach (var match in topics.Find(t => t.IsDefaultTopic && t != selectedTopic, false)) { match.IsDefaultTopic = false; } } break; default: break; } } if (sender != this) { base.RaiseEvent(new RoutedEventArgs(ContentModifiedEvent, this)); } // Update control state based on the collection content tvContent.IsEnabled = expTopicProps.IsEnabled = (topics != null && topics.Count != 0); CommandManager.InvalidateRequerySuggested(); // We must clear the enumerator or it may throw an exception due to collection changes if (matchEnumerator != null) { matchEnumerator.Dispose(); matchEnumerator = null; } }
/// <summary> /// Update the file related to the specified tree node with its new /// sort order and/or default topic indicator. /// </summary> /// <param name="tn">The tree node containing the TOC entry</param> /// <param name="sortOrder">The new sort order</param> private void UpdateTocEntry(TreeNode tn, int sortOrder) { Encoding enc; string content; TocEntry toc = (TocEntry)tn.Tag; if (toc.SortOrder != sortOrder || (toc.IsDefaultTopic && defaultNode != tn) || (!toc.IsDefaultTopic && defaultNode == tn)) { enc = Encoding.Default; if (toc.SourceFile.EndsWith(@"\")) { toc.SourceFile += toc.Title + ".html"; // Exclude the new folder file if we create it content = "<!-- @TOCExclude -->"; } else { content = BuildProcess.ReadWithEncoding( toc.SourceFile, ref enc); } // Add or remove the default topic indicator if (defaultNode != tn) { content = BuildProcess.reIsDefaultTopic.Replace(content, String.Empty); } else if (!BuildProcess.reIsDefaultTopic.IsMatch(content)) { content += "<!-- @DefaultTopic -->"; } // Add or update the sort order if (BuildProcess.reSortOrder.IsMatch(content)) { content = BuildProcess.reSortOrder.Replace(content, String.Format(CultureInfo.InvariantCulture, "<!-- @SortOrder {0} -->", sortOrder)); } else { content += String.Format(CultureInfo.InvariantCulture, "<!-- @SortOrder {0} -->", sortOrder); } using (StreamWriter sw = new StreamWriter(toc.SourceFile, false, enc)) { sw.Write(content); } } }
/// <summary> /// Load the topic and display it when selected /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void tvContent_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs <object> e) { TocEntry t = tvContent.SelectedItem as TocEntry; if (t != null) { try { Mouse.OverrideCursor = Cursors.Wait; // If the file is open in an editor, use its current content var args = new TopicContentNeededEventArgs(TopicContentNeededEvent, this, t.SourceFile); if (!String.IsNullOrEmpty(t.SourceFile)) { base.RaiseEvent(args); } txtTitle.Text = t.PreviewerTitle; fdViewer.Document = converter.ToFlowDocument(t.SourceFile, args.TopicContent); this.AdjustPageWidth(); // Scroll to the top if not the first time shown DependencyObject child = fdViewer; if (VisualTreeHelper.GetChildrenCount(child) != 0) { while (!(child is ScrollViewer)) { child = VisualTreeHelper.GetChild(child as Visual, 0); } ((ScrollViewer)child).ScrollToHome(); } } catch (Exception ex) { // If we get here, something went really wrong MessageBox.Show("Unable to convert topic. Possible converter error. Reason: " + ex.Message, Constants.AppName, MessageBoxButton.OK, MessageBoxImage.Error); } finally { Mouse.OverrideCursor = null; } if (!isNavigating && !String.IsNullOrEmpty(t.Id)) { this.RecordHistory(new Uri("link://" + t.Id)); } } else { txtTitle.Text = null; fdViewer.Document = null; } }
/// <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(); } }
public BBeB ParseHTML(HtmlDocument doc, BindingParams bindingParams, TocEntry tocEntries) { m_Book = new BBeB(); byte[] thumb = File.ReadAllBytes(bindingParams.IconFile); setHeaderValues(thumb.Length); m_Book.MetaData = bindingParams.MetaData; m_Book.ThumbnailData = thumb; // Create our default Attribute objects createDefaultAttributeObjects(BBeB.ReaderPageWidth, BBeB.ReaderPageHeight); // cover page works, but it's ugly createCoverPage(); m_CurrentPage = createPage(); PageObject firstBookPage = m_CurrentPage; m_StartReadingBlock = null; m_StartReadingPage = null; addBookPage(m_CurrentPage); IHTMLDocument2 dom = (IHTMLDocument2)doc.DomDocument; IHTMLDOMNode domNode = (IHTMLDOMNode)dom.body; IHTMLDOMChildrenCollection children = (IHTMLDOMChildrenCollection)domNode.childNodes; TextBlockBuilder tbBuilder = new TextBlockBuilder(GetNextObjId(), m_CharMapper); foreach (IHTMLDOMNode child in children) { tbBuilder = ParseDomNode(child, tbBuilder); } PrintHTMLElementChildren(children); // If we have any text left then add it FlushTextToBlock(m_CurrentPage, tbBuilder, m_MainBodyTextAttr); finalizePage(m_CurrentPage); // Create the table of contents createTocPage(firstBookPage, tocEntries); m_TocObject.AddEntry(m_StartReadingPage.ID, m_StartReadingBlock.ID, "Start Reading"); // Also serialize the table of contents object m_TocObject.Serialize(); finalizeBook(); return(m_Book); }
private void AddTocViewNodes(TocEntry tocEntry, TreeNodeCollection nodes) { TreeNode node = new TreeNode(tocEntry.Title); node.Tag = tocEntry; nodes.Add(node); foreach (TocEntry child in tocEntry.Children) { AddTocViewNodes(child, node.Nodes); } }
/// <summary> /// Delete the selected topic and all of its children /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdDelete_Executed(object sender, ExecutedRoutedEventArgs e) { TocEntry t = tvContent.SelectedItem as TocEntry; if (t != null && MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "Are you sure you " + "want to delete the topic '{0}' and all of its sub-topics?", t.Title), Constants.AppName, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { t.Parent.Remove(t); tvContent.Focus(); } }
private void TocTree_AfterSelect(object sender, TreeViewEventArgs e) { if (m_TocTree.SelectedNode == null) { return; } TocEntry itemTocEntry = (TocEntry)m_TocTree.SelectedNode.Tag; HtmlElement selectedElement = (HtmlElement)itemTocEntry.Tag; selectedElement.ScrollIntoView(true); }
internal object GetRtUpdateForSlideId(Guid guid) { if (guid.Equals(Guid.Empty)) { return(null); } TocEntry entry = this.LookupBySlideId(guid); if (entry == null) { return(null); } return(entry.ToRtUpdate()); }
/// <summary> /// Clear the topic associated with the selected node /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void btnClearTopic_Click(object sender, RoutedEventArgs e) { TocEntry t = tvContent.SelectedItem as TocEntry; if (t != null && MessageBox.Show("Do you want to clear the file associated with this topic?", "Site Map Editor", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { t.SourceFile = null; txtTopicFilename.GetBindingExpression(TextBox.TextProperty).UpdateTarget(); this.topics_ListChanged(tvContent, new ListChangedEventArgs(ListChangedType.Reset, -1)); } }
//===================================================================== /// <summary> /// Refresh the table of contents and the current topic to show any changes made to them /// </summary> /// <param name="reloadLastTopic">True to reload the last topic, false to ignore it</param> public void Refresh(bool reloadLastTopic) { TocEntry t = this.CurrentTopic; if (t != null) { this.LoadTableOfContentsInfo(); // Go back to the last selected topic if it is still there and wanted if (reloadLastTopic) { this.FindAndDisplay(t.SourceFile); } } }
public void RoundTripTocEntry() { XDocument doc = Utils.ReadXmlResource("UnitTestMergeWebToEpub.TestData.toc.ncx"); var e1 = doc.Root.Element(Epub.ncxNs + "navMap") .Element(Epub.ncxNs + "navPoint"); string ncxPath = "OEPBS/content.opf"; string ncxFolder = ncxPath.GetZipPath(); var api = Utils.FakeAbsolutePathIndex(doc, ncxPath); var tocEntry = new TocEntry(e1, ncxFolder, api); int playOrder = 0; var e2 = tocEntry.ToNavPoint(ref playOrder, ncxFolder); Assert.IsTrue(XNode.DeepEquals(e1, e2)); }
/// <summary> /// This is used to extract table of contents information from a file /// that will appear in the help file's table of contents. /// </summary> /// <param name="filename">The file from which to extract the /// information</param> /// <returns>The table of contents entry</returns> internal static TocEntry GetTocInfo(string filename) { TocEntry tocEntry; Encoding enc = Encoding.Default; string content; content = BuildProcess.ReadWithEncoding(filename, ref enc); tocEntry = new TocEntry(null); tocEntry.IncludePage = !reTocExclude.IsMatch(content); tocEntry.IsDefaultTopic = reIsDefaultTopic.IsMatch(content); if (reSplitToc.IsMatch(content)) { tocEntry.ApiParentMode = ApiParentMode.InsertAfter; } Match m = reSortOrder.Match(content); if (m.Success) { tocEntry.SortOrder = Convert.ToInt32(m.Groups["SortOrder"].Value, CultureInfo.InvariantCulture); } // Get the page title if possible. If not found, use the filename // without the path or extension as the page title. m = rePageTitle.Match(content); if (!m.Success) { tocEntry.Title = Path.GetFileNameWithoutExtension(filename); } else { tocEntry.Title = HttpUtility.HtmlDecode(m.Groups["Title"].Value).Replace( "\r", String.Empty).Replace("\n", String.Empty); } // Since we've got the file loaded, see if there are links // that need to be resolved when the file is copied, if it // contains <pre> blocks that should be colorized, or if it // contains tags or shared content items that need replacing. tocEntry.HasLinks = reResolveLinks.IsMatch(content); tocEntry.HasCodeBlocks = reCodeBlock.IsMatch(content); tocEntry.NeedsColorizing = reColorizeCheck.IsMatch(content); tocEntry.HasProjectTags = (reProjectTags.IsMatch(content) || reSharedContent.IsMatch(content)); return(tocEntry); }
/// <summary> /// Get the text to copy as a link to the clipboard /// </summary> /// <returns>The string to copy to the clipboard or null if there is nothing to copy</returns> private string GetTextToCopy() { TocEntry t = tvContent.SelectedItem as TocEntry; string textToCopy; if (t != null) { textToCopy = t.ToAnchor(!String.IsNullOrEmpty(t.Title) ? t.Title : "(No title)"); } else { textToCopy = null; } return(textToCopy); }
/// <summary> /// Move the selected topic down within its parent collection /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void cmdMoveDown_Executed(object sender, ExecutedRoutedEventArgs e) { TocEntry t = tvContent.SelectedItem as TocEntry; TocEntryCollection parent; int idx; if (t != null) { parent = t.Parent; idx = parent.IndexOf(t); parent.Remove(t); parent.Insert(idx + 1, t); t.IsSelected = true; tvContent.Focus(); } }
/// <summary> /// Associate a QuickPollId in the TOC with the given SlideId, so that when we get QuickPoll results later /// we can look up the TOC entry by this ID. /// </summary> /// <param name="slideId"></param> /// <param name="quickPollId"></param> internal void AddQuickPollIdForSlide(Guid slideId, Guid quickPollId) { //First make sure the SlideId exists in the TOC TocEntry entry = this.LookupBySlideId(slideId); if (entry == null) { Debug.WriteLine("!!!Failed to find slide to which to add QuickPoll ID!!!"); return; } //Remove existing mapping if any. if (QuickPollIdToSlideId.ContainsKey(quickPollId)) { QuickPollIdToSlideId.Remove(quickPollId); } //Add mapping QuickPollIdToSlideId.Add(quickPollId, slideId); }
/// <summary> /// Fill the table of contents tree view. /// </summary> private void FillTocView() { TocEntry toc = CreateTableOfContents(); // The top entry is a fake entry to contain the others. if (m_TocEntryRoot == null || !m_TocEntryRoot.Equals(toc)) { m_TocTree.Nodes.Clear(); foreach (TocEntry entry in toc.Children) { AddTocViewNodes(entry, m_TocTree.Nodes); } m_TocTree.ExpandAll(); m_TocEntryRoot = toc; } }
public static void Regenerate(string directory) { var allMetadata = Directory .GetFiles(directory, "*.yml") .Where(file => Path.GetFileName(file) != "toc.yml") .SelectMany(file => MetadataFile.LoadYaml(file).Items) .ToDictionary(item => item.Uid); var assemblies = allMetadata.Values .SelectMany(item => item.Assemblies) .Distinct() .OrderBy(assembly => assembly, StringComparer.Ordinal) .ToList(); var tocEntries = assemblies.Select(assembly => CreateTocEntryForAssembly(assembly, allMetadata)).ToList(); var tocText = TocEntry.CreateToc(tocEntries); File.WriteAllText(Path.Combine(directory, "toc.yml"), tocText); }
/// <summary> /// Associate a project file with the current topic /// </summary> /// <param name="sender">The sender of the event</param> /// <param name="e">The event arguments</param> private void ucSiteMapEditor_AssociateTopic(object sender, RoutedEventArgs e) { FileNode thisNode = this.FileNode; TocEntry t = base.UIControl.CurrentTopic; string newPath, projectPath = Path.GetDirectoryName( siteMapFile.ProjectElement.Project.Filename); if (t != null) { using (WinFormsOpenFileDialog dlg = new WinFormsOpenFileDialog()) { dlg.Title = "Select the additional content topic file"; dlg.Filter = "Additional Content Topics (*.htm, *.html)|*.htm;*.html|" + "All files (*.*)|*.*"; dlg.DefaultExt = "html"; dlg.InitialDirectory = projectPath; dlg.CheckFileExists = true; if (dlg.ShowDialog() == WinFormsDialogResult.OK) { // The file must reside under the project path newPath = dlg.FileName; if (!Path.GetDirectoryName(newPath).StartsWith(projectPath, StringComparison.OrdinalIgnoreCase)) { newPath = Path.Combine(projectPath, Path.GetFileName(newPath)); } // Add the file to the project if not already there siteMapFile.ProjectElement.Project.AddFileToProject(dlg.FileName, newPath); t.SourceFile = new FilePath(newPath, siteMapFile.ProjectElement.Project); // Let the caller know we associated a file with the topic e.Handled = true; if (thisNode != null) { thisNode.ProjectManager.RefreshProject(); } } } } }
/// <summary> /// This is used to copy site map files to the help format output folders including those for any child /// site map entries. /// </summary> /// <param name="site">The site entry containing the files to copy</param> private void CopySiteMapFiles(TocEntry site) { if(site.SourceFile.Path.Length != 0) { // Set the destination filename which will always match the source filename for site map files. site.DestinationFile = site.SourceFile.PersistablePath; foreach(string baseFolder in this.HelpFormatOutputFolders) if(!File.Exists(baseFolder + site.DestinationFile)) { this.ReportProgress("{0} -> {1}{2}", site.SourceFile, baseFolder, site.DestinationFile); // All attributes are turned off so that we can delete it later File.Copy(site.SourceFile, baseFolder + site.DestinationFile, true); File.SetAttributes(baseFolder + site.DestinationFile, FileAttributes.Normal); } } if(site.Children.Count != 0) foreach(TocEntry entry in site.Children) this.CopySiteMapFiles(entry); }
/// <summary> /// This is called to copy the additional content files and build a /// list of them for the help file project. /// </summary> /// <remarks>Note that for wilcard content items, the folders are /// copied recursively.</remarks> protected void CopyAdditionalContent() { Dictionary<string, TocEntryCollection> tocItems = new Dictionary<string, TocEntryCollection>(); TocEntryCollection parentToc; TocEntry tocEntry, tocFolder; FileItemCollection contentItems; string projectPath, source, filename, dirName; string[] parts; int part; this.ReportProgress(BuildStep.CopyAdditionalContent, "Copying additional content files..."); if(this.ExecutePlugIns(ExecutionBehaviors.InsteadOf)) return; // A plug-in might add or remove additional content so call // them before checking to see if there is anything to copy. this.ExecutePlugIns(ExecutionBehaviors.Before); if(!project.HasItems(BuildAction.Content) && !project.HasItems(BuildAction.SiteMap)) { this.ReportProgress("No additional content to copy"); this.ExecutePlugIns(ExecutionBehaviors.After); return; } toc = new TocEntryCollection(); tocItems.Add(String.Empty, toc); // Now copy the content files contentItems = new FileItemCollection(project, BuildAction.Content); projectPath = FolderPath.TerminatePath(Path.GetDirectoryName(originalProjectName)); foreach(FileItem fileItem in contentItems) { source = fileItem.Include; dirName = Path.GetDirectoryName(fileItem.Link.ToString().Substring(projectPath.Length)); filename = Path.Combine(dirName, Path.GetFileName(source)); if(source.EndsWith(".htm", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".html", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) { tocEntry = BuildProcess.GetTocInfo(source); // Exclude the page if so indicated via the item metadata if(fileItem.ExcludeFromToc) tocEntry.IncludePage = false; // .topic files get transformed into .html files if(source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) filename = Path.ChangeExtension(filename, ".html"); tocEntry.SourceFile = new FilePath(source, project); tocEntry.DestinationFile = filename; // Figure out where to add the entry parts = tocEntry.DestinationFile.Split('\\'); pathToRoot = String.Empty; parentToc = toc; for(part = 0; part < parts.Length - 1; part++) { pathToRoot += parts[part] + @"\"; // Create place holders if necessary if(!tocItems.TryGetValue(pathToRoot, out parentToc)) { tocFolder = new TocEntry(project); tocFolder.Title = parts[part]; if(part == 0) toc.Add(tocFolder); else tocItems[String.Join(@"\", parts, 0, part) + @"\"].Add(tocFolder); parentToc = tocFolder.Children; tocItems.Add(pathToRoot, parentToc); } } parentToc.Add(tocEntry); if(tocEntry.IncludePage && tocEntry.IsDefaultTopic) defaultTopic = tocEntry.DestinationFile; } else tocEntry = null; this.EnsureOutputFoldersExist(dirName); foreach(string baseFolder in this.HelpFormatOutputFolders) { // If the file contains items that need to be resolved, // it is handled separately. if(tocEntry != null && (tocEntry.HasLinks || tocEntry.HasCodeBlocks || tocEntry.NeedsColorizing || tocEntry.HasProjectTags || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase))) { // Figure out the path to the root if needed parts = tocEntry.DestinationFile.Split('\\'); pathToRoot = String.Empty; for(part = 0; part < parts.Length - 1; part++) pathToRoot += "../"; this.ResolveLinksAndCopy(source, baseFolder + filename, tocEntry); } else { this.ReportProgress("{0} -> {1}{2}", source, baseFolder, filename); // All attributes are turned off so that we can delete it later File.Copy(source, baseFolder + filename, true); File.SetAttributes(baseFolder + filename, FileAttributes.Normal); } } } // Remove excluded nodes, merge folder item info into the root // nodes, and sort the items. If a site map isn't defined, this // will define the layout of the items. toc.RemoveExcludedNodes(null); toc.Sort(); codeColorizer = null; sharedContent = sharedBuilderContent = styleContent = null; this.ExecutePlugIns(ExecutionBehaviors.After); }
/// <summary> /// This is used to merge destination file information into the site /// map TOC. /// </summary> /// <param name="site">The site entry to update</param> /// <remarks>In addition, files in the site map that do not exist in /// the TOC built from the defined content will be processed and /// copied to the root folder.</remarks> private void MergeTocInfo(TocEntry site) { TocEntry match; string source, filename; if(site.SourceFile.Path.Length != 0) { match = toc.Find(site.SourceFile); if(match != null) site.DestinationFile = match.DestinationFile; else { source = site.SourceFile; site.DestinationFile = Path.GetFileName(source); filename = site.DestinationFile; // .topic files get transformed into .html files if(source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) site.DestinationFile = Path.ChangeExtension(site.DestinationFile, ".html"); // Check to see if anything needs resolving if(source.EndsWith(".htm", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".html", StringComparison.OrdinalIgnoreCase) || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) match = BuildProcess.GetTocInfo(source); foreach(string baseFolder in this.HelpFormatOutputFolders) { // If the file contains items that need to be resolved, it is handled separately if(match != null && (match.HasLinks || match.HasCodeBlocks || match.NeedsColorizing || match.HasProjectTags || source.EndsWith(".topic", StringComparison.OrdinalIgnoreCase))) { // Files are always copied to the root pathToRoot = String.Empty; this.ResolveLinksAndCopy(source, baseFolder + filename, match); } else { this.ReportProgress("{0} -> {1}{2}", source, baseFolder, filename); // All attributes are turned off so that we can delete it later File.Copy(source, baseFolder + filename, true); File.SetAttributes(baseFolder + filename, FileAttributes.Normal); } } } } if(site.Children.Count != 0) foreach(TocEntry entry in site.Children) this.MergeTocInfo(entry); }
/// <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) { TocEntry 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 topics.FileItem.ProjectElement.Project.AddFileToProject(filename, newPath); topic = new TocEntry(topics.FileItem.ProjectElement.Project); topic.SourceFile = new FilePath(newPath, topic.BasePathProvider); topic.Title = Path.GetFileNameWithoutExtension(newPath); tnNew = new TreeNode(topic.Title); tnNew.Name = topic.SourceFile; tnNew.Tag = topic; if(addAsChild) { tvContent.SelectedNode.Nodes.Add(tnNew); parentTopic = (TocEntry)tvContent.SelectedNode.Tag; parentTopic.Children.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 = (TocEntry)tvContent.SelectedNode.Tag; parentTopic.Parent.Insert( parentTopic.Parent.IndexOf(parentTopic) + 1, topic); } tvContent.SelectedNode = tnNew; }
/// <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(TocEntry selectedEntry) { TreeNode node; try { tvContent.SuspendLayout(); tvContent.Nodes.Clear(); defaultNode = splitTocNode = firstNode = null; firstSelection = selectedEntry; if(topics.Count != 0) { foreach(TocEntry t in topics) { node = tvContent.Nodes.Add(t.Title); node.Name = t.SourceFile; node.Tag = t; if(t.IsDefaultTopic) defaultNode = node; // This is only valid at the root level if(t.ApiParentMode != ApiParentMode.None) splitTocNode = node; if(t == firstSelection) firstNode = node; if(t.Children.Count != 0) this.AddChildren(t.Children, node); } if(defaultNode != null) { defaultNode.ToolTipText = "Default topic"; defaultNode.ImageIndex = defaultNode.SelectedImageIndex = 1; } if(splitTocNode != null) { splitTocNode.ToolTipText = "Split Table of Contents"; splitTocNode.ImageIndex = splitTocNode.SelectedImageIndex = 10; } if(defaultNode != null && defaultNode == splitTocNode) { defaultNode.ToolTipText = "Default topic/Split TOC"; defaultNode.ImageIndex = defaultNode.SelectedImageIndex = 11; } 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; TocEntry topic, parentTopic; TreeNode tnNew, tnParent; topic = new TocEntry(topics.FileItem.ProjectElement.Project); topic.Title = "Table of Contents Container"; tnNew = new TreeNode(topic.Title); tnNew.Name = topic.SourceFile; tnNew.Tag = topic; if(tiAdd == tsbAddChildTopic || tiAdd.Owner == cmsNewChildTopic) { tvContent.SelectedNode.Nodes.Add(tnNew); parentTopic = (TocEntry)tvContent.SelectedNode.Tag; parentTopic.Children.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 = (TocEntry)tvContent.SelectedNode.Tag; parentTopic.Parent.Insert( parentTopic.Parent.IndexOf(parentTopic) + 1, topic); } tvContent.SelectedNode = tnNew; }
/// <summary> /// Insert a link to a site map table of contents entry (HTML only) /// </summary> /// <param name="extension">The extension of the file in which the /// link is being inserted.</param> /// <param name="tocEntry">The TOC entry for which to create a link</param> /// <remarks>If dropped inside some selected text, the link will /// wrap the selected text.</remarks> private void InsertTocLink(string extension, TocEntry tocEntry) { 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, tocEntry.ToAnchor(selectedText)); else ContentEditorControl.InsertString(textArea, tocEntry.Title); // Not supported in MAML topics }
//===================================================================== /// <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) { TocEntry currentTopic = this.CurrentTopic, newTopic = new TocEntry(topics.ContentLayoutFile.BasePathProvider) { Title = "Table of Contents Container", UniqueId = Guid.NewGuid() }; // 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.Children.Add(newTopic); currentTopic.IsExpanded = true; } newTopic.IsSelected = true; }
/// <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) { TocEntry 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.Children.Add(newTopic); targetTopic.IsExpanded = true; } else targetTopic.Parent.Insert(targetTopic.Parent.IndexOf(targetTopic) + 1, newTopic); } newTopic.IsSelected = true; tvContent.Focus(); } }
/// <summary> /// This is called to load an additional content file, resolve links /// to namespace content and copy it to the output folder. /// </summary> /// <param name="sourceFile">The source filename to copy</param> /// <param name="destFile">The destination filename</param> /// <param name="entry">The entry being resolved.</param> internal void ResolveLinksAndCopy(string sourceFile, string destFile, TocEntry entry) { Encoding enc = Encoding.Default; string content, script, syntaxFile; int pos; // For topics, change the extenstion back to ".topic". It's // ".html" in the TOC as that's what it ends up as after // transformation. if(sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) destFile = Path.ChangeExtension(destFile, ".topic"); this.ReportProgress("{0} -> {1}", sourceFile, destFile); // When reading the file, use the default encoding but detect the // encoding if byte order marks are present. content = BuildProcess.ReadWithEncoding(sourceFile, ref enc); // Expand <code> tags if necessary if(entry.HasCodeBlocks) content = reCodeBlock.Replace(content, codeBlockMatchEval); // Colorize <pre> tags if necessary if(entry.NeedsColorizing || entry.HasCodeBlocks) { // Initialize code colorizer on first use if(codeColorizer == null) codeColorizer = new CodeColorizer(shfbFolder + @"Colorizer\highlight.xml", shfbFolder + @"Colorizer\highlight.xsl"); // Set the path the "Copy" image codeColorizer.CopyImageUrl = pathToRoot + "icons/CopyCode.gif"; // Colorize it and replace the "Copy" literal text with the // shared content include item so that it gets localized. content = codeColorizer.ProcessAndHighlightText(content); content = content.Replace(codeColorizer.CopyText + "</span", "<include item=\"copyCode\"/></span"); entry.HasProjectTags = true; // Add the links to the colorizer stylesheet and script files // unless it's going to be transformed. In which case, the // links should be in the XSL stylesheet. if(!sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase) && !sourceFile.EndsWith(".xsl", StringComparison.OrdinalIgnoreCase)) { script = String.Format(CultureInfo.InvariantCulture, "<link type='text/css' rel='stylesheet' href='{0}styles/highlight.css' />" + "<script type='text/javascript' src='{0}scripts/highlight.js'></script>", pathToRoot); pos = content.IndexOf("</head>", StringComparison.Ordinal); // Create a <head> section if one doesn't exist if(pos == -1) { script = "<head>" + script + "</head>"; pos = content.IndexOf("<html>", StringComparison.Ordinal); if(pos != -1) pos += 6; else pos = 0; } content = content.Insert(pos, script); } // Copy the colorizer files if not already there this.EnsureOutputFoldersExist("icons"); this.EnsureOutputFoldersExist("styles"); this.EnsureOutputFoldersExist("scripts"); foreach(string baseFolder in this.HelpFormatOutputFolders) if(!File.Exists(baseFolder + @"styles\highlight.css")) { syntaxFile = baseFolder + @"styles\highlight.css"; File.Copy(shfbFolder + @"Colorizer\highlight.css", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); syntaxFile = baseFolder + @"scripts\highlight.js"; File.Copy(shfbFolder + @"Colorizer\highlight.js", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); // Always copy the image files, they may be different. Also, delete the // destination file first if it exists as the filename casing may be different. syntaxFile = baseFolder + @"icons\CopyCode.gif"; if(File.Exists(syntaxFile)) { File.SetAttributes(syntaxFile, FileAttributes.Normal); File.Delete(syntaxFile); } File.Copy(shfbFolder + @"Colorizer\CopyCode.gif", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); syntaxFile = baseFolder + @"icons\CopyCode_h.gif"; if(File.Exists(syntaxFile)) { File.SetAttributes(syntaxFile, FileAttributes.Normal); File.Delete(syntaxFile); } File.Copy(shfbFolder + @"Colorizer\CopyCode_h.gif", syntaxFile); File.SetAttributes(syntaxFile, FileAttributes.Normal); } } // Use a regular expression to find and replace all tags with // cref attributes with a link to the help file content. This // needs to happen after the code block processing as they // may contain <see> tags that need to be resolved. if(entry.HasLinks || entry.HasCodeBlocks) content = reResolveLinks.Replace(content, linkMatchEval); // Replace project option tags with project option values if(entry.HasProjectTags) { // Project tags can be nested while(reProjectTags.IsMatch(content)) content = reProjectTags.Replace(content, fieldMatchEval); // Shared content items can be nested while(reSharedContent.IsMatch(content)) content = reSharedContent.Replace(content, contentMatchEval); } // Write the file back out with the appropriate encoding using(StreamWriter sw = new StreamWriter(destFile, false, enc)) { sw.Write(content); } // Transform .topic files into .html files if(sourceFile.EndsWith(".topic", StringComparison.OrdinalIgnoreCase)) this.XslTransform(destFile); }
/// <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 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 TocEntry AddTopicFile(string filename, bool addAsChild) { TocEntry newTopic, currentTopic = ucSiteMapEditor.CurrentTopic; string newPath = filename, projectPath = Path.GetDirectoryName( siteMapFile.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 siteMapFile.Project.AddFileToProject(filename, newPath); // Add the topic to the editor's collection newTopic = new TocEntry(siteMapFile.Project) { SourceFile = new FilePath(newPath, siteMapFile.Project), Title = Path.GetFileNameWithoutExtension(newPath) }; if(addAsChild && currentTopic != null) { currentTopic.Children.Add(newTopic); currentTopic.IsExpanded = true; } else if(currentTopic == null) ucSiteMapEditor.Topics.Add(newTopic); else currentTopic.Parent.Insert(currentTopic.Parent.IndexOf(currentTopic) + 1, newTopic); newTopic.IsSelected = true; return newTopic; }
/// <summary> /// This is used to recursively add child TOC entries to the entity reference collection /// </summary> /// <param name="t">The TOC entry</param> /// <param name="er">The parent entity reference</param> /// <param name="hasSelectedItem">The selected item state. Only first selected item found is /// marked as the selected item.</param> private bool AddChildTocEntries(TocEntry t, EntityReference er, bool hasSelectedItem) { EntityReference subEnt; foreach(var child in t.Children) { subEnt = new EntityReference { EntityType = EntityType.TocEntry, Id = child.Id, Label = (child.Title ?? child.Id ?? "(No title)"), ToolTip = String.Format(CultureInfo.CurrentCulture, "ID: {0}\nFile: {1}", (child.Id ?? child.Title ?? "(No ID)"), child.SourceFile), Tag = child, IsExpanded = child.IsExpanded, IsSelected = (child.IsSelected && !hasSelectedItem) }; if(subEnt.IsSelected) hasSelectedItem = true; er.SubEntities.Add(subEnt); if(child.Children.Count != 0) hasSelectedItem = this.AddChildTocEntries(child, subEnt, hasSelectedItem); } return hasSelectedItem; }
/// <summary> /// This is used to extract table of contents information from a file /// that will appear in the help file's table of contents. /// </summary> /// <param name="filename">The file from which to extract the /// information</param> /// <returns>The table of contents entry</returns> internal static TocEntry GetTocInfo(string filename) { TocEntry tocEntry; Encoding enc = Encoding.Default; string content; content = BuildProcess.ReadWithEncoding(filename, ref enc); tocEntry = new TocEntry(null); tocEntry.IncludePage = !reTocExclude.IsMatch(content); tocEntry.IsDefaultTopic = reIsDefaultTopic.IsMatch(content); if(reSplitToc.IsMatch(content)) tocEntry.ApiParentMode = ApiParentMode.InsertAfter; Match m = reSortOrder.Match(content); if(m.Success) tocEntry.SortOrder = Convert.ToInt32(m.Groups["SortOrder"].Value, CultureInfo.InvariantCulture); // Get the page title if possible. If not found, use the filename // without the path or extension as the page title. m = rePageTitle.Match(content); if(!m.Success) tocEntry.Title = Path.GetFileNameWithoutExtension(filename); else tocEntry.Title = HttpUtility.HtmlDecode(m.Groups["Title"].Value).Replace( "\r", String.Empty).Replace("\n", String.Empty); // Since we've got the file loaded, see if there are links // that need to be resolved when the file is copied, if it // contains <pre> blocks that should be colorized, or if it // contains tags or shared content items that need replacing. tocEntry.HasLinks = reResolveLinks.IsMatch(content); tocEntry.HasCodeBlocks = reCodeBlock.IsMatch(content); tocEntry.NeedsColorizing = reColorizeCheck.IsMatch(content); tocEntry.HasProjectTags = (reProjectTags.IsMatch(content) || reSharedContent.IsMatch(content)); return tocEntry; }