/******************************************************************************************** Copyright (c) Microsoft Corporation All rights reserved. Microsoft Public License: This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 1. Definitions The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. 2. Grant of Rights (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 3. Conditions and Limitations (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. ********************************************************************************************/ using System; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; namespace Microsoft.VisualStudio.Project { /// /// All public properties on Nodeproperties or derived classes are assumed to be used by Automation by default. /// Set this attribute to false on Properties that should not be visible for Automation. /// [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class AutomationBrowsableAttribute : System.Attribute { public AutomationBrowsableAttribute(bool browsable) { this.browsable = browsable; } public bool Browsable { get { return this.browsable; } } private bool browsable; } /// /// To create your own localizable node properties, subclass this and add public properties /// decorated with your own localized display name, category and description attributes. /// public class NodeProperties : LocalizableProperties, ISpecifyPropertyPages, IVsGetCfgProvider, IVsSpecifyProjectDesignerPages, EnvDTE80.IInternalExtenderProvider, IVsBrowseObject { #region fields private HierarchyNode node; #endregion #region properties [Browsable(false)] [AutomationBrowsable(false)] public HierarchyNode Node { get { return this.node; } } /// /// Used by Property Pages Frame to set it's title bar. The Caption of the Hierarchy Node is returned. /// [Browsable(false)] [AutomationBrowsable(false)] public virtual string Name { get { return this.node.Caption; } } #endregion #region ctors public NodeProperties(HierarchyNode node) { if(node == null) { throw new ArgumentNullException("node"); } this.node = node; } #endregion #region ISpecifyPropertyPages methods public virtual void GetPages(CAUUID[] pages) { this.GetCommonPropertyPages(pages); } #endregion #region IVsSpecifyProjectDesignerPages /// /// Implementation of the IVsSpecifyProjectDesignerPages. It will retun the pages that are configuration independent. /// /// The pages to return. /// public virtual int GetProjectDesignerPages(CAUUID[] pages) { this.GetCommonPropertyPages(pages); return VSConstants.S_OK; } #endregion #region IVsGetCfgProvider methods public virtual int GetCfgProvider(out IVsCfgProvider p) { p = null; return VSConstants.E_NOTIMPL; } #endregion #region IVsBrowseObject methods /// /// Maps back to the hierarchy or project item object corresponding to the browse object. /// /// Reference to the hierarchy object. /// Reference to the project item. /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. public virtual int GetProjectItem(out IVsHierarchy hier, out uint itemid) { if(this.node == null) { throw new InvalidOperationException(); } hier = this.node.ProjectMgr.InteropSafeIVsHierarchy; itemid = this.node.ID; return VSConstants.S_OK; } #endregion #region overridden methods /// /// Get the Caption of the Hierarchy Node instance. If Caption is null or empty we delegate to base /// /// Caption of Hierarchy node instance public override string GetComponentName() { string caption = this.Node.Caption; if(string.IsNullOrEmpty(caption)) { return base.GetComponentName(); } else { return caption; } } #endregion #region helper methods protected string GetProperty(string name, string def) { string a = this.Node.ItemNode.GetMetadata(name); return (a == null) ? def : a; } protected void SetProperty(string name, string value) { this.Node.ItemNode.SetMetadata(name, value); } /// /// Retrieves the common property pages. The NodeProperties is the BrowseObject and that will be called to support /// configuration independent properties. /// /// The pages to return. private void GetCommonPropertyPages(CAUUID[] pages) { // We do not check whether the supportsProjectDesigner is set to false on the ProjectNode. // We rely that the caller knows what to call on us. if(pages == null) { throw new ArgumentNullException("pages"); } if(pages.Length == 0) { throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "pages"); } // Only the project should show the property page the rest should show the project properties. if(this.node != null && (this.node is ProjectNode)) { // Retrieve the list of guids from hierarchy properties. // Because a flavor could modify that list we must make sure we are calling the outer most implementation of IVsHierarchy string guidsList = String.Empty; IVsHierarchy hierarchy = this.Node.ProjectMgr.InteropSafeIVsHierarchy; object variant = null; ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList, out variant)); guidsList = (string)variant; Guid[] guids = Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(guidsList); if(guids == null || guids.Length == 0) { pages[0] = new CAUUID(); pages[0].cElems = 0; } else { pages[0] = PackageUtilities.CreateCAUUIDFromGuidArray(guids); } } else { pages[0] = new CAUUID(); pages[0].cElems = 0; } } #endregion #region IInternalExtenderProvider Members bool EnvDTE80.IInternalExtenderProvider.CanExtend(string extenderCATID, string extenderName, object extendeeObject) { EnvDTE80.IInternalExtenderProvider outerHierarchy = this.Node.ProjectMgr.InteropSafeIVsHierarchy as EnvDTE80.IInternalExtenderProvider; if(outerHierarchy != null) { return outerHierarchy.CanExtend(extenderCATID, extenderName, extendeeObject); } return false; } object EnvDTE80.IInternalExtenderProvider.GetExtender(string extenderCATID, string extenderName, object extendeeObject, EnvDTE.IExtenderSite extenderSite, int cookie) { EnvDTE80.IInternalExtenderProvider outerHierarchy = this.Node.ProjectMgr.InteropSafeIVsHierarchy as EnvDTE80.IInternalExtenderProvider; if(outerHierarchy != null) { return outerHierarchy.GetExtender(extenderCATID, extenderName, extendeeObject, extenderSite, cookie); } return null; } object EnvDTE80.IInternalExtenderProvider.GetExtenderNames(string extenderCATID, object extendeeObject) { EnvDTE80.IInternalExtenderProvider outerHierarchy = this.Node.ProjectMgr.InteropSafeIVsHierarchy as EnvDTE80.IInternalExtenderProvider; if(outerHierarchy != null) { return outerHierarchy.GetExtenderNames(extenderCATID, extendeeObject); } return null; } #endregion #region ExtenderSupport [Browsable(false)] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")] public virtual string ExtenderCATID { get { Guid catid = this.Node.ProjectMgr.GetCATIDForType(this.GetType()); if(Guid.Empty.CompareTo(catid) == 0) { return null; } return catid.ToString("B"); } } [Browsable(false)] public object ExtenderNames() { EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.Node.GetService(typeof(EnvDTE.ObjectExtenders)); Debug.Assert(extenderService != null, "Could not get the ObjectExtenders object from the services exposed by this property object"); if(extenderService == null) { throw new InvalidOperationException(); } return extenderService.GetExtenderNames(this.ExtenderCATID, this); } public object Extender(string extenderName) { EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.Node.GetService(typeof(EnvDTE.ObjectExtenders)); Debug.Assert(extenderService != null, "Could not get the ObjectExtenders object from the services exposed by this property object"); if(extenderService == null) { throw new InvalidOperationException(); } return extenderService.GetExtender(this.ExtenderCATID, extenderName, this); } #endregion } public class FileNodeProperties : NodeProperties { #region properties [SRCategoryAttribute(SR.Advanced)] [LocDisplayName(SR.BuildAction)] [SRDescriptionAttribute(SR.BuildActionDescription)] public virtual BuildAction BuildAction { get { string value = this.Node.ItemNode.ItemName; if(value == null || value.Length == 0) { return BuildAction.None; } return (BuildAction)Enum.Parse(typeof(BuildAction), value); } set { this.Node.ItemNode.ItemName = value.ToString(); } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FileName)] [SRDescriptionAttribute(SR.FileNameDescription)] public string FileName { get { return this.Node.Caption; } set { this.Node.SetEditLabel(value); } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FullPath)] [SRDescriptionAttribute(SR.FullPathDescription)] public string FullPath { get { return this.Node.Url; } } #region non-browsable properties - used for automation only [Browsable(false)] public string Extension { get { return Path.GetExtension(this.Node.Caption); } } #endregion #endregion #region ctors public FileNodeProperties(HierarchyNode node) : base(node) { } #endregion #region overridden methods public override string GetClassName() { return SR.GetString(SR.FileProperties, CultureInfo.CurrentUICulture); } #endregion } public class DependentFileNodeProperties : NodeProperties { #region properties [SRCategoryAttribute(SR.Advanced)] [LocDisplayName(SR.BuildAction)] [SRDescriptionAttribute(SR.BuildActionDescription)] public virtual BuildAction BuildAction { get { string value = this.Node.ItemNode.ItemName; if(value == null || value.Length == 0) { return BuildAction.None; } return (BuildAction)Enum.Parse(typeof(BuildAction), value); } set { this.Node.ItemNode.ItemName = value.ToString(); } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FileName)] [SRDescriptionAttribute(SR.FileNameDescription)] public virtual string FileName { get { return this.Node.Caption; } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FullPath)] [SRDescriptionAttribute(SR.FullPathDescription)] public string FullPath { get { return this.Node.Url; } } #endregion #region ctors public DependentFileNodeProperties(HierarchyNode node) : base(node) { } #endregion #region overridden methods public override string GetClassName() { return SR.GetString(SR.FileProperties, CultureInfo.CurrentUICulture); } #endregion } public class SingleFileGeneratorNodeProperties : FileNodeProperties { #region fields private EventHandler onCustomToolChanged; private EventHandler onCustomToolNameSpaceChanged; #endregion #region custom tool events internal event EventHandler OnCustomToolChanged { add { onCustomToolChanged += value; } remove { onCustomToolChanged -= value; } } internal event EventHandler OnCustomToolNameSpaceChanged { add { onCustomToolNameSpaceChanged += value; } remove { onCustomToolNameSpaceChanged -= value; } } #endregion #region properties [SRCategoryAttribute(SR.Advanced)] [LocDisplayName(SR.CustomTool)] [SRDescriptionAttribute(SR.CustomToolDescription)] public virtual string CustomTool { get { return this.Node.ItemNode.GetMetadata(ProjectFileConstants.Generator); } set { if (CustomTool != value) { this.Node.ItemNode.SetMetadata(ProjectFileConstants.Generator, value != string.Empty ? value : null); HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this.Node); if (onCustomToolChanged != null) { onCustomToolChanged(this.Node, args); } } } } [SRCategoryAttribute(VisualStudio.Project.SR.Advanced)] [LocDisplayName(SR.CustomToolNamespace)] [SRDescriptionAttribute(SR.CustomToolNamespaceDescription)] public virtual string CustomToolNamespace { get { return this.Node.ItemNode.GetMetadata(ProjectFileConstants.CustomToolNamespace); } set { if (CustomToolNamespace != value) { this.Node.ItemNode.SetMetadata(ProjectFileConstants.CustomToolNamespace, value != String.Empty ? value : null); HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this.Node); if (onCustomToolNameSpaceChanged != null) { onCustomToolNameSpaceChanged(this.Node, args); } } } } #endregion #region ctors public SingleFileGeneratorNodeProperties(HierarchyNode node) : base(node) { } #endregion } public class ProjectNodeProperties : NodeProperties { #region properties [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.ProjectFolder)] [SRDescriptionAttribute(SR.ProjectFolderDescription)] [AutomationBrowsable(false)] public string ProjectFolder { get { return this.Node.ProjectMgr.ProjectFolder; } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.ProjectFile)] [SRDescriptionAttribute(SR.ProjectFileDescription)] [AutomationBrowsable(false)] public string ProjectFile { get { return this.Node.ProjectMgr.ProjectFile; } set { this.Node.ProjectMgr.ProjectFile = value; } } #region non-browsable properties - used for automation only [Browsable(false)] public string FileName { get { return this.Node.ProjectMgr.ProjectFile; } set { this.Node.ProjectMgr.ProjectFile = value; } } [Browsable(false)] public string FullPath { get { string fullPath = this.Node.ProjectMgr.ProjectFolder; if(!fullPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { return fullPath + Path.DirectorySeparatorChar; } else { return fullPath; } } } #endregion #endregion #region ctors public ProjectNodeProperties(ProjectNode node) : base(node) { } #endregion #region overridden methods public override string GetClassName() { return SR.GetString(SR.ProjectProperties, CultureInfo.CurrentUICulture); } /// /// ICustomTypeDescriptor.GetEditor /// To enable the "Property Pages" button on the properties browser /// the browse object (project properties) need to be unmanaged /// or it needs to provide an editor of type ComponentEditor. /// /// Type of the editor /// Editor [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification="The service provider is used by the PropertiesEditorLauncher")] public override object GetEditor(Type editorBaseType) { // Override the scenario where we are asked for a ComponentEditor // as this is how the Properties Browser calls us if(editorBaseType == typeof(ComponentEditor)) { IOleServiceProvider sp; ErrorHandler.ThrowOnFailure(this.Node.GetSite(out sp)); return new PropertiesEditorLauncher(new ServiceProvider(sp)); } return base.GetEditor(editorBaseType); } public override int GetCfgProvider(out IVsCfgProvider p) { if(this.Node != null && this.Node.ProjectMgr != null) { return this.Node.ProjectMgr.GetCfgProvider(out p); } return base.GetCfgProvider(out p); } #endregion } public class FolderNodeProperties : NodeProperties { #region properties [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FolderName)] [SRDescriptionAttribute(SR.FolderNameDescription)] [AutomationBrowsable(false)] public string FolderName { get { return this.Node.Caption; } set { this.Node.SetEditLabel(value); this.Node.ReDraw(UIHierarchyElement.Caption); } } #region properties - used for automation only [Browsable(false)] [AutomationBrowsable(true)] public string FileName { get { return this.Node.Caption; } set { this.Node.SetEditLabel(value); } } [Browsable(false)] [AutomationBrowsable(true)] public string FullPath { get { string fullPath = this.Node.GetMkDocument(); if(!fullPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { return fullPath + Path.DirectorySeparatorChar; } else { return fullPath; } } } #endregion #endregion #region ctors public FolderNodeProperties(HierarchyNode node) : base(node) { } #endregion #region overridden methods public override string GetClassName() { return SR.GetString(SR.FolderProperties, CultureInfo.CurrentUICulture); } #endregion } public class ReferenceNodeProperties : NodeProperties { #region properties [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.RefName)] [SRDescriptionAttribute(SR.RefNameDescription)] [Browsable(true)] [AutomationBrowsable(true)] public override string Name { get { return this.Node.Caption; } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.CopyToLocal)] [SRDescriptionAttribute(SR.CopyToLocalDescription)] public bool CopyToLocal { get { string copyLocal = this.GetProperty(ProjectFileConstants.Private, "False"); if(copyLocal == null || copyLocal.Length == 0) return true; return bool.Parse(copyLocal); } set { this.SetProperty(ProjectFileConstants.Private, value.ToString()); } } [SRCategoryAttribute(SR.Misc)] [LocDisplayName(SR.FullPath)] [SRDescriptionAttribute(SR.FullPathDescription)] public virtual string FullPath { get { return this.Node.Url; } } #endregion #region ctors public ReferenceNodeProperties(HierarchyNode node) : base(node) { } #endregion #region overridden methods public override string GetClassName() { return SR.GetString(SR.ReferenceProperties, CultureInfo.CurrentUICulture); } #endregion } [ComVisible(true)] public class ProjectReferencesProperties : ReferenceNodeProperties { #region ctors public ProjectReferencesProperties(ProjectReferenceNode node) : base(node) { } #endregion #region overriden methods public override string FullPath { get { return ((ProjectReferenceNode)Node).ReferencedProjectOutputPath; } } #endregion } [ComVisible(true)] public class ComReferenceProperties : ReferenceNodeProperties { public ComReferenceProperties(ComReferenceNode node) : base(node) { } [SRCategory(SR.Misc)] [LocDisplayName(SR.EmbedInteropTypes)] [SRDescription(SR.EmbedInteropTypesDescription)] public virtual bool EmbedInteropTypes { get { return ((ComReferenceNode)this.Node).EmbedInteropTypes; } set { ((ComReferenceNode)this.Node).EmbedInteropTypes = value; } } } }