PageRenderTime 66ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/Tools/IronStudio/IronStudio/VisualStudio/Project/HierarchyNode.cs

http://github.com/IronLanguages/main
C# | 2008 lines | 1395 code | 229 blank | 384 comment | 265 complexity | e153a4817df2a5a660a7983afc61f827 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. * ***************************************************************************/
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.Diagnostics;
  18. using System.Diagnostics.CodeAnalysis;
  19. using System.Globalization;
  20. using System.IO;
  21. using System.Runtime.InteropServices;
  22. using System.Text;
  23. using Microsoft.VisualStudio;
  24. using Microsoft.VisualStudio.OLE.Interop;
  25. using Microsoft.VisualStudio.Shell;
  26. //#define CCI_TRACING
  27. using Microsoft.VisualStudio.Shell.Interop;
  28. using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
  29. using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants;
  30. using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID;
  31. using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID;
  32. namespace Microsoft.VisualStudio.Project
  33. {
  34. /// <summary>
  35. /// 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
  36. /// can itself be a hierarchy.
  37. /// </summary>
  38. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), CLSCompliant(false), ComVisible(true)]
  39. public abstract class HierarchyNode :
  40. IVsUIHierarchy,
  41. IVsPersistHierarchyItem2,
  42. Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget,
  43. IVsHierarchyDropDataSource2,
  44. IVsHierarchyDropDataSource,
  45. IVsHierarchyDropDataTarget,
  46. IVsHierarchyDeleteHandler,
  47. IDisposable
  48. //, IVsBuildStatusCallback
  49. {
  50. #region nested types
  51. /// <summary>
  52. /// DropEffect as defined in oleidl.h
  53. /// </summary>
  54. internal enum DropEffect
  55. {
  56. None,
  57. Copy = 1,
  58. Move = 2,
  59. Link = 4
  60. };
  61. #endregion
  62. #region Events
  63. internal event EventHandler<HierarchyNodeEventArgs> OnChildAdded
  64. {
  65. add { onChildAdded += value; }
  66. remove { onChildAdded -= value; }
  67. }
  68. internal event EventHandler<HierarchyNodeEventArgs> OnChildRemoved
  69. {
  70. add { onChildRemoved += value; }
  71. remove { onChildRemoved -= value; }
  72. }
  73. #endregion
  74. #region static/const fields
  75. public static readonly Guid SolutionExplorer = new Guid(EnvDTE.Constants.vsWindowKindSolutionExplorer);
  76. public const int NoImage = -1;
  77. #if DEBUG
  78. internal static int LastTracedProperty;
  79. #endif
  80. #endregion
  81. #region fields
  82. private EventSinkCollection hierarchyEventSinks = new EventSinkCollection();
  83. private ProjectNode projectMgr;
  84. private ProjectElement itemNode;
  85. private HierarchyNode parentNode;
  86. private HierarchyNode nextSibling;
  87. private HierarchyNode firstChild;
  88. private HierarchyNode lastChild;
  89. private bool isExpanded;
  90. private uint hierarchyId;
  91. private uint docCookie;
  92. private bool hasDesigner;
  93. private string virtualNodeName = String.Empty; // Only used by virtual nodes
  94. private IVsHierarchy parentHierarchy;
  95. private int parentHierarchyItemId;
  96. private NodeProperties nodeProperties;
  97. private OleServiceProvider oleServiceProvider = new OleServiceProvider();
  98. private bool excludeNodeFromScc;
  99. private EventHandler<HierarchyNodeEventArgs> onChildAdded;
  100. private EventHandler<HierarchyNodeEventArgs> onChildRemoved;
  101. private bool hasParentNodeNameRelation;
  102. private List<HierarchyNode> itemsDraggedOrCutOrCopied;
  103. private bool sourceDraggedOrCutOrCopied;
  104. /// <summary>
  105. /// Has the object been disposed.
  106. /// </summary>
  107. /// <devremark>We will not specify a property for isDisposed, rather it is expected that the a private flag is defined
  108. /// 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>
  109. private bool isDisposed;
  110. #endregion
  111. #region abstract properties
  112. /// <summary>
  113. /// The URL of the node.
  114. /// </summary>
  115. /// <value></value>
  116. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
  117. public abstract string Url
  118. {
  119. get;
  120. }
  121. /// <summary>
  122. /// The Caption of the node.
  123. /// </summary>
  124. /// <value></value>
  125. public abstract string Caption
  126. {
  127. get;
  128. }
  129. /// <summary>
  130. /// The item type guid associated to a node.
  131. /// </summary>
  132. /// <value></value>
  133. public abstract Guid ItemTypeGuid
  134. {
  135. get;
  136. }
  137. #endregion
  138. #region virtual properties
  139. /// <summary>
  140. /// Defines a string that is used to separate the name relation from the extension
  141. /// </summary>
  142. public virtual string NameRelationSeparator
  143. {
  144. get
  145. {
  146. return ".";
  147. }
  148. }
  149. public virtual int MenuCommandId
  150. {
  151. get { return VsMenus.IDM_VS_CTXT_NOCOMMANDS; }
  152. }
  153. /// <summary>
  154. /// Return an imageindex
  155. /// </summary>
  156. /// <returns></returns>
  157. public virtual int ImageIndex
  158. {
  159. get { return NoImage; }
  160. }
  161. /// <summary>
  162. /// Return an state icon index
  163. /// </summary>
  164. /// <returns></returns>
  165. /// <summary>
  166. /// Sets the state icon for a file.
  167. /// </summary>
  168. public virtual VsStateIcon StateIconIndex
  169. {
  170. get
  171. {
  172. if(!this.ExcludeNodeFromScc)
  173. {
  174. IVsSccManager2 sccManager = this.ProjectMgr.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2;
  175. if(sccManager != null)
  176. {
  177. VsStateIcon[] statIcons = new VsStateIcon[1] { VsStateIcon.STATEICON_NOSTATEICON };
  178. uint[] sccStatus = new uint[1] { 0 };
  179. // Get the glyph from the scc manager. Note that it will fail in command line
  180. // scenarios.
  181. if(ErrorHandler.Succeeded(sccManager.GetSccGlyph(1, new string[] { this.GetMkDocument() }, statIcons, sccStatus)))
  182. {
  183. return statIcons[0];
  184. }
  185. }
  186. }
  187. return VsStateIcon.STATEICON_NOSTATEICON;
  188. }
  189. }
  190. /// <summary>
  191. /// Defines whether a node can execute a command if in selection.
  192. /// </summary>
  193. public virtual bool CanExecuteCommand
  194. {
  195. get
  196. {
  197. return true;
  198. }
  199. }
  200. /// <summary>
  201. /// Used to determine the sort order of different node types
  202. /// in the solution explorer window.
  203. /// Nodes with the same priorities are sorted based on their captions.
  204. /// </summary>
  205. public virtual int SortPriority
  206. {
  207. get { return DefaultSortOrderNode.HierarchyNode; }
  208. }
  209. /// <summary>
  210. /// Defines the properties attached to this node.
  211. /// </summary>
  212. public virtual NodeProperties NodeProperties
  213. {
  214. get
  215. {
  216. if(null == nodeProperties)
  217. {
  218. nodeProperties = CreatePropertiesObject();
  219. }
  220. return this.nodeProperties;
  221. }
  222. }
  223. /// <summary>
  224. /// Returns an object that is a special view over this object; this is the value
  225. /// returned by the Object property of the automation objects.
  226. /// </summary>
  227. internal virtual object Object
  228. {
  229. get { return this; }
  230. }
  231. #endregion
  232. #region properties
  233. public OleServiceProvider OleServiceProvider
  234. {
  235. get
  236. {
  237. return this.oleServiceProvider;
  238. }
  239. }
  240. [System.ComponentModel.BrowsableAttribute(false)]
  241. public ProjectNode ProjectMgr
  242. {
  243. get
  244. {
  245. return this.projectMgr;
  246. }
  247. set
  248. {
  249. this.projectMgr = value;
  250. }
  251. }
  252. [System.ComponentModel.BrowsableAttribute(false)]
  253. public HierarchyNode NextSibling
  254. {
  255. get
  256. {
  257. return this.nextSibling;
  258. }
  259. set
  260. {
  261. this.nextSibling = value;
  262. }
  263. }
  264. [System.ComponentModel.BrowsableAttribute(false)]
  265. public HierarchyNode FirstChild
  266. {
  267. get
  268. {
  269. return this.firstChild;
  270. }
  271. set
  272. {
  273. this.firstChild = value;
  274. }
  275. }
  276. [System.ComponentModel.BrowsableAttribute(false)]
  277. public HierarchyNode LastChild
  278. {
  279. get
  280. {
  281. return this.lastChild;
  282. }
  283. set
  284. {
  285. this.lastChild = value;
  286. }
  287. }
  288. [System.ComponentModel.BrowsableAttribute(false)]
  289. public HierarchyNode Parent
  290. {
  291. get
  292. {
  293. return this.parentNode;
  294. }
  295. set
  296. {
  297. this.parentNode = value;
  298. }
  299. }
  300. [System.ComponentModel.BrowsableAttribute(false)]
  301. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")]
  302. public uint ID
  303. {
  304. get
  305. {
  306. return this.hierarchyId;
  307. }
  308. internal set
  309. {
  310. this.hierarchyId = value;
  311. }
  312. }
  313. [System.ComponentModel.BrowsableAttribute(false)]
  314. public ProjectElement ItemNode
  315. {
  316. get
  317. {
  318. return itemNode;
  319. }
  320. set
  321. {
  322. itemNode = value;
  323. }
  324. }
  325. [System.ComponentModel.BrowsableAttribute(false)]
  326. public bool HasDesigner
  327. {
  328. get
  329. {
  330. return this.hasDesigner;
  331. }
  332. set { this.hasDesigner = value; }
  333. }
  334. [System.ComponentModel.BrowsableAttribute(false)]
  335. public bool IsExpanded
  336. {
  337. get
  338. {
  339. return this.isExpanded;
  340. }
  341. set { this.isExpanded = value; }
  342. }
  343. public string VirtualNodeName
  344. {
  345. get
  346. {
  347. return this.virtualNodeName;
  348. }
  349. set
  350. {
  351. this.virtualNodeName = value;
  352. }
  353. }
  354. [System.ComponentModel.BrowsableAttribute(false)]
  355. public HierarchyNode PreviousSibling
  356. {
  357. get
  358. {
  359. if(this.parentNode == null) return null;
  360. HierarchyNode prev = null;
  361. for(HierarchyNode child = this.parentNode.firstChild; child != null; child = child.nextSibling)
  362. {
  363. if(child == this)
  364. break;
  365. prev = child;
  366. }
  367. return prev;
  368. }
  369. }
  370. public uint DocCookie
  371. {
  372. get
  373. {
  374. return this.docCookie;
  375. }
  376. set
  377. {
  378. this.docCookie = value;
  379. }
  380. }
  381. /// <summary>
  382. /// Specifies if a Node is under source control.
  383. /// </summary>
  384. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
  385. public bool ExcludeNodeFromScc
  386. {
  387. get
  388. {
  389. return this.excludeNodeFromScc;
  390. }
  391. set
  392. {
  393. this.excludeNodeFromScc = value;
  394. }
  395. }
  396. /// <summary>
  397. /// Defines if a node a name relation to its parent node
  398. ///
  399. /// </summary>
  400. public bool HasParentNodeNameRelation
  401. {
  402. get
  403. {
  404. return this.hasParentNodeNameRelation;
  405. }
  406. set
  407. {
  408. this.hasParentNodeNameRelation = value;
  409. }
  410. }
  411. protected bool SourceDraggedOrCutOrCopied
  412. {
  413. get
  414. {
  415. return this.sourceDraggedOrCutOrCopied;
  416. }
  417. set
  418. {
  419. this.sourceDraggedOrCutOrCopied = value;
  420. }
  421. }
  422. protected IList<HierarchyNode> ItemsDraggedOrCutOrCopied
  423. {
  424. get
  425. {
  426. return this.itemsDraggedOrCutOrCopied;
  427. }
  428. }
  429. #endregion
  430. #region ctors
  431. protected HierarchyNode()
  432. {
  433. this.IsExpanded = true;
  434. }
  435. protected HierarchyNode(ProjectNode root, ProjectElement element)
  436. {
  437. if (root == null)
  438. {
  439. throw new ArgumentNullException("root");
  440. }
  441. this.projectMgr = root;
  442. this.itemNode = element;
  443. this.hierarchyId = this.projectMgr.ItemIdMap.Add(this);
  444. this.oleServiceProvider.AddService(typeof(IVsHierarchy), root, false);
  445. }
  446. /// <summary>
  447. /// Overloaded ctor.
  448. /// </summary>
  449. /// <param name="root"></param>
  450. protected HierarchyNode(ProjectNode root)
  451. {
  452. if (root == null)
  453. {
  454. throw new ArgumentNullException("root");
  455. }
  456. this.projectMgr = root;
  457. this.itemNode = new ProjectElement(this.projectMgr, null, true);
  458. this.hierarchyId = this.projectMgr.ItemIdMap.Add(this);
  459. this.oleServiceProvider.AddService(typeof(IVsHierarchy), root, false);
  460. }
  461. #endregion
  462. #region static methods
  463. /// <summary>
  464. /// Get the outer IVsHierarchy implementation.
  465. /// This is used for scenario where a flavor may be modifying the behavior
  466. /// </summary>
  467. internal static IVsHierarchy GetOuterHierarchy(HierarchyNode node)
  468. {
  469. IVsHierarchy hierarchy = null;
  470. // The hierarchy of a node is its project node hierarchy
  471. IntPtr projectUnknown = Marshal.GetIUnknownForObject(node.projectMgr);
  472. try
  473. {
  474. hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(projectUnknown, typeof(IVsHierarchy));
  475. }
  476. finally
  477. {
  478. if(projectUnknown != IntPtr.Zero)
  479. {
  480. Marshal.Release(projectUnknown);
  481. }
  482. }
  483. return hierarchy;
  484. }
  485. #endregion
  486. #region virtual methods
  487. /// <summary>
  488. /// Creates an object derived from NodeProperties that will be used to expose properties
  489. /// spacific for this object to the property browser.
  490. /// </summary>
  491. /// <returns></returns>
  492. protected virtual NodeProperties CreatePropertiesObject()
  493. {
  494. return null;
  495. }
  496. /// <summary>
  497. /// Return an iconhandle
  498. /// </summary>
  499. /// <param name="open"></param>
  500. /// <returns></returns>
  501. public virtual object GetIconHandle(bool open)
  502. {
  503. return null;
  504. }
  505. /// <summary>
  506. /// AddChild - add a node, sorted in the right location.
  507. /// </summary>
  508. /// <param name="node">The node to add.</param>
  509. public virtual void AddChild(HierarchyNode node)
  510. {
  511. if(node == null)
  512. {
  513. throw new ArgumentNullException("node");
  514. }
  515. // make sure the node is in the map.
  516. Object nodeWithSameID = this.projectMgr.ItemIdMap[node.hierarchyId];
  517. if(!Object.ReferenceEquals(node, nodeWithSameID as HierarchyNode))
  518. {
  519. if(nodeWithSameID == null && node.ID <= this.ProjectMgr.ItemIdMap.Count)
  520. { // reuse our hierarchy id if possible.
  521. this.projectMgr.ItemIdMap.SetAt(node.hierarchyId, this);
  522. }
  523. else
  524. {
  525. throw new InvalidOperationException();
  526. }
  527. }
  528. HierarchyNode previous = null;
  529. for(HierarchyNode n = this.firstChild; n != null; n = n.nextSibling)
  530. {
  531. if(this.ProjectMgr.CompareNodes(node, n) > 0) break;
  532. previous = n;
  533. }
  534. // insert "node" after "previous".
  535. if(previous != null)
  536. {
  537. node.nextSibling = previous.nextSibling;
  538. previous.nextSibling = node;
  539. if(previous == this.lastChild)
  540. {
  541. this.lastChild = node;
  542. }
  543. }
  544. else
  545. {
  546. if(this.lastChild == null)
  547. {
  548. this.lastChild = node;
  549. }
  550. node.nextSibling = this.firstChild;
  551. this.firstChild = node;
  552. }
  553. node.parentNode = this;
  554. this.OnItemAdded(this, node);
  555. }
  556. /// <summary>
  557. /// Removes a node from the hierarchy.
  558. /// </summary>
  559. /// <param name="node">The node to remove.</param>
  560. public virtual void RemoveChild(HierarchyNode node)
  561. {
  562. if(node == null)
  563. {
  564. throw new ArgumentNullException("node");
  565. }
  566. this.projectMgr.ItemIdMap.Remove(node);
  567. HierarchyNode last = null;
  568. for(HierarchyNode n = this.firstChild; n != null; n = n.nextSibling)
  569. {
  570. if(n == node)
  571. {
  572. if(last != null)
  573. {
  574. last.nextSibling = n.nextSibling;
  575. }
  576. if(n == this.lastChild)
  577. {
  578. if(last == this.lastChild)
  579. {
  580. this.lastChild = null;
  581. }
  582. else
  583. {
  584. this.lastChild = last;
  585. }
  586. }
  587. if(n == this.firstChild)
  588. {
  589. this.firstChild = n.nextSibling;
  590. }
  591. return;
  592. }
  593. last = n;
  594. }
  595. throw new InvalidOperationException("Node not found");
  596. }
  597. /// <summary>
  598. /// Returns an automation object representing this node
  599. /// </summary>
  600. /// <returns>The automation object</returns>
  601. public virtual object GetAutomationObject()
  602. {
  603. return new Automation.OAProjectItem<HierarchyNode>(this.projectMgr.GetAutomationObject() as Automation.OAProject, this);
  604. }
  605. /// <summary>
  606. /// Returns a property object based on a property id
  607. /// </summary>
  608. /// <param name="propId">the property id of the property requested</param>
  609. /// <returns>the property object requested</returns>
  610. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
  611. public virtual object GetProperty(int propId)
  612. {
  613. object result = null;
  614. switch((__VSHPROPID)propId)
  615. {
  616. case __VSHPROPID.VSHPROPID_Expandable:
  617. result = (this.firstChild != null);
  618. break;
  619. case __VSHPROPID.VSHPROPID_Caption:
  620. result = this.Caption;
  621. break;
  622. case __VSHPROPID.VSHPROPID_Name:
  623. result = this.Caption;
  624. break;
  625. case __VSHPROPID.VSHPROPID_ExpandByDefault:
  626. result = false;
  627. break;
  628. case __VSHPROPID.VSHPROPID_IconImgList:
  629. result = this.ProjectMgr.ImageHandler.ImageList.Handle;
  630. break;
  631. case __VSHPROPID.VSHPROPID_OpenFolderIconIndex:
  632. case __VSHPROPID.VSHPROPID_IconIndex:
  633. int index = this.ImageIndex;
  634. if(index != NoImage)
  635. {
  636. result = index;
  637. }
  638. break;
  639. case __VSHPROPID.VSHPROPID_StateIconIndex:
  640. result = (int)this.StateIconIndex;
  641. break;
  642. case __VSHPROPID.VSHPROPID_IconHandle:
  643. result = GetIconHandle(false);
  644. break;
  645. case __VSHPROPID.VSHPROPID_OpenFolderIconHandle:
  646. result = GetIconHandle(true);
  647. break;
  648. case __VSHPROPID.VSHPROPID_NextVisibleSibling:
  649. goto case __VSHPROPID.VSHPROPID_NextSibling;
  650. case __VSHPROPID.VSHPROPID_NextSibling:
  651. result = (int)((this.nextSibling != null) ? this.nextSibling.hierarchyId : VSConstants.VSITEMID_NIL);
  652. break;
  653. case __VSHPROPID.VSHPROPID_FirstChild:
  654. goto case __VSHPROPID.VSHPROPID_FirstVisibleChild;
  655. case __VSHPROPID.VSHPROPID_FirstVisibleChild:
  656. result = (int)((this.firstChild != null) ? this.firstChild.hierarchyId : VSConstants.VSITEMID_NIL);
  657. break;
  658. case __VSHPROPID.VSHPROPID_Parent:
  659. if(null == this.parentNode)
  660. {
  661. unchecked { result = new IntPtr((int)VSConstants.VSITEMID_NIL); }
  662. }
  663. else
  664. {
  665. result = new IntPtr((int)this.parentNode.hierarchyId); // see bug 176470
  666. }
  667. break;
  668. case __VSHPROPID.VSHPROPID_ParentHierarchyItemid:
  669. if(parentHierarchy != null)
  670. {
  671. result = (IntPtr)parentHierarchyItemId; // VS requires VT_I4 | VT_INT_PTR
  672. }
  673. break;
  674. case __VSHPROPID.VSHPROPID_ParentHierarchy:
  675. result = parentHierarchy;
  676. break;
  677. case __VSHPROPID.VSHPROPID_Root:
  678. result = Marshal.GetIUnknownForObject(this.projectMgr);
  679. break;
  680. case __VSHPROPID.VSHPROPID_Expanded:
  681. result = this.isExpanded;
  682. break;
  683. case __VSHPROPID.VSHPROPID_BrowseObject:
  684. result = this.NodeProperties;
  685. if(result != null) result = new DispatchWrapper(result);
  686. break;
  687. case __VSHPROPID.VSHPROPID_EditLabel:
  688. if(this.ProjectMgr != null && !this.ProjectMgr.IsClosed && !this.ProjectMgr.IsCurrentStateASuppressCommandsMode())
  689. {
  690. result = GetEditLabel();
  691. }
  692. break;
  693. case __VSHPROPID.VSHPROPID_SaveName:
  694. //SaveName is the name shown in the Save and the Save Changes dialog boxes.
  695. result = this.Caption;
  696. break;
  697. case __VSHPROPID.VSHPROPID_ItemDocCookie:
  698. if(this.docCookie != 0) return (IntPtr)this.docCookie; //cast to IntPtr as some callers expect VT_INT
  699. break;
  700. case __VSHPROPID.VSHPROPID_ExtObject:
  701. result = GetAutomationObject();
  702. break;
  703. }
  704. __VSHPROPID2 id2 = (__VSHPROPID2)propId;
  705. switch(id2)
  706. {
  707. case __VSHPROPID2.VSHPROPID_NoDefaultNestedHierSorting:
  708. return true; // We are doing the sorting ourselves through VSHPROPID_FirstChild and VSHPROPID_NextSibling
  709. case __VSHPROPID2.VSHPROPID_BrowseObjectCATID:
  710. {
  711. // If there is a browse object and it is a NodeProperties, then get it's CATID
  712. object browseObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_BrowseObject);
  713. if(browseObject != null)
  714. {
  715. if(browseObject is DispatchWrapper)
  716. browseObject = ((DispatchWrapper)browseObject).WrappedObject;
  717. result = this.ProjectMgr.GetCATIDForType(browseObject.GetType()).ToString("B");
  718. if(String.CompareOrdinal(result as string, Guid.Empty.ToString("B")) == 0)
  719. result = null;
  720. }
  721. break;
  722. }
  723. case __VSHPROPID2.VSHPROPID_ExtObjectCATID:
  724. {
  725. // If there is a extensibility object and it is a NodeProperties, then get it's CATID
  726. object extObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_ExtObject);
  727. if(extObject != null)
  728. {
  729. if(extObject is DispatchWrapper)
  730. extObject = ((DispatchWrapper)extObject).WrappedObject;
  731. result = this.ProjectMgr.GetCATIDForType(extObject.GetType()).ToString("B");
  732. if(String.CompareOrdinal(result as string, Guid.Empty.ToString("B")) == 0)
  733. result = null;
  734. }
  735. break;
  736. }
  737. }
  738. #if DEBUG
  739. if(propId != LastTracedProperty)
  740. {
  741. string trailer = (result == null) ? "null" : result.ToString();
  742. CCITracing.TraceCall(this.hierarchyId + "," + propId.ToString() + " = " + trailer);
  743. LastTracedProperty = propId; // some basic filtering here...
  744. }
  745. #endif
  746. return result;
  747. }
  748. /// <summary>
  749. /// Sets the value of a property for a given property id
  750. /// </summary>
  751. /// <param name="propid">the property id of the property to be set</param>
  752. /// <param name="value">value of the property</param>
  753. /// <returns>S_OK if succeeded</returns>
  754. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
  755. public virtual int SetProperty(int propid, object value)
  756. {
  757. __VSHPROPID id = (__VSHPROPID)propid;
  758. CCITracing.TraceCall(this.hierarchyId + "," + id.ToString());
  759. switch(id)
  760. {
  761. case __VSHPROPID.VSHPROPID_Expanded:
  762. this.isExpanded = (bool)value;
  763. break;
  764. case __VSHPROPID.VSHPROPID_ParentHierarchy:
  765. parentHierarchy = (IVsHierarchy)value;
  766. break;
  767. case __VSHPROPID.VSHPROPID_ParentHierarchyItemid:
  768. parentHierarchyItemId = (int)value;
  769. break;
  770. case __VSHPROPID.VSHPROPID_EditLabel:
  771. return SetEditLabel((string)value);
  772. default:
  773. CCITracing.TraceCall(" unhandled");
  774. break;
  775. }
  776. return VSConstants.S_OK;
  777. }
  778. /// <summary>
  779. /// Get a guid property
  780. /// </summary>
  781. /// <param name="propid">property id for the guid property requested</param>
  782. /// <param name="guid">the requested guid</param>
  783. /// <returns>S_OK if succeded</returns>
  784. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
  785. public virtual int GetGuidProperty(int propid, out Guid guid)
  786. {
  787. guid = Guid.Empty;
  788. if(propid == (int)__VSHPROPID.VSHPROPID_TypeGuid)
  789. {
  790. guid = this.ItemTypeGuid;
  791. }
  792. if(guid.CompareTo(Guid.Empty) == 0)
  793. {
  794. return VSConstants.DISP_E_MEMBERNOTFOUND;
  795. }
  796. return VSConstants.S_OK;
  797. }
  798. /// <summary>
  799. /// Set a guid property.
  800. /// </summary>
  801. /// <param name="propid">property id of the guid property to be set</param>
  802. /// <param name="guid">the guid to be set</param>
  803. /// <returns>E_NOTIMPL</returns>
  804. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")]
  805. public virtual int SetGuidProperty(int propid, ref Guid guid)
  806. {
  807. return VSConstants.E_NOTIMPL;
  808. }
  809. /// <summary>
  810. /// Called by the shell when a node has been renamed from the GUI
  811. /// </summary>
  812. /// <param name="label"></param>
  813. /// <returns>E_NOTIMPL</returns>
  814. public virtual int SetEditLabel(string label)
  815. {
  816. return VSConstants.E_NOTIMPL;
  817. }
  818. /// <summary>
  819. /// Called by the shell to get the node caption when the user tries to rename from the GUI
  820. /// </summary>
  821. /// <returns>the node cation</returns>
  822. public virtual string GetEditLabel()
  823. {
  824. return this.Caption;
  825. }
  826. /// <summary>
  827. /// This method is called by the interface method GetMkDocument to specify the item moniker.
  828. /// </summary>
  829. /// <returns>The moniker for this item</returns>
  830. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Mk")]
  831. public virtual string GetMkDocument()
  832. {
  833. return String.Empty;
  834. }
  835. /// <summary>
  836. /// Removes items from the hierarchy. Project overwrites this
  837. /// </summary>
  838. /// <param name="removeFromStorage"></param>
  839. public virtual void Remove(bool removeFromStorage)
  840. {
  841. string documentToRemove = this.GetMkDocument();
  842. // Ask Document tracker listeners if we can remove the item.
  843. string[] filesToBeDeleted = new string[1] { documentToRemove };
  844. VSQUERYREMOVEFILEFLAGS[] queryRemoveFlags = this.GetQueryRemoveFileFlags(filesToBeDeleted);
  845. if(!this.ProjectMgr.Tracker.CanRemoveItems(filesToBeDeleted, queryRemoveFlags))
  846. {
  847. return;
  848. }
  849. // Close the document if it has a manager.
  850. DocumentManager manager = this.GetDocumentManager();
  851. if(manager != null)
  852. {
  853. if(manager.Close(!removeFromStorage ? __FRAMECLOSE.FRAMECLOSE_PromptSave : __FRAMECLOSE.FRAMECLOSE_NoSave) == VSConstants.E_ABORT)
  854. {
  855. // User cancelled operation in message box.
  856. return;
  857. }
  858. }
  859. // Check out the project file.
  860. if(!this.ProjectMgr.QueryEditProjectFile(false))
  861. {
  862. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  863. }
  864. // Notify hierarchy event listeners that the file is going to be removed.
  865. OnItemDeleted();
  866. // Remove child if any before removing from the hierarchy
  867. for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling)
  868. {
  869. child.Remove(removeFromStorage);
  870. }
  871. // the project node has no parentNode
  872. if(this.parentNode != null)
  873. {
  874. // Remove from the Hierarchy
  875. this.parentNode.RemoveChild(this);
  876. }
  877. // We save here the path to delete since this.Url might call the Include which will be deleted by the RemoveFromProjectFile call.
  878. string pathToDelete = this.GetMkDocument();
  879. this.itemNode.RemoveFromProjectFile();
  880. if(removeFromStorage)
  881. {
  882. this.DeleteFromStorage(pathToDelete);
  883. }
  884. // Close the document window if opened.
  885. CloseDocumentWindow(this);
  886. // Notify document tracker listeners that we have removed the item.
  887. VSREMOVEFILEFLAGS[] removeFlags = this.GetRemoveFileFlags(filesToBeDeleted);
  888. Debug.Assert(removeFlags != null, "At least an empty array should be returned for the GetRemoveFileFlags");
  889. this.ProjectMgr.Tracker.OnItemRemoved(documentToRemove, removeFlags[0]);
  890. // Notify hierarchy event listeners that we have removed the item
  891. if(null != this.parentNode.onChildRemoved)
  892. {
  893. HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this);
  894. parentNode.onChildRemoved(parentNode, args);
  895. }
  896. // Notify hierarchy event listeners that items have been invalidated
  897. OnInvalidateItems(this.parentNode);
  898. // Dispose the node now that is deleted.
  899. this.Dispose(true);
  900. }
  901. /// <summary>
  902. /// Returns the relational name which is defined as the first part of the caption until indexof NameRelationSeparator
  903. /// </summary>
  904. public virtual string GetRelationalName()
  905. {
  906. //Get the first part of the caption
  907. string[] partsOfParent = this.Caption.Split(new string[] { this.NameRelationSeparator }, StringSplitOptions.None);
  908. return partsOfParent[0];
  909. }
  910. /// <summary>
  911. /// Returns the 'extension' of the relational name
  912. /// e.g. form1.resx returns .resx, form1.designer.cs returns .designer.cs
  913. /// </summary>
  914. /// <returns>The extension</returns>
  915. public virtual string GetRelationNameExtension()
  916. {
  917. return this.Caption.Substring(this.Caption.IndexOf(this.NameRelationSeparator, StringComparison.Ordinal));
  918. }
  919. /// <summary>
  920. /// Close open document frame for a specific node.
  921. /// </summary>
  922. protected void CloseDocumentWindow(HierarchyNode node)
  923. {
  924. if (node == null)
  925. {
  926. throw new ArgumentNullException("node");
  927. }
  928. // We walk the RDT looking for all running documents attached to this hierarchy and itemid. There
  929. // are cases where there may be two different editors (not views) open on the same document.
  930. IEnumRunningDocuments pEnumRdt;
  931. IVsRunningDocumentTable pRdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable;
  932. if(pRdt == null)
  933. {
  934. throw new InvalidOperationException();
  935. }
  936. if(ErrorHandler.Succeeded(pRdt.GetRunningDocumentsEnum(out pEnumRdt)))
  937. {
  938. uint[] cookie = new uint[1];
  939. uint fetched;
  940. uint saveOptions = (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave;
  941. IVsHierarchy srpOurHier = node.projectMgr as IVsHierarchy;
  942. ErrorHandler.ThrowOnFailure(pEnumRdt.Reset());
  943. while(VSConstants.S_OK == pEnumRdt.Next(1, cookie, out fetched))
  944. {
  945. // Note we can pass NULL for all parameters we don't care about
  946. uint empty;
  947. string emptyStr;
  948. IntPtr ppunkDocData;
  949. IVsHierarchy srpHier;
  950. uint itemid = VSConstants.VSITEMID_NIL;
  951. ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo(
  952. cookie[0],
  953. out empty,
  954. out empty,
  955. out empty,
  956. out emptyStr,
  957. out srpHier,
  958. out itemid,
  959. out ppunkDocData));
  960. // Is this one of our documents?
  961. if(Utilities.IsSameComObject(srpOurHier, srpHier) && itemid == node.ID)
  962. {
  963. IVsSolution soln = GetService(typeof(SVsSolution)) as IVsSolution;
  964. ErrorHandler.ThrowOnFailure(soln.CloseSolutionElement(saveOptions, srpOurHier, cookie[0]));
  965. }
  966. if(ppunkDocData != IntPtr.Zero)
  967. Marshal.Release(ppunkDocData);
  968. }
  969. }
  970. }
  971. /// <summary>
  972. /// Redraws the state icon if the node is not excluded from source control.
  973. /// </summary>
  974. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")]
  975. protected internal virtual void UpdateSccStateIcons()
  976. {
  977. if(!this.ExcludeNodeFromScc)
  978. {
  979. this.ReDraw(UIHierarchyElement.SccState);
  980. }
  981. }
  982. /// <summary>
  983. /// To be overwritten by descendants.
  984. /// </summary>
  985. protected internal virtual int SetEditLabel(string label, string relativePath)
  986. {
  987. throw new NotImplementedException();
  988. }
  989. /// <summary>
  990. /// Called by the drag and drop implementation to ask the node
  991. /// which is being dragged/droped over which nodes should
  992. /// process the operation.
  993. /// This allows for dragging to a node that cannot contain
  994. /// items to let its parent accept the drop
  995. /// </summary>
  996. /// <returns>HierarchyNode that accept the drop handling</returns>
  997. protected internal virtual HierarchyNode GetDragTargetHandlerNode()
  998. {
  999. return this;
  1000. }
  1001. /// <summary>
  1002. /// Add a new Folder to the project hierarchy.
  1003. /// </summary>
  1004. /// <returns>S_OK if succeeded, otherwise an error</returns>
  1005. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
  1006. protected virtual int AddNewFolder()
  1007. {
  1008. // Check out the project file.
  1009. if(!this.ProjectMgr.QueryEditProjectFile(false))
  1010. {
  1011. throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED);
  1012. }
  1013. try
  1014. {
  1015. // Generate a new folder name
  1016. string newFolderName;
  1017. ErrorHandler.ThrowOnFailure(this.projectMgr.GenerateUniqueItemName(this.hierarchyId, String.Empty, String.Empty, out newFolderName));
  1018. // create the project part of it, the project file
  1019. HierarchyNode child = this.ProjectMgr.CreateFolderNodes(Path.Combine(this.virtualNodeName, newFolderName));
  1020. if(child is FolderNode)
  1021. {
  1022. ((FolderNode)child).CreateDirectory();
  1023. }
  1024. // If we are in automation mode then skip the ui part which is about renaming the folder
  1025. if(!Utilities.IsInAutomationFunction(this.projectMgr.Site))
  1026. {
  1027. IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.projectMgr.Site, SolutionExplorer);
  1028. // we need to get into label edit mode now...
  1029. // so first select the new guy...
  1030. ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.projectMgr, child.hierarchyId, EXPANDFLAGS.EXPF_SelectItem));
  1031. // them post the rename command to the shell. Folder verification and creation will
  1032. // happen in the setlabel code...
  1033. IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
  1034. Debug.Assert(shell != null, "Could not get the ui shell from the project");
  1035. if(shell == null)
  1036. {
  1037. return VSConstants.E_FAIL;
  1038. }
  1039. object dummy = null;
  1040. Guid cmdGroup = VsMenus.guidStandardCommandSet97;
  1041. ErrorHandler.ThrowOnFailure(shell.PostExecCommand(ref cmdGroup, (uint)VsCommands.Rename, 0, ref dummy));
  1042. }
  1043. }
  1044. catch(COMException e)
  1045. {
  1046. Trace.WriteLine("Exception : " + e.Message);
  1047. return e.ErrorCode;
  1048. }
  1049. return VSConstants.S_OK;
  1050. }
  1051. protected virtual int AddItemToHierarchy(HierarchyAddType addType)
  1052. {
  1053. CCITracing.TraceCall();
  1054. IVsAddProjectItemDlg addItemDialog;
  1055. string strFilter = String.Empty;
  1056. int iDontShowAgain;
  1057. uint uiFlags;
  1058. IVsProject3 project = (IVsProject3)this.projectMgr;
  1059. string strBrowseLocations = Path.GetDirectoryName(this.projectMgr.BaseURI.Uri.LocalPath);
  1060. System.Guid projectGuid = this.projectMgr.ProjectGuid;
  1061. addItemDialog = this.GetService(typeof(IVsAddProjectItemDlg)) as IVsAddProjectItemDlg;
  1062. if(addType == HierarchyAddType.AddNewItem)
  1063. uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddNewItems | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView);
  1064. else
  1065. uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddExistingItems | __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter);
  1066. ErrorHandler.ThrowOnFailure(addItemDialog.AddProjectItemDlg(this.hierarchyId, ref projectGuid, project, uiFlags, null, null, ref strBrowseLocations, ref strFilter, out iDontShowAgain)); /*&fDontShowAgain*/
  1067. return VSConstants.S_OK;
  1068. }
  1069. /// <summary>
  1070. /// Overwritten in subclasses
  1071. /// </summary>
  1072. protected virtual void DoDefaultAction()
  1073. {
  1074. CCITracing.TraceCall();
  1075. }
  1076. /// <summary>
  1077. /// Handles the exclude from project command.
  1078. /// </summary>
  1079. /// <returns></returns>
  1080. protected virtual int ExcludeFromProject()
  1081. {
  1082. Debug.Assert(this.ProjectMgr != null, "The project item " + this.ToString() + " has not been initialised correctly. It has a null ProjectMgr");
  1083. this.Remove(false);
  1084. return VSConstants.S_OK;
  1085. }
  1086. /// <summary>
  1087. /// Handles the Show in Designer command.
  1088. /// </summary>
  1089. /// <returns></returns>
  1090. protected virtual int ShowInDesigner(IList<HierarchyNode> selectedNodes)
  1091. {
  1092. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1093. }
  1094. /// <summary>
  1095. /// Prepares a selected node for clipboard.
  1096. /// It takes the the project reference string of this item and adds it to a stringbuilder.
  1097. /// </summary>
  1098. /// <returns>A stringbuilder.</returns>
  1099. /// <devremark>This method has to be public since seleceted nodes will call it.</devremark>
  1100. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ClipBoard")]
  1101. protected internal virtual StringBuilder PrepareSelectedNodesForClipBoard()
  1102. {
  1103. Debug.Assert(this.ProjectMgr != null, " No project mananager available for this node " + ToString());
  1104. Debug.Assert(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null, " The itemsdragged list should have been initialized prior calling this method");
  1105. StringBuilder sb = new StringBuilder();
  1106. if(this.hierarchyId == VSConstants.VSITEMID_ROOT)
  1107. {
  1108. if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
  1109. {
  1110. this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort
  1111. }
  1112. return sb;
  1113. }
  1114. if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
  1115. {
  1116. this.ProjectMgr.ItemsDraggedOrCutOrCopied.Add(this);
  1117. }
  1118. string projref = String.Empty;
  1119. IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution;
  1120. if(solution != null)
  1121. {
  1122. ErrorHandler.ThrowOnFailure(solution.GetProjrefOfItem(this.ProjectMgr, this.hierarchyId, out projref));
  1123. if(String.IsNullOrEmpty(projref))
  1124. {
  1125. if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null)
  1126. {
  1127. this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort
  1128. }
  1129. return sb;
  1130. }
  1131. }
  1132. // Append the projectref and a null terminator to the string builder
  1133. sb.Append(projref);
  1134. sb.Append('\0');
  1135. return sb;
  1136. }
  1137. /// <summary>
  1138. /// Returns the Cannonical Name
  1139. /// </summary>
  1140. /// <returns>Cannonical Name</returns>
  1141. protected virtual string GetCanonicalName()
  1142. {
  1143. return this.GetMkDocument();
  1144. }
  1145. /// <summary>
  1146. /// Factory method for the Document Manager object
  1147. /// </summary>
  1148. /// <returns>null object, since a hierarchy node does not know its kind of document</returns>
  1149. /// <remarks>Must be overriden by derived node classes if a document manager is needed</remarks>
  1150. protected internal virtual DocumentManager GetDocumentManager()
  1151. {
  1152. return null;
  1153. }
  1154. /// <summary>
  1155. /// Displays the context menu.
  1156. /// </summary>
  1157. /// <param name="selectedNodes">list of selected nodes.</param>
  1158. /// <param name="pointerToVariant">contains the location (x,y) at which to show the menu.</param>
  1159. [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "pointer")]
  1160. protected virtual int DisplayContextMenu(IList<HierarchyNode> selectedNodes, IntPtr pointerToVariant)
  1161. {
  1162. if(selectedNodes == null || selectedNodes.Count == 0 || pointerToVariant == IntPtr.Zero)
  1163. {
  1164. return NativeMethods.OLECMDERR_E_NOTSUPPORTED;
  1165. }
  1166. int idmxStoredMenu = 0;
  1167. foreach(HierarchyNode node in selectedNodes)
  1168. {
  1169. // We check here whether we have a multiple selection of
  1170. // nodes of differing type.
  1171. if(idmxStoredMenu == 0)
  1172. {
  1173. // First time through or single node case
  1174. idmxStoredMenu = node.MenuCommandId;
  1175. }
  1176. else if(idmxStoredMenu != node.MenuCommandId)
  1177. {
  1178. // We have different node types. Check if any of the nodes is
  1179. // the project node and set the menu accordingly.
  1180. if(node.MenuCommandId == VsMenus.IDM_VS_CTXT_PROJNODE)
  1181. {
  1182. idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_PROJITEM;
  1183. }
  1184. else
  1185. {
  1186. idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_MULTIITEM;
  1187. }
  1188. }
  1189. }
  1190. object variant = Marshal.GetObjectForNativeVariant(pointerToVariant);
  1191. UInt32 pointsAsUint = (UInt32)variant;
  1192. short x = (short)(pointsAsUint & 0x0000ffff);
  1193. short y = (short)((pointsAsUint & 0xffff0000) / 0x10000);
  1194. POINTS points = new POINTS();
  1195. points.x = x;
  1196. points.y = y;
  1197. return ShowContextMenu(idmxStoredMenu, VsMenus.guidSHLMainMenu, points);
  1198. }
  1199. /// <summary>
  1200. /// Shows the specified context menu at a specified location.
  1201. /// </summary>
  1202. /// <param name="menuId">The context menu ID.</param>
  1203. /// <param name="groupGuid">The GUID of the menu group.</param>
  1204. /// <param name="points">The location at which to show the menu.</param>
  1205. protected virtual int ShowContextMenu(int menuId, Guid menuGroup, POINTS points)
  1206. {
  1207. IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell;
  1208. Debug.Assert(shell != null, "Could not get the ui shell from the project");
  1209. if(shell == null)
  1210. {
  1211. return VSConstants.E_FAIL;
  1212. }
  1213. POINTS[] pnts = new POINTS[1];
  1214. pnts[0].x = points.x;
  1215. pnts[0].y = points.y;
  1216. return shell.ShowContextMenu(0, ref menuGroup, menuId, pnts, (Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget)this);
  1217. }
  1218. #region initiation of command execution
  1219. /// <summary>
  1220. /// Handles command execution.
  1221. /// </summary>
  1222. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1223. /// <param name="cmd">The command to be executed.</param>
  1224. /// <param name="nCmdexecopt">Values describe how the object should execute the command.</param>
  1225. /// <param name="pvaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
  1226. /// <param name="pvaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
  1227. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1228. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmdexecopt")]
  1229. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n")]
  1230. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "pva")]
  1231. protected virtual int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
  1232. {
  1233. if(this.projectMgr == null || this.projectMgr.IsClosed)
  1234. {
  1235. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1236. }
  1237. if(cmdGroup == Guid.Empty)
  1238. {
  1239. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1240. }
  1241. else if(cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds)
  1242. {
  1243. switch(cmd)
  1244. {
  1245. case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick:
  1246. case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey:
  1247. this.DoDefaultAction();
  1248. return VSConstants.S_OK;
  1249. }
  1250. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1251. }
  1252. else if(cmdGroup == VsMenus.guidStandardCommandSet97)
  1253. {
  1254. HierarchyNode nodeToAddTo = this.GetDragTargetHandlerNode();
  1255. switch((VsCommands)cmd)
  1256. {
  1257. case VsCommands.AddNewItem:
  1258. return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddNewItem);
  1259. case VsCommands.AddExistingItem:
  1260. return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddExistingItem);
  1261. case VsCommands.NewFolder:
  1262. return nodeToAddTo.AddNewFolder();
  1263. case VsCommands.Paste:
  1264. return this.ProjectMgr.PasteFromClipboard(this);
  1265. }
  1266. }
  1267. else if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1268. {
  1269. switch((VsCommands2K)cmd)
  1270. {
  1271. case VsCommands2K.EXCLUDEFROMPROJECT:
  1272. return this.ExcludeFromProject();
  1273. }
  1274. }
  1275. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1276. }
  1277. /// <summary>
  1278. /// Executes a command that can only be executed once the whole selection is known.
  1279. /// </summary>
  1280. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1281. /// <param name="cmdId">The command to be executed.</param>
  1282. /// <param name="cmdExecOpt">Values describe how the object should execute the command.</param>
  1283. /// <param name="vaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
  1284. /// <param name="vaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
  1285. /// <param name="commandOrigin">The origin of the command. From IOleCommandTarget or hierarchy.</param>
  1286. /// <param name="selectedNodes">The list of the selected nodes.</param>
  1287. /// <param name="handled">An out parameter specifying that the command was handled.</param>
  1288. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1289. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")]
  1290. protected virtual int ExecCommandThatDependsOnSelectedNodes(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, IList<HierarchyNode> selectedNodes, out bool handled)
  1291. {
  1292. handled = false;
  1293. if(cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds)
  1294. {
  1295. switch(cmdId)
  1296. {
  1297. case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_RightClick:
  1298. // The UIHWCMDID_RightClick is what tells an IVsUIHierarchy in a UIHierarchyWindow
  1299. // to put up the context menu. Since the mouse may have moved between the
  1300. // mouse down and the mouse up, GetCursorPos won't tell you the right place
  1301. // to put the context menu (especially if it came through the keyboard).
  1302. // So we pack the proper menu position into pvaIn by
  1303. // memcpy'ing a POINTS struct into the VT_UI4 part of the pvaIn variant. The
  1304. // code to unpack it looks like this:
  1305. // ULONG ulPts = V_UI4(pvaIn);
  1306. // POINTS pts;
  1307. // memcpy((void*)&pts, &ulPts, sizeof(POINTS));
  1308. // You then pass that POINTS into DisplayContextMenu.
  1309. handled = true;
  1310. return this.DisplayContextMenu(selectedNodes, vaIn);
  1311. default:
  1312. break;
  1313. }
  1314. }
  1315. else if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1316. {
  1317. switch((VsCommands2K)cmdId)
  1318. {
  1319. case VsCommands2K.ViewInClassDiagram:
  1320. handled = true;
  1321. return this.ShowInDesigner(selectedNodes);
  1322. }
  1323. }
  1324. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1325. }
  1326. /// <summary>
  1327. /// Executes command that are independent of a selection.
  1328. /// </summary>
  1329. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1330. /// <param name="cmdId">The command to be executed.</param>
  1331. /// <param name="cmdExecOpt">Values describe how the object should execute the command.</param>
  1332. /// <param name="vaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
  1333. /// <param name="vaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
  1334. /// <param name="commandOrigin">The origin of the command. From IOleCommandTarget or hierarchy.</param>
  1335. /// <param name="handled">An out parameter specifying that the command was handled.</param>
  1336. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1337. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")]
  1338. protected virtual int ExecCommandIndependentOfSelection(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, out bool handled)
  1339. {
  1340. handled = false;
  1341. if(this.projectMgr == null || this.projectMgr.IsClosed)
  1342. {
  1343. return VSConstants.E_FAIL;
  1344. }
  1345. if(cmdGroup == VsMenus.guidStandardCommandSet97)
  1346. {
  1347. if(commandOrigin == CommandOrigin.OleCommandTarget)
  1348. {
  1349. switch((VsCommands)cmdId)
  1350. {
  1351. case VsCommands.Cut:
  1352. case VsCommands.Copy:
  1353. case VsCommands.Paste:
  1354. case VsCommands.Rename:
  1355. handled = true;
  1356. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1357. }
  1358. }
  1359. switch((VsCommands)cmdId)
  1360. {
  1361. case VsCommands.Copy:
  1362. handled = true;
  1363. return this.ProjectMgr.CopyToClipboard();
  1364. case VsCommands.Cut:
  1365. handled = true;
  1366. return this.ProjectMgr.CutToClipboard();
  1367. case VsCommands.SolutionCfg:
  1368. handled = true;
  1369. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1370. case VsCommands.SearchCombo:
  1371. handled = true;
  1372. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1373. }
  1374. }
  1375. else if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1376. {
  1377. // There should only be the project node who handles these and should manifest in the same action regardles of selection.
  1378. switch((VsCommands2K)cmdId)
  1379. {
  1380. case VsCommands2K.SHOWALLFILES:
  1381. handled = true;
  1382. return this.projectMgr.ShowAllFiles();
  1383. case VsCommands2K.ADDREFERENCE:
  1384. handled = true;
  1385. return this.projectMgr.AddProjectReference();
  1386. case VsCommands2K.ADDWEBREFERENCE:
  1387. handled = true;
  1388. return this.projectMgr.AddWebReference();
  1389. }
  1390. }
  1391. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1392. }
  1393. /// <summary>
  1394. /// The main entry point for command excection. Gets called from the IVsUIHierarchy and IOleCommandTarget methods.
  1395. /// </summary>
  1396. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1397. /// <param name="cmdId">The command to be executed.</param>
  1398. /// <param name="cmdExecOpt">Values describe how the object should execute the command.</param>
  1399. /// <param name="vaIn">Pointer to a VARIANTARG structure containing input arguments. Can be NULL</param>
  1400. /// <param name="vaOut">VARIANTARG structure to receive command output. Can be NULL.</param>
  1401. /// <param name="commandOrigin">The origin of the command. From IOleCommandTarget or hierarchy.</param>
  1402. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1403. [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")]
  1404. protected virtual int InternalExecCommand(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin)
  1405. {
  1406. CCITracing.TraceCall(cmdGroup.ToString() + "," + cmdId.ToString());
  1407. if(this.projectMgr == null || this.projectMgr.IsClosed)
  1408. {
  1409. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1410. }
  1411. if(cmdGroup == Guid.Empty)
  1412. {
  1413. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1414. }
  1415. IList<HierarchyNode> selectedNodes = this.projectMgr.GetSelectedNodes();
  1416. // Check if all nodes can execute a command. If there is at least one that cannot return not handled.
  1417. foreach(HierarchyNode node in selectedNodes)
  1418. {
  1419. if(!node.CanExecuteCommand)
  1420. {
  1421. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1422. }
  1423. }
  1424. // Handle commands that are independent of a selection.
  1425. bool handled = false;
  1426. int returnValue = this.ExecCommandIndependentOfSelection(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, out handled);
  1427. if(handled)
  1428. {
  1429. return returnValue;
  1430. }
  1431. // Now handle commands that need the selected nodes as input parameter.
  1432. returnValue = this.ExecCommandThatDependsOnSelectedNodes(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, selectedNodes, out handled);
  1433. if(handled)
  1434. {
  1435. return returnValue;
  1436. }
  1437. returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1438. // Handle commands iteratively. The same action will be executed for all of the selected items.
  1439. foreach(HierarchyNode node in selectedNodes)
  1440. {
  1441. try
  1442. {
  1443. returnValue = node.ExecCommandOnNode(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut);
  1444. }
  1445. catch(COMException e)
  1446. {
  1447. Trace.WriteLine("Exception : " + e.Message);
  1448. returnValue = e.ErrorCode;
  1449. }
  1450. if(returnValue != VSConstants.S_OK)
  1451. {
  1452. break;
  1453. }
  1454. }
  1455. if(returnValue == VSConstants.E_ABORT || returnValue == VSConstants.OLE_E_PROMPTSAVECANCELLED)
  1456. {
  1457. returnValue = VSConstants.S_OK;
  1458. }
  1459. return returnValue;
  1460. }
  1461. #endregion
  1462. #region query command handling
  1463. /// <summary>
  1464. /// Handles menus originating from IOleCommandTarget.
  1465. /// </summary>
  1466. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1467. /// <param name="cmd">The command to be executed.</param>
  1468. /// <param name="handled">Specifies whether the menu was handled.</param>
  1469. /// <returns>A QueryStatusResult describing the status of the menu.</returns>
  1470. protected virtual QueryStatusResult QueryStatusCommandFromOleCommandTarget(Guid cmdGroup, uint cmd, out bool handled)
  1471. {
  1472. handled = false;
  1473. // NOTE: We only want to support Cut/Copy/Paste/Delete/Rename commands
  1474. // if focus is in the project window. This means that we should only
  1475. // support these commands if they are dispatched via IVsUIHierarchy
  1476. // interface and not if they are dispatch through IOleCommandTarget
  1477. // during the command routing to the active project/hierarchy.
  1478. if(VsMenus.guidStandardCommandSet97 == cmdGroup)
  1479. {
  1480. switch((VsCommands)cmd)
  1481. {
  1482. case VsCommands.Copy:
  1483. case VsCommands.Paste:
  1484. case VsCommands.Cut:
  1485. case VsCommands.Rename:
  1486. handled = true;
  1487. return QueryStatusResult.NOTSUPPORTED;
  1488. }
  1489. }
  1490. // The reference menu and the web reference menu should always be shown.
  1491. else if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1492. {
  1493. switch((VsCommands2K)cmd)
  1494. {
  1495. case VsCommands2K.ADDREFERENCE:
  1496. handled = true;
  1497. return QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1498. }
  1499. }
  1500. return QueryStatusResult.NOTSUPPORTED;
  1501. }
  1502. /// <summary>
  1503. /// Specifies which command does not support multiple selection and should be disabled if multi-selected.
  1504. /// </summary>
  1505. /// <param name="cmdGroup">Unique identifier of the command group</param>
  1506. /// <param name="cmd">The command to be executed.</param>
  1507. /// <param name="selectedNodes">The list of selected nodes.</param>
  1508. /// <param name="handled">Specifies whether the menu was handled.</param>
  1509. /// <returns>A QueryStatusResult describing the status of the menu.</returns>
  1510. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
  1511. protected virtual QueryStatusResult DisableCommandOnNodesThatDoNotSupportMultiSelection(Guid cmdGroup, uint cmd, IList<HierarchyNode> selectedNodes, out bool handled)
  1512. {
  1513. handled = false;
  1514. QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED;
  1515. if(selectedNodes == null || selectedNodes.Count == 1)
  1516. {
  1517. return queryResult;
  1518. }
  1519. if(VsMenus.guidStandardCommandSet97 == cmdGroup)
  1520. {
  1521. switch((VsCommands)cmd)
  1522. {
  1523. case VsCommands.Cut:
  1524. case VsCommands.Copy:
  1525. // If the project node is selected then cut and copy is not supported.
  1526. if(selectedNodes.Contains(this.projectMgr))
  1527. {
  1528. queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
  1529. handled = true;
  1530. }
  1531. break;
  1532. case VsCommands.Paste:
  1533. case VsCommands.NewFolder:
  1534. queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
  1535. handled = true;
  1536. break;
  1537. }
  1538. }
  1539. else if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1540. {
  1541. switch((VsCommands2K)cmd)
  1542. {
  1543. case VsCommands2K.QUICKOBJECTSEARCH:
  1544. case VsCommands2K.SETASSTARTPAGE:
  1545. case VsCommands2K.ViewInClassDiagram:
  1546. queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
  1547. handled = true;
  1548. break;
  1549. }
  1550. }
  1551. return queryResult;
  1552. }
  1553. /// <summary>
  1554. /// Handles command status on a node. Should be overridden by descendant nodes. If a command cannot be handled then the base should be called.
  1555. /// </summary>
  1556. /// <param name="cmdGroup">A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group.</param>
  1557. /// <param name="cmd">The command to query status for.</param>
  1558. /// <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>
  1559. /// <param name="result">An out parameter specifying the QueryStatusResult of the command.</param>
  1560. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1561. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
  1562. protected virtual int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result)
  1563. {
  1564. if(cmdGroup == VsMenus.guidStandardCommandSet2K)
  1565. {
  1566. if((VsCommands2K)cmd == VsCommands2K.SHOWALLFILES)
  1567. {
  1568. result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED;
  1569. return VSConstants.S_OK;
  1570. }
  1571. }
  1572. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1573. }
  1574. /// <summary>
  1575. /// Disables commands when the project is in run/break mode.
  1576. /// </summary>/
  1577. /// <param name="commandGroup">Unique identifier of the command group</param>
  1578. /// <param name="command">The command to be executed.</param>
  1579. /// <returns>A QueryStatusResult describing the status of the menu.</returns>
  1580. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "InCurrent")]
  1581. protected virtual bool DisableCmdInCurrentMode(Guid commandGroup, uint command)
  1582. {
  1583. if(this.ProjectMgr == null || this.ProjectMgr.IsClosed)
  1584. {
  1585. return false;
  1586. }
  1587. // Don't ask if it is not these two commandgroups.
  1588. if(commandGroup == VsMenus.guidStandardCommandSet97 || commandGroup == VsMenus.guidStandardCommandSet2K)
  1589. {
  1590. if(this.ProjectMgr.IsCurrentStateASuppressCommandsMode())
  1591. {
  1592. if(commandGroup == VsMenus.guidStandardCommandSet97)
  1593. {
  1594. switch((VsCommands)command)
  1595. {
  1596. default:
  1597. break;
  1598. case VsCommands.AddExistingItem:
  1599. case VsCommands.AddNewItem:
  1600. case VsCommands.NewFolder:
  1601. case VsCommands.Remove:
  1602. case VsCommands.Cut:
  1603. case VsCommands.Paste:
  1604. case VsCommands.Copy:
  1605. case VsCommands.EditLabel:
  1606. case VsCommands.Rename:
  1607. case VsCommands.UnloadProject:
  1608. return true;
  1609. }
  1610. }
  1611. else if(commandGroup == VsMenus.guidStandardCommandSet2K)
  1612. {
  1613. switch((VsCommands2K)command)
  1614. {
  1615. default:
  1616. break;
  1617. case VsCommands2K.EXCLUDEFROMPROJECT:
  1618. case VsCommands2K.INCLUDEINPROJECT:
  1619. case VsCommands2K.ADDWEBREFERENCECTX:
  1620. case VsCommands2K.ADDWEBREFERENCE:
  1621. case VsCommands2K.ADDREFERENCE:
  1622. case VsCommands2K.SETASSTARTPAGE:
  1623. return true;
  1624. }
  1625. }
  1626. }
  1627. // If we are not in a cut or copy mode then disable the paste command
  1628. else if(!this.ProjectMgr.AllowPasteCommand())
  1629. {
  1630. if(commandGroup == VsMenus.guidStandardCommandSet97 && (VsCommands)command == VsCommands.Paste)
  1631. {
  1632. return true;
  1633. }
  1634. }
  1635. }
  1636. return false;
  1637. }
  1638. /// <summary>
  1639. /// Queries the object for the command status on a list of selected nodes.
  1640. /// </summary>
  1641. /// <param name="cmdGroup">A unique identifier of the command group.</param>
  1642. /// <param name="cCmds">The number of commands in the prgCmds array</param>
  1643. /// <param name="prgCmds">A caller-allocated array of OLECMD structures that indicate the commands for which the caller requires status information. This method fills the cmdf member of each structure with values taken from the OLECMDF enumeration</param>
  1644. /// <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>
  1645. /// <param name="commandOrigin">Specifies the origin of the command. Either it was called from the QueryStatusCommand on IVsUIHierarchy or from the IOleCommandTarget</param>
  1646. /// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
  1647. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmds")]
  1648. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")]
  1649. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
  1650. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "prg")]
  1651. protected virtual int QueryStatusSelection(Guid cmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText, CommandOrigin commandOrigin)
  1652. {
  1653. if(this.projectMgr.IsClosed)
  1654. {
  1655. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1656. }
  1657. if(cmdGroup == Guid.Empty)
  1658. {
  1659. return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP;
  1660. }
  1661. if (prgCmds == null)
  1662. {
  1663. throw new ArgumentNullException("prgCmds");
  1664. }
  1665. uint cmd = prgCmds[0].cmdID;
  1666. QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED;
  1667. // For now ask this node (that is the project node) to disable or enable a node.
  1668. // This is an optimization. Why should we ask each node for its current state? They all are in the same state.
  1669. // Also please note that we return QueryStatusResult.INVISIBLE instead of just QueryStatusResult.SUPPORTED.
  1670. // The reason is that if the project has nested projects, then providing just QueryStatusResult.SUPPORTED is not enough.
  1671. // What will happen is that the nested project will show grayed commands that belong to this project and does not belong to the nested project. (like special commands implemented by subclassed projects).
  1672. // The reason is that a special command comes in that is not handled because we are in debug mode. Then VsCore asks the nested project can you handle it.
  1673. // The nested project does not know about it, thus it shows it on the nested project as grayed.
  1674. if(this.DisableCmdInCurrentMode(cmdGroup, cmd))
  1675. {
  1676. queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE;
  1677. }
  1678. else
  1679. {
  1680. bool handled = false;
  1681. if(commandOrigin == CommandOrigin.OleCommandTarget)
  1682. {
  1683. queryResult = this.QueryStatusCommandFromOleCommandTarget(cmdGroup, cmd, out handled);
  1684. }
  1685. if(!handled)
  1686. {
  1687. IList<HierarchyNode> selectedNodes = this.projectMgr.GetSelectedNodes();
  1688. // Want to disable in multiselect case.
  1689. if(selectedNodes != null && selectedNodes.Count > 1)
  1690. {
  1691. queryResult = this.DisableCommandOnNodesThatDoNotSupportMultiSelection(cmdGroup, cmd, selectedNodes, out handled);
  1692. }
  1693. // Now go and do the job on the nodes.
  1694. if(!handled)
  1695. {
  1696. queryResult = this.QueryStatusSelectionOnNodes(selectedNodes, cmdGroup, cmd, pCmdText);
  1697. }
  1698. }
  1699. }
  1700. // Process the results set in the QueryStatusResult
  1701. if(queryResult != QueryStatusResult.NOTSUPPORTED)
  1702. {
  1703. // Set initial value
  1704. prgCmds[0].cmdf = (uint)OLECMDF.OLECMDF_SUPPORTED;
  1705. if((queryResult & QueryStatusResult.ENABLED) != 0)
  1706. {
  1707. prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_ENABLED;
  1708. }
  1709. if((queryResult & QueryStatusResult.INVISIBLE) != 0)
  1710. {
  1711. prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_INVISIBLE;
  1712. }
  1713. if((queryResult & QueryStatusResult.LATCHED) != 0)
  1714. {
  1715. prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_LATCHED;
  1716. }
  1717. return VSConstants.S_OK;
  1718. }
  1719. return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
  1720. }
  1721. /// <summary>
  1722. /// Queries the selected nodes for the command status.
  1723. /// A command is supported iff any nodes supports it.
  1724. /// A command is enabled iff all nodes enable it.
  1725. /// A command is invisible iff any node sets invisibility.
  1726. /// A command is latched only if all are latched.
  1727. /// </summary>
  1728. /// <param name="selectedNodes">The list of selected nodes.</param>
  1729. /// <param name="cmdGroup">A unique identifier of the command group.</param>
  1730. /// <param name="cmd">The command id to query for.</param>
  1731. /// <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>
  1732. /// <returns>Retuns the result of the query on the slected nodes.</returns>
  1733. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")]
  1734. protected virtual QueryStatusResult QueryStatusSelectionOnNodes(IList<HierarchyNode> selectedNodes, Guid cmdGroup, uint cmd, IntPtr pCmdText)
  1735. {
  1736. if(selectedNodes == null || selectedNodes.Count == 0)
  1737. {
  1738. return QueryStatusResult.NOTSUPPORTED;
  1739. }
  1740. int result = 0;
  1741. bool supported = false;
  1742. bool enabled = true;
  1743. bool invisible = false;
  1744. bool latched = true;
  1745. QueryStatusResult tempQueryResult = QueryStatusResult.NOTSUPPORTED;
  1746. foreach(HierarchyNode node in selectedNodes)
  1747. {
  1748. result = node.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref tempQueryResult);
  1749. if(result < 0)
  1750. {
  1751. break;
  1752. }
  1753. // cmd is supported iff any node supports cmd
  1754. // cmd is enabled iff all nodes enable cmd
  1755. // cmd is invisible iff any node sets invisibility
  1756. // cmd is latched only if all are latched.
  1757. supported = supported || ((tempQueryResult & QueryStatusResult.SUPPORTED) != 0);
  1758. enabled = enabled && ((tempQueryResult & QueryStatusResult.ENABLED) != 0);
  1759. invisible = invisible || ((tempQueryResult & QueryStatusResult.INVISIBLE) != 0);
  1760. latched = latched && ((tempQueryResult & QueryStatusResult.LATCHED) != 0);
  1761. }
  1762. QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED;
  1763. if(result >= 0 && supported)
  1764. {
  1765. queryResult = QueryStatusResult.SUPPORTED;
  1766. if(enabled)
  1767. {
  1768. queryResult |= QueryStatusResult.ENABLED;
  1769. }
  1770. if(invisible)
  1771. {
  1772. queryResult |= QueryStatusResult.INVISIBLE;
  1773. }
  1774. if(latched)
  1775. {
  1776. queryResult |= QueryStatusResult.LATCHED;
  1777. }
  1778. }
  1779. return queryRes