OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections; | |
5 using System.Collections.Generic; | |
6 using System.Diagnostics; | |
7 using System.Globalization; | |
8 using System.IO; | |
9 using System.Runtime.InteropServices; | |
10 using System.Security.Permissions; | |
11 using System.Text; | |
12 using Microsoft.VisualStudio; | |
13 using Microsoft.VisualStudio.OLE.Interop; | |
14 using Microsoft.VisualStudio.Shell; | |
15 using Microsoft.VisualStudio.Shell.Interop; | |
16 using IOleDataObject = Microsoft.VisualStudio.OLE.Interop.IDataObject; | |
17 using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; | |
18 | |
19 namespace Microsoft.VisualStudio.Project | |
20 { | |
21 /// <summary> | |
22 /// Manages the CopyPaste and Drag and Drop scenarios for a Project. | |
23 /// </summary> | |
24 /// <remarks>This is a partial class.</remarks> | |
25 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainabil
ity", "CA1506:AvoidExcessiveClassCoupling")] | |
26 public partial class ProjectNode : IVsUIHierWinClipboardHelperEvents | |
27 { | |
28 #region fields | |
29 private uint copyPasteCookie; | |
30 private DropDataType dropDataType; | |
31 #endregion | |
32 | |
33 #region override of IVsHierarchyDropDataTarget methods | |
34 /// <summary> | |
35 /// Called as soon as the mouse drags an item over a new hierarc
hy or hierarchy window | |
36 /// </summary> | |
37 /// <param name="pDataObject">reference to interface IDataObject
of the item being dragged</param> | |
38 /// <param name="grfKeyState">Current state of the keyboard and
the mouse modifier keys. See docs for a list of possible values</param> | |
39 /// <param name="itemid">Item identifier for the item currently
being dragged</param> | |
40 /// <param name="pdwEffect">On entry, a pointer to the current D
ropEffect. On return, must contain the new valid DropEffect</param> | |
41 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
42 public override int DragEnter(IOleDataObject pDataObject, uint g
rfKeyState, uint itemid, ref uint pdwEffect) | |
43 { | |
44 pdwEffect = (uint)DropEffect.None; | |
45 | |
46 if(this.SourceDraggedOrCutOrCopied) | |
47 { | |
48 return VSConstants.S_OK; | |
49 } | |
50 | |
51 this.dropDataType = QueryDropDataType(pDataObject); | |
52 if(this.dropDataType != DropDataType.None) | |
53 { | |
54 pdwEffect = (uint)this.QueryDropEffect(this.drop
DataType, grfKeyState); | |
55 } | |
56 | |
57 return VSConstants.S_OK; | |
58 } | |
59 | |
60 /// <summary> | |
61 /// Called when one or more items are dragged out of the hierarc
hy or hierarchy window, or when the drag-and-drop operation is cancelled or comp
leted. | |
62 /// </summary> | |
63 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
64 public override int DragLeave() | |
65 { | |
66 this.dropDataType = DropDataType.None; | |
67 return VSConstants.S_OK; | |
68 } | |
69 | |
70 /// <summary> | |
71 /// Called when one or more items are dragged over the target hi
erarchy or hierarchy window. | |
72 /// </summary> | |
73 /// <param name="grfKeyState">Current state of the keyboard keys
and the mouse modifier buttons. See <seealso cref="IVsHierarchyDropDataTarget"/
></param> | |
74 /// <param name="itemid">Item identifier of the drop data target
over which the item is being dragged</param> | |
75 /// <param name="pdwEffect"> On entry, reference to the value of
the pdwEffect parameter of the IVsHierarchy object, identifying all effects tha
t the hierarchy supports. | |
76 /// On return, the pdwEffect parameter must contain one of the e
ffect flags that indicate the result of the drop operation. For a list of pwdEff
ects values, see <seealso cref="DragEnter"/></param> | |
77 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
78 public override int DragOver(uint grfKeyState, uint itemid, ref
uint pdwEffect) | |
79 { | |
80 pdwEffect = (uint)DropEffect.None; | |
81 | |
82 // Dragging items to a project that is being debugged is
not supported | |
83 // (see VSWhidbey 144785) | |
84 DBGMODE dbgMode = VsShellUtilities.GetDebugMode(this.Sit
e) & ~DBGMODE.DBGMODE_EncMask; | |
85 if(dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.
DBGMODE_Break) | |
86 { | |
87 return VSConstants.S_OK; | |
88 } | |
89 | |
90 if(this.isClosed || this.site == null) | |
91 { | |
92 return VSConstants.E_UNEXPECTED; | |
93 } | |
94 | |
95 // We should also analyze if the node being dragged over
can accept the drop. | |
96 if(!this.CanTargetNodeAcceptDrop(itemid)) | |
97 { | |
98 return VSConstants.E_NOTIMPL; | |
99 } | |
100 | |
101 if(this.dropDataType != DropDataType.None) | |
102 { | |
103 pdwEffect = (uint)this.QueryDropEffect(this.drop
DataType, grfKeyState); | |
104 } | |
105 | |
106 return VSConstants.S_OK; | |
107 } | |
108 | |
109 /// <summary> | |
110 /// Called when one or more items are dropped into the target hi
erarchy or hierarchy window when the mouse button is released. | |
111 /// </summary> | |
112 /// <param name="pDataObject">Reference to the IDataObject inter
face on the item being dragged. This data object contains the data being transfe
rred in the drag-and-drop operation. | |
113 /// If the drop occurs, then this data object (item) is incorpor
ated into the target hierarchy or hierarchy window.</param> | |
114 /// <param name="grfKeyState">Current state of the keyboard and
the mouse modifier keys. See <seealso cref="IVsHierarchyDropDataTarget"/></param
> | |
115 /// <param name="itemid">Item identifier of the drop data target
over which the item is being dragged</param> | |
116 /// <param name="pdwEffect">Visual effects associated with the d
rag-and drop-operation, such as a cursor, bitmap, and so on. | |
117 /// The value of dwEffects passed to the source object via the O
nDropNotify method is the value of pdwEffects returned by the Drop method</param
> | |
118 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
119 public override int Drop(IOleDataObject pDataObject, uint grfKey
State, uint itemid, ref uint pdwEffect) | |
120 { | |
121 if(pDataObject == null) | |
122 { | |
123 return VSConstants.E_INVALIDARG; | |
124 } | |
125 | |
126 pdwEffect = (uint)DropEffect.None; | |
127 | |
128 // Get the node that is being dragged over and ask it wh
ich node should handle this call | |
129 HierarchyNode targetNode = NodeFromItemId(itemid); | |
130 if(targetNode != null) | |
131 { | |
132 targetNode = targetNode.GetDragTargetHandlerNode
(); | |
133 } | |
134 else | |
135 { | |
136 // There is no target node. The drop can not be
completed. | |
137 return VSConstants.S_FALSE; | |
138 } | |
139 | |
140 int returnValue; | |
141 try | |
142 { | |
143 DropDataType dropDataType = DropDataType.None; | |
144 dropDataType = ProcessSelectionDataObject(pDataO
bject, targetNode); | |
145 pdwEffect = (uint)this.QueryDropEffect(dropDataT
ype, grfKeyState); | |
146 | |
147 // If it is a drop from windows and we get any k
ind of error we return S_FALSE and dropeffect none. This | |
148 // prevents bogus messages from the shell from b
eing displayed | |
149 returnValue = (dropDataType != DropDataType.Shel
l) ? VSConstants.E_FAIL : VSConstants.S_OK; | |
150 } | |
151 catch(System.IO.FileNotFoundException e) | |
152 { | |
153 Trace.WriteLine("Exception : " + e.Message); | |
154 | |
155 if(!Utilities.IsInAutomationFunction(this.Site)) | |
156 { | |
157 string message = e.Message; | |
158 string title = string.Empty; | |
159 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_
CRITICAL; | |
160 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEM
SGBUTTON_OK; | |
161 OLEMSGDEFBUTTON defaultButton = OLEMSGDE
FBUTTON.OLEMSGDEFBUTTON_FIRST; | |
162 VsShellUtilities.ShowMessageBox(this.Sit
e, title, message, icon, buttons, defaultButton); | |
163 } | |
164 | |
165 returnValue = VSConstants.E_FAIL; | |
166 } | |
167 | |
168 return returnValue; | |
169 } | |
170 #endregion | |
171 | |
172 #region override of IVsHierarchyDropDataSource2 methods | |
173 /// <summary> | |
174 /// Returns information about one or more of the items being dra
gged | |
175 /// </summary> | |
176 /// <param name="pdwOKEffects">Pointer to a DWORD value describi
ng the effects displayed while the item is being dragged, | |
177 /// such as cursor icons that change during the drag-and-drop op
eration. | |
178 /// For example, if the item is dragged over an invalid target p
oint | |
179 /// (such as the item's original location), the cursor icon chan
ges to a circle with a line through it. | |
180 /// Similarly, if the item is dragged over a valid target point,
the cursor icon changes to a file or folder.</param> | |
181 /// <param name="ppDataObject">Pointer to the IDataObject interf
ace on the item being dragged. | |
182 /// This data object contains the data being transferred in the
drag-and-drop operation. | |
183 /// If the drop occurs, then this data object (item) is incorpor
ated into the target hierarchy or hierarchy window.</param> | |
184 /// <param name="ppDropSource">Pointer to the IDropSource interf
ace of the item being dragged.</param> | |
185 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
186 public override int GetDropInfo(out uint pdwOKEffects, out IOleD
ataObject ppDataObject, out IDropSource ppDropSource) | |
187 { | |
188 //init out params | |
189 pdwOKEffects = (uint)DropEffect.None; | |
190 ppDataObject = null; | |
191 ppDropSource = null; | |
192 | |
193 IOleDataObject dataObject = PackageSelectionDataObject(f
alse); | |
194 if(dataObject == null) | |
195 { | |
196 return VSConstants.E_NOTIMPL; | |
197 } | |
198 | |
199 this.SourceDraggedOrCutOrCopied = true; | |
200 | |
201 pdwOKEffects = (uint)(DropEffect.Move | DropEffect.Copy)
; | |
202 | |
203 ppDataObject = dataObject; | |
204 return VSConstants.S_OK; | |
205 } | |
206 | |
207 /// <summary> | |
208 /// Notifies clients that the dragged item was dropped. | |
209 /// </summary> | |
210 /// <param name="fDropped">If true, then the dragged item was dr
opped on the target. If false, then the drop did not occur.</param> | |
211 /// <param name="dwEffects">Visual effects associated with the d
rag-and-drop operation, such as cursors, bitmaps, and so on. | |
212 /// The value of dwEffects passed to the source object via OnDro
pNotify method is the value of pdwEffects returned by Drop method.</param> | |
213 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
214 public override int OnDropNotify(int fDropped, uint dwEffects) | |
215 { | |
216 if(!this.SourceDraggedOrCutOrCopied) | |
217 { | |
218 return VSConstants.S_FALSE; | |
219 } | |
220 | |
221 this.CleanupSelectionDataObject(fDropped != 0, false, dw
Effects == (uint)DropEffect.Move); | |
222 | |
223 this.SourceDraggedOrCutOrCopied = false; | |
224 | |
225 return VSConstants.S_OK; | |
226 } | |
227 | |
228 /// <summary> | |
229 /// Allows the drag source to prompt to save unsaved items being
dropped. | |
230 /// Notifies the source hierarchy that information dragged from
it is about to be dropped on a target. | |
231 /// This method is called immediately after the mouse button is
released on a drop. | |
232 /// </summary> | |
233 /// <param name="o">Reference to the IDataObject interface on th
e item being dragged. | |
234 /// This data object contains the data being transferred in the
drag-and-drop operation. | |
235 /// If the drop occurs, then this data object (item) is incorpor
ated into the hierarchy window of the new hierarchy.</param> | |
236 /// <param name="dwEffect">Current state of the keyboard and the
mouse modifier keys.</param> | |
237 /// <param name="fCancelDrop">If true, then the drop is cancelle
d by the source hierarchy. If false, then the drop can continue.</param> | |
238 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
239 public override int OnBeforeDropNotify(IOleDataObject o, uint dw
Effect, out int fCancelDrop) | |
240 { | |
241 // If there is nothing to be dropped just return that dr
op should be cancelled. | |
242 if(this.ItemsDraggedOrCutOrCopied == null) | |
243 { | |
244 fCancelDrop = 1; | |
245 return VSConstants.S_OK; | |
246 } | |
247 | |
248 fCancelDrop = 0; | |
249 bool dirty = false; | |
250 foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCo
pied) | |
251 { | |
252 bool isDirty, isOpen, isOpenedByUs; | |
253 uint docCookie; | |
254 IVsPersistDocData ppIVsPersistDocData; | |
255 DocumentManager manager = node.GetDocumentManage
r(); | |
256 if(manager != null) | |
257 { | |
258 manager.GetDocInfo(out isOpen, out isDir
ty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData); | |
259 if(isDirty && isOpenedByUs) | |
260 { | |
261 dirty = true; | |
262 break; | |
263 } | |
264 } | |
265 } | |
266 | |
267 // if there are no dirty docs we are ok to proceed | |
268 if(!dirty) | |
269 { | |
270 return VSConstants.S_OK; | |
271 } | |
272 | |
273 // Prompt to save if there are dirty docs | |
274 string message = SR.GetString(SR.SaveModifiedDocuments,
CultureInfo.CurrentUICulture); | |
275 string title = string.Empty; | |
276 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_WARNING; | |
277 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNOCA
NCEL; | |
278 OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDE
FBUTTON_FIRST; | |
279 int result = VsShellUtilities.ShowMessageBox(Site, title
, message, icon, buttons, defaultButton); | |
280 switch(result) | |
281 { | |
282 case NativeMethods.IDYES: | |
283 break; | |
284 | |
285 case NativeMethods.IDNO: | |
286 return VSConstants.S_OK; | |
287 | |
288 case NativeMethods.IDCANCEL: goto default; | |
289 | |
290 default: | |
291 fCancelDrop = 1; | |
292 return VSConstants.S_OK; | |
293 } | |
294 | |
295 // Save all dirty documents | |
296 foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCo
pied) | |
297 { | |
298 DocumentManager manager = node.GetDocumentManage
r(); | |
299 if(manager != null) | |
300 { | |
301 manager.Save(true); | |
302 } | |
303 } | |
304 | |
305 return VSConstants.S_OK; | |
306 } | |
307 | |
308 #endregion | |
309 | |
310 #region IVsUIHierWinClipboardHelperEvents Members | |
311 /// <summary> | |
312 /// Called after your cut/copied items has been pasted | |
313 /// </summary> | |
314 ///<param name="wasCut">If true, then the IDataObject has been s
uccessfully pasted into a target hierarchy. | |
315 /// If false, then the cut or copy operation was cancelled.</par
am> | |
316 /// <param name="dropEffect">Visual effects associated with the
drag and drop operation, such as cursors, bitmaps, and so on. | |
317 /// These should be the same visual effects used in OnDropNotify
</param> | |
318 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
319 public virtual int OnPaste(int wasCut, uint dropEffect) | |
320 { | |
321 if(!this.SourceDraggedOrCutOrCopied) | |
322 { | |
323 return VSConstants.S_FALSE; | |
324 } | |
325 | |
326 if(dropEffect == (uint)DropEffect.None) | |
327 { | |
328 return OnClear(wasCut); | |
329 } | |
330 | |
331 this.CleanupSelectionDataObject(false, wasCut != 0, drop
Effect == (uint)DropEffect.Move); | |
332 this.SourceDraggedOrCutOrCopied = false; | |
333 return VSConstants.S_OK; | |
334 } | |
335 | |
336 /// <summary> | |
337 /// Called when your cut/copied operation is canceled | |
338 /// </summary> | |
339 /// <param name="wasCut">This flag informs the source that the C
ut method was called (true), | |
340 /// rather than Copy (false), so the source knows whether to "un
-cut-highlight" the items that were cut.</param> | |
341 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code. </returns> | |
342 public virtual int OnClear(int wasCut) | |
343 { | |
344 if(!this.SourceDraggedOrCutOrCopied) | |
345 { | |
346 return VSConstants.S_FALSE; | |
347 } | |
348 | |
349 this.CleanupSelectionDataObject(false, wasCut != 0, fals
e, true); | |
350 this.SourceDraggedOrCutOrCopied = false; | |
351 return VSConstants.S_OK; | |
352 } | |
353 #endregion | |
354 | |
355 #region virtual methods | |
356 /// <summary> | |
357 /// Determines if a node can accept drop opertaion. | |
358 /// </summary> | |
359 /// <param name="itemid">The id of the node.</param> | |
360 /// <returns>true if the node acceots drag operation.</returns> | |
361 protected internal virtual bool CanTargetNodeAcceptDrop(uint ite
mId) | |
362 { | |
363 HierarchyNode targetNode = NodeFromItemId(itemId); | |
364 if(targetNode is ReferenceContainerNode || targetNode is
ReferenceNode) | |
365 { | |
366 return false; | |
367 } | |
368 else | |
369 { | |
370 return true; | |
371 } | |
372 } | |
373 | |
374 /// <summary> | |
375 /// Returns a dataobject from selected nodes | |
376 /// </summary> | |
377 /// <param name="cutHighlightItems">boolean that defines if the
selected items must be cut</param> | |
378 /// <returns>data object for selected items</returns> | |
379 internal virtual DataObject PackageSelectionDataObject(bool cutH
ighlightItems) | |
380 { | |
381 this.CleanupSelectionDataObject(false, false, false); | |
382 StringBuilder sb = new StringBuilder(); | |
383 | |
384 DataObject dataObject = null; | |
385 | |
386 try | |
387 { | |
388 IList<HierarchyNode> selectedNodes = this.GetSel
ectedNodes(); | |
389 if(selectedNodes != null) | |
390 { | |
391 this.InstantiateItemsDraggedOrCutOrCopie
dList(); | |
392 | |
393 StringBuilder selectionContent = null; | |
394 | |
395 // If there is a selection package the d
ata | |
396 if(selectedNodes.Count > 1) | |
397 { | |
398 foreach(HierarchyNode node in se
lectedNodes) | |
399 { | |
400 selectionContent = node.
PrepareSelectedNodesForClipBoard(); | |
401 if(selectionContent != n
ull) | |
402 { | |
403 sb.Append(select
ionContent); | |
404 } | |
405 } | |
406 } | |
407 else if(selectedNodes.Count == 1) | |
408 { | |
409 HierarchyNode selectedNode = sel
ectedNodes[0]; | |
410 selectionContent = selectedNode.
PrepareSelectedNodesForClipBoard(); | |
411 if(selectionContent != null) | |
412 { | |
413 sb.Append(selectionConte
nt); | |
414 } | |
415 } | |
416 } | |
417 | |
418 // Add the project items first. | |
419 IntPtr ptrToItems = this.PackageSelectionData(sb
, false); | |
420 if(ptrToItems == IntPtr.Zero) | |
421 { | |
422 return null; | |
423 } | |
424 | |
425 FORMATETC fmt = DragDropHelper.CreateFormatEtc(D
ragDropHelper.CF_VSSTGPROJECTITEMS); | |
426 dataObject = new DataObject(); | |
427 dataObject.SetData(fmt, ptrToItems); | |
428 | |
429 // Now add the project path that sourced data. W
e just write the project file path. | |
430 IntPtr ptrToProjectPath = this.PackageSelectionD
ata(new StringBuilder(this.GetMkDocument()), true); | |
431 | |
432 if(ptrToProjectPath != IntPtr.Zero) | |
433 { | |
434 dataObject.SetData(DragDropHelper.Create
FormatEtc(DragDropHelper.CF_VSPROJECTCLIPDESCRIPTOR), ptrToProjectPath); | |
435 } | |
436 | |
437 if(cutHighlightItems) | |
438 { | |
439 bool first = true; | |
440 IVsUIHierarchyWindow w = UIHierarchyUtil
ities.GetUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer); | |
441 | |
442 foreach(HierarchyNode node in this.Items
DraggedOrCutOrCopied) | |
443 { | |
444 ErrorHandler.ThrowOnFailure(w.Ex
pandItem((IVsUIHierarchy)this, node.ID, first ? EXPANDFLAGS.EXPF_CutHighlightIte
m : EXPANDFLAGS.EXPF_AddCutHighlightItem)); | |
445 first = false; | |
446 } | |
447 } | |
448 } | |
449 catch(COMException e) | |
450 { | |
451 Trace.WriteLine("Exception : " + e.Message); | |
452 | |
453 dataObject = null; | |
454 } | |
455 | |
456 return dataObject; | |
457 } | |
458 | |
459 | |
460 /// <summary> | |
461 /// This is used to recursively add a folder from an other proje
ct. | |
462 /// Note that while we copy the folder content completely, we on
ly | |
463 /// add to the project items which are part of the source projec
t. | |
464 /// </summary> | |
465 /// <param name="folderToAdd">Project reference (from data objec
t) using the format: {Guid}|project|folderPath</param> | |
466 /// <param name="targetNode">Node to add the new folder to</para
m> | |
467 protected internal virtual void AddFolderFromOtherProject(string
folderToAdd, HierarchyNode targetNode) | |
468 { | |
469 if(String.IsNullOrEmpty(folderToAdd)) | |
470 throw new ArgumentNullException("folderToAdd"); | |
471 if(targetNode == null) | |
472 throw new ArgumentNullException("targetNode"); | |
473 | |
474 // Split the reference in its 3 parts | |
475 int index1 = Guid.Empty.ToString("B").Length; | |
476 if(index1 + 1 >= folderToAdd.Length) | |
477 throw new ArgumentOutOfRangeException("folderToA
dd"); | |
478 | |
479 // Get the Guid | |
480 string guidString = folderToAdd.Substring(1, index1 - 2)
; | |
481 Guid projectInstanceGuid = new Guid(guidString); | |
482 | |
483 // Get the project path | |
484 int index2 = folderToAdd.IndexOf('|', index1 + 1); | |
485 if(index2 < 0 || index2 + 1 >= folderToAdd.Length) | |
486 throw new ArgumentOutOfRangeException("folderToA
dd"); | |
487 | |
488 // Finally get the source path | |
489 string folder = folderToAdd.Substring(index2 + 1); | |
490 | |
491 // Get the target path | |
492 string folderName = Path.GetFileName(Path.GetDirectoryNa
me(folder)); | |
493 string targetPath = Path.Combine(GetBaseDirectoryForAddi
ngFiles(targetNode), folderName); | |
494 | |
495 // Recursively copy the directory to the new location | |
496 Utilities.RecursivelyCopyDirectory(folder, targetPath); | |
497 | |
498 // Retrieve the project from which the items are being c
opied | |
499 IVsHierarchy sourceHierarchy; | |
500 IVsSolution solution = (IVsSolution)GetService(typeof(SV
sSolution)); | |
501 ErrorHandler.ThrowOnFailure(solution.GetProjectOfGuid(re
f projectInstanceGuid, out sourceHierarchy)); | |
502 | |
503 // Then retrieve the item ID of the item to copy | |
504 uint itemID = VSConstants.VSITEMID_ROOT; | |
505 ErrorHandler.ThrowOnFailure(sourceHierarchy.ParseCanonic
alName(folder, out itemID)); | |
506 | |
507 // Ensure we don't end up in an endless recursion | |
508 if(Utilities.IsSameComObject(this, sourceHierarchy)) | |
509 { | |
510 HierarchyNode cursorNode = targetNode; | |
511 while(cursorNode != null) | |
512 { | |
513 if(String.Compare(folder, cursorNode.Get
MkDocument(), StringComparison.OrdinalIgnoreCase) == 0) | |
514 throw new Exception(); | |
515 cursorNode = cursorNode.Parent; | |
516 } | |
517 } | |
518 | |
519 // Now walk the source project hierarchy to see which no
de needs to be added. | |
520 WalkSourceProjectAndAdd(sourceHierarchy, itemID, targetN
ode, false); | |
521 } | |
522 | |
523 /// <summary> | |
524 /// Recursive method that walk a hierarchy and add items it find
to our project. | |
525 /// Note that this is meant as an helper to the Copy&Paste/Drag&
Drop functionality. | |
526 /// </summary> | |
527 /// <param name="sourceHierarchy">Hierarchy to walk</param> | |
528 /// <param name="itemId">Item ID where to start walking the hier
archy</param> | |
529 /// <param name="targetNode">Node to start adding to</param> | |
530 /// <param name="addSibblings">Typically false on first call and
true after that</param> | |
531 protected virtual void WalkSourceProjectAndAdd(IVsHierarchy sour
ceHierarchy, uint itemId, HierarchyNode targetNode, bool addSiblings) | |
532 { | |
533 // Before we start the walk, add the current node | |
534 object variant = null; | |
535 HierarchyNode newNode = targetNode; | |
536 if(itemId != VSConstants.VSITEMID_NIL) | |
537 { | |
538 // Calculate the corresponding path in our proje
ct | |
539 string source; | |
540 ErrorHandler.ThrowOnFailure(((IVsProject)sourceH
ierarchy).GetMkDocument(itemId, out source)); | |
541 string name = Path.GetFileName(source.TrimEnd(ne
w char[] { '/', '\\' })); | |
542 string targetPath = Path.Combine(GetBaseDirector
yForAddingFiles(targetNode), name); | |
543 | |
544 // See if this is a linked item (file can be lin
ked, not folders) | |
545 ErrorHandler.ThrowOnFailure(sourceHierarchy.GetP
roperty(itemId, (int)__VSHPROPID.VSHPROPID_BrowseObject, out variant), VSConstan
ts.E_NOTIMPL); | |
546 VSLangProj.FileProperties fileProperties = varia
nt as VSLangProj.FileProperties; | |
547 if(fileProperties != null && fileProperties.IsLi
nk) | |
548 { | |
549 // Since we don't support linked item, w
e make a copy of the file into our storage where it would have been linked | |
550 File.Copy(source, targetPath, true); | |
551 } | |
552 | |
553 newNode = AddNodeIfTargetExistInStorage(targetNo
de, name, targetPath); | |
554 | |
555 | |
556 // Start with child nodes (depth first) | |
557 variant = null; | |
558 ErrorHandler.ThrowOnFailure(sourceHierarchy.GetP
roperty(itemId, (int)__VSHPROPID.VSHPROPID_FirstVisibleChild, out variant)); | |
559 uint currentItemID = (uint)(int)variant; | |
560 WalkSourceProjectAndAdd(sourceHierarchy, current
ItemID, newNode, true); | |
561 | |
562 if(addSiblings) | |
563 { | |
564 // Then look at siblings | |
565 currentItemID = itemId; | |
566 while(currentItemID != VSConstants.VSITE
MID_NIL) | |
567 { | |
568 variant = null; | |
569 ErrorHandler.ThrowOnFailure(sour
ceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_NextVisibleSibling, o
ut variant)); | |
570 currentItemID = (uint)(int)varia
nt; | |
571 WalkSourceProjectAndAdd(sourceHi
erarchy, currentItemID, targetNode, true); | |
572 } | |
573 } | |
574 } | |
575 } | |
576 | |
577 /// <summary> | |
578 /// Add an existing item (file/folder) to the project if it alre
ady exist in our storage. | |
579 /// </summary> | |
580 /// <param name="parentNode">Node to that this item to</param> | |
581 /// <param name="name">Name of the item being added</param> | |
582 /// <param name="targetPath">Path of the item being added</param
> | |
583 /// <returns>Node that was added</returns> | |
584 protected virtual HierarchyNode AddNodeIfTargetExistInStorage(Hi
erarchyNode parentNode, string name, string targetPath) | |
585 { | |
586 HierarchyNode newNode = parentNode; | |
587 // If the file/directory exist, add a node for it | |
588 if(File.Exists(targetPath)) | |
589 { | |
590 VSADDRESULT[] result = new VSADDRESULT[1]; | |
591 ErrorHandler.ThrowOnFailure(this.AddItem(parentN
ode.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, name, 1, new string[] { targetP
ath }, IntPtr.Zero, result)); | |
592 if(result[0] != VSADDRESULT.ADDRESULT_Success) | |
593 throw new Exception(); | |
594 newNode = this.FindChild(targetPath); | |
595 if(newNode == null) | |
596 throw new Exception(); | |
597 } | |
598 else if(Directory.Exists(targetPath)) | |
599 { | |
600 newNode = this.CreateFolderNodes(targetPath); | |
601 } | |
602 return newNode; | |
603 } | |
604 #endregion | |
605 | |
606 #region non-virtual methods | |
607 /// <summary> | |
608 /// Handle the Cut operation to the clipboard | |
609 /// </summary> | |
610 protected internal override int CutToClipboard() | |
611 { | |
612 int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPO
RTED; | |
613 try | |
614 { | |
615 this.RegisterClipboardNotifications(true); | |
616 | |
617 // Create our data object and change the selecti
on to show item(s) being cut | |
618 IOleDataObject dataObject = this.PackageSelectio
nDataObject(true); | |
619 if(dataObject != null) | |
620 { | |
621 this.SourceDraggedOrCutOrCopied = true; | |
622 | |
623 // Add our cut item(s) to the clipboard | |
624 ErrorHandler.ThrowOnFailure(UnsafeNative
Methods.OleSetClipboard(dataObject)); | |
625 | |
626 // Inform VS (UiHierarchyWindow) of the
cut | |
627 IVsUIHierWinClipboardHelper clipboardHel
per = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper
)); | |
628 if(clipboardHelper == null) | |
629 { | |
630 return VSConstants.E_FAIL; | |
631 } | |
632 | |
633 returnValue = ErrorHandler.ThrowOnFailur
e(clipboardHelper.Cut(dataObject)); | |
634 } | |
635 } | |
636 catch(COMException e) | |
637 { | |
638 Trace.WriteLine("Exception : " + e.Message); | |
639 returnValue = e.ErrorCode; | |
640 } | |
641 | |
642 return returnValue; | |
643 } | |
644 | |
645 /// <summary> | |
646 /// Handle the Copy operation to the clipboard | |
647 /// </summary> | |
648 protected internal override int CopyToClipboard() | |
649 { | |
650 int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPO
RTED; | |
651 try | |
652 { | |
653 this.RegisterClipboardNotifications(true); | |
654 | |
655 // Create our data object and change the selecti
on to show item(s) being copy | |
656 IOleDataObject dataObject = this.PackageSelectio
nDataObject(false); | |
657 if(dataObject != null) | |
658 { | |
659 this.SourceDraggedOrCutOrCopied = true; | |
660 | |
661 // Add our copy item(s) to the clipboard | |
662 ErrorHandler.ThrowOnFailure(UnsafeNative
Methods.OleSetClipboard(dataObject)); | |
663 | |
664 // Inform VS (UiHierarchyWindow) of the
copy | |
665 IVsUIHierWinClipboardHelper clipboardHel
per = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper
)); | |
666 if(clipboardHelper == null) | |
667 { | |
668 return VSConstants.E_FAIL; | |
669 } | |
670 returnValue = ErrorHandler.ThrowOnFailur
e(clipboardHelper.Copy(dataObject)); | |
671 } | |
672 } | |
673 catch(COMException e) | |
674 { | |
675 Trace.WriteLine("Exception : " + e.Message); | |
676 returnValue = e.ErrorCode; | |
677 } | |
678 catch(ArgumentException e) | |
679 { | |
680 Trace.WriteLine("Exception : " + e.Message); | |
681 returnValue = Marshal.GetHRForException(e); | |
682 } | |
683 | |
684 return returnValue; | |
685 } | |
686 | |
687 /// <summary> | |
688 /// Handle the Paste operation to a targetNode | |
689 /// </summary> | |
690 protected internal override int PasteFromClipboard(HierarchyNode
targetNode) | |
691 { | |
692 int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPO
RTED; | |
693 | |
694 //Get the clipboardhelper service and use it after proce
ssing dataobject | |
695 IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHier
WinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); | |
696 if(clipboardHelper == null) | |
697 { | |
698 return VSConstants.E_FAIL; | |
699 } | |
700 | |
701 try | |
702 { | |
703 //Get dataobject from clipboard | |
704 IOleDataObject dataObject = null; | |
705 ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.
OleGetClipboard(out dataObject)); | |
706 if(dataObject == null) | |
707 { | |
708 return VSConstants.E_UNEXPECTED; | |
709 } | |
710 | |
711 DropEffect dropEffect = DropEffect.None; | |
712 DropDataType dropDataType = DropDataType.None; | |
713 try | |
714 { | |
715 dropDataType = this.ProcessSelectionData
Object(dataObject, targetNode.GetDragTargetHandlerNode()); | |
716 dropEffect = this.QueryDropEffect(dropDa
taType, 0); | |
717 } | |
718 catch(ExternalException e) | |
719 { | |
720 Trace.WriteLine("Exception : " + e.Messa
ge); | |
721 | |
722 // If it is a drop from windows and we g
et any kind of error ignore it. This | |
723 // prevents bogus messages from the shel
l from being displayed | |
724 if(dropDataType != DropDataType.Shell) | |
725 { | |
726 throw; | |
727 } | |
728 } | |
729 finally | |
730 { | |
731 // Inform VS (UiHierarchyWindow) of the
paste | |
732 returnValue = clipboardHelper.Paste(data
Object, (uint)dropEffect); | |
733 } | |
734 } | |
735 catch(COMException e) | |
736 { | |
737 Trace.WriteLine("Exception : " + e.Message); | |
738 | |
739 returnValue = e.ErrorCode; | |
740 } | |
741 | |
742 return returnValue; | |
743 } | |
744 | |
745 /// <summary> | |
746 /// Determines if the paste command should be allowed. | |
747 /// </summary> | |
748 /// <returns></returns> | |
749 protected internal override bool AllowPasteCommand() | |
750 { | |
751 IOleDataObject dataObject = null; | |
752 try | |
753 { | |
754 ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.
OleGetClipboard(out dataObject)); | |
755 if(dataObject == null) | |
756 { | |
757 return false; | |
758 } | |
759 | |
760 // First see if this is a set of storage based i
tems | |
761 FORMATETC format = DragDropHelper.CreateFormatEt
c((ushort)DragDropHelper.CF_VSSTGPROJECTITEMS); | |
762 if(dataObject.QueryGetData(new FORMATETC[] { for
mat }) == VSConstants.S_OK) | |
763 return true; | |
764 // Try reference based items | |
765 format = DragDropHelper.CreateFormatEtc((ushort)
DragDropHelper.CF_VSREFPROJECTITEMS); | |
766 if(dataObject.QueryGetData(new FORMATETC[] { for
mat }) == VSConstants.S_OK) | |
767 return true; | |
768 // Try windows explorer files format | |
769 format = DragDropHelper.CreateFormatEtc((ushort)
NativeMethods.CF_HDROP); | |
770 return (dataObject.QueryGetData(new FORMATETC[]
{ format }) == VSConstants.S_OK); | |
771 } | |
772 // We catch External exceptions since it might be that i
t is not our data on the clipboard. | |
773 catch(ExternalException e) | |
774 { | |
775 Trace.WriteLine("Exception :" + e.Message); | |
776 return false; | |
777 } | |
778 } | |
779 | |
780 /// <summary> | |
781 /// Register/Unregister for Clipboard events for the UiHierarchy
Window (solution explorer) | |
782 /// </summary> | |
783 /// <param name="register">true for register, false for unregist
er</param> | |
784 protected internal override void RegisterClipboardNotifications(
bool register) | |
785 { | |
786 // Get the UiHierarchy window clipboard helper service | |
787 IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHier
WinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); | |
788 if(clipboardHelper == null) | |
789 { | |
790 return; | |
791 } | |
792 | |
793 if(register && this.copyPasteCookie == 0) | |
794 { | |
795 // Register | |
796 ErrorHandler.ThrowOnFailure(clipboardHelper.Advi
seClipboardHelperEvents(this, out this.copyPasteCookie)); | |
797 Debug.Assert(this.copyPasteCookie != 0, "AdviseC
lipboardHelperEvents returned an invalid cookie"); | |
798 } | |
799 else if(!register && this.copyPasteCookie != 0) | |
800 { | |
801 // Unregister | |
802 ErrorHandler.ThrowOnFailure(clipboardHelper.Unad
viseClipboardHelperEvents(this.copyPasteCookie)); | |
803 this.copyPasteCookie = 0; | |
804 } | |
805 } | |
806 | |
807 /// <summary> | |
808 /// Process dataobject from Drag/Drop/Cut/Copy/Paste operation | |
809 /// </summary> | |
810 /// <remarks>The targetNode is set if the method is called from
a drop operation, otherwise it is null</remarks> | |
811 internal DropDataType ProcessSelectionDataObject(IOleDataObject
dataObject, HierarchyNode targetNode) | |
812 { | |
813 DropDataType dropDataType = DropDataType.None; | |
814 bool isWindowsFormat = false; | |
815 | |
816 // Try to get it as a directory based project. | |
817 List<string> filesDropped = DragDropHelper.GetDroppedFil
es(DragDropHelper.CF_VSSTGPROJECTITEMS, dataObject, out dropDataType); | |
818 if(filesDropped.Count == 0) | |
819 { | |
820 filesDropped = DragDropHelper.GetDroppedFiles(Dr
agDropHelper.CF_VSREFPROJECTITEMS, dataObject, out dropDataType); | |
821 } | |
822 if(filesDropped.Count == 0) | |
823 { | |
824 filesDropped = DragDropHelper.GetDroppedFiles(Na
tiveMethods.CF_HDROP, dataObject, out dropDataType); | |
825 isWindowsFormat = (filesDropped.Count > 0); | |
826 } | |
827 | |
828 if(dropDataType != DropDataType.None && filesDropped.Cou
nt > 0) | |
829 { | |
830 string[] filesDroppedAsArray = filesDropped.ToAr
ray(); | |
831 | |
832 HierarchyNode node = (targetNode == null) ? this
: targetNode; | |
833 | |
834 // For directory based projects the content of t
he clipboard is a double-NULL terminated list of Projref strings. | |
835 if(isWindowsFormat) | |
836 { | |
837 // This is the code path when source is
windows explorer | |
838 VSADDRESULT[] vsaddresults = new VSADDRE
SULT[1]; | |
839 vsaddresults[0] = VSADDRESULT.ADDRESULT_
Failure; | |
840 int addResult = AddItem(node.ID, VSADDIT
EMOPERATION.VSADDITEMOP_OPENFILE, null, (uint)filesDropped.Count, filesDroppedAs
Array, IntPtr.Zero, vsaddresults); | |
841 if(addResult != VSConstants.S_OK && addR
esult != VSConstants.S_FALSE && addResult != (int)OleConstants.OLECMDERR_E_CANCE
LED | |
842 && vsaddresults[0] != VSADDRESUL
T.ADDRESULT_Success) | |
843 { | |
844 ErrorHandler.ThrowOnFailure(addR
esult); | |
845 } | |
846 | |
847 return dropDataType; | |
848 } | |
849 else | |
850 { | |
851 if(AddFilesFromProjectReferences(node, f
ilesDroppedAsArray)) | |
852 { | |
853 return dropDataType; | |
854 } | |
855 } | |
856 } | |
857 | |
858 // If we reached this point then the drop data must be s
et to None. | |
859 // Otherwise the OnPaste will be called with a valid Dro
pData and that would actually delete the item. | |
860 return DropDataType.None; | |
861 } | |
862 | |
863 /// <summary> | |
864 /// Get the dropdatatype from the dataobject | |
865 /// </summary> | |
866 /// <param name="pDataObject">The dataobject to be analysed for
its format</param> | |
867 /// <returns>dropdatatype or none if dataobject does not contain
known format</returns> | |
868 internal static DropDataType QueryDropDataType(IOleDataObject pD
ataObject) | |
869 { | |
870 if(pDataObject == null) | |
871 { | |
872 return DropDataType.None; | |
873 } | |
874 | |
875 // known formats include File Drops (as from WindowsExpl
orer), | |
876 // VSProject Reference Items and VSProject Storage Items
. | |
877 FORMATETC fmt = DragDropHelper.CreateFormatEtc(NativeMet
hods.CF_HDROP); | |
878 | |
879 if(DragDropHelper.QueryGetData(pDataObject, ref fmt) ==
VSConstants.S_OK) | |
880 { | |
881 return DropDataType.Shell; | |
882 } | |
883 | |
884 fmt.cfFormat = DragDropHelper.CF_VSREFPROJECTITEMS; | |
885 if(DragDropHelper.QueryGetData(pDataObject, ref fmt) ==
VSConstants.S_OK) | |
886 { | |
887 // Data is from a Ref-based project. | |
888 return DropDataType.VsRef; | |
889 } | |
890 | |
891 fmt.cfFormat = DragDropHelper.CF_VSSTGPROJECTITEMS; | |
892 if(DragDropHelper.QueryGetData(pDataObject, ref fmt) ==
VSConstants.S_OK) | |
893 { | |
894 return DropDataType.VsStg; | |
895 } | |
896 | |
897 return DropDataType.None; | |
898 } | |
899 | |
900 /// <summary> | |
901 /// Returns the drop effect. | |
902 /// </summary> | |
903 /// <remarks> | |
904 /// // A directory based project should perform as follow: | |
905 /// NO MODIFIER | |
906 /// - COPY if not from current hierarchy, | |
907 /// - MOVE if from current hierarchy | |
908 /// SHIFT DRAG - MOVE | |
909 /// CTRL DRAG - COPY | |
910 /// CTRL-SHIFT DRAG - NO DROP (used for reference ba
sed projects only) | |
911 /// </remarks> | |
912 internal DropEffect QueryDropEffect(DropDataType dropDataType, u
int grfKeyState) | |
913 { | |
914 //Validate the dropdatatype | |
915 if((dropDataType != DropDataType.Shell) && (dropDataType
!= DropDataType.VsRef) && (dropDataType != DropDataType.VsStg)) | |
916 { | |
917 return DropEffect.None; | |
918 } | |
919 | |
920 // CTRL-SHIFT | |
921 if((grfKeyState & NativeMethods.MK_CONTROL) != 0 && (grf
KeyState & NativeMethods.MK_SHIFT) != 0) | |
922 { | |
923 // Because we are not referenced base, we don't
support link | |
924 return DropEffect.None; | |
925 } | |
926 | |
927 // CTRL | |
928 if((grfKeyState & NativeMethods.MK_CONTROL) != 0) | |
929 return DropEffect.Copy; | |
930 | |
931 // SHIFT | |
932 if((grfKeyState & NativeMethods.MK_SHIFT) != 0) | |
933 return DropEffect.Move; | |
934 | |
935 // no modifier | |
936 if(this.SourceDraggedOrCutOrCopied) | |
937 { | |
938 return DropEffect.Move; | |
939 } | |
940 else | |
941 { | |
942 return DropEffect.Copy; | |
943 } | |
944 } | |
945 | |
946 internal void CleanupSelectionDataObject(bool dropped, bool cut,
bool moved) | |
947 { | |
948 this.CleanupSelectionDataObject(dropped, cut, moved, fal
se); | |
949 } | |
950 | |
951 /// <summary> | |
952 /// After a drop or paste, will use the dwEffects | |
953 /// to determine whether we need to clean up the source nodes o
r not. If | |
954 /// justCleanup is set, it only does the cleanup work. | |
955 /// </summary> | |
956 internal void CleanupSelectionDataObject(bool dropped, bool cut,
bool moved, bool justCleanup) | |
957 { | |
958 if(this.ItemsDraggedOrCutOrCopied == null || this.ItemsD
raggedOrCutOrCopied.Count == 0) | |
959 { | |
960 return; | |
961 } | |
962 | |
963 try | |
964 { | |
965 IVsUIHierarchyWindow w = UIHierarchyUtilities.Ge
tUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer); | |
966 foreach(HierarchyNode node in this.ItemsDraggedO
rCutOrCopied) | |
967 { | |
968 if((moved && (cut || dropped) && !justCl
eanup)) | |
969 { | |
970 // do not close it if the doc is
dirty or we do not own it | |
971 bool isDirty, isOpen, isOpenedBy
Us; | |
972 uint docCookie; | |
973 IVsPersistDocData ppIVsPersistDo
cData; | |
974 DocumentManager manager = node.G
etDocumentManager(); | |
975 if(manager != null) | |
976 { | |
977 manager.GetDocInfo(out i
sOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData); | |
978 if(isDirty || (isOpen &&
!isOpenedByUs)) | |
979 { | |
980 continue; | |
981 } | |
982 | |
983 // close it if opened | |
984 if(isOpen) | |
985 { | |
986 manager.Close(__
FRAMECLOSE.FRAMECLOSE_NoSave); | |
987 } | |
988 } | |
989 | |
990 node.Remove(true); | |
991 } | |
992 else if(w != null) | |
993 { | |
994 ErrorHandler.ThrowOnFailure(w.Ex
pandItem((IVsUIHierarchy)this, node.ID, EXPANDFLAGS.EXPF_UnCutHighlightItem)); | |
995 } | |
996 } | |
997 } | |
998 finally | |
999 { | |
1000 try | |
1001 { | |
1002 // Now delete the memory allocated by th
e packaging of datasources. | |
1003 // If we just did a cut, or we are told
to cleanup, then we need to free the data object. Otherwise, we leave it | |
1004 // alone so that you can continue to pas
te the data in new locations. | |
1005 if(moved || cut || justCleanup) | |
1006 { | |
1007 this.ItemsDraggedOrCutOrCopied.C
lear(); | |
1008 this.CleanAndFlushClipboard(); | |
1009 } | |
1010 } | |
1011 finally | |
1012 { | |
1013 this.dropDataType = DropDataType.None; | |
1014 } | |
1015 } | |
1016 } | |
1017 | |
1018 /// <summary> | |
1019 /// Moves files from one part of our project to another. | |
1020 /// </summary> | |
1021 /// <param name="targetNode">the targetHandler node</param> | |
1022 /// <param name="projectReferences">List of projectref string</p
aram> | |
1023 /// <returns>true if succeeded</returns> | |
1024 internal bool AddFilesFromProjectReferences(HierarchyNode target
Node, string[] projectReferences) | |
1025 { | |
1026 //Validate input | |
1027 if(projectReferences == null) | |
1028 { | |
1029 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "projectReferences"); | |
1030 } | |
1031 if(targetNode == null) | |
1032 { | |
1033 throw new InvalidOperationException(); | |
1034 } | |
1035 | |
1036 //Iteratively add files from projectref | |
1037 foreach(string projectReference in projectReferences) | |
1038 { | |
1039 if(projectReference == null) | |
1040 { | |
1041 // bad projectref, bail out | |
1042 return false; | |
1043 } | |
1044 if(projectReference.EndsWith("/", StringComparis
on.Ordinal) || projectReference.EndsWith("\\", StringComparison.Ordinal)) | |
1045 { | |
1046 AddFolderFromOtherProject(projectReferen
ce, targetNode); | |
1047 } | |
1048 else if(!AddFileToNodeFromProjectReference(proje
ctReference, targetNode)) | |
1049 { | |
1050 return false; | |
1051 } | |
1052 } | |
1053 | |
1054 return true; | |
1055 } | |
1056 | |
1057 #endregion | |
1058 | |
1059 #region private helper methods | |
1060 /// <summary> | |
1061 /// Empties all the data structures added to the clipboard and f
lushes the clipboard. | |
1062 /// </summary> | |
1063 private void CleanAndFlushClipboard() | |
1064 { | |
1065 IOleDataObject oleDataObject = null; | |
1066 ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetCl
ipboard(out oleDataObject)); | |
1067 if(oleDataObject == null) | |
1068 { | |
1069 return; | |
1070 } | |
1071 | |
1072 | |
1073 string sourceProjectPath = DragDropHelper.GetSourceProje
ctPath(oleDataObject); | |
1074 | |
1075 if(!String.IsNullOrEmpty(sourceProjectPath) && NativeMet
hods.IsSamePath(sourceProjectPath, this.GetMkDocument())) | |
1076 { | |
1077 ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.
OleFlushClipboard()); | |
1078 int clipboardOpened = 0; | |
1079 try | |
1080 { | |
1081 ErrorHandler.ThrowOnFailure(clipboardOpe
ned = UnsafeNativeMethods.OpenClipboard(IntPtr.Zero)); | |
1082 ErrorHandler.ThrowOnFailure(UnsafeNative
Methods.EmptyClipboard()); | |
1083 } | |
1084 finally | |
1085 { | |
1086 if(clipboardOpened == 1) | |
1087 { | |
1088 ErrorHandler.ThrowOnFailure(Unsa
feNativeMethods.CloseClipboard()); | |
1089 } | |
1090 } | |
1091 } | |
1092 } | |
1093 | |
1094 private IntPtr PackageSelectionData(StringBuilder sb, bool addEn
dFormatDelimiter) | |
1095 { | |
1096 if(sb == null || sb.ToString().Length == 0 || this.Items
DraggedOrCutOrCopied.Count == 0) | |
1097 { | |
1098 return IntPtr.Zero; | |
1099 } | |
1100 | |
1101 // Double null at end. | |
1102 if(addEndFormatDelimiter) | |
1103 { | |
1104 if(sb.ToString()[sb.Length - 1] != '\0') | |
1105 { | |
1106 sb.Append('\0'); | |
1107 } | |
1108 } | |
1109 | |
1110 // We request unmanaged permission to execute the below. | |
1111 new SecurityPermission(SecurityPermissionFlag.UnmanagedC
ode).Demand(); | |
1112 | |
1113 _DROPFILES df = new _DROPFILES(); | |
1114 int dwSize = Marshal.SizeOf(df); | |
1115 Int16 wideChar = 0; | |
1116 int dwChar = Marshal.SizeOf(wideChar); | |
1117 int structSize = dwSize + ((sb.Length + 1) * dwChar); | |
1118 IntPtr ptr = Marshal.AllocHGlobal(structSize); | |
1119 df.pFiles = dwSize; | |
1120 df.fWide = 1; | |
1121 IntPtr data = IntPtr.Zero; | |
1122 try | |
1123 { | |
1124 data = UnsafeNativeMethods.GlobalLock(ptr); | |
1125 Marshal.StructureToPtr(df, data, false); | |
1126 IntPtr strData = new IntPtr((long)data + dwSize)
; | |
1127 DragDropHelper.CopyStringToHGlobal(sb.ToString()
, strData, structSize); | |
1128 } | |
1129 finally | |
1130 { | |
1131 if(data != IntPtr.Zero) | |
1132 UnsafeNativeMethods.GlobalUnLock(data); | |
1133 } | |
1134 | |
1135 return ptr; | |
1136 } | |
1137 | |
1138 #endregion | |
1139 } | |
1140 } | |
OLD | NEW |