OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Diagnostics; | |
5 using System.Diagnostics.CodeAnalysis; | |
6 using System.Globalization; | |
7 using System.IO; | |
8 using System.Runtime.InteropServices; | |
9 using Microsoft.VisualStudio; | |
10 using Microsoft.VisualStudio.OLE.Interop; | |
11 using Microsoft.VisualStudio.Shell; | |
12 using Microsoft.VisualStudio.Shell.Interop; | |
13 using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; | |
14 using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; | |
15 | |
16 namespace Microsoft.VisualStudio.Project | |
17 { | |
18 [CLSCompliant(false), ComVisible(true)] | |
19 public class NestedProjectNode : HierarchyNode, IPropertyNotifySink | |
20 { | |
21 #region fields | |
22 private IVsHierarchy nestedHierarchy; | |
23 | |
24 Guid projectInstanceGuid = Guid.Empty; | |
25 | |
26 private string projectName = String.Empty; | |
27 | |
28 private string projectPath = String.Empty; | |
29 | |
30 private ImageHandler imageHandler; | |
31 | |
32 /// <summary> | |
33 /// Defines an object that will be a mutex for this object for s
ynchronizing thread calls. | |
34 /// </summary> | |
35 private static volatile object Mutex = new object(); | |
36 | |
37 /// <summary> | |
38 /// Sets the dispose flag on the object. | |
39 /// </summary> | |
40 private bool isDisposed; | |
41 | |
42 // A cooike retrieved when advising on property chnanged events. | |
43 private uint projectPropertyNotifySinkCookie; | |
44 #endregion | |
45 | |
46 #region properties | |
47 internal IVsHierarchy NestedHierarchy | |
48 { | |
49 get | |
50 { | |
51 return this.nestedHierarchy; | |
52 } | |
53 } | |
54 #endregion | |
55 | |
56 #region virtual properties | |
57 /// <summary> | |
58 /// Returns the __VSADDVPFLAGS that will be passed in when calli
ng AddVirtualProjectEx | |
59 /// </summary> | |
60 protected virtual uint VirtualProjectFlags | |
61 { | |
62 get { return 0; } | |
63 } | |
64 #endregion | |
65 | |
66 #region overridden properties | |
67 /// <summary> | |
68 /// The path of the nested project. | |
69 /// </summary> | |
70 public override string Url | |
71 { | |
72 get | |
73 { | |
74 return this.projectPath; | |
75 } | |
76 } | |
77 | |
78 /// <summary> | |
79 /// The Caption of the nested project. | |
80 /// </summary> | |
81 public override string Caption | |
82 { | |
83 get | |
84 { | |
85 return Path.GetFileNameWithoutExtension(this.pro
jectName); | |
86 } | |
87 } | |
88 | |
89 public override Guid ItemTypeGuid | |
90 { | |
91 get | |
92 { | |
93 return VSConstants.GUID_ItemType_SubProject; | |
94 } | |
95 } | |
96 | |
97 /// <summary> | |
98 /// Defines whether a node can execute a command if in selection
. | |
99 /// We do this in order to let the nested project to handle the
execution of its own commands. | |
100 /// </summary> | |
101 public override bool CanExecuteCommand | |
102 { | |
103 get | |
104 { | |
105 return false; | |
106 } | |
107 } | |
108 | |
109 public override int SortPriority | |
110 { | |
111 get { return DefaultSortOrderNode.NestedProjectNode; } | |
112 } | |
113 | |
114 protected bool IsDisposed | |
115 { | |
116 get { return this.isDisposed; } | |
117 set { this.isDisposed = value; } | |
118 } | |
119 | |
120 | |
121 | |
122 #endregion | |
123 | |
124 #region ctor | |
125 | |
126 protected NestedProjectNode() | |
127 { | |
128 } | |
129 | |
130 public NestedProjectNode(ProjectNode root, ProjectElement elemen
t) | |
131 : base(root, element) | |
132 { | |
133 this.IsExpanded = true; | |
134 } | |
135 #endregion | |
136 | |
137 #region IPropertyNotifySink Members | |
138 /// <summary> | |
139 /// Notifies a sink that the [bindable] property specified by di
spID has changed. | |
140 /// If dispID is DISPID_UNKNOWN, then multiple properties have c
hanged together. | |
141 /// The client (owner of the sink) should then retrieve the curr
ent value of each property of interest from the object that generated the notifi
cation. | |
142 /// In our case we will care about the VSLangProj80.VsProjPropI
d.VBPROJPROPID_FileName and update the changes in the parent project file. | |
143 /// </summary> | |
144 /// <param name="dispid">Dispatch identifier of the property tha
t is about to change or DISPID_UNKNOWN if multiple properties are about to chang
e.</param> | |
145 public virtual void OnChanged(int dispid) | |
146 { | |
147 if(dispid == (int)VSLangProj80.VsProjPropId.VBPROJPROPID
_FileName) | |
148 { | |
149 // Get the filename of the nested project. Inete
ad of asking the label on the nested we ask the filename, since the label might
not yet been set. | |
150 IVsProject3 nestedProject = this.nestedHierarchy
as IVsProject3; | |
151 | |
152 if(nestedProject != null) | |
153 { | |
154 string document; | |
155 ErrorHandler.ThrowOnFailure(nestedProjec
t.GetMkDocument(VSConstants.VSITEMID_ROOT, out document)); | |
156 this.RenameNestedProjectInParentProject(
Path.GetFileNameWithoutExtension(document)); | |
157 | |
158 // We need to redraw the caption since f
or some reason, by intervining to the OnChanged event the Caption is not updated
. | |
159 this.ReDraw(UIHierarchyElement.Caption); | |
160 } | |
161 } | |
162 } | |
163 | |
164 /// <summary> | |
165 /// Notifies a sink that a [requestedit] property is about to ch
ange and that the object is asking the sink how to proceed. | |
166 /// </summary> | |
167 /// <param name="dispid">Dispatch identifier of the property tha
t is about to change or DISPID_UNKNOWN if multiple properties are about to chang
e.</param> | |
168 public virtual void OnRequestEdit(int dispid) | |
169 { | |
170 | |
171 } | |
172 | |
173 #endregion | |
174 | |
175 #region public methods | |
176 #endregion | |
177 | |
178 #region overridden methods | |
179 | |
180 /// <summary> | |
181 /// Get the automation object for the NestedProjectNode | |
182 /// </summary> | |
183 /// <returns>An instance of the Automation.OANestedProjectItem t
ype if succeded</returns> | |
184 public override object GetAutomationObject() | |
185 { | |
186 //Validate that we are not disposed or the project is cl
osing | |
187 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
188 { | |
189 return null; | |
190 } | |
191 | |
192 return new Automation.OANestedProjectItem(this.ProjectMg
r.GetAutomationObject() as Automation.OAProject, this); | |
193 } | |
194 | |
195 /// <summary> | |
196 /// Gets properties of a given node or of the hierarchy. | |
197 /// </summary> | |
198 /// <param name="propId">Identifier of the hierarchy property</p
aram> | |
199 /// <returns>It return an object which type is dependent on the
propid.</returns> | |
200 public override object GetProperty(int propId) | |
201 { | |
202 __VSHPROPID vshPropId = (__VSHPROPID)propId; | |
203 switch(vshPropId) | |
204 { | |
205 default: | |
206 return base.GetProperty(propId); | |
207 | |
208 case __VSHPROPID.VSHPROPID_Expandable: | |
209 return true; | |
210 | |
211 case __VSHPROPID.VSHPROPID_BrowseObject: | |
212 case __VSHPROPID.VSHPROPID_HandlesOwnReload: | |
213 return this.DelegateGetPropertyToNested(
propId); | |
214 } | |
215 } | |
216 | |
217 | |
218 /// <summary> | |
219 /// Gets properties whose values are GUIDs. | |
220 /// </summary> | |
221 /// <param name="propid">Identifier of the hierarchy property</p
aram> | |
222 /// <param name="guid"> Pointer to a GUID property specified in
propid</param> | |
223 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
224 public override int GetGuidProperty(int propid, out Guid guid) | |
225 { | |
226 guid = Guid.Empty; | |
227 switch((__VSHPROPID)propid) | |
228 { | |
229 case __VSHPROPID.VSHPROPID_ProjectIDGuid: | |
230 guid = this.projectInstanceGuid; | |
231 break; | |
232 | |
233 default: | |
234 return base.GetGuidProperty(propid, out
guid); | |
235 } | |
236 | |
237 CCITracing.TraceCall(String.Format(CultureInfo.CurrentCu
lture, "Guid for {0} property", propid)); | |
238 if(guid.CompareTo(Guid.Empty) == 0) | |
239 { | |
240 return VSConstants.DISP_E_MEMBERNOTFOUND; | |
241 } | |
242 | |
243 return VSConstants.S_OK; | |
244 } | |
245 | |
246 | |
247 /// <summary> | |
248 /// Determines whether the hierarchy item changed. | |
249 /// </summary> | |
250 /// <param name="itemId">Item identifier of the hierarchy item c
ontained in VSITEMID</param> | |
251 /// <param name="punkDocData">Pointer to the IUnknown interface
of the hierarchy item. </param> | |
252 /// <param name="pfDirty">TRUE if the hierarchy item changed.</p
aram> | |
253 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
254 public override int IsItemDirty(uint itemId, IntPtr punkDocData,
out int pfDirty) | |
255 { | |
256 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
257 Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr
was zero"); | |
258 | |
259 // Get an IPersistFileFormat object from docData object | |
260 IPersistFileFormat persistFileFormat = Marshal.GetTypedO
bjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat; | |
261 Debug.Assert(persistFileFormat != null, "The docData obj
ect does not implement the IPersistFileFormat interface"); | |
262 | |
263 // Call IsDirty on the IPersistFileFormat interface | |
264 ErrorHandler.ThrowOnFailure(persistFileFormat.IsDirty(ou
t pfDirty)); | |
265 | |
266 return VSConstants.S_OK; | |
267 } | |
268 | |
269 /// <summary> | |
270 /// Saves the hierarchy item to disk. | |
271 /// </summary> | |
272 /// <param name="dwSave">Flags whose values are taken from the V
SSAVEFLAGS enumeration.</param> | |
273 /// <param name="silentSaveAsName">File name to be applied when
dwSave is set to VSSAVE_SilentSave. </param> | |
274 /// <param name="itemid">Item identifier of the hierarchy item s
aved from VSITEMID. </param> | |
275 /// <param name="punkDocData">Pointer to the IUnknown interface
of the hierarchy item saved.</param> | |
276 /// <param name="pfCancelled">TRUE if the save action was cancel
ed. </param> | |
277 /// <returns>If the method succeeds, it returns S_OK. If it fail
s, it returns an error code.</returns> | |
278 public override int SaveItem(VSSAVEFLAGS dwSave, string silentSa
veAsName, uint itemid, IntPtr punkDocData, out int pfCancelled) | |
279 { | |
280 // Don't ignore/unignore file changes | |
281 // Use Advise/Unadvise to work around rename situations | |
282 try | |
283 { | |
284 this.StopObservingNestedProjectFile(); | |
285 Debug.Assert(this.nestedHierarchy != null, "The
nested hierarchy object must be created before calling this method"); | |
286 Debug.Assert(punkDocData != IntPtr.Zero, "docDat
a intptr was zero"); | |
287 | |
288 // Get an IPersistFileFormat object from docData
object (we don't call release on punkDocData since did not increment its ref co
unt) | |
289 IPersistFileFormat persistFileFormat = Marshal.G
etTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFil
eFormat; | |
290 Debug.Assert(persistFileFormat != null, "The doc
Data object does not implement the IPersistFileFormat interface"); | |
291 | |
292 IVsUIShell uiShell = this.GetService(typeof(SVsU
IShell)) as IVsUIShell; | |
293 string newName; | |
294 ErrorHandler.ThrowOnFailure(uiShell.SaveDocDataT
oFile(dwSave, persistFileFormat, silentSaveAsName, out newName, out pfCancelled)
); | |
295 | |
296 // When supported do a rename of the nested proj
ect here | |
297 } | |
298 finally | |
299 { | |
300 // Succeeded or not we must hook to the file cha
nge events | |
301 // Don't ignore/unignore file changes | |
302 // Use Advise/Unadvise to work around rename sit
uations | |
303 this.ObserveNestedProjectFile(); | |
304 } | |
305 | |
306 return VSConstants.S_OK; | |
307 } | |
308 | |
309 /// <summary> | |
310 /// Gets the icon handle. It tries first the nested to get the i
con handle. If that is not supported it will get it from | |
311 /// the image list of the nested if that is supported. If neithe
r of these is supported a default image will be shown. | |
312 /// </summary> | |
313 /// <returns>An object representing the icon.</returns> | |
314 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usag
e", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell
.Interop.IVsHierarchy.GetProperty(System.UInt32,System.Int32,System.Object@)")] | |
315 public override object GetIconHandle(bool open) | |
316 { | |
317 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
318 | |
319 object iconHandle = null; | |
320 this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_RO
OT, (int)__VSHPROPID.VSHPROPID_IconHandle, out iconHandle); | |
321 if(iconHandle == null) | |
322 { | |
323 if(null == imageHandler) | |
324 { | |
325 InitImageHandler(); | |
326 } | |
327 // Try to get an icon from the nested hierrachy
image list. | |
328 if(imageHandler.ImageList != null) | |
329 { | |
330 object imageIndexAsObject = null; | |
331 if(this.nestedHierarchy.GetProperty(VSCo
nstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconIndex, out imageIndexAsObj
ect) == VSConstants.S_OK && | |
332 imageIndexAsObject != null) | |
333 { | |
334 int imageIndex = (int)imageIndex
AsObject; | |
335 if(imageIndex < imageHandler.Ima
geList.Images.Count) | |
336 { | |
337 iconHandle = imageHandle
r.GetIconHandle(imageIndex); | |
338 } | |
339 } | |
340 } | |
341 | |
342 if(null == iconHandle) | |
343 { | |
344 iconHandle = this.ProjectMgr.ImageHandle
r.GetIconHandle((int)ProjectNode.ImageName.Application); | |
345 } | |
346 } | |
347 | |
348 return iconHandle; | |
349 } | |
350 | |
351 /// <summary> | |
352 /// Return S_OK. Implementation of Closing a nested project is d
one in CloseNestedProject which is called by CloseChildren. | |
353 /// </summary> | |
354 /// <returns>S_OK</returns> | |
355 public override int Close() | |
356 { | |
357 return VSConstants.S_OK; | |
358 } | |
359 | |
360 | |
361 /// <summary> | |
362 /// Returns the moniker of the nested project. | |
363 /// </summary> | |
364 /// <returns></returns> | |
365 public override string GetMkDocument() | |
366 { | |
367 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
368 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
369 { | |
370 return String.Empty; | |
371 } | |
372 | |
373 return this.projectPath; | |
374 } | |
375 | |
376 /// <summary> | |
377 /// Called by the shell when a node has been renamed from the GU
I | |
378 /// </summary> | |
379 /// <param name="label">The name of the new label.</param> | |
380 /// <returns>A success or failure value.</returns> | |
381 public override int SetEditLabel(string label) | |
382 { | |
383 int result = this.DelegateSetPropertyToNested((int)__VSH
PROPID.VSHPROPID_EditLabel, label); | |
384 if(ErrorHandler.Succeeded(result)) | |
385 { | |
386 this.RenameNestedProjectInParentProject(label); | |
387 } | |
388 | |
389 return result; | |
390 } | |
391 | |
392 /// <summary> | |
393 /// Called by the shell to get the node caption when the user tr
ies to rename from the GUI | |
394 /// </summary> | |
395 /// <returns>the node cation</returns> | |
396 public override string GetEditLabel() | |
397 { | |
398 return (string)this.DelegateGetPropertyToNested((int)__V
SHPROPID.VSHPROPID_EditLabel); | |
399 } | |
400 | |
401 /// <summary> | |
402 /// This is temporary until we have support for re-adding a nest
ed item | |
403 /// </summary> | |
404 protected override bool CanDeleteItem(__VSDELETEITEMOPERATION de
leteOperation) | |
405 { | |
406 return false; | |
407 } | |
408 | |
409 /// <summary> | |
410 /// Delegates the call to the inner hierarchy. | |
411 /// </summary> | |
412 /// <param name="reserved">Reserved parameter defined at the IVs
PersistHierarchyItem2::ReloadItem parameter.</param> | |
413 protected internal override void ReloadItem(uint reserved) | |
414 { | |
415 #region precondition | |
416 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
417 { | |
418 throw new InvalidOperationException(); | |
419 } | |
420 | |
421 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
422 #endregion | |
423 | |
424 IVsPersistHierarchyItem2 persistHierachyItem = this.nest
edHierarchy as IVsPersistHierarchyItem2; | |
425 | |
426 // We are expecting that if we get called then the neste
dhierarchy supports IVsPersistHierarchyItem2, since then hierrachy should suppor
t handling its own reload. | |
427 // There should be no errormessage to the user since thi
s is an internal error, that it cannot be fixed at user level. | |
428 if(persistHierachyItem == null) | |
429 { | |
430 throw new InvalidOperationException(); | |
431 } | |
432 | |
433 ErrorHandler.ThrowOnFailure(persistHierachyItem.ReloadIt
em(VSConstants.VSITEMID_ROOT, reserved)); | |
434 } | |
435 | |
436 /// <summary> | |
437 /// Flag indicating that changes to a file can be ignored when i
tem is saved or reloaded. | |
438 /// </summary> | |
439 /// <param name="ignoreFlag">Flag indicating whether or not to i
gnore changes (1 to ignore, 0 to stop ignoring).</param> | |
440 protected internal override void IgnoreItemFileChanges(bool igno
reFlag) | |
441 { | |
442 #region precondition | |
443 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
444 { | |
445 throw new InvalidOperationException(); | |
446 } | |
447 | |
448 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
449 #endregion | |
450 | |
451 this.IgnoreNestedProjectFile(ignoreFlag); | |
452 | |
453 IVsPersistHierarchyItem2 persistHierachyItem = this.nest
edHierarchy as IVsPersistHierarchyItem2; | |
454 | |
455 // If the IVsPersistHierarchyItem2 is not implemented by
the nested just return | |
456 if(persistHierachyItem == null) | |
457 { | |
458 return; | |
459 } | |
460 | |
461 ErrorHandler.ThrowOnFailure(persistHierachyItem.IgnoreIt
emFileChanges(VSConstants.VSITEMID_ROOT, ignoreFlag ? 1 : 0)); | |
462 } | |
463 | |
464 /// <summary> | |
465 /// Sets the VSADDFILEFLAGS that will be used to call the IVsTr
ackProjectDocumentsEvents2 OnAddFiles | |
466 /// </summary> | |
467 /// <param name="files">The files to which an array of VSADDFILE
FLAGS has to be specified.</param> | |
468 /// <returns></returns> | |
469 protected internal override VSADDFILEFLAGS[] GetAddFileFlags(str
ing[] files) | |
470 { | |
471 if(files == null || files.Length == 0) | |
472 { | |
473 return new VSADDFILEFLAGS[1] { VSADDFILEFLAGS.VS
ADDFILEFLAGS_NoFlags }; | |
474 } | |
475 | |
476 VSADDFILEFLAGS[] addFileFlags = new VSADDFILEFLAGS[files
.Length]; | |
477 | |
478 for(int i = 0; i < files.Length; i++) | |
479 { | |
480 addFileFlags[i] = VSADDFILEFLAGS.VSADDFILEFLAGS_
IsNestedProjectFile; | |
481 } | |
482 | |
483 return addFileFlags; | |
484 } | |
485 | |
486 /// <summary> | |
487 /// Sets the VSQUERYADDFILEFLAGS that will be used to call the
IVsTrackProjectDocumentsEvents2 OnQueryAddFiles | |
488 /// </summary> | |
489 /// <param name="files">The files to which an array of VSADDFILE
FLAGS has to be specified.</param> | |
490 /// <returns></returns> | |
491 protected internal override VSQUERYADDFILEFLAGS[] GetQueryAddFil
eFlags(string[] files) | |
492 { | |
493 if(files == null || files.Length == 0) | |
494 { | |
495 return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFI
LEFLAGS.VSQUERYADDFILEFLAGS_NoFlags }; | |
496 } | |
497 | |
498 VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADD
FILEFLAGS[files.Length]; | |
499 | |
500 for(int i = 0; i < files.Length; i++) | |
501 { | |
502 queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUE
RYADDFILEFLAGS_IsNestedProjectFile; | |
503 } | |
504 | |
505 return queryAddFileFlags; | |
506 } | |
507 | |
508 /// <summary> | |
509 /// Sets the VSREMOVEFILEFLAGS that will be used to call the IV
sTrackProjectDocumentsEvents2 OnRemoveFiles | |
510 /// </summary> | |
511 /// <param name="files">The files to which an array of VSREMOVEF
ILEFLAGS has to be specified.</param> | |
512 /// <returns></returns> | |
513 protected internal override VSREMOVEFILEFLAGS[] GetRemoveFileFla
gs(string[] files) | |
514 { | |
515 if(files == null || files.Length == 0) | |
516 { | |
517 return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFL
AGS.VSREMOVEFILEFLAGS_NoFlags }; | |
518 } | |
519 | |
520 VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFL
AGS[files.Length]; | |
521 | |
522 for(int i = 0; i < files.Length; i++) | |
523 { | |
524 removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEF
ILEFLAGS_IsNestedProjectFile; | |
525 } | |
526 | |
527 return removeFileFlags; | |
528 } | |
529 | |
530 /// <summary> | |
531 /// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call th
e IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles | |
532 /// </summary> | |
533 /// <param name="files">The files to which an array of VSQUERYRE
MOVEFILEFLAGS has to be specified.</param> | |
534 /// <returns></returns> | |
535 protected internal override VSQUERYREMOVEFILEFLAGS[] GetQueryRem
oveFileFlags(string[] files) | |
536 { | |
537 if(files == null || files.Length == 0) | |
538 { | |
539 return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYRE
MOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags }; | |
540 } | |
541 | |
542 VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQU
ERYREMOVEFILEFLAGS[files.Length]; | |
543 | |
544 for(int i = 0; i < files.Length; i++) | |
545 { | |
546 queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS
.VSQUERYREMOVEFILEFLAGS_IsNestedProjectFile; | |
547 } | |
548 | |
549 return queryRemoveFileFlags; | |
550 } | |
551 #endregion | |
552 | |
553 #region virtual methods | |
554 /// <summary> | |
555 /// Initialize the nested hierarhy node. | |
556 /// </summary> | |
557 /// <param name="fileName">The file name of the nested project.<
/param> | |
558 /// <param name="destination">The location of the nested project
.</param> | |
559 /// <param name="projectName">The name of the project.</param> | |
560 /// <param name="createFlags">The nested project creation flags
</param> | |
561 /// <remarks>This methos should be called just after a NestedPro
jectNode object is created.</remarks> | |
562 public virtual void Init(string fileName, string destination, st
ring projectName, __VSCREATEPROJFLAGS createFlags) | |
563 { | |
564 if(String.IsNullOrEmpty(fileName)) | |
565 { | |
566 throw new ArgumentException(SR.GetString(SR.Para
meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "fileName"); | |
567 } | |
568 | |
569 if(String.IsNullOrEmpty(destination)) | |
570 { | |
571 throw new ArgumentException(SR.GetString(SR.Para
meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "destination"); | |
572 } | |
573 | |
574 this.projectName = Path.GetFileName(fileName); | |
575 this.projectPath = Path.Combine(destination, this.projec
tName); | |
576 | |
577 // get the IVsSolution interface from the global service
provider | |
578 IVsSolution solution = this.GetService(typeof(IVsSolutio
n)) as IVsSolution; | |
579 Debug.Assert(solution != null, "Could not get the IVsSol
ution object from the services exposed by this project"); | |
580 if(solution == null) | |
581 { | |
582 throw new InvalidOperationException(); | |
583 } | |
584 | |
585 // Get the project type guid from project element
| |
586 string typeGuidString = this.ItemNode.GetMetadataAndThro
w(ProjectFileConstants.TypeGuid, new InvalidOperationException()); | |
587 Guid projectFactoryGuid = Guid.Empty; | |
588 if(!String.IsNullOrEmpty(typeGuidString)) | |
589 { | |
590 projectFactoryGuid = new Guid(typeGuidString); | |
591 } | |
592 | |
593 // Get the project factory. | |
594 IVsProjectFactory projectFactory; | |
595 ErrorHandler.ThrowOnFailure(solution.GetProjectFactory((
uint)0, new Guid[] { projectFactoryGuid }, fileName, out projectFactory)); | |
596 | |
597 this.CreateProjectDirectory(); | |
598 | |
599 //Create new project using factory | |
600 int cancelled; | |
601 Guid refiid = NativeMethods.IID_IUnknown; | |
602 IntPtr projectPtr = IntPtr.Zero; | |
603 | |
604 // For a nested project the creation at unsafe location
is governed by the parent project since the nested project will end up in the co
ne of the parent project. | |
605 bool dontShowAgain = DontShowAgainDialog.ReadDontShowAga
inValue(ProjectFactory.DontShowProjectSecurityWarningAgain); | |
606 | |
607 try | |
608 { | |
609 DontShowAgainDialog.WriteDontShowAgainValue(Proj
ectFactory.DontShowProjectSecurityWarningAgain, 1); | |
610 ErrorHandler.ThrowOnFailure(projectFactory.Creat
eProject(fileName, destination, projectName, (uint)createFlags, ref refiid, out
projectPtr, out cancelled)); | |
611 | |
612 if(projectPtr != IntPtr.Zero) | |
613 { | |
614 this.nestedHierarchy = Marshal.GetTypedO
bjectForIUnknown(projectPtr, typeof(IVsHierarchy)) as IVsHierarchy; | |
615 Debug.Assert(this.nestedHierarchy != nul
l, "Nested hierarchy could not be created"); | |
616 Debug.Assert(cancelled == 0); | |
617 } | |
618 } | |
619 finally | |
620 { | |
621 if(projectPtr != IntPtr.Zero) | |
622 { | |
623 // We created a new instance of the proj
ect, we need to call release to decrement the ref count | |
624 // the RCW (this.nestedHierarchy) still
has a reference to it which will keep it alive | |
625 Marshal.Release(projectPtr); | |
626 } | |
627 | |
628 // Revert back the old value that security quest
ions about unsafe location are stil asked if that was the value. | |
629 if(!dontShowAgain) | |
630 { | |
631 DontShowAgainDialog.WriteDontShowAgainVa
lue(ProjectFactory.DontShowProjectSecurityWarningAgain, 0); | |
632 } | |
633 } | |
634 | |
635 if(cancelled != 0 && this.nestedHierarchy == null) | |
636 { | |
637 ErrorHandler.ThrowOnFailure(VSConstants.OLE_E_PR
OMPTSAVECANCELLED); | |
638 } | |
639 | |
640 // Link into the nested VS hierarchy. | |
641 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProp
erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchy, this
.ProjectMgr)); | |
642 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProp
erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchyItemid
, (object)(int)this.ID)); | |
643 | |
644 this.LockRDTEntry(); | |
645 | |
646 this.ConnectPropertyNotifySink(); | |
647 } | |
648 | |
649 /// <summary> | |
650 /// Links a nested project as a virtual project to the solution. | |
651 /// </summary> | |
652 protected internal virtual void AddVirtualProject() | |
653 { | |
654 // This is the second step in creating and adding a nest
ed project. The inner hierarchy must have been | |
655 // already initialized at this point. | |
656 #region precondition | |
657 if(this.nestedHierarchy == null) | |
658 { | |
659 throw new InvalidOperationException(); | |
660 } | |
661 #endregion | |
662 // get the IVsSolution interface from the global service
provider | |
663 IVsSolution solution = this.GetService(typeof(IVsSolutio
n)) as IVsSolution; | |
664 Debug.Assert(solution != null, "Could not get the IVsSol
ution object from the services exposed by this project"); | |
665 if(solution == null) | |
666 { | |
667 throw new InvalidOperationException(); | |
668 } | |
669 | |
670 this.InitializeInstanceGuid(); | |
671 | |
672 // Add virtual project to solution. | |
673 ErrorHandler.ThrowOnFailure(solution.AddVirtualProjectEx
(this.nestedHierarchy, this.VirtualProjectFlags, ref this.projectInstanceGuid)); | |
674 | |
675 // Now set up to listen on file changes on the nested pr
oject node. | |
676 this.ObserveNestedProjectFile(); | |
677 } | |
678 | |
679 /// <summary> | |
680 /// The method that does the cleanup. | |
681 /// </summary> | |
682 /// <param name="disposing"></param> | |
683 protected override void Dispose(bool disposing) | |
684 { | |
685 // Everybody can go here. | |
686 if(!this.isDisposed) | |
687 { | |
688 try | |
689 { | |
690 // Synchronize calls to the Dispose simu
lteniously. | |
691 lock(Mutex) | |
692 { | |
693 if(disposing) | |
694 { | |
695 this.DisconnectPropertyN
otifySink(); | |
696 this.StopObservingNested
ProjectFile(); | |
697 this.imageHandler.Close(
); | |
698 } | |
699 } | |
700 } | |
701 finally | |
702 { | |
703 base.Dispose(disposing); | |
704 this.isDisposed = true; | |
705 } | |
706 } | |
707 } | |
708 | |
709 /// <summary> | |
710 /// Creates the project directory if it does not exist. | |
711 /// </summary> | |
712 /// <returns></returns> | |
713 protected virtual void CreateProjectDirectory() | |
714 { | |
715 string directoryName = Path.GetDirectoryName(this.projec
tPath); | |
716 | |
717 if(!Directory.Exists(directoryName)) | |
718 { | |
719 Directory.CreateDirectory(directoryName); | |
720 } | |
721 } | |
722 | |
723 | |
724 /// <summary> | |
725 /// Lock the RDT Entry for the nested project. | |
726 /// By default this document is marked as "Dont Save as". That m
eans the menu File->SaveAs is disabled for the | |
727 /// nested project node. | |
728 /// </summary> | |
729 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBe
CasedCorrectly", MessageId = "RDT")] | |
730 protected virtual void LockRDTEntry() | |
731 { | |
732 // Define flags for the nested project document | |
733 _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_VirtualDocument | _V
SRDTFLAGS.RDT_ProjSlnDocument; ; | |
734 | |
735 // Request the RDT service | |
736 IVsRunningDocumentTable rdt = this.GetService(typeof(SVs
RunningDocumentTable)) as IVsRunningDocumentTable; | |
737 Debug.Assert(rdt != null, " Could not get running docume
nt table from the services exposed by this project"); | |
738 if(rdt == null) | |
739 { | |
740 throw new InvalidOperationException(); | |
741 } | |
742 | |
743 // First we see if someone else has opened the requested
view of the file. | |
744 uint itemid; | |
745 IntPtr docData = IntPtr.Zero; | |
746 IVsHierarchy ivsHierarchy; | |
747 uint docCookie; | |
748 IntPtr projectPtr = IntPtr.Zero; | |
749 | |
750 try | |
751 { | |
752 ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocum
ent((uint)flags, this.projectPath, out ivsHierarchy, out itemid, out docData, ou
t docCookie)); | |
753 flags |= _VSRDTFLAGS.RDT_EditLock; | |
754 | |
755 if(ivsHierarchy != null && docCookie != (uint)Sh
ellConstants.VSDOCCOOKIE_NIL) | |
756 { | |
757 if(docCookie != this.DocCookie) | |
758 { | |
759 this.DocCookie = docCookie; | |
760 } | |
761 } | |
762 else | |
763 { | |
764 | |
765 // get inptr for hierarchy | |
766 projectPtr = Marshal.GetIUnknownForObjec
t(this.nestedHierarchy); | |
767 Debug.Assert(projectPtr != IntPtr.Zero,
" Project pointer for the nested hierarchy has not been initialized"); | |
768 ErrorHandler.ThrowOnFailure(rdt.Register
AndLockDocument((uint)flags, this.projectPath, this.ProjectMgr, this.ID, project
Ptr, out docCookie)); | |
769 | |
770 this.DocCookie = docCookie; | |
771 Debug.Assert(this.DocCookie != (uint)She
llConstants.VSDOCCOOKIE_NIL, "Invalid cookie when registering document in the ru
nning document table."); | |
772 | |
773 //we must also set the doc cookie on the
nested hier | |
774 this.SetDocCookieOnNestedHier(this.DocCo
okie); | |
775 } | |
776 } | |
777 finally | |
778 { | |
779 // Release all Inptr's that that were given as o
ut pointers | |
780 if(docData != IntPtr.Zero) | |
781 { | |
782 Marshal.Release(docData); | |
783 } | |
784 if(projectPtr != IntPtr.Zero) | |
785 { | |
786 Marshal.Release(projectPtr); | |
787 } | |
788 } | |
789 | |
790 } | |
791 | |
792 /// <summary> | |
793 /// Unlock the RDT entry for the nested project | |
794 /// </summary> | |
795 [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBe
CasedCorrectly", MessageId = "RDT")] | |
796 protected virtual void UnlockRDTEntry() | |
797 { | |
798 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
799 { | |
800 return; | |
801 } | |
802 // First we see if someone else has opened the requested
view of the file. | |
803 IVsRunningDocumentTable rdt = this.GetService(typeof(SVs
RunningDocumentTable)) as IVsRunningDocumentTable; | |
804 if(rdt != null && this.DocCookie != (int)ShellConstants.
VSDOCCOOKIE_NIL) | |
805 { | |
806 _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_EditLock; | |
807 | |
808 ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((
uint)flags, (uint)this.DocCookie)); | |
809 } | |
810 | |
811 this.DocCookie = (int)ShellConstants.VSDOCCOOKIE_NIL; | |
812 } | |
813 | |
814 /// <summary> | |
815 /// Renames the project file in the parent project structure. | |
816 /// </summary> | |
817 /// <param name="label">The new label.</param> | |
818 protected virtual void RenameNestedProjectInParentProject(string
label) | |
819 { | |
820 string existingLabel = this.Caption; | |
821 | |
822 if(String.Compare(existingLabel, label, StringComparison
.Ordinal) == 0) | |
823 { | |
824 return; | |
825 } | |
826 | |
827 string oldFileName = this.projectPath; | |
828 string oldPath = this.Url; | |
829 | |
830 try | |
831 { | |
832 this.StopObservingNestedProjectFile(); | |
833 this.ProjectMgr.SuspendMSBuild(); | |
834 | |
835 // Check out the project file if necessary. | |
836 if(!this.ProjectMgr.QueryEditProjectFile(false)) | |
837 { | |
838 throw Marshal.GetExceptionForHR(VSConsta
nts.OLE_E_PROMPTSAVECANCELLED); | |
839 } | |
840 | |
841 | |
842 string newFileName = label + Path.GetExtension(o
ldFileName); | |
843 this.SaveNestedProjectItemInProjectFile(newFileN
ame); | |
844 | |
845 string projectDirectory = Path.GetDirectoryName(
oldFileName); | |
846 | |
847 // update state. | |
848 this.projectName = newFileName; | |
849 this.projectPath = Path.Combine(projectDirectory
, this.projectName); | |
850 | |
851 // Unload and lock the RDT entries | |
852 this.UnlockRDTEntry(); | |
853 this.LockRDTEntry(); | |
854 | |
855 // Since actually this is a rename in our hierar
chy notify the tracker that a rename has happened. | |
856 this.ProjectMgr.Tracker.OnItemRenamed(oldPath, t
his.projectPath, VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_IsNestedProjectFile); | |
857 } | |
858 finally | |
859 { | |
860 this.ObserveNestedProjectFile(); | |
861 this.ProjectMgr.ResumeMSBuild(this.ProjectMgr.Re
EvaluateProjectFileTargetName); | |
862 } | |
863 } | |
864 /// <summary> | |
865 /// Saves the nested project information in the project file. | |
866 /// </summary> | |
867 /// <param name="newFileName"></param> | |
868 protected virtual void SaveNestedProjectItemInProjectFile(string
newFileName) | |
869 { | |
870 string existingInclude = this.ItemNode.Item.Include; | |
871 string existingRelativePath = Path.GetDirectoryName(exis
tingInclude); | |
872 string newRelativePath = Path.Combine(existingRelativePa
th, newFileName); | |
873 this.ItemNode.Rename(newRelativePath); | |
874 } | |
875 #endregion | |
876 | |
877 #region helper methods | |
878 /// <summary> | |
879 /// Closes a nested project and releases the nested hierrachy po
inter. | |
880 /// </summary> | |
881 internal void CloseNestedProjectNode() | |
882 { | |
883 if(this.isDisposed || this.ProjectMgr == null || this.Pr
ojectMgr.IsClosed) | |
884 { | |
885 return; | |
886 } | |
887 | |
888 uint itemid = VSConstants.VSITEMID_NIL; | |
889 try | |
890 { | |
891 this.DisconnectPropertyNotifySink(); | |
892 | |
893 IVsUIHierarchy hier; | |
894 | |
895 IVsWindowFrame windowFrame; | |
896 VsShellUtilities.IsDocumentOpen(this.ProjectMgr.
Site, this.projectPath, Guid.Empty, out hier, out itemid, out windowFrame); | |
897 | |
898 | |
899 if(itemid == VSConstants.VSITEMID_NIL) | |
900 { | |
901 this.UnlockRDTEntry(); | |
902 } | |
903 | |
904 IVsSolution solution = this.GetService(typeof(IV
sSolution)) as IVsSolution; | |
905 if(solution == null) | |
906 { | |
907 throw new InvalidOperationException(); | |
908 } | |
909 | |
910 ErrorHandler.ThrowOnFailure(solution.RemoveVirtu
alProject(this.nestedHierarchy, 0)); | |
911 | |
912 } | |
913 finally | |
914 { | |
915 this.StopObservingNestedProjectFile(); | |
916 | |
917 // if we haven't already release the RDT cookie,
do so now. | |
918 if(itemid == VSConstants.VSITEMID_NIL) | |
919 { | |
920 this.UnlockRDTEntry(); | |
921 } | |
922 | |
923 this.Dispose(true); | |
924 } | |
925 } | |
926 | |
927 private void InitializeInstanceGuid() | |
928 { | |
929 if(this.projectInstanceGuid != Guid.Empty) | |
930 { | |
931 return; | |
932 } | |
933 | |
934 Guid instanceGuid = Guid.Empty; | |
935 | |
936 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
937 | |
938 // This method should be called from the open children m
ethod, then we can safely use the IsNewProject property | |
939 if(this.ProjectMgr.IsNewProject) | |
940 { | |
941 instanceGuid = Guid.NewGuid(); | |
942 this.ItemNode.SetMetadata(ProjectFileConstants.I
nstanceGuid, instanceGuid.ToString("B")); | |
943 ErrorHandler.ThrowOnFailure(this.nestedHierarchy
.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectID
Guid, ref instanceGuid)); | |
944 } | |
945 else | |
946 { | |
947 // Get a guid from the nested hiererachy. | |
948 Guid nestedHiererachyInstanceGuid; | |
949 ErrorHandler.ThrowOnFailure(this.nestedHierarchy
.GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectID
Guid, out nestedHiererachyInstanceGuid)); | |
950 | |
951 // Get instance guid from the project file. If i
t does not exist then we create one. | |
952 string instanceGuidAsString = this.ItemNode.GetM
etadata(ProjectFileConstants.InstanceGuid); | |
953 | |
954 // 1. nestedHiererachyInstanceGuid is empty and
instanceGuidAsString is empty then create a new one. | |
955 // 2. nestedHiererachyInstanceGuid is empty and
instanceGuidAsString not empty use instanceGuidAsString and update the nested pr
oject object by calling SetGuidProperty. | |
956 // 3. nestedHiererachyInstanceGuid is not empty
instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update t
he outer project element. | |
957 // 4. nestedHiererachyInstanceGuid is not empty
instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update t
he outer project element. | |
958 | |
959 if(nestedHiererachyInstanceGuid == Guid.Empty &&
String.IsNullOrEmpty(instanceGuidAsString)) | |
960 { | |
961 instanceGuid = Guid.NewGuid(); | |
962 } | |
963 else if(nestedHiererachyInstanceGuid == Guid.Emp
ty && !String.IsNullOrEmpty(instanceGuidAsString)) | |
964 { | |
965 instanceGuid = new Guid(instanceGuidAsSt
ring); | |
966 | |
967 ErrorHandler.ThrowOnFailure(this.nestedH
ierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_P
rojectIDGuid, ref instanceGuid)); | |
968 } | |
969 else if(nestedHiererachyInstanceGuid != Guid.Emp
ty) | |
970 { | |
971 instanceGuid = nestedHiererachyInstanceG
uid; | |
972 | |
973 // If the instanceGuidAsString is empty
then creating a guid out of it would throw an exception. | |
974 if(String.IsNullOrEmpty(instanceGuidAsSt
ring) || nestedHiererachyInstanceGuid != new Guid(instanceGuidAsString)) | |
975 { | |
976 this.ItemNode.SetMetadata(Projec
tFileConstants.InstanceGuid, instanceGuid.ToString("B")); | |
977 } | |
978 } | |
979 } | |
980 | |
981 this.projectInstanceGuid = instanceGuid; | |
982 } | |
983 | |
984 private void SetDocCookieOnNestedHier(uint itemDocCookie) | |
985 { | |
986 object docCookie = (int)itemDocCookie; | |
987 | |
988 try | |
989 { | |
990 ErrorHandler.ThrowOnFailure(this.nestedHierarchy
.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ItemDocCookie
, docCookie)); | |
991 } | |
992 catch(NotImplementedException) | |
993 { | |
994 //we swallow this exception on purpose | |
995 } | |
996 } | |
997 | |
998 private void InitImageHandler() | |
999 { | |
1000 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
1001 | |
1002 if(null == imageHandler) | |
1003 { | |
1004 imageHandler = new ImageHandler(); | |
1005 } | |
1006 object imageListAsPointer = null; | |
1007 ErrorHandler.ThrowOnFailure(this.nestedHierarchy.GetProp
erty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconImgList, out imag
eListAsPointer)); | |
1008 if(imageListAsPointer != null) | |
1009 { | |
1010 this.imageHandler.ImageList = Utilities.GetImage
List(imageListAsPointer); | |
1011 } | |
1012 } | |
1013 | |
1014 /// <summary> | |
1015 /// Delegates Getproperty calls to the inner nested. | |
1016 /// </summary> | |
1017 /// <param name="propID">The property to delegate.</param> | |
1018 /// <returns>The return of the GetProperty from nested.</returns
> | |
1019 private object DelegateGetPropertyToNested(int propID) | |
1020 { | |
1021 object returnValue = null; | |
1022 if(!this.ProjectMgr.IsClosed) | |
1023 { | |
1024 Debug.Assert(this.nestedHierarchy != null, "The
nested hierarchy object must be created before calling this method"); | |
1025 | |
1026 // Do not throw since some project types will re
turn E_FAIL if they do not support a property. | |
1027 ErrorHandler.ThrowOnFailure(this.nestedHierarchy
.GetProperty(VSConstants.VSITEMID_ROOT, propID, out returnValue)); | |
1028 } | |
1029 return returnValue; | |
1030 } | |
1031 | |
1032 /// <summary> | |
1033 /// Delegates Setproperty calls to the inner nested. | |
1034 /// </summary> | |
1035 /// <param name="propID">The property to delegate.</param> | |
1036 /// <param name="value">The property to set.</param> | |
1037 /// <returns>The return of the SetProperty from nested.</returns
> | |
1038 private int DelegateSetPropertyToNested(int propID, object value
) | |
1039 { | |
1040 if(this.ProjectMgr.IsClosed) | |
1041 { | |
1042 return VSConstants.E_FAIL; | |
1043 } | |
1044 | |
1045 Debug.Assert(this.nestedHierarchy != null, "The nested h
ierarchy object must be created before calling this method"); | |
1046 | |
1047 // Do not throw since some project types will return E_F
AIL if they do not support a property. | |
1048 return this.nestedHierarchy.SetProperty(VSConstants.VSIT
EMID_ROOT, propID, value); | |
1049 } | |
1050 | |
1051 /// <summary> | |
1052 /// Starts observing changes on this file. | |
1053 /// </summary> | |
1054 private void ObserveNestedProjectFile() | |
1055 { | |
1056 ProjectContainerNode parent = this.ProjectMgr as Project
ContainerNode; | |
1057 Debug.Assert(parent != null, "The parent project for nes
ted projects should be subclassed from ProjectContainerNode"); | |
1058 parent.NestedProjectNodeReloader.ObserveItem(this.GetMkD
ocument(), this.ID); | |
1059 } | |
1060 | |
1061 /// <summary> | |
1062 /// Stops observing changes on this file. | |
1063 /// </summary> | |
1064 private void StopObservingNestedProjectFile() | |
1065 { | |
1066 ProjectContainerNode parent = this.ProjectMgr as Project
ContainerNode; | |
1067 Debug.Assert(parent != null, "The parent project for nes
ted projects should be subclassed from ProjectContainerNode"); | |
1068 parent.NestedProjectNodeReloader.StopObservingItem(this.
GetMkDocument()); | |
1069 } | |
1070 | |
1071 /// <summary> | |
1072 /// Ignores observing changes on this file depending on the bool
ean flag. | |
1073 /// </summary> | |
1074 /// <param name="ignoreFlag">Flag indicating whether or not to i
gnore changes (1 to ignore, 0 to stop ignoring).</param> | |
1075 private void IgnoreNestedProjectFile(bool ignoreFlag) | |
1076 { | |
1077 ProjectContainerNode parent = this.ProjectMgr as Project
ContainerNode; | |
1078 Debug.Assert(parent != null, "The parent project for nes
ted projects should be subclassed from ProjectContainerNode"); | |
1079 parent.NestedProjectNodeReloader.IgnoreItemChanges(this.
GetMkDocument(), ignoreFlag); | |
1080 } | |
1081 | |
1082 /// <summary> | |
1083 /// We need to advise property notify sink on project properties
so that | |
1084 /// we know when the project file is renamed through a property.
| |
1085 /// </summary> | |
1086 private void ConnectPropertyNotifySink() | |
1087 { | |
1088 if(this.projectPropertyNotifySinkCookie != (uint)ShellCo
nstants.VSCOOKIE_NIL) | |
1089 { | |
1090 return; | |
1091 } | |
1092 | |
1093 IConnectionPoint connectionPoint = this.GetConnectionPoi
ntFromPropertySink(); | |
1094 if(connectionPoint != null) | |
1095 { | |
1096 connectionPoint.Advise(this, out this.projectPro
pertyNotifySinkCookie); | |
1097 } | |
1098 } | |
1099 | |
1100 /// <summary> | |
1101 /// Disconnects the propertynotify sink | |
1102 /// </summary> | |
1103 private void DisconnectPropertyNotifySink() | |
1104 { | |
1105 if(this.projectPropertyNotifySinkCookie == (uint)ShellCo
nstants.VSCOOKIE_NIL) | |
1106 { | |
1107 return; | |
1108 } | |
1109 | |
1110 IConnectionPoint connectionPoint = this.GetConnectionPoi
ntFromPropertySink(); | |
1111 if(connectionPoint != null) | |
1112 { | |
1113 connectionPoint.Unadvise(this.projectPropertyNot
ifySinkCookie); | |
1114 this.projectPropertyNotifySinkCookie = (uint)She
llConstants.VSCOOKIE_NIL; | |
1115 } | |
1116 } | |
1117 | |
1118 /// <summary> | |
1119 /// Gets a ConnectionPoint for the IPropertyNotifySink interface
. | |
1120 /// </summary> | |
1121 /// <returns></returns> | |
1122 private IConnectionPoint GetConnectionPointFromPropertySink() | |
1123 { | |
1124 IConnectionPoint connectionPoint = null; | |
1125 object browseObject = this.GetProperty((int)__VSHPROPID.
VSHPROPID_BrowseObject); | |
1126 IConnectionPointContainer connectionPointContainer = bro
wseObject as IConnectionPointContainer; | |
1127 | |
1128 if(connectionPointContainer != null) | |
1129 { | |
1130 Guid guid = typeof(IPropertyNotifySink).GUID; | |
1131 connectionPointContainer.FindConnectionPoint(ref
guid, out connectionPoint); | |
1132 } | |
1133 | |
1134 return connectionPoint; | |
1135 } | |
1136 #endregion | |
1137 } | |
1138 } | |
OLD | NEW |