mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
1207 lines
53 KiB
C#
1207 lines
53 KiB
C#
/********************************************************************************************
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
All rights reserved.
|
|
|
|
Microsoft Public License:
|
|
|
|
This license governs use of the accompanying software. If you use the software, you
|
|
accept this license. If you do not accept the license, do not use the software.
|
|
|
|
1. Definitions
|
|
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
|
|
same meaning here as under U.S. copyright law.
|
|
A "contribution" is the original software, or any additions or changes to the software.
|
|
A "contributor" is any person that distributes its contribution under this license.
|
|
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
|
|
|
2. Grant of Rights
|
|
(A) Copyright Grant- Subject to the terms of this license, including the license conditions
|
|
and limitations in section 3, each contributor grants you a non-exclusive, worldwide,
|
|
royalty-free copyright license to reproduce its contribution, prepare derivative works of
|
|
its contribution, and distribute its contribution or any derivative works that you create.
|
|
(B) Patent Grant- Subject to the terms of this license, including the license conditions
|
|
and limitations in section 3, each contributor grants you a non-exclusive, worldwide,
|
|
royalty-free license under its licensed patents to make, have made, use, sell, offer for
|
|
sale, import, and/or otherwise dispose of its contribution in the software or derivative
|
|
works of the contribution in the software.
|
|
|
|
3. Conditions and Limitations
|
|
(A) No Trademark License- This license does not grant you rights to use any contributors'
|
|
name, logo, or trademarks.
|
|
(B) If you bring a patent claim against any contributor over patents that you claim are
|
|
infringed by the software, your patent license from such contributor to the software ends
|
|
automatically.
|
|
(C) If you distribute any portion of the software, you must retain all copyright, patent,
|
|
trademark, and attribution notices that are present in the software.
|
|
(D) If you distribute any portion of the software in source code form, you may do so only
|
|
under this license by including a complete copy of this license with your distribution.
|
|
If you distribute any portion of the software in compiled or object code form, you may only
|
|
do so under a license that complies with this license.
|
|
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give
|
|
no express warranties, guarantees or conditions. You may have additional consumer rights
|
|
under your local laws which this license cannot change. To the extent permitted under your
|
|
local laws, the contributors exclude the implied warranties of merchantability, fitness for
|
|
a particular purpose and non-infringement.
|
|
|
|
********************************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Permissions;
|
|
using System.Text;
|
|
using Microsoft.VisualStudio;
|
|
using Microsoft.VisualStudio.OLE.Interop;
|
|
using Microsoft.VisualStudio.Shell;
|
|
using Microsoft.VisualStudio.Shell.Interop;
|
|
using IOleDataObject = Microsoft.VisualStudio.OLE.Interop.IDataObject;
|
|
using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants;
|
|
|
|
namespace Microsoft.VisualStudio.Project
|
|
{
|
|
/// <summary>
|
|
/// Manages the CopyPaste and Drag and Drop scenarios for a Project.
|
|
/// </summary>
|
|
/// <remarks>This is a partial class.</remarks>
|
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
|
|
public partial class ProjectNode : IVsUIHierWinClipboardHelperEvents
|
|
{
|
|
#region fields
|
|
private uint copyPasteCookie;
|
|
private DropDataType dropDataType;
|
|
#endregion
|
|
|
|
#region override of IVsHierarchyDropDataTarget methods
|
|
/// <summary>
|
|
/// Called as soon as the mouse drags an item over a new hierarchy or hierarchy window
|
|
/// </summary>
|
|
/// <param name="pDataObject">reference to interface IDataObject of the item being dragged</param>
|
|
/// <param name="grfKeyState">Current state of the keyboard and the mouse modifier keys. See docs for a list of possible values</param>
|
|
/// <param name="itemid">Item identifier for the item currently being dragged</param>
|
|
/// <param name="pdwEffect">On entry, a pointer to the current DropEffect. On return, must contain the new valid DropEffect</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
|
|
public override int DragEnter(IOleDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect)
|
|
{
|
|
pdwEffect = (uint)DropEffect.None;
|
|
|
|
if(this.SourceDraggedOrCutOrCopied)
|
|
{
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
this.dropDataType = QueryDropDataType(pDataObject);
|
|
if(this.dropDataType != DropDataType.None)
|
|
{
|
|
pdwEffect = (uint)this.QueryDropEffect(this.dropDataType, grfKeyState);
|
|
}
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when one or more items are dragged out of the hierarchy or hierarchy window, or when the drag-and-drop operation is cancelled or completed.
|
|
/// </summary>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
|
|
public override int DragLeave()
|
|
{
|
|
this.dropDataType = DropDataType.None;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when one or more items are dragged over the target hierarchy or hierarchy window.
|
|
/// </summary>
|
|
/// <param name="grfKeyState">Current state of the keyboard keys and the mouse modifier buttons. See <seealso cref="IVsHierarchyDropDataTarget"/></param>
|
|
/// <param name="itemid">Item identifier of the drop data target over which the item is being dragged</param>
|
|
/// <param name="pdwEffect"> On entry, reference to the value of the pdwEffect parameter of the IVsHierarchy object, identifying all effects that the hierarchy supports.
|
|
/// On return, the pdwEffect parameter must contain one of the effect flags that indicate the result of the drop operation. For a list of pwdEffects values, see <seealso cref="DragEnter"/></param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
|
|
public override int DragOver(uint grfKeyState, uint itemid, ref uint pdwEffect)
|
|
{
|
|
pdwEffect = (uint)DropEffect.None;
|
|
|
|
// Dragging items to a project that is being debugged is not supported
|
|
// (see VSWhidbey 144785)
|
|
DBGMODE dbgMode = VsShellUtilities.GetDebugMode(this.Site) & ~DBGMODE.DBGMODE_EncMask;
|
|
if(dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.DBGMODE_Break)
|
|
{
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
if(this.isClosed || this.site == null)
|
|
{
|
|
return VSConstants.E_UNEXPECTED;
|
|
}
|
|
|
|
// We should also analyze if the node being dragged over can accept the drop.
|
|
if(!this.CanTargetNodeAcceptDrop(itemid))
|
|
{
|
|
return VSConstants.E_NOTIMPL;
|
|
}
|
|
|
|
if(this.dropDataType != DropDataType.None)
|
|
{
|
|
pdwEffect = (uint)this.QueryDropEffect(this.dropDataType, grfKeyState);
|
|
}
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when one or more items are dropped into the target hierarchy or hierarchy window when the mouse button is released.
|
|
/// </summary>
|
|
/// <param name="pDataObject">Reference to the IDataObject interface on the item being dragged. This data object contains the data being transferred in the drag-and-drop operation.
|
|
/// If the drop occurs, then this data object (item) is incorporated into the target hierarchy or hierarchy window.</param>
|
|
/// <param name="grfKeyState">Current state of the keyboard and the mouse modifier keys. See <seealso cref="IVsHierarchyDropDataTarget"/></param>
|
|
/// <param name="itemid">Item identifier of the drop data target over which the item is being dragged</param>
|
|
/// <param name="pdwEffect">Visual effects associated with the drag-and drop-operation, such as a cursor, bitmap, and so on.
|
|
/// The value of dwEffects passed to the source object via the OnDropNotify method is the value of pdwEffects returned by the Drop method</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
|
|
public override int Drop(IOleDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect)
|
|
{
|
|
if(pDataObject == null)
|
|
{
|
|
return VSConstants.E_INVALIDARG;
|
|
}
|
|
|
|
pdwEffect = (uint)DropEffect.None;
|
|
|
|
// Get the node that is being dragged over and ask it which node should handle this call
|
|
HierarchyNode targetNode = NodeFromItemId(itemid);
|
|
if(targetNode != null)
|
|
{
|
|
targetNode = targetNode.GetDragTargetHandlerNode();
|
|
}
|
|
else
|
|
{
|
|
// There is no target node. The drop can not be completed.
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
int returnValue;
|
|
try
|
|
{
|
|
DropDataType dropDataType = DropDataType.None;
|
|
dropDataType = ProcessSelectionDataObject(pDataObject, targetNode);
|
|
pdwEffect = (uint)this.QueryDropEffect(dropDataType, grfKeyState);
|
|
|
|
// If it is a drop from windows and we get any kind of error we return S_FALSE and dropeffect none. This
|
|
// prevents bogus messages from the shell from being displayed
|
|
returnValue = (dropDataType != DropDataType.Shell) ? VSConstants.E_FAIL : VSConstants.S_OK;
|
|
}
|
|
catch(System.IO.FileNotFoundException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
|
|
if(!Utilities.IsInAutomationFunction(this.Site))
|
|
{
|
|
string message = e.Message;
|
|
string title = string.Empty;
|
|
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL;
|
|
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK;
|
|
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
|
|
VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton);
|
|
}
|
|
|
|
returnValue = VSConstants.E_FAIL;
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
#endregion
|
|
|
|
#region override of IVsHierarchyDropDataSource2 methods
|
|
/// <summary>
|
|
/// Returns information about one or more of the items being dragged
|
|
/// </summary>
|
|
/// <param name="pdwOKEffects">Pointer to a DWORD value describing the effects displayed while the item is being dragged,
|
|
/// such as cursor icons that change during the drag-and-drop operation.
|
|
/// For example, if the item is dragged over an invalid target point
|
|
/// (such as the item's original location), the cursor icon changes to a circle with a line through it.
|
|
/// Similarly, if the item is dragged over a valid target point, the cursor icon changes to a file or folder.</param>
|
|
/// <param name="ppDataObject">Pointer to the IDataObject interface on the item being dragged.
|
|
/// This data object contains the data being transferred in the drag-and-drop operation.
|
|
/// If the drop occurs, then this data object (item) is incorporated into the target hierarchy or hierarchy window.</param>
|
|
/// <param name="ppDropSource">Pointer to the IDropSource interface of the item being dragged.</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code.</returns>
|
|
public override int GetDropInfo(out uint pdwOKEffects, out IOleDataObject ppDataObject, out IDropSource ppDropSource)
|
|
{
|
|
//init out params
|
|
pdwOKEffects = (uint)DropEffect.None;
|
|
ppDataObject = null;
|
|
ppDropSource = null;
|
|
|
|
IOleDataObject dataObject = PackageSelectionDataObject(false);
|
|
if(dataObject == null)
|
|
{
|
|
return VSConstants.E_NOTIMPL;
|
|
}
|
|
|
|
this.SourceDraggedOrCutOrCopied = true;
|
|
|
|
pdwOKEffects = (uint)(DropEffect.Move | DropEffect.Copy);
|
|
|
|
ppDataObject = dataObject;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Notifies clients that the dragged item was dropped.
|
|
/// </summary>
|
|
/// <param name="fDropped">If true, then the dragged item was dropped on the target. If false, then the drop did not occur.</param>
|
|
/// <param name="dwEffects">Visual effects associated with the drag-and-drop operation, such as cursors, bitmaps, and so on.
|
|
/// The value of dwEffects passed to the source object via OnDropNotify method is the value of pdwEffects returned by Drop method.</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
|
|
public override int OnDropNotify(int fDropped, uint dwEffects)
|
|
{
|
|
if(!this.SourceDraggedOrCutOrCopied)
|
|
{
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
this.CleanupSelectionDataObject(fDropped != 0, false, dwEffects == (uint)DropEffect.Move);
|
|
|
|
this.SourceDraggedOrCutOrCopied = false;
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allows the drag source to prompt to save unsaved items being dropped.
|
|
/// Notifies the source hierarchy that information dragged from it is about to be dropped on a target.
|
|
/// This method is called immediately after the mouse button is released on a drop.
|
|
/// </summary>
|
|
/// <param name="o">Reference to the IDataObject interface on the item being dragged.
|
|
/// This data object contains the data being transferred in the drag-and-drop operation.
|
|
/// If the drop occurs, then this data object (item) is incorporated into the hierarchy window of the new hierarchy.</param>
|
|
/// <param name="dwEffect">Current state of the keyboard and the mouse modifier keys.</param>
|
|
/// <param name="fCancelDrop">If true, then the drop is cancelled by the source hierarchy. If false, then the drop can continue.</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
|
|
public override int OnBeforeDropNotify(IOleDataObject o, uint dwEffect, out int fCancelDrop)
|
|
{
|
|
// If there is nothing to be dropped just return that drop should be cancelled.
|
|
if(this.ItemsDraggedOrCutOrCopied == null)
|
|
{
|
|
fCancelDrop = 1;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
fCancelDrop = 0;
|
|
bool dirty = false;
|
|
foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied)
|
|
{
|
|
bool isDirty, isOpen, isOpenedByUs;
|
|
uint docCookie;
|
|
IVsPersistDocData ppIVsPersistDocData;
|
|
DocumentManager manager = node.GetDocumentManager();
|
|
if(manager != null)
|
|
{
|
|
manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData);
|
|
if(isDirty && isOpenedByUs)
|
|
{
|
|
dirty = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there are no dirty docs we are ok to proceed
|
|
if(!dirty)
|
|
{
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
// Prompt to save if there are dirty docs
|
|
string message = SR.GetString(SR.SaveModifiedDocuments, CultureInfo.CurrentUICulture);
|
|
string title = string.Empty;
|
|
OLEMSGICON icon = OLEMSGICON.OLEMSGICON_WARNING;
|
|
OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL;
|
|
OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST;
|
|
int result = VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons, defaultButton);
|
|
switch(result)
|
|
{
|
|
case NativeMethods.IDYES:
|
|
break;
|
|
|
|
case NativeMethods.IDNO:
|
|
return VSConstants.S_OK;
|
|
|
|
case NativeMethods.IDCANCEL: goto default;
|
|
|
|
default:
|
|
fCancelDrop = 1;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
// Save all dirty documents
|
|
foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied)
|
|
{
|
|
DocumentManager manager = node.GetDocumentManager();
|
|
if(manager != null)
|
|
{
|
|
manager.Save(true);
|
|
}
|
|
}
|
|
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IVsUIHierWinClipboardHelperEvents Members
|
|
/// <summary>
|
|
/// Called after your cut/copied items has been pasted
|
|
/// </summary>
|
|
///<param name="wasCut">If true, then the IDataObject has been successfully pasted into a target hierarchy.
|
|
/// If false, then the cut or copy operation was cancelled.</param>
|
|
/// <param name="dropEffect">Visual effects associated with the drag and drop operation, such as cursors, bitmaps, and so on.
|
|
/// These should be the same visual effects used in OnDropNotify</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
|
|
public virtual int OnPaste(int wasCut, uint dropEffect)
|
|
{
|
|
if(!this.SourceDraggedOrCutOrCopied)
|
|
{
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
if(dropEffect == (uint)DropEffect.None)
|
|
{
|
|
return OnClear(wasCut);
|
|
}
|
|
|
|
this.CleanupSelectionDataObject(false, wasCut != 0, dropEffect == (uint)DropEffect.Move);
|
|
this.SourceDraggedOrCutOrCopied = false;
|
|
return VSConstants.S_OK;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when your cut/copied operation is canceled
|
|
/// </summary>
|
|
/// <param name="wasCut">This flag informs the source that the Cut method was called (true),
|
|
/// rather than Copy (false), so the source knows whether to "un-cut-highlight" the items that were cut.</param>
|
|
/// <returns>If the method succeeds, it returns S_OK. If it fails, it returns an error code. </returns>
|
|
public virtual int OnClear(int wasCut)
|
|
{
|
|
if(!this.SourceDraggedOrCutOrCopied)
|
|
{
|
|
return VSConstants.S_FALSE;
|
|
}
|
|
|
|
this.CleanupSelectionDataObject(false, wasCut != 0, false, true);
|
|
this.SourceDraggedOrCutOrCopied = false;
|
|
return VSConstants.S_OK;
|
|
}
|
|
#endregion
|
|
|
|
#region virtual methods
|
|
/// <summary>
|
|
/// Determines if a node can accept drop opertaion.
|
|
/// </summary>
|
|
/// <param name="itemid">The id of the node.</param>
|
|
/// <returns>true if the node acceots drag operation.</returns>
|
|
protected internal virtual bool CanTargetNodeAcceptDrop(uint itemId)
|
|
{
|
|
HierarchyNode targetNode = NodeFromItemId(itemId);
|
|
if(targetNode is ReferenceContainerNode || targetNode is ReferenceNode)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a dataobject from selected nodes
|
|
/// </summary>
|
|
/// <param name="cutHighlightItems">boolean that defines if the selected items must be cut</param>
|
|
/// <returns>data object for selected items</returns>
|
|
internal virtual DataObject PackageSelectionDataObject(bool cutHighlightItems)
|
|
{
|
|
this.CleanupSelectionDataObject(false, false, false);
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
DataObject dataObject = null;
|
|
|
|
try
|
|
{
|
|
IList<HierarchyNode> selectedNodes = this.GetSelectedNodes();
|
|
if(selectedNodes != null)
|
|
{
|
|
this.InstantiateItemsDraggedOrCutOrCopiedList();
|
|
|
|
StringBuilder selectionContent = null;
|
|
|
|
// If there is a selection package the data
|
|
if(selectedNodes.Count > 1)
|
|
{
|
|
foreach(HierarchyNode node in selectedNodes)
|
|
{
|
|
selectionContent = node.PrepareSelectedNodesForClipBoard();
|
|
if(selectionContent != null)
|
|
{
|
|
sb.Append(selectionContent);
|
|
}
|
|
}
|
|
}
|
|
else if(selectedNodes.Count == 1)
|
|
{
|
|
HierarchyNode selectedNode = selectedNodes[0];
|
|
selectionContent = selectedNode.PrepareSelectedNodesForClipBoard();
|
|
if(selectionContent != null)
|
|
{
|
|
sb.Append(selectionContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the project items first.
|
|
IntPtr ptrToItems = this.PackageSelectionData(sb, false);
|
|
if(ptrToItems == IntPtr.Zero)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
FORMATETC fmt = DragDropHelper.CreateFormatEtc(DragDropHelper.CF_VSSTGPROJECTITEMS);
|
|
dataObject = new DataObject();
|
|
dataObject.SetData(fmt, ptrToItems);
|
|
|
|
// Now add the project path that sourced data. We just write the project file path.
|
|
IntPtr ptrToProjectPath = this.PackageSelectionData(new StringBuilder(this.GetMkDocument()), true);
|
|
|
|
if(ptrToProjectPath != IntPtr.Zero)
|
|
{
|
|
dataObject.SetData(DragDropHelper.CreateFormatEtc(DragDropHelper.CF_VSPROJECTCLIPDESCRIPTOR), ptrToProjectPath);
|
|
}
|
|
|
|
if (cutHighlightItems)
|
|
{
|
|
bool first = true;
|
|
IVsUIHierarchyWindow w = UIHierarchyUtilities.GetUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer);
|
|
|
|
// This happens in the context of cutting multiple items from the project.
|
|
// Since we are already in solution explorer, it is extremely unlikely that we get a null return.
|
|
// If we do, the icons for the cut items will not fade. The cut operation will still succeed.
|
|
if (w != null)
|
|
{
|
|
foreach (HierarchyNode node in this.ItemsDraggedOrCutOrCopied)
|
|
{
|
|
ErrorHandler.ThrowOnFailure(w.ExpandItem(this.InteropSafeIVsUIHierarchy, node.ID, first ? EXPANDFLAGS.EXPF_CutHighlightItem : EXPANDFLAGS.EXPF_AddCutHighlightItem));
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(COMException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
|
|
dataObject = null;
|
|
}
|
|
|
|
return dataObject;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// This is used to recursively add a folder from an other project.
|
|
/// Note that while we copy the folder content completely, we only
|
|
/// add to the project items which are part of the source project.
|
|
/// </summary>
|
|
/// <param name="folderToAdd">Project reference (from data object) using the format: {Guid}|project|folderPath</param>
|
|
/// <param name="targetNode">Node to add the new folder to</param>
|
|
protected internal virtual void AddFolderFromOtherProject(string folderToAdd, HierarchyNode targetNode)
|
|
{
|
|
if(String.IsNullOrEmpty(folderToAdd))
|
|
throw new ArgumentNullException("folderToAdd");
|
|
if(targetNode == null)
|
|
throw new ArgumentNullException("targetNode");
|
|
|
|
// Split the reference in its 3 parts
|
|
int index1 = Guid.Empty.ToString("B").Length;
|
|
if(index1 + 1 >= folderToAdd.Length)
|
|
throw new ArgumentOutOfRangeException("folderToAdd");
|
|
|
|
// Get the Guid
|
|
string guidString = folderToAdd.Substring(1, index1 - 2);
|
|
Guid projectInstanceGuid = new Guid(guidString);
|
|
|
|
// Get the project path
|
|
int index2 = folderToAdd.IndexOf('|', index1 + 1);
|
|
if(index2 < 0 || index2 + 1 >= folderToAdd.Length)
|
|
throw new ArgumentOutOfRangeException("folderToAdd");
|
|
|
|
// Finally get the source path
|
|
string folder = folderToAdd.Substring(index2 + 1);
|
|
|
|
// Get the target path
|
|
string folderName = Path.GetFileName(Path.GetDirectoryName(folder));
|
|
string targetPath = Path.Combine(GetBaseDirectoryForAddingFiles(targetNode), folderName);
|
|
|
|
// Recursively copy the directory to the new location
|
|
Utilities.RecursivelyCopyDirectory(folder, targetPath);
|
|
|
|
// Retrieve the project from which the items are being copied
|
|
IVsHierarchy sourceHierarchy;
|
|
IVsSolution solution = (IVsSolution)GetService(typeof(SVsSolution));
|
|
ErrorHandler.ThrowOnFailure(solution.GetProjectOfGuid(ref projectInstanceGuid, out sourceHierarchy));
|
|
|
|
// Then retrieve the item ID of the item to copy
|
|
uint itemID = VSConstants.VSITEMID_ROOT;
|
|
ErrorHandler.ThrowOnFailure(sourceHierarchy.ParseCanonicalName(folder, out itemID));
|
|
|
|
// Ensure we don't end up in an endless recursion
|
|
if(Utilities.IsSameComObject(this.InteropSafeIVsHierarchy, sourceHierarchy))
|
|
{
|
|
HierarchyNode cursorNode = targetNode;
|
|
while(cursorNode != null)
|
|
{
|
|
if(String.Compare(folder, cursorNode.GetMkDocument(), StringComparison.OrdinalIgnoreCase) == 0)
|
|
throw new Exception();
|
|
cursorNode = cursorNode.Parent;
|
|
}
|
|
}
|
|
|
|
// Now walk the source project hierarchy to see which node needs to be added.
|
|
WalkSourceProjectAndAdd(sourceHierarchy, itemID, targetNode, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recursive method that walk a hierarchy and add items it find to our project.
|
|
/// Note that this is meant as an helper to the Copy&Paste/Drag&Drop functionality.
|
|
/// </summary>
|
|
/// <param name="sourceHierarchy">Hierarchy to walk</param>
|
|
/// <param name="itemId">Item ID where to start walking the hierarchy</param>
|
|
/// <param name="targetNode">Node to start adding to</param>
|
|
/// <param name="addSibblings">Typically false on first call and true after that</param>
|
|
protected virtual void WalkSourceProjectAndAdd(IVsHierarchy sourceHierarchy, uint itemId, HierarchyNode targetNode, bool addSiblings)
|
|
{
|
|
if (sourceHierarchy == null)
|
|
{
|
|
throw new ArgumentNullException("sourceHierarchy");
|
|
}
|
|
|
|
// Before we start the walk, add the current node
|
|
object variant = null;
|
|
HierarchyNode newNode = targetNode;
|
|
if(itemId != VSConstants.VSITEMID_NIL)
|
|
{
|
|
// Calculate the corresponding path in our project
|
|
string source;
|
|
ErrorHandler.ThrowOnFailure(((IVsProject)sourceHierarchy).GetMkDocument(itemId, out source));
|
|
string name = Path.GetFileName(source.TrimEnd(new char[] { '/', '\\' }));
|
|
string targetPath = Path.Combine(GetBaseDirectoryForAddingFiles(targetNode), name);
|
|
|
|
// See if this is a linked item (file can be linked, not folders)
|
|
ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_BrowseObject, out variant), VSConstants.E_NOTIMPL);
|
|
VSLangProj.FileProperties fileProperties = variant as VSLangProj.FileProperties;
|
|
if(fileProperties != null && fileProperties.IsLink)
|
|
{
|
|
// Since we don't support linked item, we make a copy of the file into our storage where it would have been linked
|
|
File.Copy(source, targetPath, true);
|
|
}
|
|
|
|
newNode = AddNodeIfTargetExistInStorage(targetNode, name, targetPath);
|
|
|
|
|
|
// Start with child nodes (depth first)
|
|
variant = null;
|
|
ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_FirstVisibleChild, out variant));
|
|
uint currentItemID = (uint)(int)variant;
|
|
WalkSourceProjectAndAdd(sourceHierarchy, currentItemID, newNode, true);
|
|
|
|
if(addSiblings)
|
|
{
|
|
// Then look at siblings
|
|
currentItemID = itemId;
|
|
while(currentItemID != VSConstants.VSITEMID_NIL)
|
|
{
|
|
variant = null;
|
|
ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_NextVisibleSibling, out variant));
|
|
currentItemID = (uint)(int)variant;
|
|
WalkSourceProjectAndAdd(sourceHierarchy, currentItemID, targetNode, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add an existing item (file/folder) to the project if it already exist in our storage.
|
|
/// </summary>
|
|
/// <param name="parentNode">Node to that this item to</param>
|
|
/// <param name="name">Name of the item being added</param>
|
|
/// <param name="targetPath">Path of the item being added</param>
|
|
/// <returns>Node that was added</returns>
|
|
protected virtual HierarchyNode AddNodeIfTargetExistInStorage(HierarchyNode parentNode, string name, string targetPath)
|
|
{
|
|
if (parentNode == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
HierarchyNode newNode = parentNode;
|
|
// If the file/directory exist, add a node for it
|
|
if(File.Exists(targetPath))
|
|
{
|
|
VSADDRESULT[] result = new VSADDRESULT[1];
|
|
ErrorHandler.ThrowOnFailure(this.AddItem(parentNode.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, name, 1, new string[] { targetPath }, IntPtr.Zero, result));
|
|
if(result[0] != VSADDRESULT.ADDRESULT_Success)
|
|
throw new Exception();
|
|
newNode = this.FindChild(targetPath);
|
|
if(newNode == null)
|
|
throw new Exception();
|
|
}
|
|
else if(Directory.Exists(targetPath))
|
|
{
|
|
newNode = this.CreateFolderNodes(targetPath);
|
|
}
|
|
return newNode;
|
|
}
|
|
#endregion
|
|
|
|
#region non-virtual methods
|
|
/// <summary>
|
|
/// Handle the Cut operation to the clipboard
|
|
/// </summary>
|
|
protected internal override int CutToClipboard()
|
|
{
|
|
int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
|
|
try
|
|
{
|
|
this.RegisterClipboardNotifications(true);
|
|
|
|
// Create our data object and change the selection to show item(s) being cut
|
|
IOleDataObject dataObject = this.PackageSelectionDataObject(true);
|
|
if(dataObject != null)
|
|
{
|
|
this.SourceDraggedOrCutOrCopied = true;
|
|
|
|
// Add our cut item(s) to the clipboard
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleSetClipboard(dataObject));
|
|
|
|
// Inform VS (UiHierarchyWindow) of the cut
|
|
IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper));
|
|
if(clipboardHelper == null)
|
|
{
|
|
return VSConstants.E_FAIL;
|
|
}
|
|
|
|
returnValue = ErrorHandler.ThrowOnFailure(clipboardHelper.Cut(dataObject));
|
|
}
|
|
}
|
|
catch(COMException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
returnValue = e.ErrorCode;
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle the Copy operation to the clipboard
|
|
/// </summary>
|
|
protected internal override int CopyToClipboard()
|
|
{
|
|
int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
|
|
try
|
|
{
|
|
this.RegisterClipboardNotifications(true);
|
|
|
|
// Create our data object and change the selection to show item(s) being copy
|
|
IOleDataObject dataObject = this.PackageSelectionDataObject(false);
|
|
if(dataObject != null)
|
|
{
|
|
this.SourceDraggedOrCutOrCopied = true;
|
|
|
|
// Add our copy item(s) to the clipboard
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleSetClipboard(dataObject));
|
|
|
|
// Inform VS (UiHierarchyWindow) of the copy
|
|
IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper));
|
|
if(clipboardHelper == null)
|
|
{
|
|
return VSConstants.E_FAIL;
|
|
}
|
|
returnValue = ErrorHandler.ThrowOnFailure(clipboardHelper.Copy(dataObject));
|
|
}
|
|
}
|
|
catch(COMException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
returnValue = e.ErrorCode;
|
|
}
|
|
catch(ArgumentException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
returnValue = Marshal.GetHRForException(e);
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle the Paste operation to a targetNode
|
|
/// </summary>
|
|
protected internal override int PasteFromClipboard(HierarchyNode targetNode)
|
|
{
|
|
int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED;
|
|
|
|
if (targetNode == null)
|
|
{
|
|
return VSConstants.E_INVALIDARG;
|
|
}
|
|
|
|
//Get the clipboardhelper service and use it after processing dataobject
|
|
IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper));
|
|
if(clipboardHelper == null)
|
|
{
|
|
return VSConstants.E_FAIL;
|
|
}
|
|
|
|
try
|
|
{
|
|
//Get dataobject from clipboard
|
|
IOleDataObject dataObject = null;
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out dataObject));
|
|
if(dataObject == null)
|
|
{
|
|
return VSConstants.E_UNEXPECTED;
|
|
}
|
|
|
|
DropEffect dropEffect = DropEffect.None;
|
|
DropDataType dropDataType = DropDataType.None;
|
|
try
|
|
{
|
|
dropDataType = this.ProcessSelectionDataObject(dataObject, targetNode.GetDragTargetHandlerNode());
|
|
dropEffect = this.QueryDropEffect(dropDataType, 0);
|
|
}
|
|
catch(ExternalException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
|
|
// If it is a drop from windows and we get any kind of error ignore it. This
|
|
// prevents bogus messages from the shell from being displayed
|
|
if(dropDataType != DropDataType.Shell)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
// Inform VS (UiHierarchyWindow) of the paste
|
|
returnValue = clipboardHelper.Paste(dataObject, (uint)dropEffect);
|
|
}
|
|
}
|
|
catch(COMException e)
|
|
{
|
|
Trace.WriteLine("Exception : " + e.Message);
|
|
|
|
returnValue = e.ErrorCode;
|
|
}
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the paste command should be allowed.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected internal override bool AllowPasteCommand()
|
|
{
|
|
IOleDataObject dataObject = null;
|
|
try
|
|
{
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out dataObject));
|
|
if(dataObject == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// First see if this is a set of storage based items
|
|
FORMATETC format = DragDropHelper.CreateFormatEtc((ushort)DragDropHelper.CF_VSSTGPROJECTITEMS);
|
|
if(dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK)
|
|
return true;
|
|
// Try reference based items
|
|
format = DragDropHelper.CreateFormatEtc((ushort)DragDropHelper.CF_VSREFPROJECTITEMS);
|
|
if(dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK)
|
|
return true;
|
|
// Try windows explorer files format
|
|
format = DragDropHelper.CreateFormatEtc((ushort)NativeMethods.CF_HDROP);
|
|
return (dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK);
|
|
}
|
|
// We catch External exceptions since it might be that it is not our data on the clipboard.
|
|
catch(ExternalException e)
|
|
{
|
|
Trace.WriteLine("Exception :" + e.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register/Unregister for Clipboard events for the UiHierarchyWindow (solution explorer)
|
|
/// </summary>
|
|
/// <param name="register">true for register, false for unregister</param>
|
|
protected internal override void RegisterClipboardNotifications(bool register)
|
|
{
|
|
// Get the UiHierarchy window clipboard helper service
|
|
IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper));
|
|
if(clipboardHelper == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(register && this.copyPasteCookie == 0)
|
|
{
|
|
// Register
|
|
ErrorHandler.ThrowOnFailure(clipboardHelper.AdviseClipboardHelperEvents(this.InteropSafeIVsUIHierWinClipboardHelperEvents, out this.copyPasteCookie));
|
|
Debug.Assert(this.copyPasteCookie != 0, "AdviseClipboardHelperEvents returned an invalid cookie");
|
|
}
|
|
else if(!register && this.copyPasteCookie != 0)
|
|
{
|
|
// Unregister
|
|
ErrorHandler.ThrowOnFailure(clipboardHelper.UnadviseClipboardHelperEvents(this.copyPasteCookie));
|
|
this.copyPasteCookie = 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process dataobject from Drag/Drop/Cut/Copy/Paste operation
|
|
/// </summary>
|
|
/// <remarks>The targetNode is set if the method is called from a drop operation, otherwise it is null</remarks>
|
|
internal DropDataType ProcessSelectionDataObject(IOleDataObject dataObject, HierarchyNode targetNode)
|
|
{
|
|
DropDataType dropDataType = DropDataType.None;
|
|
bool isWindowsFormat = false;
|
|
|
|
// Try to get it as a directory based project.
|
|
List<string> filesDropped = DragDropHelper.GetDroppedFiles(DragDropHelper.CF_VSSTGPROJECTITEMS, dataObject, out dropDataType);
|
|
if(filesDropped.Count == 0)
|
|
{
|
|
filesDropped = DragDropHelper.GetDroppedFiles(DragDropHelper.CF_VSREFPROJECTITEMS, dataObject, out dropDataType);
|
|
}
|
|
if(filesDropped.Count == 0)
|
|
{
|
|
filesDropped = DragDropHelper.GetDroppedFiles(NativeMethods.CF_HDROP, dataObject, out dropDataType);
|
|
isWindowsFormat = (filesDropped.Count > 0);
|
|
}
|
|
|
|
if(dropDataType != DropDataType.None && filesDropped.Count > 0)
|
|
{
|
|
string[] filesDroppedAsArray = filesDropped.ToArray();
|
|
|
|
HierarchyNode node = (targetNode == null) ? this : targetNode;
|
|
|
|
// For directory based projects the content of the clipboard is a double-NULL terminated list of Projref strings.
|
|
if(isWindowsFormat)
|
|
{
|
|
// This is the code path when source is windows explorer
|
|
VSADDRESULT[] vsaddresults = new VSADDRESULT[1];
|
|
vsaddresults[0] = VSADDRESULT.ADDRESULT_Failure;
|
|
int addResult = AddItem(node.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, (uint)filesDropped.Count, filesDroppedAsArray, IntPtr.Zero, vsaddresults);
|
|
if(addResult != VSConstants.S_OK && addResult != VSConstants.S_FALSE && addResult != (int)OleConstants.OLECMDERR_E_CANCELED
|
|
&& vsaddresults[0] != VSADDRESULT.ADDRESULT_Success)
|
|
{
|
|
ErrorHandler.ThrowOnFailure(addResult);
|
|
}
|
|
|
|
return dropDataType;
|
|
}
|
|
else
|
|
{
|
|
if(AddFilesFromProjectReferences(node, filesDroppedAsArray))
|
|
{
|
|
return dropDataType;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we reached this point then the drop data must be set to None.
|
|
// Otherwise the OnPaste will be called with a valid DropData and that would actually delete the item.
|
|
return DropDataType.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the dropdatatype from the dataobject
|
|
/// </summary>
|
|
/// <param name="pDataObject">The dataobject to be analysed for its format</param>
|
|
/// <returns>dropdatatype or none if dataobject does not contain known format</returns>
|
|
internal static DropDataType QueryDropDataType(IOleDataObject pDataObject)
|
|
{
|
|
if(pDataObject == null)
|
|
{
|
|
return DropDataType.None;
|
|
}
|
|
|
|
// known formats include File Drops (as from WindowsExplorer),
|
|
// VSProject Reference Items and VSProject Storage Items.
|
|
FORMATETC fmt = DragDropHelper.CreateFormatEtc(NativeMethods.CF_HDROP);
|
|
|
|
if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK)
|
|
{
|
|
return DropDataType.Shell;
|
|
}
|
|
|
|
fmt.cfFormat = DragDropHelper.CF_VSREFPROJECTITEMS;
|
|
if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK)
|
|
{
|
|
// Data is from a Ref-based project.
|
|
return DropDataType.VsRef;
|
|
}
|
|
|
|
fmt.cfFormat = DragDropHelper.CF_VSSTGPROJECTITEMS;
|
|
if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK)
|
|
{
|
|
return DropDataType.VsStg;
|
|
}
|
|
|
|
return DropDataType.None;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the drop effect.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// // A directory based project should perform as follow:
|
|
/// NO MODIFIER
|
|
/// - COPY if not from current hierarchy,
|
|
/// - MOVE if from current hierarchy
|
|
/// SHIFT DRAG - MOVE
|
|
/// CTRL DRAG - COPY
|
|
/// CTRL-SHIFT DRAG - NO DROP (used for reference based projects only)
|
|
/// </remarks>
|
|
internal DropEffect QueryDropEffect(DropDataType dropDataType, uint grfKeyState)
|
|
{
|
|
//Validate the dropdatatype
|
|
if((dropDataType != DropDataType.Shell) && (dropDataType != DropDataType.VsRef) && (dropDataType != DropDataType.VsStg))
|
|
{
|
|
return DropEffect.None;
|
|
}
|
|
|
|
// CTRL-SHIFT
|
|
if((grfKeyState & NativeMethods.MK_CONTROL) != 0 && (grfKeyState & NativeMethods.MK_SHIFT) != 0)
|
|
{
|
|
// Because we are not referenced base, we don't support link
|
|
return DropEffect.None;
|
|
}
|
|
|
|
// CTRL
|
|
if((grfKeyState & NativeMethods.MK_CONTROL) != 0)
|
|
return DropEffect.Copy;
|
|
|
|
// SHIFT
|
|
if((grfKeyState & NativeMethods.MK_SHIFT) != 0)
|
|
return DropEffect.Move;
|
|
|
|
// no modifier
|
|
if(this.SourceDraggedOrCutOrCopied)
|
|
{
|
|
return DropEffect.Move;
|
|
}
|
|
else
|
|
{
|
|
return DropEffect.Copy;
|
|
}
|
|
}
|
|
|
|
internal void CleanupSelectionDataObject(bool dropped, bool cut, bool moved)
|
|
{
|
|
this.CleanupSelectionDataObject(dropped, cut, moved, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// After a drop or paste, will use the dwEffects
|
|
/// to determine whether we need to clean up the source nodes or not. If
|
|
/// justCleanup is set, it only does the cleanup work.
|
|
/// </summary>
|
|
internal void CleanupSelectionDataObject(bool dropped, bool cut, bool moved, bool justCleanup)
|
|
{
|
|
if(this.ItemsDraggedOrCutOrCopied == null || this.ItemsDraggedOrCutOrCopied.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
IVsUIHierarchyWindow w = UIHierarchyUtilities.GetUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer);
|
|
foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied)
|
|
{
|
|
if((moved && (cut || dropped) && !justCleanup))
|
|
{
|
|
// do not close it if the doc is dirty or we do not own it
|
|
bool isDirty, isOpen, isOpenedByUs;
|
|
uint docCookie;
|
|
IVsPersistDocData ppIVsPersistDocData;
|
|
DocumentManager manager = node.GetDocumentManager();
|
|
if(manager != null)
|
|
{
|
|
manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData);
|
|
if(isDirty || (isOpen && !isOpenedByUs))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// close it if opened
|
|
if(isOpen)
|
|
{
|
|
manager.Close(__FRAMECLOSE.FRAMECLOSE_NoSave);
|
|
}
|
|
}
|
|
|
|
node.Remove(true);
|
|
}
|
|
else if(w != null)
|
|
{
|
|
ErrorHandler.ThrowOnFailure(w.ExpandItem(this.InteropSafeIVsUIHierarchy, node.ID, EXPANDFLAGS.EXPF_UnCutHighlightItem));
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
// Now delete the memory allocated by the packaging of datasources.
|
|
// If we just did a cut, or we are told to cleanup, then we need to free the data object. Otherwise, we leave it
|
|
// alone so that you can continue to paste the data in new locations.
|
|
if(moved || cut || justCleanup)
|
|
{
|
|
this.ItemsDraggedOrCutOrCopied.Clear();
|
|
this.CleanAndFlushClipboard();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
this.dropDataType = DropDataType.None;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves files from one part of our project to another.
|
|
/// </summary>
|
|
/// <param name="targetNode">the targetHandler node</param>
|
|
/// <param name="projectReferences">List of projectref string</param>
|
|
/// <returns>true if succeeded</returns>
|
|
internal bool AddFilesFromProjectReferences(HierarchyNode targetNode, string[] projectReferences)
|
|
{
|
|
//Validate input
|
|
if(projectReferences == null)
|
|
{
|
|
throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "projectReferences");
|
|
}
|
|
if(targetNode == null)
|
|
{
|
|
throw new InvalidOperationException();
|
|
}
|
|
|
|
//Iteratively add files from projectref
|
|
foreach(string projectReference in projectReferences)
|
|
{
|
|
if(projectReference == null)
|
|
{
|
|
// bad projectref, bail out
|
|
return false;
|
|
}
|
|
if(projectReference.EndsWith("/", StringComparison.Ordinal) || projectReference.EndsWith("\\", StringComparison.Ordinal))
|
|
{
|
|
AddFolderFromOtherProject(projectReference, targetNode);
|
|
}
|
|
else if(!AddFileToNodeFromProjectReference(projectReference, targetNode))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region private helper methods
|
|
/// <summary>
|
|
/// Empties all the data structures added to the clipboard and flushes the clipboard.
|
|
/// </summary>
|
|
private void CleanAndFlushClipboard()
|
|
{
|
|
IOleDataObject oleDataObject = null;
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out oleDataObject));
|
|
if(oleDataObject == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
string sourceProjectPath = DragDropHelper.GetSourceProjectPath(oleDataObject);
|
|
|
|
if(!String.IsNullOrEmpty(sourceProjectPath) && NativeMethods.IsSamePath(sourceProjectPath, this.GetMkDocument()))
|
|
{
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleFlushClipboard());
|
|
int clipboardOpened = 0;
|
|
try
|
|
{
|
|
ErrorHandler.ThrowOnFailure(clipboardOpened = UnsafeNativeMethods.OpenClipboard(IntPtr.Zero));
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.EmptyClipboard());
|
|
}
|
|
finally
|
|
{
|
|
if(clipboardOpened == 1)
|
|
{
|
|
ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.CloseClipboard());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private IntPtr PackageSelectionData(StringBuilder sb, bool addEndFormatDelimiter)
|
|
{
|
|
if(sb == null || sb.ToString().Length == 0 || this.ItemsDraggedOrCutOrCopied.Count == 0)
|
|
{
|
|
return IntPtr.Zero;
|
|
}
|
|
|
|
// Double null at end.
|
|
if(addEndFormatDelimiter)
|
|
{
|
|
if(sb.ToString()[sb.Length - 1] != '\0')
|
|
{
|
|
sb.Append('\0');
|
|
}
|
|
}
|
|
|
|
// We request unmanaged permission to execute the below.
|
|
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
|
|
|
|
_DROPFILES df = new _DROPFILES();
|
|
int dwSize = Marshal.SizeOf(df);
|
|
Int16 wideChar = 0;
|
|
int dwChar = Marshal.SizeOf(wideChar);
|
|
int structSize = dwSize + ((sb.Length + 1) * dwChar);
|
|
IntPtr ptr = Marshal.AllocHGlobal(structSize);
|
|
df.pFiles = dwSize;
|
|
df.fWide = 1;
|
|
IntPtr data = IntPtr.Zero;
|
|
try
|
|
{
|
|
data = UnsafeNativeMethods.GlobalLock(ptr);
|
|
Marshal.StructureToPtr(df, data, false);
|
|
IntPtr strData = new IntPtr((long)data + dwSize);
|
|
DragDropHelper.CopyStringToHGlobal(sb.ToString(), strData, structSize);
|
|
}
|
|
finally
|
|
{
|
|
if(data != IntPtr.Zero)
|
|
UnsafeNativeMethods.GlobalUnLock(data);
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|