/* ****************************************************************************
*
* 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
}
}