Konstantin Koch 8287c54432 Included the Visual Studio extension and made the necessary changes to make it run.
Replaced the old VS templates with ones that offer more flexiblity.
Started replacing the Content Project for the samples with our custom project type.
Inlcuded a basic not yet working AssimpImporter.
2015-04-08 14:50:03 +02:00

2054 lines
74 KiB
C#

/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
* ***************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
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;
//#define CCI_TRACING
using Microsoft.VisualStudio.Shell.Interop;
using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Reflection;
namespace Microsoft.VisualStudio.Project
{
/// <summary>
/// An object that deals with user interaction via a GUI in the form a hierarchy: a parent node with zero or more child nodes, each of which
/// can itself be a hierarchy.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
public abstract class HierarchyNode :
IDisposable
{
public static readonly Guid SolutionExplorer = new Guid(EnvDTE.Constants.vsWindowKindSolutionExplorer);
public const int NoImage = -1;
#if DEBUG
public static int LastTracedProperty;
#endif
private ProjectElement itemNode;
private ProjectNode projectMgr;
private HierarchyNode parentNode;
private HierarchyNode nextSibling;
private HierarchyNode firstChild;
private uint hierarchyId;
private HierarchyNodeFlags flags;
private NodeProperties nodeProperties;
private OleServiceProvider oleServiceProvider = new OleServiceProvider();
/// <summary>
/// Has the object been disposed.
/// </summary>
/// <devremark>We will not specify a property for isDisposed, rather it is expected that the a private flag is defined
/// on all subclasses. We do not want get in a situation where the base class's dipose is not called because a child sets the flag through the property.</devremark>
private bool isDisposed;
#region abstract properties
/// <summary>
/// The URL of the node.
/// </summary>
/// <value></value>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
public abstract string Url
{
get;
}
/// <summary>
/// The Caption of the node.
/// </summary>
/// <value></value>
public abstract string Caption
{
get;
}
/// <summary>
/// The item type guid associated to a node.
/// </summary>
/// <value></value>
public abstract Guid ItemTypeGuid
{
get;
}
#endregion
#region virtual properties
public virtual bool IsNonMemberItem
{
get
{
return false;
}
}
/// <summary>
/// Gets the full path to where children of this node live on disk.
///
/// This should only be called on nodes which actually can have children, such
/// as folders and project nodes. For all other nodes this will raise an
/// InvalidOperationException.
///
/// For a project node, this returns the project home folder. For folder
/// nodes this returns the folder's path.
/// </summary>
public virtual string FullPathToChildren {
get {
Debug.Fail("This node cannot have children");
throw new InvalidOperationException();
}
}
/// <summary>
/// Defines a string that is used to separate the name relation from the extension
/// </summary>
public virtual string NameRelationSeparator
{
get
{
return ".";
}
}
public virtual int MenuCommandId
{
get { return VsMenus.IDM_VS_CTXT_NOCOMMANDS; }
}
public virtual Guid MenuCommandGuid
{
get { return VsMenus.guidSHLMainMenu; }
}
/// <summary>
/// Return an imageindex
/// </summary>
/// <returns></returns>
public virtual int ImageIndex
{
get { return NoImage; }
}
/// <summary>
/// Return an state icon index
/// </summary>
/// <returns></returns>
/// <summary>
/// Sets the state icon for a file.
/// </summary>
public virtual VsStateIcon StateIconIndex
{
get
{
if (!this.ExcludeNodeFromScc)
{
IVsSccManager2 sccManager = this.ProjectMgr.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;
if (sccManager != null)
{
VsStateIcon[] statIcons = new VsStateIcon[1] { VsStateIcon.STATEICON_NOSTATEICON };
uint[] sccStatus = new uint[1] { 0 };
// Get the glyph from the scc manager. Note that it will fail in command line
// scenarios.
if (ErrorHandler.Succeeded(sccManager.GetSccGlyph(1, new string[] { this.GetMkDocument() }, statIcons, sccStatus)))
{
return statIcons[0];
}
}
}
return VsStateIcon.STATEICON_NOSTATEICON;
}
}
public virtual bool IsLinkFile
{
get
{
return false;
}
}
protected virtual VSOVERLAYICON OverlayIconIndex
{
get
{
return VSOVERLAYICON.OVERLAYICON_NONE;
}
}
/// <summary>
/// Defines whether a node can execute a command if in selection.
/// </summary>
public virtual bool CanExecuteCommand
{
get
{
return true;
}
}
/// <summary>
/// Used to determine the sort order of different node types
/// in the solution explorer window.
/// Nodes with the same priorities are sorted based on their captions.
/// </summary>
public virtual int SortPriority
{
get { return DefaultSortOrderNode.HierarchyNode; }
}
/// <summary>
/// Returns an object that is a special view over this object; this is the value
/// returned by the Object property of the automation objects.
/// </summary>
public virtual object Object
{
get { return this; }
}
#endregion
#region properties
/// <summary>
/// Defines the properties attached to this node.
/// </summary>
public NodeProperties NodeProperties
{
get
{
if (null == nodeProperties)
{
nodeProperties = CreatePropertiesObject();
}
return this.nodeProperties;
}
}
public OleServiceProvider OleServiceProvider
{
get
{
return this.oleServiceProvider;
}
}
[System.ComponentModel.BrowsableAttribute(false)]
public ProjectNode ProjectMgr
{
get
{
return this.projectMgr;
}
set
{
this.projectMgr = value;
}
}
[System.ComponentModel.BrowsableAttribute(false)]
public HierarchyNode NextSibling
{
get
{
return this.nextSibling;
}
set
{
this.nextSibling = value;
}
}
public HierarchyNode FirstChild
{
get
{
return this.firstChild;
}
set
{
this.firstChild = value;
}
}
/// <summary>
/// Returns a sequence containing all of this node's children.
/// </summary>
public IEnumerable<HierarchyNode> AllChildren
{
get
{
for (HierarchyNode node = this.firstChild; node != null; node = node.nextSibling)
{
yield return node;
}
}
}
/// <summary>
/// Gets or sets whether the node is currently visible in the hierarchy.
///
/// Enables subsetting or supersetting the hierarchy view.
/// </summary>
public bool IsVisible
{
get
{
return flags.HasFlag(HierarchyNodeFlags.IsVisible);
}
set
{
if (value)
{
flags |= HierarchyNodeFlags.IsVisible;
}
else
{
flags &= ~HierarchyNodeFlags.IsVisible;
}
}
}
public HierarchyNode PreviousVisibleSibling
{
get
{
var prev = PreviousSibling;
while (prev != null && !prev.IsVisible)
{
prev = prev.PreviousSibling;
}
return prev;
}
}
public HierarchyNode NextVisibleSibling
{
get
{
var next = nextSibling;
while (next != null && !next.IsVisible)
{
next = next.NextSibling;
}
return next;
}
}
public HierarchyNode FirstVisibleChild
{
get
{
var next = FirstChild;
while (next != null && !next.IsVisible)
{
next = next.NextSibling;
}
return next;
}
}
[System.ComponentModel.BrowsableAttribute(false)]
public HierarchyNode Parent
{
get
{
return this.parentNode;
}
set
{
this.parentNode = value;
}
}
[System.ComponentModel.BrowsableAttribute(false)]
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
public uint ID
{
get
{
return this.hierarchyId;
}
set
{
this.hierarchyId = value;
}
}
[System.ComponentModel.BrowsableAttribute(false)]
public ProjectElement ItemNode
{
get
{
return itemNode;
}
set
{
itemNode = value;
}
}
protected string GetAbsoluteUrlFromMsbuild()
{
string path = this.ItemNode.GetMetadata(ProjectFileConstants.Include);
if (String.IsNullOrEmpty(path))
{
return String.Empty;
}
return CommonUtils.GetAbsoluteFilePath(this.ProjectMgr.ProjectHome, path);
}
[System.ComponentModel.BrowsableAttribute(false)]
public bool IsExpanded
{
get
{
return flags.HasFlag(HierarchyNodeFlags.IsExpanded);
}
set
{
if (value)
{
flags |= HierarchyNodeFlags.IsExpanded;
}
else
{
flags &= ~HierarchyNodeFlags.IsExpanded;
}
}
}
public HierarchyNode PreviousSibling
{
get
{
if (this.parentNode == null) return null;
HierarchyNode prev = null;
for (HierarchyNode child = this.parentNode.firstChild; child != null; child = child.nextSibling)
{
if (child == this)
break;
prev = child;
}
return prev;
}
}
/// <summary>
/// Specifies if a Node is under source control.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
public bool ExcludeNodeFromScc
{
get
{
return flags.HasFlag(HierarchyNodeFlags.ExcludeFromScc);
}
set
{
if (value)
{
flags |= HierarchyNodeFlags.ExcludeFromScc;
}
else
{
flags &= ~HierarchyNodeFlags.ExcludeFromScc;
}
}
}
/// <summary>
/// Defines if a node a name relation to its parent node
///
/// </summary>
public bool HasParentNodeNameRelation
{
get
{
return flags.HasFlag(HierarchyNodeFlags.HasParentNodeNameRelation);
}
set
{
if (value)
{
flags |= HierarchyNodeFlags.HasParentNodeNameRelation;
}
else
{
flags &= HierarchyNodeFlags.HasParentNodeNameRelation;
}
}
}
#endregion
#region ctors
protected HierarchyNode()
{
IsExpanded = true;
IsVisible = true;
}
protected HierarchyNode(ProjectNode root, ProjectElement element)
{
VsUtilities.ArgumentNotNull("root", root);
this.projectMgr = root;
this.itemNode = element;
this.hierarchyId = this.projectMgr.ItemIdMap.Add(this);
this.OleServiceProvider.AddService(typeof(IVsHierarchy), root, false);
IsVisible = true;
}
/// <summary>
/// Overloaded ctor.
/// </summary>
/// <param name="root"></param>
protected HierarchyNode(ProjectNode root)
{
VsUtilities.ArgumentNotNull("root", root);
this.projectMgr = root;
this.itemNode = new VirtualProjectElement(this.projectMgr);
this.hierarchyId = this.projectMgr.ItemIdMap.Add(this);
this.OleServiceProvider.AddService(typeof(IVsHierarchy), root, false);
IsVisible = true;
}
#endregion
#region virtual methods
/// <summary>
/// Creates an object derived from NodeProperties that will be used to expose properties
/// spacific for this object to the property browser.
/// </summary>
/// <returns></returns>
protected virtual NodeProperties CreatePropertiesObject()
{
return null;
}
/// <summary>
/// Return an iconhandle
/// </summary>
/// <param name="open"></param>
/// <returns></returns>
public virtual object GetIconHandle(bool open)
{
return null;
}
/// <summary>
/// Removes a node from the hierarchy.
/// </summary>
/// <param name="node">The node to remove.</param>
public virtual void RemoveChild(HierarchyNode node)
{
VsUtilities.ArgumentNotNull("node", node);
this.projectMgr.ItemIdMap.Remove(node);
HierarchyNode last = null;
for (HierarchyNode n = this.firstChild; n != null; n = n.nextSibling)
{
if (n == node)
{
if (last != null)
{
last.nextSibling = n.nextSibling;
}
if (n == this.firstChild)
{
this.firstChild = n.nextSibling;
}
return;
}
last = n;
}
throw new InvalidOperationException("Node not found");
}
/// <summary>
/// Returns an automation object representing this node
/// </summary>
/// <returns>The automation object</returns>
public virtual object GetAutomationObject()
{
return new Automation.OAProjectItem(this.projectMgr.GetAutomationObject() as Automation.OAProject, this);
}
/// <summary>
/// Returns a property object based on a property id
/// </summary>
/// <param name="propId">the property id of the property requested</param>
/// <returns>the property object requested</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
public virtual object GetProperty(int propId)
{
object result = null;
switch ((__VSHPROPID)propId)
{
case __VSHPROPID.VSHPROPID_Expandable:
result = (this.firstChild != null);
break;
case __VSHPROPID.VSHPROPID_Caption:
result = this.Caption;
break;
case __VSHPROPID.VSHPROPID_Name:
result = this.Caption;
break;
case __VSHPROPID.VSHPROPID_ExpandByDefault:
result = false;
break;
case __VSHPROPID.VSHPROPID_IconImgList:
result = this.ProjectMgr.ImageHandler.ImageList.Handle;
break;
case __VSHPROPID.VSHPROPID_OpenFolderIconIndex:
case __VSHPROPID.VSHPROPID_IconIndex:
int index = this.ImageIndex;
if (index != NoImage)
{
result = index;
}
break;
case __VSHPROPID.VSHPROPID_StateIconIndex:
result = (int)this.StateIconIndex;
break;
case __VSHPROPID.VSHPROPID_OverlayIconIndex:
result = (int)this.OverlayIconIndex;
break;
case __VSHPROPID.VSHPROPID_IconHandle:
result = GetIconHandle(false);
break;
case __VSHPROPID.VSHPROPID_OpenFolderIconHandle:
result = GetIconHandle(true);
break;
case __VSHPROPID.VSHPROPID_NextVisibleSibling:
var nextVisible = NextVisibleSibling;
result = (int)((nextVisible != null) ? nextVisible.ID : VSConstants.VSITEMID_NIL);
break;
case __VSHPROPID.VSHPROPID_NextSibling:
result = (int)((this.nextSibling != null) ? this.nextSibling.hierarchyId : VSConstants.VSITEMID_NIL);
break;
case __VSHPROPID.VSHPROPID_IsNonMemberItem:
result = IsNonMemberItem;
break;
case __VSHPROPID.VSHPROPID_IsHiddenItem:
case __VSHPROPID.VSHPROPID_IsNonSearchable:
result = !IsVisible;
break;
case __VSHPROPID.VSHPROPID_FirstChild:
result = (int)((this.firstChild != null) ? this.firstChild.hierarchyId : VSConstants.VSITEMID_NIL);
break;
case __VSHPROPID.VSHPROPID_FirstVisibleChild:
var firstVisible = FirstVisibleChild;
result = (int)((firstVisible != null) ? firstVisible.hierarchyId : VSConstants.VSITEMID_NIL);
break;
case __VSHPROPID.VSHPROPID_Parent:
if (null == this.parentNode)
{
unchecked { result = new IntPtr((int)VSConstants.VSITEMID_NIL); }
}
else
{
result = new IntPtr((int)this.parentNode.hierarchyId); // see bug 176470
}
break;
case __VSHPROPID.VSHPROPID_Root:
result = Marshal.GetIUnknownForObject(this.projectMgr);
break;
case __VSHPROPID.VSHPROPID_Expanded:
result = this.IsExpanded;
break;
case __VSHPROPID.VSHPROPID_BrowseObject:
result = this.NodeProperties;
if (result != null) result = new DispatchWrapper(result);
break;
case __VSHPROPID.VSHPROPID_EditLabel:
if (this.ProjectMgr != null && !this.ProjectMgr.IsClosed && !this.ProjectMgr.IsCurrentStateASuppressCommandsMode())
{
result = GetEditLabel();
}
break;
case __VSHPROPID.VSHPROPID_SaveName:
//SaveName is the name shown in the Save and the Save Changes dialog boxes.
result = this.Caption;
break;
case __VSHPROPID.VSHPROPID_ExtObject:
result = GetAutomationObject();
break;
}
__VSHPROPID2 id2 = (__VSHPROPID2)propId;
switch (id2)
{
case __VSHPROPID2.VSHPROPID_IsLinkFile:
result = IsLinkFile;
break;
case __VSHPROPID2.VSHPROPID_NoDefaultNestedHierSorting:
return true; // We are doing the sorting ourselves through VSHPROPID_FirstChild and VSHPROPID_NextSibling
case __VSHPROPID2.VSHPROPID_CfgBrowseObjectCATID:
case __VSHPROPID2.VSHPROPID_BrowseObjectCATID:
{
// If there is a browse object and it is a NodeProperties, then get it's CATID
object browseObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_BrowseObject);
if (browseObject != null)
{
if (browseObject is DispatchWrapper)
browseObject = ((DispatchWrapper)browseObject).WrappedObject;
result = this.ProjectMgr.GetCATIDForType(browseObject.GetType()).ToString("B");
if (String.Equals(result as string, Guid.Empty.ToString("B"), StringComparison.Ordinal))
result = null;
}
break;
}
case __VSHPROPID2.VSHPROPID_ExtObjectCATID:
{
// If there is a extensibility object and it is a NodeProperties, then get it's CATID
object extObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_ExtObject);
if (extObject != null)
{
if (extObject is DispatchWrapper)
extObject = ((DispatchWrapper)extObject).WrappedObject;
result = this.ProjectMgr.GetCATIDForType(extObject.GetType()).ToString("B");
if (String.Equals(result as string, Guid.Empty.ToString("B"), StringComparison.Ordinal))
result = null;
}
break;
}
}
#if DEV11_OR_LATER
__VSHPROPID5 id5 = (__VSHPROPID5)propId;
switch(id5) {
case __VSHPROPID5.VSHPROPID_ProvisionalViewingStatus:
result = ProvisionalViewingStatus;
break;
}
#endif
#if DEBUG
if (propId != LastTracedProperty)
{
string trailer = (result == null) ? "null" : result.ToString();
LastTracedProperty = propId; // some basic filtering here...
}
#endif
return result;
}
#if DEV11_OR_LATER
public virtual __VSPROVISIONALVIEWINGSTATUS ProvisionalViewingStatus {
get {
return __VSPROVISIONALVIEWINGSTATUS.PVS_Disabled;
}
}
#endif
/// <summary>
/// Sets the value of a property for a given property id
/// </summary>
/// <param name="propid">the property id of the property to be set</param>
/// <param name="value">value of the property</param>
/// <returns>S_OK if succeeded</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
public virtual int SetProperty(int propid, object value)
{
__VSHPROPID id = (__VSHPROPID)propid;
switch (id)
{
case __VSHPROPID.VSHPROPID_Expanded:
this.IsExpanded = (bool)value;
break;
case __VSHPROPID.VSHPROPID_EditLabel:
return SetEditLabel((string)value);
default:
break;
}
return VSConstants.S_OK;
}
/// <summary>
/// Get a guid property
/// </summary>
/// <param name="propid">property id for the guid property requested</param>
/// <param name="guid">the requested guid</param>
/// <returns>S_OK if succeded</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
public virtual int GetGuidProperty(int propid, out Guid guid)
{
guid = Guid.Empty;
if (propid == (int)__VSHPROPID.VSHPROPID_TypeGuid)
{
guid = this.ItemTypeGuid;
}
if (guid.Equals(Guid.Empty))
{
return VSConstants.DISP_E_MEMBERNOTFOUND;
}
return VSConstants.S_OK;
}
public virtual bool CanAddFiles
{
get
{
return false;
}
}
/// <summary>
/// Set a guid property.
/// </summary>
/// <param name="propid">property id of the guid property to be set</param>
/// <param name="guid">the guid to be set</param>
/// <returns>E_NOTIMPL</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
public virtual int SetGuidProperty(int propid, ref Guid guid)
{
return VSConstants.E_NOTIMPL;
}
/// <summary>
/// Called by the shell when a node has been renamed from the GUI
/// </summary>
/// <param name="label"></param>
/// <returns>E_NOTIMPL</returns>
public virtual int SetEditLabel(string label)
{
return VSConstants.E_NOTIMPL;
}
/// <summary>
/// Called by the shell to get the node caption when the user tries to rename from the GUI
/// </summary>
/// <returns>the node cation</returns>
public virtual string GetEditLabel()
{
return this.Caption;
}
/// <summary>
/// This method is called by the interface method GetMkDocument to specify the item moniker.
/// </summary>
/// <returns>The moniker for this item</returns>
[SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Mk")]
public virtual string GetMkDocument()
{
return String.Empty;
}
/// <summary>
/// Removes items from the hierarchy. Project overwrites this
/// </summary>
/// <param name="removeFromStorage"></param>
public virtual void Remove(bool removeFromStorage)
{
string documentToRemove = this.GetMkDocument();
// Ask Document tracker listeners if we can remove the item.
string[] filesToBeDeleted = new string[1] { documentToRemove };
VSQUERYREMOVEFILEFLAGS[] queryRemoveFlags = this.GetQueryRemoveFileFlags(filesToBeDeleted);
if (!this.ProjectMgr.Tracker.CanRemoveItems(filesToBeDeleted, queryRemoveFlags))
{
return;
}
// Close the document if it has a manager.
DocumentManager manager = this.GetDocumentManager();
if (manager != null)
{
if (manager.Close(!removeFromStorage ? __FRAMECLOSE.FRAMECLOSE_PromptSave : __FRAMECLOSE.FRAMECLOSE_NoSave) == VSConstants.E_ABORT)
{
// User cancelled operation in message box.
return;
}
}
if (removeFromStorage)
{
this.DeleteFromStorage(documentToRemove);
}
RemoveNonDocument(removeFromStorage);
// Close the document window if opened.
CloseDocumentWindow(this);
// Notify document tracker listeners that we have removed the item.
VSREMOVEFILEFLAGS[] removeFlags = this.GetRemoveFileFlags(filesToBeDeleted);
Debug.Assert(removeFlags != null, "At least an empty array should be returned for the GetRemoveFileFlags");
this.ProjectMgr.Tracker.OnItemRemoved(documentToRemove, removeFlags[0]);
// Notify hierarchy event listeners that items have been invalidated
ProjectMgr.OnInvalidateItems(this);
// Dispose the node now that is deleted.
this.Dispose(true);
}
public void RemoveNonDocument(bool removeFromStorage)
{
// Check out the project file.
if (!this.ProjectMgr.QueryEditProjectFile(false))
{
throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
}
// Notify hierarchy event listeners that the file is going to be removed.
ProjectMgr.OnItemDeleted(this);
// Remove child if any before removing from the hierarchy
for (HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling)
{
child.Remove(removeFromStorage);
}
// the project node has no parentNode
if (this.parentNode != null)
{
// Remove from the Hierarchy
this.parentNode.RemoveChild(this);
}
this.itemNode.RemoveFromProjectFile();
}
/// <summary>
/// Returns the relational name which is defined as the first part of the caption until indexof NameRelationSeparator
/// </summary>
public virtual string GetRelationalName()
{
//Get the first part of the caption
string[] partsOfParent = this.Caption.Split(new string[] { this.NameRelationSeparator }, StringSplitOptions.None);
return partsOfParent[0];
}
/// <summary>
/// Returns the 'extension' of the relational name
/// e.g. form1.resx returns .resx, form1.designer.cs returns .designer.cs
/// </summary>
/// <returns>The extension</returns>
public virtual string GetRelationNameExtension()
{
return this.Caption.Substring(this.Caption.IndexOf(this.NameRelationSeparator, StringComparison.Ordinal));
}
/// <summary>
/// Close open document frame for a specific node.
/// </summary>
protected void CloseDocumentWindow(HierarchyNode node)
{
VsUtilities.ArgumentNotNull("node", node);
// We walk the RDT looking for all running documents attached to this hierarchy and itemid. There
// are cases where there may be two different editors (not views) open on the same document.
IEnumRunningDocuments pEnumRdt;
IVsRunningDocumentTable pRdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
VsUtilities.CheckNotNull(pRdt);
if (ErrorHandler.Succeeded(pRdt.GetRunningDocumentsEnum(out pEnumRdt)))
{
uint[] cookie = new uint[1];
uint fetched;
uint saveOptions = (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave;
IVsHierarchy srpOurHier = node.projectMgr as IVsHierarchy;
ErrorHandler.ThrowOnFailure(pEnumRdt.Reset());
while (VSConstants.S_OK == pEnumRdt.Next(1, cookie, out fetched))
{
// Note we can pass NULL for all parameters we don't care about
uint empty;
string emptyStr;
IntPtr ppunkDocData;
IVsHierarchy srpHier;
uint itemid = VSConstants.VSITEMID_NIL;
ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo(
cookie[0],
out empty,
out empty,
out empty,
out emptyStr,
out srpHier,
out itemid,
out ppunkDocData));
// Is this one of our documents?
if (VsUtilities.IsSameComObject(srpOurHier, srpHier) && itemid == node.ID)
{
IVsSolution soln = GetService(typeof(SVsSolution)) as IVsSolution;
ErrorHandler.ThrowOnFailure(soln.CloseSolutionElement(saveOptions, srpOurHier, cookie[0]));
}
if (ppunkDocData != IntPtr.Zero)
Marshal.Release(ppunkDocData);
}
}
}
/// <summary>
/// Redraws the state icon if the node is not excluded from source control.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
public virtual void UpdateSccStateIcons()
{
if (!this.ExcludeNodeFromScc)
{
ProjectMgr.ReDrawNode(this, UIHierarchyElement.SccState);
}
}
/// <summary>
/// To be overwritten by descendants.
/// </summary>
public virtual int SetEditLabel(string label, string relativePath)
{
throw new NotImplementedException();
}
/// <summary>
/// Called by the drag and drop implementation to ask the node
/// which is being dragged/droped over which nodes should
/// process the operation.
/// This allows for dragging to a node that cannot contain
/// items to let its parent accept the drop
/// </summary>
/// <returns>HierarchyNode that accept the drop handling</returns>
public virtual HierarchyNode GetDragTargetHandlerNode()
{
return this;
}
/// <summary>
/// Add a new Folder to the project hierarchy.
/// </summary>
/// <returns>S_OK if succeeded, otherwise an error</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
protected virtual int AddNewFolder()
{
// Check out the project file.
if (!this.ProjectMgr.QueryEditProjectFile(false))
{
throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
}
try
{
// Generate a new folder name
string newFolderName;
ErrorHandler.ThrowOnFailure(this.projectMgr.GenerateUniqueItemName(this.hierarchyId, String.Empty, String.Empty, out newFolderName));
// create the folder node, this will add it to MS build but we won't have the directory created yet.
var folderNode = ProjectMgr.CreateFolderNode(Path.Combine(FullPathToChildren, newFolderName));
folderNode.IsBeingCreated = true;
AddChild(folderNode);
folderNode.ExpandItem(EXPANDFLAGS.EXPF_SelectItem);
IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
// let the user rename the folder which will create the directory when finished
int hr;
object dummy = null;
Guid cmdGroup = VsMenus.guidStandardCommandSet97;
if (ErrorHandler.Failed(hr = shell.PostExecCommand(ref cmdGroup, (uint)VsCommands.Rename, 0, ref dummy)))
{
// make sure the directory is created...
folderNode.OnCancelLabelEdit();
}
}
catch (COMException e)
{
Trace.WriteLine("Exception : " + e.Message);
return e.ErrorCode;
}
return VSConstants.S_OK;
}
protected virtual int AddItemToHierarchy(HierarchyAddType addType)
{
string strFilter = String.Empty;
int iDontShowAgain;
uint uiFlags;
IVsProject3 project = (IVsProject3)this.projectMgr;
string strBrowseLocations = this.projectMgr.ProjectHome;
System.Guid projectGuid = this.projectMgr.ProjectGuid;
if (addType == HierarchyAddType.AddNewItem)
{
IVsAddProjectItemDlg addItemDialog = this.GetService(typeof(IVsAddProjectItemDlg)) as IVsAddProjectItemDlg;
uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddNewItems | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView);
return addItemDialog.AddProjectItemDlg(this.hierarchyId, ref projectGuid, project, uiFlags, null, null, ref strBrowseLocations, ref strFilter, out iDontShowAgain);
}
else
{
uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddExistingItems | __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter);
string filters = this.ProjectMgr.GetExistingFilesFilter();
using (System.Windows.Forms.OpenFileDialog dialog = new System.Windows.Forms.OpenFileDialog())
{
dialog.InitialDirectory = strBrowseLocations;
dialog.FileName = "";
dialog.Filter = filters + (filters.Length > 0 ? "|" : "") + SR.GetString(SR.AllFilesFilter);
dialog.Multiselect = true;
dialog.Title = SR.GetString(SR.AddExistingFile) + " - " + this.ProjectMgr.GetProjectProperty(ProjectFileConstants.Name);
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
foreach (var fileName in dialog.FileNames)
{
VSADDRESULT[] result = new VSADDRESULT[dialog.FileNames.Length];
project.AddItem(this.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, fileName, 0, new string[] { fileName }, IntPtr.Zero, result);
}
}
}
//Once the dialog has been opened, the filters unfortunately don't change, that's why we don't use the usual dialog.
/*RegisterFileTypes(filters);
try
{
return addItemDialog.AddProjectItemDlg(this.hierarchyId, ref projectGuid, project, uiFlags, null, null, ref strBrowseLocations, ref strFilter, out iDontShowAgain);
}
finally
{
UnregisterFileTypes(filters);
}*/
return VSConstants.S_OK;
}
}
//Doesn't change the filters of the addItemDialog after the first time it has been shown.
/*private void RegisterFileTypes(string filter)
{
if (string.IsNullOrWhiteSpace(filter))
return;
string[] parts = filter.Split('|');
if (parts.Length % 2 != 0)
throw new ArgumentException("Existing file filter doesn't contain a filter value for every filter name.");
string name = this.ProjectMgr.Package.ApplicationRegistryRoot.Name;
if (name.StartsWith(Registry.CurrentUser.Name))
{
name = name.Substring(Registry.CurrentUser.Name.Length);
name = name.TrimStart('\\');
}
var context = Registry.CurrentUser.OpenSubKey(name, true);
using (var key = context.CreateSubKey("Projects\\" + this.ProjectMgr.ProjectGuid.ToString("B") + "\\Filters", Win32.RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.Flush();
for (int i = 0; i < parts.Length; i += 2)
{
string filterName = parts[i];
string filterValue = parts[i + 1].Replace(';', ',');
using (var filterKey = key.CreateSubKey("/" + (i / 2 + 1).ToString()))
{
filterKey.SetValue(null, filterName + ";" + filterValue);
filterKey.SetValue("SortPriority", i / 2 + 1);
//filterKey.SetValue("CommonOpenFilesFilter", 1);
}
}
}
}
private void UnregisterFileTypes(string filter)
{
if (string.IsNullOrWhiteSpace(filter))
return;
string[] parts = filter.Split('|');
if (parts.Length % 2 != 0)
throw new ArgumentException("Existing file filter doesn't contain a filter value for every filter name.");
var context = this.ProjectMgr.Package.ApplicationRegistryRoot;
using (var key = context.OpenSubKey("Projects\\" + this.ProjectMgr.ProjectGuid.ToString("B") + "\\Filters", true))
{
for (int i = 0; i < parts.Length; i += 2)
{
key.DeleteSubKey("/" + (i / 2 + 1).ToString());
}
}
}*/
/// <summary>
/// Overwritten in subclasses
/// </summary>
protected virtual void DoDefaultAction()
{
}
/// <summary>
/// Handles the exclude from project command.
/// </summary>
/// <returns></returns>
public virtual int ExcludeFromProject()
{
Debug.Assert(this.ProjectMgr != null, "The project item " + this.ToString() + " has not been initialised correctly. It has a null ProjectMgr");
this.Remove(false);
return VSConstants.S_OK;
}
/// <summary>
/// Handles the include in project command.
/// </summary>
/// <returns></returns>
public virtual int IncludeInProject(bool includeChildren)
{
return VSConstants.E_FAIL;
}
/// <summary>
/// Handles the Show in Designer command.
/// </summary>
/// <returns></returns>
protected virtual int ShowInDesigner(IList<HierarchyNode> selectedNodes)
{
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
/// <summary>
/// Prepares a selected node for clipboard.
/// It takes the the project reference string of this item and adds it to a stringbuilder.
/// </summary>
/// <returns>A stringbuilder.</returns>
/// <devremark>This method has to be public since seleceted nodes will call it.</devremark>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ClipBoard")]
public virtual string PrepareSelectedNodesForClipBoard()
{
Debug.Assert(this.ProjectMgr != null, " No project mananager available for this node " + ToString());
Debug.Assert(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null, " The itemsdragged list should have been initialized prior calling this method");
if (this.hierarchyId == VSConstants.VSITEMID_ROOT)
{
if (this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
{
this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort
}
return null;
}
if (this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
{
this.ProjectMgr.ItemsDraggedOrCutOrCopied.Add(this);
}
string projref = String.Empty;
IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution;
if (solution != null)
{
ErrorHandler.ThrowOnFailure(solution.GetProjrefOfItem(this.ProjectMgr, this.hierarchyId, out projref));
if (String.IsNullOrEmpty(projref))
{
if (this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
{
this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort
}
return null;
}
}
// Append the projectref and a null terminator to the string builder
return projref + '\0';
}
/// <summary>
/// Returns the Cannonical Name
/// </summary>
/// <returns>Cannonical Name</returns>
public virtual string GetCanonicalName()
{
return this.GetMkDocument();
}
/// <summary>
/// Factory method for the Document Manager object
/// </summary>
/// <returns>null object, since a hierarchy node does not know its kind of document</returns>
/// <remarks>Must be overriden by derived node classes if a document manager is needed</remarks>
public virtual DocumentManager GetDocumentManager()
{
return null;
}
/// <summary>
/// Displays the context menu.
/// </summary>
/// <param name="selectedNodes">list of selected nodes.</param>
/// <param name="pointerToVariant">contains the location (x,y) at which to show the menu.</param>
[SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "pointer")]
protected virtual int DisplayContextMenu(IList<HierarchyNode> selectedNodes, IntPtr pointerToVariant)
{
if (selectedNodes == null || selectedNodes.Count == 0 || pointerToVariant == IntPtr.Zero)
{
return NativeMethods.OLECMDERR_E_NOTSUPPORTED;
}
int idmxStoredMenu = 0;
Guid? menuGuid = null;
foreach (HierarchyNode node in selectedNodes)
{
// We check here whether we have a multiple selection of
// nodes of differing type.
if (idmxStoredMenu == 0)
{
// First time through or single node case
idmxStoredMenu = node.MenuCommandId;
}
else if (idmxStoredMenu != node.MenuCommandId)
{
// We have different node types. Check if any of the nodes is
// the project node and set the menu accordingly.
if (node.MenuCommandId == VsMenus.IDM_VS_CTXT_PROJNODE)
{
idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_PROJITEM;
}
else
{
idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_MULTIITEM;
}
}
if (node.MenuCommandGuid != menuGuid)
if (menuGuid == null)
menuGuid = node.MenuCommandGuid;
else
//We can't handle different menu guids.
return NativeMethods.E_FAIL;
}
if (menuGuid == null)
menuGuid = VsMenus.guidSHLMainMenu;
object variant = Marshal.GetObjectForNativeVariant(pointerToVariant);
UInt32 pointsAsUint = (UInt32)variant;
short x = (short)(pointsAsUint & 0x0000ffff);
short y = (short)((pointsAsUint & 0xffff0000) / 0x10000);
POINTS points = new POINTS();
points.x = x;
points.y = y;
return ShowContextMenu(idmxStoredMenu, menuGuid.Value, points);
}
/// <summary>
/// Shows the specified context menu at a specified location.
/// </summary>
/// <param name="menuId">The context menu ID.</param>
/// <param name="groupGuid">The GUID of the menu group.</param>
/// <param name="points">The location at which to show the menu.</param>
protected virtual int ShowContextMenu(int menuId, Guid menuGroup, POINTS points)
{
IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
Debug.Assert(shell != null, "Could not get the ui shell from the project");
if (shell == null)
{
return VSConstants.E_FAIL;
}
POINTS[] pnts = new POINTS[1];
pnts[0].x = points.x;
pnts[0].y = points.y;
return shell.ShowContextMenu(0, ref menuGroup, menuId, pnts, (Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget)ProjectMgr);
}
#region initiation of command execution
/// <summary>
/// Handles command execution.
/// </summary>
/// <param name="cmdGroup">Unique identifier of the command group</param>
/// <param name="cmd">The command to be executed.</param>
/// <param name="nCmdexecopt">Values describe how the object should execute the command.</param>
/// <param name="pvaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
/// <param name="pvaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmdexecopt")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "pva")]
public virtual int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
{
if (InvalidProject())
{
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
if (cmdGroup == Guid.Empty)
{
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
else if (cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds)
{
switch (cmd)
{
case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick:
case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey:
this.DoDefaultAction();
return VSConstants.S_OK;
case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_CancelLabelEdit:
this.OnCancelLabelEdit();
return VSConstants.S_OK;
}
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
else if (cmdGroup == VsMenus.guidStandardCommandSet97)
{
HierarchyNode nodeToAddTo = this.GetDragTargetHandlerNode();
switch ((VsCommands)cmd)
{
case VsCommands.AddNewItem:
return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddNewItem);
case VsCommands.AddExistingItem:
return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddExistingItem);
case VsCommands.NewFolder:
return nodeToAddTo.AddNewFolder();
case VsCommands.Paste:
return this.ProjectMgr.PasteFromClipboard(this);
}
}
else if (cmdGroup == VsMenus.guidStandardCommandSet2K)
{
switch ((VsCommands2K)cmd)
{
case VsCommands2K.EXCLUDEFROMPROJECT:
return this.ExcludeFromProject();
case VsCommands2K.INCLUDEINPROJECT:
return this.IncludeInProject(true);
case VsCommands2K.PROJSTARTDEBUG:
return this.StartDebug();
}
}
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
#endregion
#region query command handling
/// <summary>
/// Handles command status on a node. Should be overridden by descendant nodes. If a command cannot be handled then the base should be called.
/// </summary>
/// <param name="cmdGroup">A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group.</param>
/// <param name="cmd">The command to query status for.</param>
/// <param name="pCmdText">Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information.</param>
/// <param name="result">An out parameter specifying the QueryStatusResult of the command.</param>
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
public virtual int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
{
if (cmdGroup == VsMenus.guidStandardCommandSet97)
{
switch ((VsCommands)cmd)
{
case VsCommands.AddNewItem:
case VsCommands.AddExistingItem:
if (!IsNonMemberItem) {
result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
return VSConstants.S_OK;
}
break;
}
}
else if (cmdGroup == VsMenus.guidStandardCommandSet2K)
{
// http://social.msdn.microsoft.com/Forums/en/vsx/thread/f348aaed-cdcc-4709-9118-c0fd8b9e154d
if ((VsCommands2K)cmd == VsCommands2K.SHOWALLFILES)
{
if (ProjectMgr.CanShowAllFiles)
{
if (ProjectMgr.IsShowingAllFiles)
{
result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED | QueryStatusResult.LATCHED;
}
else
{
result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
}
}
else
{
result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE;
}
return VSConstants.S_OK;
}
}
return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
}
#endregion
protected virtual int StartDebug()
{
return this.ProjectMgr.StartDebug();
}
public virtual bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation)
{
return this.ProjectMgr.CanProjectDeleteItems;
}
/// <summary>
/// Overwrite this method to tell that you support the default icon for this node.
/// </summary>
/// <returns></returns>
protected virtual bool CanShowDefaultIcon()
{
return false;
}
/// <summary>
/// Performs save as operation for an item after the save as dialog has been processed.
/// </summary>
/// <param name="docData">A pointer to the rdt</param>
/// <param name="newName">The newName of the item</param>
/// <returns></returns>
public virtual int AfterSaveItemAs(IntPtr docData, string newName)
{
throw new NotImplementedException();
}
/// <summary>
/// Invoked when the node receives UIHWCMDID_CancelLabelEdit hierarchy window command, which occurs
/// when user cancels the label editing operation.
/// </summary>
protected virtual void OnCancelLabelEdit()
{
}
/// <summary>
/// The method that does the cleanup.
/// </summary>
/// <param name="disposing">Is the Dispose called by some public member, or it is called by from GC.</param>
protected virtual void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
// This will dispose any subclassed project node that implements IDisposable.
if (this.OleServiceProvider != null)
{
// Dispose the ole service provider object.
this.OleServiceProvider.Dispose();
}
}
this.isDisposed = true;
}
/// <summary>
/// Sets the VSQUERYADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryAddFiles
/// </summary>
/// <param name="files">The files to which an array of VSADDFILEFLAGS has to be specified.</param>
/// <returns></returns>
public virtual VSQUERYADDFILEFLAGS[] GetQueryAddFileFlags(string[] files)
{
if (files == null || files.Length == 0)
{
return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags };
}
VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADDFILEFLAGS[files.Length];
for (int i = 0; i < files.Length; i++)
{
queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags;
}
return queryAddFileFlags;
}
/// <summary>
/// Sets the VSREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnRemoveFiles
/// </summary>
/// <param name="files">The files to which an array of VSREMOVEFILEFLAGS has to be specified.</param>
/// <returns></returns>
public virtual VSREMOVEFILEFLAGS[] GetRemoveFileFlags(string[] files)
{
if (files == null || files.Length == 0)
{
return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags };
}
VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFLAGS[files.Length];
for (int i = 0; i < files.Length; i++)
{
removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags;
}
return removeFileFlags;
}
/// <summary>
/// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles
/// </summary>
/// <param name="files">The files to which an array of VSQUERYREMOVEFILEFLAGS has to be specified.</param>
/// <returns></returns>
public virtual VSQUERYREMOVEFILEFLAGS[] GetQueryRemoveFileFlags(string[] files)
{
if (files == null || files.Length == 0)
{
return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags };
}
VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQUERYREMOVEFILEFLAGS[files.Length];
for (int i = 0; i < files.Length; i++)
{
queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags;
}
return queryRemoveFileFlags;
}
/// <summary>
/// This method should be overridden to provide the list of files and associated flags for source control.
/// </summary>
/// <param name="files">The list of files to be placed under source control.</param>
/// <param name="flags">The flags that are associated to the files.</param>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
public virtual void GetSccFiles(IList<string> files, IList<tagVsSccFilesFlags> flags)
{
if (this.ExcludeNodeFromScc || this.IsNonMemberItem)
{
return;
}
VsUtilities.ArgumentNotNull("files", files);
VsUtilities.ArgumentNotNull("flags", flags);
files.Add(this.GetMkDocument());
tagVsSccFilesFlags flagsToAdd = (this.firstChild != null && (this.firstChild is DependentFileNode)) ? tagVsSccFilesFlags.SFF_HasSpecialFiles : tagVsSccFilesFlags.SFF_NoFlags;
flags.Add(flagsToAdd);
}
/// <summary>
/// This method should be overridden to provide the list of special files and associated flags for source control.
/// </summary>
/// <param name="sccFile">One of the file associated to the node.</param>
/// <param name="files">The list of files to be placed under source control.</param>
/// <param name="flags">The flags that are associated to the files.</param>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "scc")]
public virtual void GetSccSpecialFiles(string sccFile, IList<string> files, IList<tagVsSccFilesFlags> flags)
{
if (this.ExcludeNodeFromScc)
{
return;
}
VsUtilities.ArgumentNotNull("files", files);
VsUtilities.ArgumentNotNull("flags", flags);
}
public void OnInvalidateItems(HierarchyNode parent)
{
if (parent == null)
{
throw new ArgumentNullException("parent");
}
ProjectNode projectNode = this.projectMgr;
if (projectNode == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0)
{
return;
}
foreach (IVsHierarchyEvents sink in projectNode.EventSinks)
{
int result = sink.OnInvalidateItems(parent.hierarchyId);
if (ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL)
{
ErrorHandler.ThrowOnFailure(result);
}
}
}
/// <summary>
/// Delete the item corresponding to the specified path from storage.
/// </summary>
/// <param name="path">Url of the item to delete</param>
public virtual void DeleteFromStorage(string path)
{
}
/// <summary>
/// Determines whether a file change should be ignored or not.
/// </summary>
/// <param name="ignoreFlag">Flag indicating whether or not to ignore changes (true to ignore changes).</param>
public virtual void IgnoreItemFileChanges(bool ignoreFlag)
{
}
/// <summary>
/// Called to determine whether a project item is reloadable.
/// </summary>
/// <returns>True if the project item is reloadable.</returns>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Reloadable")]
public virtual bool IsItemReloadable()
{
return true;
}
/// <summary>
/// Reloads an item.
/// </summary>
/// <param name="reserved">Reserved parameter defined at the IVsPersistHierarchyItem2::ReloadItem parameter.</param>
public virtual void ReloadItem(uint reserved)
{
}
#endregion
#region public methods
/// <summary>
/// Clears the cached node properties so that it will be recreated on the next request.
/// </summary>
public void ResetNodeProperties()
{
nodeProperties = null;
}
public void ExpandItem(EXPANDFLAGS flags)
{
if (ProjectMgr == null || ProjectMgr.Site == null)
{
return;
}
IVsUIHierarchyWindow2 windows = GetUIHierarchyWindow(
ProjectMgr.Site,
new Guid(ToolWindowGuids80.SolutionExplorer)) as IVsUIHierarchyWindow2;
if (windows == null)
{
return;
}
ErrorHandler.ThrowOnFailure(windows.ExpandItem(ProjectMgr.GetOuterInterface<IVsUIHierarchy>(), ID, flags));
}
public bool GetIsExpanded() {
if (ProjectMgr == null || ProjectMgr.Site == null)
{
return false;
}
IVsUIHierarchyWindow2 windows = GetUIHierarchyWindow(
ProjectMgr.Site,
new Guid(ToolWindowGuids80.SolutionExplorer)) as IVsUIHierarchyWindow2;
if (windows == null)
{
return false;
}
uint state;
if (ErrorHandler.Succeeded(windows.GetItemState(ProjectMgr.GetOuterInterface<IVsUIHierarchy>(),
ID,
(uint)__VSHIERARCHYITEMSTATE.HIS_Expanded,
out state)))
{
return state != 0;
}
return false;
}
/// <summary>
/// Same as VsShellUtilities.GetUIHierarchyWindow, but it doesn't contain a useless cast to IVsWindowPane
/// which fails on Dev10 with the solution explorer window.
/// </summary>
private static IVsUIHierarchyWindow GetUIHierarchyWindow(System.IServiceProvider serviceProvider, Guid guidPersistenceSlot)
{
VsUtilities.ArgumentNotNull("serviceProvider", serviceProvider);
IVsUIShell service = serviceProvider.GetService(typeof(SVsUIShell)) as IVsUIShell;
if (service == null)
{
throw new InvalidOperationException();
}
object pvar = null;
IVsWindowFrame ppWindowFrame = null;
if (ErrorHandler.Succeeded(service.FindToolWindow(0, ref guidPersistenceSlot, out ppWindowFrame)) &&
ppWindowFrame != null &&
ErrorHandler.Succeeded(ppWindowFrame.GetProperty(-3001, out pvar)))
{
return (IVsUIHierarchyWindow)pvar;
}
return null;
}
/// <summary>
/// AddChild - add a node, sorted in the right location.
/// </summary>
/// <param name="node">The node to add.</param>
public virtual void AddChild(HierarchyNode node)
{
VsUtilities.ArgumentNotNull("node", node);
// make sure the node is in the map.
Object nodeWithSameID = this.projectMgr.ItemIdMap[node.hierarchyId];
if (!Object.ReferenceEquals(node, nodeWithSameID as HierarchyNode))
{
if (nodeWithSameID == null && node.ID <= this.ProjectMgr.ItemIdMap.Count)
{ // reuse our hierarchy id if possible.
this.projectMgr.ItemIdMap.SetAt(node.hierarchyId, this);
}
else
{
throw new InvalidOperationException();
}
}
HierarchyNode previous = null;
for (HierarchyNode n = this.firstChild; n != null; n = n.nextSibling)
{
if (this.ProjectMgr.CompareNodes(node, n) > 0) break;
previous = n;
}
// insert "node" after "previous".
if (previous != null)
{
node.nextSibling = previous.nextSibling;
previous.nextSibling = node;
}
else
{
node.nextSibling = this.firstChild;
this.firstChild = node;
}
node.parentNode = this;
ProjectMgr.OnItemAdded(this, node);
#if DEV10
// Dev10 won't check the IsHiddenItem flag when we add an item, and it'll just
// make it visible no matter what. So we turn around and invalidate our parent
// so it'll rescan the children items and see that we're not visible.
if (!node.IsVisible)
{
ProjectMgr.OnInvalidateItems(this);
}
#endif
}
public object GetService(Type type)
{
VsUtilities.ArgumentNotNull("type", type);
if (this.projectMgr == null || this.projectMgr.Site == null) return null;
return this.projectMgr.Site.GetService(type);
}
#endregion
#region IDisposable
/// <summary>
/// The IDispose interface Dispose method for disposing the object determinastically.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
public virtual void Close()
{
DocumentManager manager = this.GetDocumentManager();
try
{
if (manager != null)
{
manager.Close(__FRAMECLOSE.FRAMECLOSE_PromptSave);
}
}
catch { }
finally
{
this.Dispose(true);
}
}
public uint HierarchyId {
get {
return hierarchyId;
}
}
#region helper methods
/// <summary>
/// Searches the immediate children of this node for a node which matches the specified predicate.
/// </summary>
public HierarchyNode FindImmediateChild(Func<HierarchyNode, bool> predicate) {
for (HierarchyNode child = this.firstChild; child != null; child = child.NextSibling) {
if (predicate(child)) {
return child;
}
}
return null;
}
/// <summary>
/// Searches the immediate children of this node for a file who's moniker's filename (w/o path) matches
/// the requested name.
/// </summary>
public HierarchyNode FindImmediateChildByName(string name) {
for (HierarchyNode child = this.firstChild; child != null; child = child.NextSibling) {
string filename = Path.GetFileName(CommonUtils.TrimEndSeparator(child.GetMkDocument()));
if (String.Equals(filename, name, StringComparison.OrdinalIgnoreCase)) {
return child;
}
}
return null;
}
/// <summary>
/// Recursively find all nodes of type T
/// </summary>
/// <typeparam name="T">The type of hierachy node being serched for</typeparam>
/// <param name="nodes">A list of nodes of type T</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public void FindNodesOfType<T>(List<T> nodes)
where T : HierarchyNode
{
for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling)
{
T nodeAsT = n as T;
if (nodeAsT != null)
{
nodes.Add(nodeAsT);
}
n.FindNodesOfType<T>(nodes);
}
}
/// <summary>
/// Recursively find all nodes of type T
/// </summary>
/// <typeparam name="T">The type of hierachy node being serched for</typeparam>
/// <param name="nodes">A list of nodes of type T</param>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public IEnumerable<T> EnumNodesOfType<T>()
where T : HierarchyNode
{
for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling)
{
T nodeAsT = n as T;
if (nodeAsT != null)
{
yield return nodeAsT;
}
foreach(var node in n.EnumNodesOfType<T>()) {
yield return node;
}
}
}
#endregion
private bool InvalidProject()
{
return this.projectMgr == null || this.projectMgr.IsClosed;
}
#region nested types
/// <summary>
/// DropEffect as defined in oleidl.h
/// </summary>
public enum DropEffect {
None,
Copy = 1,
Move = 2,
Link = 4
};
#endregion
}
}