/* **************************************************************************** * * 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.Diagnostics; using System.Globalization; using System.IO; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Editor; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Project.Automation; using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; using VSConstants = Microsoft.VisualStudio.VSConstants; namespace Microsoft.VisualStudio.Project { public class CommonFileNode : FileNode { private OAVSProjectItem _vsProjectItem; private CommonProjectNode _project; public CommonFileNode(CommonProjectNode root, ProjectElement e) : base(root, e) { _project = root; } #region properties /// /// Returns bool indicating whether this node is of subtype "Form" /// public bool IsFormSubType { get { string result = this.ItemNode.GetMetadata(ProjectFileConstants.SubType); if (!String.IsNullOrEmpty(result) && string.Compare(result, ProjectFileAttributeValue.Form, true, CultureInfo.InvariantCulture) == 0) return true; else return false; } } /// /// Returns the SubType of a dynamic FileNode. It is /// public string SubType { get { return this.ItemNode.GetMetadata(ProjectFileConstants.SubType); } set { this.ItemNode.SetMetadata(ProjectFileConstants.SubType, value); } } public VSLangProj.VSProjectItem VSProjectItem { get { if (null == _vsProjectItem) { _vsProjectItem = new OAVSProjectItem(this); } return _vsProjectItem; } } #if DEV11_OR_LATER public override __VSPROVISIONALVIEWINGSTATUS ProvisionalViewingStatus { get { return __VSPROVISIONALVIEWINGSTATUS.PVS_Enabled; } } #endif #endregion #region overridden properties public override object Object { get { return this.VSProjectItem; } } #endregion #region overridden methods public override int ImageIndex { get { if (ItemNode.IsExcluded) { return (int)ProjectNode.ImageName.ExcludedFile; } else if (!File.Exists(Url)) { return (int)ProjectNode.ImageName.MissingFile; } else if (IsFormSubType) { return (int)ProjectNode.ImageName.WindowsForm; } else if (this._project.IsCodeFile(FileName)) { if (CommonUtils.IsSamePath(this.Url, _project.GetStartupFile())) { return CommonProjectNode.ImageOffset + (int)CommonImageName.StartupFile; } else { return CommonProjectNode.ImageOffset + (int)CommonImageName.File; } } return base.ImageIndex; } } /// /// Open a file depending on the SubType property associated with the file item in the project file /// protected override void DoDefaultAction() { FileDocumentManager manager = this.GetDocumentManager() as FileDocumentManager; Debug.Assert(manager != null, "Could not get the FileDocumentManager"); Guid viewGuid = (IsFormSubType ? VSConstants.LOGVIEWID_Designer : VSConstants.LOGVIEWID_Code); IVsWindowFrame frame; manager.Open(false, false, viewGuid, out frame, WindowFrameShowAction.Show); } private static Guid CLSID_VsTextBuffer = new Guid("{8E7B96A8-E33D-11d0-A6D5-00C04FB67F6A}"); /// /// Gets the text buffer for the file opening the document if necessary. /// public ITextBuffer GetTextBuffer() { if (UIThread.Instance.IsUIThread) { // http://pytools.codeplex.com/workitem/672 // When we FindAndLockDocument we marshal on the main UI thread, and the docdata we get // back is marshalled back so that we'll marshal any calls on it back. When we pass it // into IVsEditorAdaptersFactoryService we don't go through a COM boundary (it's a managed // call) and we therefore don't get the marshaled value, and it doesn't know what we're // talking about. So run the whole operation on the UI thread. return GetTextBufferOnUIThread(); } ITextBuffer res = null; UIThread.Instance.RunSync( () => { res = GetTextBufferOnUIThread(); } ); return res; } private ITextBuffer GetTextBufferOnUIThread() { IVsTextManager textMgr = (IVsTextManager)GetService(typeof(SVsTextManager)); var model = GetService(typeof(SComponentModel)) as IComponentModel; var adapter = model.GetService(); uint itemid; IVsRunningDocumentTable rdt = ProjectMgr.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; if (rdt != null) { IVsHierarchy hier; IVsPersistDocData persistDocData; uint cookie; bool docInRdt = true; IntPtr docData = IntPtr.Zero; int hr = NativeMethods.E_FAIL; try { //Getting a read lock on the document. Must be released later. hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, GetMkDocument(), out hier, out itemid, out docData, out cookie); if (ErrorHandler.Failed(hr) || docData == IntPtr.Zero) { Guid iid = VSConstants.IID_IUnknown; cookie = 0; docInRdt = false; ILocalRegistry localReg = this.ProjectMgr.GetService(typeof(SLocalRegistry)) as ILocalRegistry; ErrorHandler.ThrowOnFailure(localReg.CreateInstance(CLSID_VsTextBuffer, null, ref iid, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out docData)); } persistDocData = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData; } finally { if (docData != IntPtr.Zero) { Marshal.Release(docData); } } //Try to get the Text lines IVsTextLines srpTextLines = persistDocData as IVsTextLines; if (srpTextLines == null) { // Try getting a text buffer provider first IVsTextBufferProvider srpTextBufferProvider = persistDocData as IVsTextBufferProvider; if (srpTextBufferProvider != null) { hr = srpTextBufferProvider.GetTextBuffer(out srpTextLines); } } // Unlock the document in the RDT if necessary if (docInRdt && rdt != null) { ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((uint)(_VSRDTFLAGS.RDT_ReadLock | _VSRDTFLAGS.RDT_Unlock_NoSave), cookie)); } if (srpTextLines != null) { return adapter.GetDocumentBuffer(srpTextLines); } } IWpfTextView view = GetTextView(); return view.TextBuffer; } public IWpfTextView GetTextView() { var model = GetService(typeof(SComponentModel)) as IComponentModel; var adapter = model.GetService(); IVsTextView viewAdapter; uint itemid; IVsUIHierarchy hierarchy; IVsWindowFrame pWindowFrame; VsShellUtilities.OpenDocument( ProjectMgr.Site, this.GetMkDocument(), Guid.Empty, out hierarchy, out itemid, out pWindowFrame, out viewAdapter); ErrorHandler.ThrowOnFailure(pWindowFrame.Show()); return adapter.GetWpfTextView(viewAdapter); } public new CommonProjectNode ProjectMgr { get { return (CommonProjectNode)base.ProjectMgr; } } /// /// Handles the exclude from project command. /// /// public override int ExcludeFromProject() { Debug.Assert(this.ProjectMgr != null, "The project item " + this.ToString() + " has not been initialised correctly. It has a null ProjectMgr"); if (!ProjectMgr.QueryEditProjectFile(false) || !ProjectMgr.Tracker.CanRemoveItems(new[] { Url }, new [] { VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags })) { return VSConstants.E_FAIL; } ResetNodeProperties(); ItemNode.RemoveFromProjectFile(); if (!File.Exists(Url)) { Parent.RemoveChild(this); ProjectMgr.OnItemDeleted(this); } else { ItemNode = new AllFilesProjectElement(Url, ItemNode.ItemTypeName, ProjectMgr); if (!ProjectMgr.IsShowingAllFiles) { IsVisible = false; ProjectMgr.OnInvalidateItems(Parent); } ProjectMgr.ReDrawNode(this, UIHierarchyElement.Icon); ProjectMgr.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, 0); } return VSConstants.S_OK; } public override int IncludeInProject(bool includeChildren) { if (Parent.ItemNode != null && Parent.ItemNode.IsExcluded) { // if our parent is excluded it needs to first be included int hr = Parent.IncludeInProject(false); if (ErrorHandler.Failed(hr)) { return hr; } } if (!ProjectMgr.QueryEditProjectFile(false) || !ProjectMgr.Tracker.CanAddItems(new[] { Url }, new[] { VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags })) { return VSConstants.E_FAIL; } ResetNodeProperties(); ItemNode = ProjectMgr.AddFileToMsBuild(Url); IsVisible = true; ProjectMgr.OnInvalidateItems(Parent); ProjectMgr.ReDrawNode(this, UIHierarchyElement.Icon); ProjectMgr.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, 0); return VSConstants.S_OK; } /// /// Handles the menuitems /// public override int QueryStatusOnNode(Guid guidCmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { if (guidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidStandardCommandSet2K) { switch ((VsCommands2K)cmd) { case VsCommands2K.RUNCUSTOMTOOL: result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE; return VSConstants.S_OK; case VsCommands2K.EXCLUDEFROMPROJECT: if (ItemNode.IsExcluded) { result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE; return VSConstants.S_OK; } break; case VsCommands2K.INCLUDEINPROJECT: if (ItemNode.IsExcluded) { result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; return VSConstants.S_OK; } break; } } return base.QueryStatusOnNode(guidCmdGroup, cmd, pCmdText, ref result); } /// /// Common File Node can only be deleted from file system. /// public override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) { if (IsLinkFile) { // we don't delete link items, we only remove them from the project. If we were // to return true when queried for both delete from storage and remove from project // the user would be prompted about which they would like to do. return deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject; } return deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage; } #endregion #region methods public OleServiceProvider.ServiceCreatorCallback ServiceCreator { get { return new OleServiceProvider.ServiceCreatorCallback(this.CreateServices); } } protected virtual object CreateServices(Type serviceType) { object service = null; if (typeof(EnvDTE.ProjectItem) == serviceType) { service = GetAutomationObject(); } return service; } #endregion } }