//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="file">The file build item from the project</param> /// <exception cref="ArgumentNullException">This is thrown if the file /// item is null.</exception> public TopicFile(FileItem file) { if(file == null) throw new ArgumentNullException("file"); fileItem = file; revision = 1; }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public SiteMapEditorWindow(FileItem fileItem) { EventHandler onClick = new EventHandler(templateFile_OnClick); ToolStripMenuItem miTemplate; Image itemImage; string name; InitializeComponent(); sbStatusBarText.InstanceStatusBar = MainForm.Host.StatusBarTextLabel; // Look for custom templates in the local application data folder name = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData), Constants.ItemTemplates); if(!Directory.Exists(name)) miCustomSibling.Enabled = miCustomChild.Enabled = false; else { string[] files = Directory.GetFiles(name, "*.htm?"); if(files.Length == 0) miCustomSibling.Enabled = miCustomChild.Enabled = false; else foreach(string file in files) { name = Path.GetFileNameWithoutExtension(file); itemImage = null; miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miCustomSibling.DropDownItems.Add(miTemplate); miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miCustomChild.DropDownItems.Add(miTemplate); } } topics = new TocEntryCollection(fileItem); topics.Load(); topics.ListChanged += new ListChangedEventHandler(topics_ListChanged); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; this.LoadTopics(null); }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public TokenEditorWindow(FileItem fileItem) { InitializeComponent(); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; tokenFile = fileItem; ucTokenEditor.ContentModified += ucTokenEditor_ContentModified; ucTokenEditor.LoadTokenFile(tokenFile.FullPath, null); }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public ResourceItemEditorWindow(FileItem fileItem) { InitializeComponent(); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; resourceItemsFile = fileItem; resourceItemsPath = fileItem.FullPath; ucResourceItemEditor.ContentModified += ucResourceItemEditor_ContentModified; ucResourceItemEditor.LoadResourceItemsFile(resourceItemsPath, resourceItemsFile.Project); }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public TokenEditorWindow(FileItem fileItem) { InitializeComponent(); sbStatusBarText.InstanceStatusBar = MainForm.Host.StatusBarTextLabel; editor.TextEditorProperties.Font = Settings.Default.TextEditorFont; editor.TextEditorProperties.ShowLineNumbers = Settings.Default.ShowLineNumbers; editor.SetHighlighting("XML"); tokens = new TokenCollection(fileItem); tokens.Load(); tokens.ListChanged += new ListChangedEventHandler(tokens_ListChanged); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; this.LoadTokens(); }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public ResourceItemEditorWindow(FileItem fileItem) { InitializeComponent(); itemsFile = fileItem; allItems = new SortedDictionary<string, ResourceItem>(); sandcastleItems = new SortedDictionary<string, ResourceItem>(); sbStatusBarText.InstanceStatusBar = MainForm.Host.StatusBarTextLabel; editor.TextEditorProperties.Font = Settings.Default.TextEditorFont; editor.TextEditorProperties.ShowLineNumbers = Settings.Default.ShowLineNumbers; editor.SetHighlighting("XML"); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; this.LoadResourceItems(); }
/// <summary> /// This is used to locate a file by name in the project /// </summary> /// <param name="fileToFind">The fully qualified file path to find</param> /// <returns>The file item if found or null if not found</returns> public FileItem FindFile(string fileToFind) { FilePath filePath; FileItem fileItem = null; string itemPath, rootPath = Path.GetDirectoryName(msBuildProject.FullPath); if(String.IsNullOrEmpty(fileToFind) || !fileToFind.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) return null; filePath = new FilePath(fileToFind, this); foreach(ProjectItem item in msBuildProject.AllEvaluatedItems) { if(item.HasMetadata(BuildItemMetadata.LinkPath)) itemPath = item.GetMetadataValue(BuildItemMetadata.LinkPath); else itemPath = item.EvaluatedInclude; // Files don't always have a relative path in the item when first added. As such, check both // the relative and full paths for a match. if(itemPath == filePath.PersistablePath || itemPath == filePath.Path) { fileItem = new FileItem(this, item); break; } } return fileItem; }
/// <summary> /// Add a new file build item to the project /// </summary> /// <param name="sourceFile">The source filename</param> /// <param name="destFile">The optional destination path. If empty, null, or it does not start with the /// project folder, the file is copied to the root folder of the project.</param> /// <returns>The new <see cref="FileItem" /></returns> /// <remarks>If the file does not exist in the project, it is copied to the destination path or project /// folder if not already there. The default build action is determined based on the filename's /// extension. If the file is already part of the project, the existing item is returned.</remarks> public FileItem AddFileToProject(string sourceFile, string destFile) { BuildAction buildAction; FilePath filePath; FileItem newFileItem = null; string[] folders; string itemPath, rootPath = Path.GetDirectoryName(msBuildProject.FullPath); if(String.IsNullOrEmpty(destFile) || !destFile.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) destFile = Path.Combine(rootPath, Path.GetFileName(sourceFile)); filePath = new FilePath(destFile, this); buildAction = DefaultBuildAction(destFile); foreach(ProjectItem item in msBuildProject.AllEvaluatedItems) { if(item.HasMetadata(BuildItemMetadata.LinkPath)) itemPath = item.GetMetadataValue(BuildItemMetadata.LinkPath); else itemPath = item.EvaluatedInclude; // Files don't always have a relative path in the item when first added. As such, check both // the relative and full paths for a match. if(itemPath == filePath.PersistablePath || itemPath == filePath.Path) { newFileItem = new FileItem(this, item); break; } } if(!File.Exists(destFile)) { if(!Directory.Exists(Path.GetDirectoryName(destFile))) Directory.CreateDirectory(Path.GetDirectoryName(destFile)); // Make sure folder items exists for all parts of the path folders = Path.GetDirectoryName(filePath.PersistablePath).Split('\\'); itemPath = String.Empty; foreach(string path in folders) if(path.Length != 0) { itemPath += path + "\\"; this.AddFolderToProject(itemPath); } if(File.Exists(sourceFile)) { File.Copy(sourceFile, destFile); File.SetAttributes(destFile, FileAttributes.Normal); } } if(newFileItem == null) { newFileItem = new FileItem(this, buildAction.ToString(), destFile); // For images, assign the build action again so that it sets the default alternate text and ID if(buildAction == BuildAction.Image) newFileItem.BuildAction = buildAction; } return newFileItem; }
/// <summary> /// Add a new folder build item to the project /// </summary> /// <param name="folder">The folder name</param> /// <returns>The new <see cref="FileItem"/>.</returns> /// <remarks>If the folder does not exist in the project, it is added and created if not already there. /// If the folder is already part of the project, the existing item is returned.</remarks> /// <exception cref="ArgumentException">This is thrown if the path matches the project root path or is /// not below it.</exception> public FileItem AddFolderToProject(string folder) { FolderPath folderPath; FileItem newFileItem = null; string folderAction = BuildAction.Folder.ToString(), rootPath = Path.GetDirectoryName(msBuildProject.FullPath); if(folder.Length != 0 && folder[folder.Length - 1] == '\\') folder = folder.Substring(0, folder.Length - 1); if(!Path.IsPathRooted(folder)) folder = Path.GetFullPath(Path.Combine(rootPath, folder)); if(String.Compare(folder, 0, rootPath, 0, rootPath.Length, StringComparison.OrdinalIgnoreCase) != 0) throw new ArgumentException("The folder must be below the project's root path", "folder"); if(folder.Length == rootPath.Length) throw new ArgumentException("The folder cannot match the project's root path", "folder"); folderPath = new FolderPath(folder, this); // Note that Visual Studio doesn't add the trailing backslash so look for a match with and without it. // Folders don't always have a relative path in the item when first added. As such, check both the // relative and full paths for a match. foreach(ProjectItem item in msBuildProject.GetItems(folderAction)) if(item.EvaluatedInclude == folderPath.PersistablePath || item.EvaluatedInclude + @"\" == folderPath.PersistablePath || item.EvaluatedInclude == folderPath.Path || item.EvaluatedInclude + @"\" == folderPath.Path) { newFileItem = new FileItem(this, item); break; } if(!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); if(newFileItem == null) newFileItem = new FileItem(this, folderAction, folder); return newFileItem; }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public SiteMapEditorWindow(FileItem fileItem) { InitializeComponent(); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; siteMapFile = fileItem; ucSiteMapEditor = new SiteMapEditorControl(); ehSiteMapEditorHost.Child = ucSiteMapEditor; // Hook up the command bindings and event handlers ucSiteMapEditor.CommandBindings.Add(new CommandBinding(EditorCommands.Edit, cmdEdit_Executed, cmdEdit_CanExecute)); ucSiteMapEditor.CommandBindings.Add(new CommandBinding(ProjectCommands.AddFromTemplate, cmdAddFromTemplate_Executed)); ucSiteMapEditor.CommandBindings.Add(new CommandBinding(ProjectCommands.AddExistingFile, cmdAddExistingFile_Executed)); ucSiteMapEditor.CommandBindings.Add(new CommandBinding(ProjectCommands.AddAllFromFolder, cmdAddAllFromFolder_Executed)); ucSiteMapEditor.ContentModified += ucSiteMapEditor_ContentModified; ucSiteMapEditor.AssociateTopic += ucSiteMapEditor_AssociateTopic; // Load the site map file ucSiteMapEditor.LoadSiteMapFile(siteMapFile); }
/// <summary> /// Add a new folder build item to the project /// </summary> /// <param name="folder">The folder name</param> /// <returns>The new <see cref="FileItem"/>.</returns> /// <remarks>If the folder does not exist in the project, it is added /// and created if not already there. If the folder is already part of /// the project, the existing item is returned.</remarks> /// <exception cref="ArgumentException">This is thrown if the path /// matches the project root path or is not below it.</exception> public FileItem AddFolderToProject(string folder) { FolderPath folderPath; FileItem newFileItem = null; string folderAction = BuildAction.Folder.ToString(), rootPath = Path.GetDirectoryName(msBuildProject.FullFileName); if(folder.Length != 0 && folder[folder.Length - 1] == '\\') folder = folder.Substring(0, folder.Length - 1); if(!Path.IsPathRooted(folder)) folder = Path.GetFullPath(Path.Combine(rootPath, folder)); if(String.Compare(folder, 0, rootPath, 0, rootPath.Length, StringComparison.OrdinalIgnoreCase) != 0) throw new ArgumentException("The folder must be below the " + "project's root path", "folder"); if(folder.Length == rootPath.Length) throw new ArgumentException("The folder cannot match the " + "project's root path", "folder"); folderPath = new FolderPath(folder, this); foreach(BuildItem item in msBuildProject.GetEvaluatedItemsByName(folderAction)) if(item.Include == folderPath.PersistablePath) { newFileItem = new FileItem(new ProjectElement(this, item)); break; } if(!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); if(newFileItem == null) newFileItem = new FileItem(new ProjectElement(this, folderAction, folder)); return newFileItem; }
//===================================================================== /// <summary> /// Load a content layout file for editing /// </summary> /// <param name="contentLayoutFile">The content layout file item to load</param> public void LoadContentLayoutFile(FileItem contentLayoutFile) { if(contentLayoutFile == null) throw new ArgumentNullException("contentLayoutFile", "A content layout file item must be specified"); topics = new TopicCollection(contentLayoutFile.ToContentFile()); topics.Load(); topics.ListChanged += new ListChangedEventHandler(topics_ListChanged); if(topics.Count !=0 && topics.Find(t => t.IsSelected, false).Count() == 0) topics[0].IsSelected = true; tvContent.ItemsSource = topics; this.topics_ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, -1)); }
/// <summary> /// Create an editor for the specified file /// </summary> /// <param name="fullName">The full path to the file</param> /// <param name="fileItem">The project file item or null if the method should find it for itself</param> /// <returns>The editor window created for the file</returns> public DockContent CreateFileEditor(string fullName, FileItem fileItem) { DockContent editor = null; string ext = Path.GetExtension(fullName).ToLowerInvariant(); if(fileItem == null) { if(this.CurrentProject != null) fileItem = this.CurrentProject.FindFile(fullName); if(fileItem == null) return null; } if(!File.Exists(fullName)) return null; // Try for a built in editor switch(SandcastleProject.DefaultBuildAction(fullName)) { case BuildAction.None: case BuildAction.Content: switch(ext) { case ".aml": case ".asp": case ".aspx": case ".ascx": case ".cmp": case ".config": case ".css": case ".htm": case ".html": case ".js": case ".log": case ".topic": case ".txt": case ".xml": editor = new TopicEditorWindow(fullName); break; default: break; } break; case BuildAction.CodeSnippets: case BuildAction.TopicTransform: case BuildAction.XamlConfiguration: editor = new TopicEditorWindow(fullName); break; case BuildAction.ContentLayout: editor = new ContentLayoutWindow(fileItem); break; case BuildAction.ResourceItems: // The content of the resource items could be bad (ill-formed XML) so edit as text if it // cannot be loaded using the default editor. try { editor = new ResourceItemEditorWindow(fileItem); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex); MessageBox.Show("Unable to load file using the resource item editor: " + ex.Message + "\r\n\r\nThe file will be opened using the standard text editor.", Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); editor = new TopicEditorWindow(fullName); } break; case BuildAction.SiteMap: editor = new SiteMapEditorWindow(fileItem); break; case BuildAction.Tokens: // The content of the tokens could be bad (ill-formed XML) so edit as text if it cannot be // loaded using the default editor. try { editor = new TokenEditorWindow(fileItem); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex); MessageBox.Show("Unable to load file using the token editor: " + ex.Message + "\r\n\r\nThe file will be opened using the standard text editor.", Constants.AppName, MessageBoxButtons.OK, MessageBoxIcon.Error); editor = new TopicEditorWindow(fullName); } break; default: // No association, the caller may try to launch an external editor break; } // If we couldn't create one and it looks like a text file, use the topic editor if(editor == null) try { if(!reBinary.IsMatch(File.ReadAllText(fullName))) editor = new TopicEditorWindow(fullName); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } return editor; }
//===================================================================== /// <summary> /// Build the conceptual content and preview the specified file /// </summary> /// <param name="project">The current project</param> /// <param name="preview">The file to preview</param> public void PreviewTopic(SandcastleProject project, FileItem preview) { // If a build is already in progress, let it finish if(buildProcess == null) { currentProject = project; fileItem = preview; wbPreview.Navigate("about:blank"); lblLoading.Text = "Building..."; pbWait.Visible = lblLoading.Visible = true; timer.Start(); this.BuildConceptualTopics(); } }
/// <summary> /// This is used to locate a file by name in the project /// </summary> /// <param name="fileToFind">The fully qualified file path to find</param> /// <returns>The file item if found or null if not found</returns> public FileItem FindFile(string fileToFind) { FilePath filePath; FileItem fileItem = null; string itemPath, rootPath = Path.GetDirectoryName( msBuildProject.FullFileName); if(String.IsNullOrEmpty(fileToFind) || !fileToFind.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) return null; filePath = new FilePath(fileToFind, this); foreach(BuildItem item in msBuildProject.EvaluatedItems) { if(item.HasMetadata(ProjectElement.LinkPath)) itemPath = item.GetMetadata(ProjectElement.LinkPath); else itemPath = item.Include; if(itemPath == filePath.PersistablePath) { fileItem = new FileItem(new ProjectElement(this, item)); break; } } return fileItem; }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="fileItem">The project file item to edit</param> public ContentLayoutWindow(FileItem fileItem) { EventHandler onClick = new EventHandler(templateFile_OnClick); ToolStripMenuItem miTemplate; Image itemImage; string name; InitializeComponent(); sbStatusBarText.InstanceStatusBar = MainForm.Host.StatusBarTextLabel; // Add the topic templates to the New Topic context menu string[] files = Directory.GetFiles(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ConceptualTemplates"), "*.aml"); if(files.Length == 0) miStandardSibling.Enabled = miStandardChild.Enabled = false; else foreach(string file in files) { name = Path.GetFileNameWithoutExtension(file); itemImage = null; // For Conceptual.aml, make it the default action when the // toolbar button is clicked. if(name == "Conceptual") { tsbAddSiblingTopic.ButtonClick -= new EventHandler( tsbAddTopic_ButtonClick); tsbAddChildTopic.ButtonClick -= new EventHandler( tsbAddTopic_ButtonClick); tsbAddSiblingTopic.ButtonClick += new EventHandler(onClick); tsbAddChildTopic.ButtonClick += new EventHandler(onClick); tsbAddSiblingTopic.Tag = tsbAddChildTopic.Tag = file; itemImage = miAddEmptySibling.Image; miAddEmptySibling.Image = miAddEmptyChild.Image = null; } miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miStandardSibling.DropDownItems.Add(miTemplate); miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miStandardChild.DropDownItems.Add(miTemplate); } // Look for custom templates in the local application data folder name = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData), Constants.ConceptualTemplates); if(!Directory.Exists(name)) miCustomSibling.Enabled = miCustomChild.Enabled = false; else { files = Directory.GetFiles(name, "*.aml"); if(files.Length == 0) miCustomSibling.Enabled = miCustomChild.Enabled = false; else foreach(string file in files) { name = Path.GetFileNameWithoutExtension(file); itemImage = null; miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miCustomSibling.DropDownItems.Add(miTemplate); miTemplate = new ToolStripMenuItem(name, null, onClick); miTemplate.Image = itemImage; miTemplate.Tag = file; sbStatusBarText.SetStatusBarText(miTemplate, "Add new '" + name + "' topic"); miCustomChild.DropDownItems.Add(miTemplate); } } topics = new TopicCollection(fileItem); topics.Load(); topics.ListChanged += new ListChangedEventHandler(topics_ListChanged); this.Text = Path.GetFileName(fileItem.FullPath); this.ToolTipText = fileItem.FullPath; this.LoadTopics(null); }
//===================================================================== /// <summary> /// Load a site map file for editing /// </summary> /// <param name="siteMapFile">The site map file item to load</param> public void LoadSiteMapFile(FileItem siteMapFile) { if(siteMapFile == null) throw new ArgumentNullException("siteMapFile", "A site map file item must be specified"); topics = new TocEntryCollection(siteMapFile.ToContentFile()); topics.Load(); // This works around a legacy support issue related to object equality foreach(var t in topics.All()) t.UniqueId = Guid.NewGuid(); topics.ListChanged += new ListChangedEventHandler(topics_ListChanged); if(topics.Count != 0 && topics.Find(t => t.IsSelected, false).Count() == 0) topics[0].IsSelected = true; tvContent.ItemsSource = topics; this.topics_ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, -1)); }
/// <summary> /// Add a new file build item to the project /// </summary> /// <param name="sourceFile">The source filename</param> /// <param name="destFile">The optional destination path. If empty, /// null, or it does not start with the project folder, the file is /// copied to the root folder of the project.</param> /// <returns>The new <see cref="FileItem" />.</returns> /// <remarks>If the file does not exist in the project, it is copied to /// the destination path or project folder if not already there. The /// default build action is determined based on the filename's /// extension. If the file is already part of the project, the /// existing item is returned.</remarks> public FileItem AddFileToProject(string sourceFile, string destFile) { BuildAction buildAction; FilePath filePath; FileItem newFileItem = null; string[] folders; string itemPath, rootPath = Path.GetDirectoryName( msBuildProject.FullFileName); if(String.IsNullOrEmpty(destFile) || !destFile.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase)) destFile = Path.Combine(rootPath, Path.GetFileName(sourceFile)); filePath = new FilePath(destFile, this); buildAction = DefaultBuildAction(destFile); foreach(BuildItem item in msBuildProject.EvaluatedItems) { if(item.HasMetadata(ProjectElement.LinkPath)) itemPath = item.GetMetadata(ProjectElement.LinkPath); else itemPath = item.Include; if(itemPath == filePath.PersistablePath) { newFileItem = new FileItem(new ProjectElement(this, item)); break; } } if(!File.Exists(destFile)) { if(!Directory.Exists(Path.GetDirectoryName(destFile))) Directory.CreateDirectory(Path.GetDirectoryName(destFile)); // Make sure folder items exists for all parts of the path folders = Path.GetDirectoryName(filePath.PersistablePath).Split('\\'); itemPath = String.Empty; foreach(string path in folders) if(path.Length != 0) { itemPath += path + "\\"; this.AddFolderToProject(itemPath); } if(File.Exists(sourceFile)) { File.Copy(sourceFile, destFile); File.SetAttributes(destFile, FileAttributes.Normal); } } if(newFileItem == null) newFileItem = new FileItem(new ProjectElement(this, buildAction.ToString(), destFile)); return newFileItem; }