OLD | NEW |
| (Empty) |
1 /// Copyright (c) Microsoft Corporation. All rights reserved. | |
2 | |
3 using System; | |
4 using System.Collections.Generic; | |
5 using System.Diagnostics; | |
6 using System.Diagnostics.CodeAnalysis; | |
7 using System.Globalization; | |
8 using System.IO; | |
9 using System.Runtime.InteropServices; | |
10 using Microsoft.VisualStudio; | |
11 using Microsoft.VisualStudio.Shell; | |
12 using Microsoft.VisualStudio.Shell.Interop; | |
13 using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; | |
14 using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; | |
15 using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; | |
16 | |
17 namespace Microsoft.VisualStudio.Project | |
18 { | |
19 [CLSCompliant(false)] | |
20 [ComVisible(true)] | |
21 public class FileNode : HierarchyNode | |
22 { | |
23 #region static fiels | |
24 private static Dictionary<string, int> extensionIcons; | |
25 #endregion | |
26 | |
27 #region overriden Properties | |
28 /// <summary> | |
29 /// overwrites of the generic hierarchyitem. | |
30 /// </summary> | |
31 [System.ComponentModel.BrowsableAttribute(false)] | |
32 public override string Caption | |
33 { | |
34 get | |
35 { | |
36 // Use LinkedIntoProjectAt property if available | |
37 string caption = this.ItemNode.GetMetadata(Proje
ctFileConstants.LinkedIntoProjectAt); | |
38 if(caption == null || caption.Length == 0) | |
39 { | |
40 // Otherwise use filename | |
41 caption = this.ItemNode.GetMetadata(Proj
ectFileConstants.Include); | |
42 caption = Path.GetFileName(caption); | |
43 } | |
44 return caption; | |
45 } | |
46 } | |
47 public override int ImageIndex | |
48 { | |
49 get | |
50 { | |
51 // Check if the file is there. | |
52 if(!this.CanShowDefaultIcon()) | |
53 { | |
54 return (int)ProjectNode.ImageName.Missin
gFile; | |
55 } | |
56 | |
57 //Check for known extensions | |
58 int imageIndex; | |
59 string extension = System.IO.Path.GetExtension(t
his.FileName); | |
60 if((string.IsNullOrEmpty(extension)) || (!extens
ionIcons.TryGetValue(extension, out imageIndex))) | |
61 { | |
62 // Missing or unknown extension; let the
base class handle this case. | |
63 return base.ImageIndex; | |
64 } | |
65 | |
66 // The file type is known and there is an image
for it in the image list. | |
67 return imageIndex; | |
68 } | |
69 } | |
70 | |
71 public override Guid ItemTypeGuid | |
72 { | |
73 get { return VSConstants.GUID_ItemType_PhysicalFile; } | |
74 } | |
75 | |
76 public override int MenuCommandId | |
77 { | |
78 get { return VsMenus.IDM_VS_CTXT_ITEMNODE; } | |
79 } | |
80 | |
81 public override string Url | |
82 { | |
83 get | |
84 { | |
85 string path = this.ItemNode.GetMetadata(ProjectF
ileConstants.Include); | |
86 if(String.IsNullOrEmpty(path)) | |
87 { | |
88 return String.Empty; | |
89 } | |
90 | |
91 Url url; | |
92 if(Path.IsPathRooted(path)) | |
93 { | |
94 // Use absolute path | |
95 url = new Microsoft.VisualStudio.Shell.U
rl(path); | |
96 } | |
97 else | |
98 { | |
99 // Path is relative, so make it relative
to project path | |
100 url = new Url(this.ProjectMgr.BaseURI, p
ath); | |
101 } | |
102 return url.AbsoluteUrl; | |
103 | |
104 } | |
105 } | |
106 #endregion | |
107 | |
108 #region ctor | |
109 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] | |
110 static FileNode() | |
111 { | |
112 // Build the dictionary with the mapping between some we
ll known extensions | |
113 // and the index of the icons inside the standard image
list. | |
114 extensionIcons = new Dictionary<string, int>(StringCompa
rer.OrdinalIgnoreCase); | |
115 extensionIcons.Add(".aspx", (int)ProjectNode.ImageName.W
ebForm); | |
116 extensionIcons.Add(".asax", (int)ProjectNode.ImageName.G
lobalApplicationClass); | |
117 extensionIcons.Add(".asmx", (int)ProjectNode.ImageName.W
ebService); | |
118 extensionIcons.Add(".ascx", (int)ProjectNode.ImageName.W
ebUserControl); | |
119 extensionIcons.Add(".asp", (int)ProjectNode.ImageName.AS
PPage); | |
120 extensionIcons.Add(".config", (int)ProjectNode.ImageName
.WebConfig); | |
121 extensionIcons.Add(".htm", (int)ProjectNode.ImageName.HT
MLPage); | |
122 extensionIcons.Add(".html", (int)ProjectNode.ImageName.H
TMLPage); | |
123 extensionIcons.Add(".css", (int)ProjectNode.ImageName.St
yleSheet); | |
124 extensionIcons.Add(".xsl", (int)ProjectNode.ImageName.St
yleSheet); | |
125 extensionIcons.Add(".vbs", (int)ProjectNode.ImageName.Sc
riptFile); | |
126 extensionIcons.Add(".js", (int)ProjectNode.ImageName.Scr
iptFile); | |
127 extensionIcons.Add(".wsf", (int)ProjectNode.ImageName.Sc
riptFile); | |
128 extensionIcons.Add(".txt", (int)ProjectNode.ImageName.Te
xtFile); | |
129 extensionIcons.Add(".resx", (int)ProjectNode.ImageName.R
esources); | |
130 extensionIcons.Add(".rc", (int)ProjectNode.ImageName.Res
ources); | |
131 extensionIcons.Add(".bmp", (int)ProjectNode.ImageName.Bi
tmap); | |
132 extensionIcons.Add(".ico", (int)ProjectNode.ImageName.Ic
on); | |
133 extensionIcons.Add(".gif", (int)ProjectNode.ImageName.Im
age); | |
134 extensionIcons.Add(".jpg", (int)ProjectNode.ImageName.Im
age); | |
135 extensionIcons.Add(".png", (int)ProjectNode.ImageName.Im
age); | |
136 extensionIcons.Add(".map", (int)ProjectNode.ImageName.Im
ageMap); | |
137 extensionIcons.Add(".wav", (int)ProjectNode.ImageName.Au
dio); | |
138 extensionIcons.Add(".mid", (int)ProjectNode.ImageName.Au
dio); | |
139 extensionIcons.Add(".midi", (int)ProjectNode.ImageName.A
udio); | |
140 extensionIcons.Add(".avi", (int)ProjectNode.ImageName.Vi
deo); | |
141 extensionIcons.Add(".mov", (int)ProjectNode.ImageName.Vi
deo); | |
142 extensionIcons.Add(".mpg", (int)ProjectNode.ImageName.Vi
deo); | |
143 extensionIcons.Add(".mpeg", (int)ProjectNode.ImageName.V
ideo); | |
144 extensionIcons.Add(".cab", (int)ProjectNode.ImageName.CA
B); | |
145 extensionIcons.Add(".jar", (int)ProjectNode.ImageName.JA
R); | |
146 extensionIcons.Add(".xslt", (int)ProjectNode.ImageName.X
SLTFile); | |
147 extensionIcons.Add(".xsd", (int)ProjectNode.ImageName.XM
LSchema); | |
148 extensionIcons.Add(".xml", (int)ProjectNode.ImageName.XM
LFile); | |
149 extensionIcons.Add(".pfx", (int)ProjectNode.ImageName.PF
X); | |
150 extensionIcons.Add(".snk", (int)ProjectNode.ImageName.SN
K); | |
151 } | |
152 | |
153 /// <summary> | |
154 /// Constructor for the FileNode | |
155 /// </summary> | |
156 /// <param name="root">Root of the hierarchy</param> | |
157 /// <param name="e">Associated project element</param> | |
158 public FileNode(ProjectNode root, ProjectElement element) | |
159 : base(root, element) | |
160 { | |
161 if(this.ProjectMgr.NodeHasDesigner(this.ItemNode.GetMeta
data(ProjectFileConstants.Include))) | |
162 { | |
163 this.HasDesigner = true; | |
164 } | |
165 } | |
166 #endregion | |
167 | |
168 #region overridden methods | |
169 protected override NodeProperties CreatePropertiesObject() | |
170 { | |
171 ISingleFileGenerator generator = this.CreateSingleFileGe
nerator(); | |
172 return generator == null ? new FileNodeProperties(this)
: new SingleFileGeneratorNodeProperties(this); | |
173 } | |
174 | |
175 public override object GetIconHandle(bool open) | |
176 { | |
177 int index = this.ImageIndex; | |
178 if(NoImage == index) | |
179 { | |
180 // There is no image for this file; let the base
class handle this case. | |
181 return base.GetIconHandle(open); | |
182 } | |
183 // Return the handle for the image. | |
184 return this.ProjectMgr.ImageHandler.GetIconHandle(index)
; | |
185 } | |
186 | |
187 /// <summary> | |
188 /// Get an instance of the automation object for a FileNode | |
189 /// </summary> | |
190 /// <returns>An instance of the Automation.OAFileNode if succeed
ed</returns> | |
191 public override object GetAutomationObject() | |
192 { | |
193 if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) | |
194 { | |
195 return null; | |
196 } | |
197 | |
198 return new Automation.OAFileItem(this.ProjectMgr.GetAuto
mationObject() as Automation.OAProject, this); | |
199 } | |
200 | |
201 /// <summary> | |
202 /// Renames a file node. | |
203 /// </summary> | |
204 /// <param name="label">The new name.</param> | |
205 /// <returns>An errorcode for failure or S_OK.</returns> | |
206 /// <exception cref="InvalidOperationException" if the file cann
ot be validated> | |
207 /// <devremark> | |
208 /// We are going to throw instaed of showing messageboxes, since
this method is called from various places where a dialog box does not make sens
e. | |
209 /// For example the FileNodeProperties are also calling this met
hod. That should not show directly a messagebox. | |
210 /// Also the automation methods are also calling SetEditLabel | |
211 /// </devremark> | |
212 | |
213 public override int SetEditLabel(string label) | |
214 { | |
215 // IMPORTANT NOTE: This code will be called when a paren
t folder is renamed. As such, it is | |
216 // expected that we can be called with a
label which is the same as the current | |
217 // label and this should not be consider
ed a NO-OP. | |
218 | |
219 if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) | |
220 { | |
221 return VSConstants.E_FAIL; | |
222 } | |
223 | |
224 // Validate the filename. | |
225 if(String.IsNullOrEmpty(label)) | |
226 { | |
227 throw new InvalidOperationException(SR.GetString
(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); | |
228 } | |
229 else if(label.Length > NativeMethods.MAX_PATH) | |
230 { | |
231 throw new InvalidOperationException(String.Forma
t(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUI
Culture), label)); | |
232 } | |
233 else if(Utilities.IsFileNameInvalid(label)) | |
234 { | |
235 throw new InvalidOperationException(SR.GetString
(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); | |
236 } | |
237 | |
238 for(HierarchyNode n = this.Parent.FirstChild; n != null;
n = n.NextSibling) | |
239 { | |
240 if(n != this && String.Compare(n.Caption, label,
StringComparison.OrdinalIgnoreCase) == 0) | |
241 { | |
242 //A file or folder with the name '{0}' a
lready exists on disk at this location. Please choose another name. | |
243 //If this file or folder does not appear
in the Solution Explorer, then it is not currently part of your project. To vie
w files which exist on disk, but are not in the project, select Show All Files f
rom the Project menu. | |
244 throw new InvalidOperationException(Stri
ng.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists,
CultureInfo.CurrentUICulture), label)); | |
245 } | |
246 } | |
247 | |
248 string fileName = Path.GetFileNameWithoutExtension(label
); | |
249 | |
250 // If there is no filename or it starts with a leading d
ot issue an error message and quit. | |
251 if(String.IsNullOrEmpty(fileName) || fileName[0] == '.') | |
252 { | |
253 throw new InvalidOperationException(SR.GetString
(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture)); | |
254 } | |
255 | |
256 // Verify that the file extension is unchanged | |
257 string strRelPath = Path.GetFileName(this.ItemNode.GetMe
tadata(ProjectFileConstants.Include)); | |
258 if(String.Compare(Path.GetExtension(strRelPath), Path.Ge
tExtension(label), StringComparison.OrdinalIgnoreCase) != 0) | |
259 { | |
260 // Prompt to confirm that they really want to ch
ange the extension of the file | |
261 string message = SR.GetString(SR.ConfirmExtensio
nChange, CultureInfo.CurrentUICulture, new string[] { label }); | |
262 IVsUIShell shell = this.ProjectMgr.Site.GetServi
ce(typeof(SVsUIShell)) as IVsUIShell; | |
263 | |
264 Debug.Assert(shell != null, "Could not get the u
i shell from the project"); | |
265 if(shell == null) | |
266 { | |
267 return VSConstants.E_FAIL; | |
268 } | |
269 | |
270 if(!VsShellUtilities.PromptYesNo(message, null,
OLEMSGICON.OLEMSGICON_INFO, shell)) | |
271 { | |
272 // The user cancelled the confirmation f
or changing the extension. | |
273 // Return S_OK in order not to show any
extra dialog box | |
274 return VSConstants.S_OK; | |
275 } | |
276 } | |
277 | |
278 | |
279 // Build the relative path by looking at folder names ab
ove us as one scenarios | |
280 // where we get called is when a folder above us gets re
named (in which case our path is invalid) | |
281 HierarchyNode parent = this.Parent; | |
282 while(parent != null && (parent is FolderNode)) | |
283 { | |
284 strRelPath = Path.Combine(parent.Caption, strRel
Path); | |
285 parent = parent.Parent; | |
286 } | |
287 | |
288 return SetEditLabel(label, strRelPath); | |
289 } | |
290 | |
291 public override string GetMkDocument() | |
292 { | |
293 Debug.Assert(this.Url != null, "No url sepcified for thi
s node"); | |
294 | |
295 return this.Url; | |
296 } | |
297 | |
298 /// <summary> | |
299 /// Delete the item corresponding to the specified path from sto
rage. | |
300 /// </summary> | |
301 /// <param name="path"></param> | |
302 protected internal override void DeleteFromStorage(string path) | |
303 { | |
304 if(File.Exists(path)) | |
305 { | |
306 File.SetAttributes(path, FileAttributes.Normal);
// make sure it's not readonly. | |
307 File.Delete(path); | |
308 } | |
309 } | |
310 | |
311 /// <summary> | |
312 /// Rename the underlying document based on the change the user
just made to the edit label. | |
313 /// </summary> | |
314 protected internal override int SetEditLabel(string label, strin
g relativePath) | |
315 { | |
316 int returnValue = VSConstants.S_OK; | |
317 uint oldId = this.ID; | |
318 string strSavePath = Path.GetDirectoryName(relativePath)
; | |
319 | |
320 if(!Path.IsPathRooted(relativePath)) | |
321 { | |
322 strSavePath = Path.Combine(Path.GetDirectoryName
(this.ProjectMgr.BaseURI.Uri.LocalPath), strSavePath); | |
323 } | |
324 | |
325 string newName = Path.Combine(strSavePath, label); | |
326 | |
327 if(NativeMethods.IsSamePath(newName, this.Url)) | |
328 { | |
329 // If this is really a no-op, then nothing to do | |
330 if(String.Compare(newName, this.Url, StringCompa
rison.Ordinal) == 0) | |
331 return VSConstants.S_FALSE; | |
332 } | |
333 else | |
334 { | |
335 // If the renamed file already exists then quit
(unless it is the result of the parent having done the move). | |
336 if(IsFileOnDisk(newName) | |
337 && (IsFileOnDisk(this.Url) | |
338 || String.Compare(Path.GetFileName(newNa
me), Path.GetFileName(this.Url), StringComparison.Ordinal) != 0)) | |
339 { | |
340 throw new InvalidOperationException(Stri
ng.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileCannotBeRenamedToAnExi
stingFile, CultureInfo.CurrentUICulture), label)); | |
341 } | |
342 else if(newName.Length > NativeMethods.MAX_PATH) | |
343 { | |
344 throw new InvalidOperationException(Stri
ng.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.C
urrentUICulture), label)); | |
345 } | |
346 | |
347 } | |
348 | |
349 string oldName = this.Url; | |
350 // must update the caption prior to calling RenameDocume
nt, since it may | |
351 // cause queries of that property (such as from open edi
tors). | |
352 string oldrelPath = this.ItemNode.GetMetadata(ProjectFil
eConstants.Include); | |
353 | |
354 try | |
355 { | |
356 if(!RenameDocument(oldName, newName)) | |
357 { | |
358 this.ItemNode.Rename(oldrelPath); | |
359 this.ItemNode.RefreshProperties(); | |
360 } | |
361 | |
362 if(this is DependentFileNode) | |
363 { | |
364 OnInvalidateItems(this.Parent); | |
365 } | |
366 | |
367 } | |
368 catch(Exception e) | |
369 { | |
370 // Just re-throw the exception so we don't get d
uplicate message boxes. | |
371 Trace.WriteLine("Exception : " + e.Message); | |
372 this.RecoverFromRenameFailure(newName, oldrelPat
h); | |
373 returnValue = Marshal.GetHRForException(e); | |
374 throw; | |
375 } | |
376 // Return S_FALSE if the hierarchy item id has changed.
This forces VS to flush the stale | |
377 // hierarchy item id. | |
378 if(returnValue == (int)VSConstants.S_OK || returnValue =
= (int)VSConstants.S_FALSE || returnValue == VSConstants.OLE_E_PROMPTSAVECANCELL
ED) | |
379 { | |
380 return (oldId == this.ID) ? VSConstants.S_OK : (
int)VSConstants.S_FALSE; | |
381 } | |
382 | |
383 return returnValue; | |
384 } | |
385 | |
386 /// <summary> | |
387 /// Returns a specific Document manager to handle files | |
388 /// </summary> | |
389 /// <returns>Document manager object</returns> | |
390 protected internal override DocumentManager GetDocumentManager() | |
391 { | |
392 return new FileDocumentManager(this); | |
393 } | |
394 | |
395 /// <summary> | |
396 /// Called by the drag&drop implementation to ask the node | |
397 /// which is being dragged/droped over which nodes should | |
398 /// process the operation. | |
399 /// This allows for dragging to a node that cannot contain | |
400 /// items to let its parent accept the drop, while a reference | |
401 /// node delegate to the project and a folder/project node to it
self. | |
402 /// </summary> | |
403 /// <returns></returns> | |
404 protected internal override HierarchyNode GetDragTargetHandlerNo
de() | |
405 { | |
406 Debug.Assert(this.ProjectMgr != null, " The project mana
ger is null for the filenode"); | |
407 HierarchyNode handlerNode = this; | |
408 while(handlerNode != null && !(handlerNode is ProjectNod
e || handlerNode is FolderNode)) | |
409 handlerNode = handlerNode.Parent; | |
410 if(handlerNode == null) | |
411 handlerNode = this.ProjectMgr; | |
412 return handlerNode; | |
413 } | |
414 | |
415 protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd
, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) | |
416 { | |
417 if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) | |
418 { | |
419 return (int)OleConstants.OLECMDERR_E_NOTSUPPORTE
D; | |
420 } | |
421 | |
422 // Exec on special filenode commands | |
423 if(cmdGroup == VsMenus.guidStandardCommandSet97) | |
424 { | |
425 IVsWindowFrame windowFrame = null; | |
426 | |
427 switch((VsCommands)cmd) | |
428 { | |
429 case VsCommands.ViewCode: | |
430 return ((FileDocumentManager)thi
s.GetDocumentManager()).Open(false, false, VSConstants.LOGVIEWID_Code, out windo
wFrame, WindowFrameShowAction.Show); | |
431 | |
432 case VsCommands.ViewForm: | |
433 return ((FileDocumentManager)thi
s.GetDocumentManager()).Open(false, false, VSConstants.LOGVIEWID_Designer, out w
indowFrame, WindowFrameShowAction.Show); | |
434 | |
435 case VsCommands.Open: | |
436 return ((FileDocumentManager)thi
s.GetDocumentManager()).Open(false, false, WindowFrameShowAction.Show); | |
437 | |
438 case VsCommands.OpenWith: | |
439 return ((FileDocumentManager)thi
s.GetDocumentManager()).Open(false, true, VSConstants.LOGVIEWID_UserChooseView,
out windowFrame, WindowFrameShowAction.Show); | |
440 } | |
441 } | |
442 | |
443 // Exec on special filenode commands | |
444 if(cmdGroup == VsMenus.guidStandardCommandSet2K) | |
445 { | |
446 switch((VsCommands2K)cmd) | |
447 { | |
448 case VsCommands2K.RUNCUSTOMTOOL: | |
449 { | |
450 try | |
451 { | |
452 this.RunGenerato
r(); | |
453 return VSConstan
ts.S_OK; | |
454 } | |
455 catch(Exception e) | |
456 { | |
457 Trace.WriteLine(
"Running Custom Tool failed : " + e.Message); | |
458 throw; | |
459 } | |
460 } | |
461 } | |
462 } | |
463 | |
464 return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt
, pvaIn, pvaOut); | |
465 } | |
466 | |
467 | |
468 protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd
, IntPtr pCmdText, ref QueryStatusResult result) | |
469 { | |
470 if(cmdGroup == VsMenus.guidStandardCommandSet97) | |
471 { | |
472 switch((VsCommands)cmd) | |
473 { | |
474 case VsCommands.Copy: | |
475 case VsCommands.Paste: | |
476 case VsCommands.Cut: | |
477 case VsCommands.Rename: | |
478 result |= QueryStatusResult.SUPP
ORTED | QueryStatusResult.ENABLED; | |
479 return VSConstants.S_OK; | |
480 | |
481 case VsCommands.ViewCode: | |
482 //case VsCommands.Delete: goto case VsCo
mmands.OpenWith; | |
483 case VsCommands.Open: | |
484 case VsCommands.OpenWith: | |
485 result |= QueryStatusResult.SUPP
ORTED | QueryStatusResult.ENABLED; | |
486 return VSConstants.S_OK; | |
487 } | |
488 } | |
489 else if(cmdGroup == VsMenus.guidStandardCommandSet2K) | |
490 { | |
491 if((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROM
PROJECT) | |
492 { | |
493 result |= QueryStatusResult.SUPPORTED |
QueryStatusResult.ENABLED; | |
494 return VSConstants.S_OK; | |
495 } | |
496 if((VsCommands2K)cmd == VsCommands2K.RUNCUSTOMTO
OL) | |
497 { | |
498 if(string.IsNullOrEmpty(this.ItemNode.Ge
tMetadata(ProjectFileConstants.DependentUpon)) && (this.NodeProperties is Single
FileGeneratorNodeProperties)) | |
499 { | |
500 result |= QueryStatusResult.SUPP
ORTED | QueryStatusResult.ENABLED; | |
501 return VSConstants.S_OK; | |
502 } | |
503 } | |
504 } | |
505 else | |
506 { | |
507 return (int)OleConstants.OLECMDERR_E_UNKNOWNGROU
P; | |
508 } | |
509 return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, r
ef result); | |
510 } | |
511 | |
512 | |
513 protected override void DoDefaultAction() | |
514 { | |
515 CCITracing.TraceCall(); | |
516 FileDocumentManager manager = this.GetDocumentManager()
as FileDocumentManager; | |
517 Debug.Assert(manager != null, "Could not get the FileDoc
umentManager"); | |
518 manager.Open(false, false, WindowFrameShowAction.Show); | |
519 } | |
520 | |
521 /// <summary> | |
522 /// Performs a SaveAs operation of an open document. Called from
SaveItem after the running document table has been updated with the new doc dat
a. | |
523 /// </summary> | |
524 /// <param name="docData">A pointer to the document in the rdt</
param> | |
525 /// <param name="newFilePath">The new file path to the document<
/param> | |
526 /// <returns></returns> | |
527 protected override int AfterSaveItemAs(IntPtr docData, string ne
wFilePath) | |
528 { | |
529 if(String.IsNullOrEmpty(newFilePath)) | |
530 { | |
531 throw new ArgumentException(SR.GetString(SR.Para
meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "newFilePath"); | |
532 } | |
533 | |
534 int returnCode = VSConstants.S_OK; | |
535 newFilePath = newFilePath.Trim(); | |
536 | |
537 //Identify if Path or FileName are the same for old and
new file | |
538 string newDirectoryName = Path.GetDirectoryName(newFileP
ath); | |
539 Uri newDirectoryUri = new Uri(newDirectoryName); | |
540 string newCanonicalDirectoryName = newDirectoryUri.Local
Path; | |
541 newCanonicalDirectoryName = newCanonicalDirectoryName.Tr
imEnd(Path.DirectorySeparatorChar); | |
542 string oldCanonicalDirectoryName = new Uri(Path.GetDirec
toryName(this.GetMkDocument())).LocalPath; | |
543 oldCanonicalDirectoryName = oldCanonicalDirectoryName.Tr
imEnd(Path.DirectorySeparatorChar); | |
544 string errorMessage = String.Empty; | |
545 bool isSamePath = NativeMethods.IsSamePath(newCanonicalD
irectoryName, oldCanonicalDirectoryName); | |
546 bool isSameFile = NativeMethods.IsSamePath(newFilePath,
this.Url); | |
547 | |
548 // Currently we do not support if the new directory is l
ocated outside the project cone | |
549 string projectCannonicalDirecoryName = new Uri(this.Proj
ectMgr.ProjectFolder).LocalPath; | |
550 projectCannonicalDirecoryName = projectCannonicalDirecor
yName.TrimEnd(Path.DirectorySeparatorChar); | |
551 if(!isSamePath && newCanonicalDirectoryName.IndexOf(proj
ectCannonicalDirecoryName, StringComparison.OrdinalIgnoreCase) == -1) | |
552 { | |
553 errorMessage = String.Format(CultureInfo.Current
Culture, SR.GetString(SR.LinkedItemsAreNotSupported, CultureInfo.CurrentUICultur
e), Path.GetFileNameWithoutExtension(newFilePath)); | |
554 throw new InvalidOperationException(errorMessage
); | |
555 } | |
556 | |
557 //Get target container | |
558 HierarchyNode targetContainer = null; | |
559 if(isSamePath) | |
560 { | |
561 targetContainer = this.Parent; | |
562 } | |
563 else if(NativeMethods.IsSamePath(newCanonicalDirectoryNa
me, projectCannonicalDirecoryName)) | |
564 { | |
565 //the projectnode is the target container | |
566 targetContainer = this.ProjectMgr; | |
567 } | |
568 else | |
569 { | |
570 //search for the target container among existing
child nodes | |
571 targetContainer = this.ProjectMgr.FindChild(newD
irectoryName); | |
572 if(targetContainer != null && (targetContainer i
s FileNode)) | |
573 { | |
574 // We already have a file node with this
name in the hierarchy. | |
575 errorMessage = String.Format(CultureInfo
.CurrentCulture, SR.GetString(SR.FileAlreadyExistsAndCannotBeRenamed, CultureInf
o.CurrentUICulture), Path.GetFileNameWithoutExtension(newFilePath)); | |
576 throw new InvalidOperationException(erro
rMessage); | |
577 } | |
578 } | |
579 | |
580 if(targetContainer == null) | |
581 { | |
582 // Add a chain of subdirectories to the project. | |
583 string relativeUri = PackageUtilities.GetPathDis
tance(this.ProjectMgr.BaseURI.Uri, newDirectoryUri); | |
584 Debug.Assert(!String.IsNullOrEmpty(relativeUri)
&& relativeUri != newDirectoryUri.LocalPath, "Could not make pat distance of " +
this.ProjectMgr.BaseURI.Uri.LocalPath + " and " + newDirectoryUri); | |
585 targetContainer = this.ProjectMgr.CreateFolderNo
des(relativeUri); | |
586 } | |
587 Debug.Assert(targetContainer != null, "We should have fo
und a target node by now"); | |
588 | |
589 //Suspend file changes while we rename the document | |
590 string oldrelPath = this.ItemNode.GetMetadata(ProjectFil
eConstants.Include); | |
591 string oldName = Path.Combine(this.ProjectMgr.ProjectFol
der, oldrelPath); | |
592 SuspendFileChanges sfc = new SuspendFileChanges(this.Pro
jectMgr.Site, oldName); | |
593 sfc.Suspend(); | |
594 | |
595 try | |
596 { | |
597 // Rename the node. | |
598 DocumentManager.UpdateCaption(this.ProjectMgr.Si
te, Path.GetFileName(newFilePath), docData); | |
599 // Check if the file name was actually changed. | |
600 // In same cases (e.g. if the item is a file and
the user has changed its encoding) this function | |
601 // is called even if there is no real rename. | |
602 if(!isSameFile || (this.Parent.ID != targetConta
iner.ID)) | |
603 { | |
604 // The path of the file is changed or it
s parent is changed; in both cases we have | |
605 // to rename the item. | |
606 this.RenameFileNode(oldName, newFilePath
, targetContainer.ID); | |
607 OnInvalidateItems(this.Parent); | |
608 } | |
609 } | |
610 catch(Exception e) | |
611 { | |
612 Trace.WriteLine("Exception : " + e.Message); | |
613 this.RecoverFromRenameFailure(newFilePath, oldre
lPath); | |
614 throw; | |
615 } | |
616 finally | |
617 { | |
618 sfc.Resume(); | |
619 } | |
620 | |
621 return returnCode; | |
622 } | |
623 | |
624 /// <summary> | |
625 /// Determines if this is node a valid node for painting the def
ault file icon. | |
626 /// </summary> | |
627 /// <returns></returns> | |
628 protected override bool CanShowDefaultIcon() | |
629 { | |
630 string moniker = this.GetMkDocument(); | |
631 | |
632 if(String.IsNullOrEmpty(moniker) || !File.Exists(moniker
)) | |
633 { | |
634 return false; | |
635 } | |
636 | |
637 return true; | |
638 } | |
639 | |
640 #endregion | |
641 | |
642 #region virtual methods | |
643 public virtual string FileName | |
644 { | |
645 get | |
646 { | |
647 return this.Caption; | |
648 } | |
649 set | |
650 { | |
651 this.SetEditLabel(value); | |
652 } | |
653 } | |
654 | |
655 /// <summary> | |
656 /// Determine if this item is represented physical on disk and s
hows a messagebox in case that the file is not present and a UI is to be present
ed. | |
657 /// </summary> | |
658 /// <param name="showMessage">true if user should be presented f
or UI in case the file is not present</param> | |
659 /// <returns>true if file is on disk</returns> | |
660 internal protected virtual bool IsFileOnDisk(bool showMessage) | |
661 { | |
662 bool fileExist = IsFileOnDisk(this.Url); | |
663 | |
664 if(!fileExist && showMessage && !Utilities.IsInAutomatio
nFunction(this.ProjectMgr.Site)) | |
665 { | |
666 string message = String.Format(CultureInfo.Curre
ntCulture, SR.GetString(SR.ItemDoesNotExistInProjectDirectory, CultureInfo.Curre
ntUICulture), this.Caption); | |
667 string title = string.Empty; | |
668 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL
; | |
669 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON
_OK; | |
670 OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.
OLEMSGDEFBUTTON_FIRST; | |
671 VsShellUtilities.ShowMessageBox(this.ProjectMgr.
Site, title, message, icon, buttons, defaultButton); | |
672 } | |
673 | |
674 return fileExist; | |
675 } | |
676 | |
677 /// <summary> | |
678 /// Determine if the file represented by "path" exist in storage
. | |
679 /// Override this method if your files are not persisted on disk
. | |
680 /// </summary> | |
681 /// <param name="path">Url representing the file</param> | |
682 /// <returns>True if the file exist</returns> | |
683 internal protected virtual bool IsFileOnDisk(string path) | |
684 { | |
685 return File.Exists(path); | |
686 } | |
687 | |
688 /// <summary> | |
689 /// Renames the file in the hierarchy by removing old node and a
dding a new node in the hierarchy. | |
690 /// </summary> | |
691 /// <param name="oldFileName">The old file name.</param> | |
692 /// <param name="newFileName">The new file name</param> | |
693 /// <param name="newParentId">The new parent id of the item.</pa
ram> | |
694 /// <returns>The newly added FileNode.</returns> | |
695 /// <remarks>While a new node will be used to represent the item
, the underlying MSBuild item will be the same and as a result file properties s
aved in the project file will not be lost.</remarks> | |
696 protected virtual FileNode RenameFileNode(string oldFileName, st
ring newFileName, uint newParentId) | |
697 { | |
698 if(string.Compare(oldFileName, newFileName, StringCompar
ison.Ordinal) == 0) | |
699 { | |
700 // We do not want to rename the same file | |
701 return null; | |
702 } | |
703 | |
704 this.OnItemDeleted(); | |
705 this.Parent.RemoveChild(this); | |
706 | |
707 // Since this node has been removed all of its state is
zombied at this point | |
708 // Do not call virtual methods after this point since th
e object is in a deleted state. | |
709 | |
710 string[] file = new string[1]; | |
711 file[0] = newFileName; | |
712 VSADDRESULT[] result = new VSADDRESULT[1]; | |
713 Guid emptyGuid = Guid.Empty; | |
714 ErrorHandler.ThrowOnFailure(this.ProjectMgr.AddItemWithS
pecific(newParentId, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, 0, file, Int
Ptr.Zero, 0, ref emptyGuid, null, ref emptyGuid, result)); | |
715 FileNode childAdded = this.ProjectMgr.FindChild(newFileN
ame) as FileNode; | |
716 Debug.Assert(childAdded != null, "Could not find the ren
amed item in the hierarchy"); | |
717 // Update the itemid to the newly added. | |
718 this.ID = childAdded.ID; | |
719 | |
720 // Remove the item created by the add item. We need to d
o this otherwise we will have two items. | |
721 // Please be aware that we have not removed the ItemNode
associated to the removed file node from the hierrachy. | |
722 // What we want to achieve here is to reuse the existing
build item. | |
723 // We want to link to the newly created node to the exis
ting item node and addd the new include. | |
724 | |
725 //temporarily keep properties from new itemnode since we
are going to overwrite it | |
726 string newInclude = childAdded.ItemNode.Item.Include; | |
727 string dependentOf = childAdded.ItemNode.GetMetadata(Pro
jectFileConstants.DependentUpon); | |
728 childAdded.ItemNode.RemoveFromProjectFile(); | |
729 | |
730 // Assign existing msbuild item to the new childnode | |
731 childAdded.ItemNode = this.ItemNode; | |
732 childAdded.ItemNode.Item.Name = this.ItemNode.ItemName; | |
733 childAdded.ItemNode.Item.Include = newInclude; | |
734 if(!string.IsNullOrEmpty(dependentOf)) | |
735 childAdded.ItemNode.SetMetadata(ProjectFileConst
ants.DependentUpon, dependentOf); | |
736 childAdded.ItemNode.RefreshProperties(); | |
737 | |
738 //Update the new document in the RDT. | |
739 DocumentManager.RenameDocument(this.ProjectMgr.Site, old
FileName, newFileName, childAdded.ID); | |
740 | |
741 //Select the new node in the hierarchy | |
742 IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.Get
UIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); | |
743 ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.Pro
jectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); | |
744 | |
745 //Update FirstChild | |
746 childAdded.FirstChild = this.FirstChild; | |
747 | |
748 //Update ChildNodes | |
749 SetNewParentOnChildNodes(childAdded); | |
750 RenameChildNodes(childAdded); | |
751 | |
752 return childAdded; | |
753 } | |
754 | |
755 /// <summary> | |
756 /// Rename all childnodes | |
757 /// </summary> | |
758 /// <param name="newFileNode">The newly added Parent node.</para
m> | |
759 protected virtual void RenameChildNodes(FileNode parentNode) | |
760 { | |
761 foreach(HierarchyNode child in GetChildNodes()) | |
762 { | |
763 FileNode childNode = child as FileNode; | |
764 if(null == childNode) | |
765 { | |
766 continue; | |
767 } | |
768 string newfilename; | |
769 if(childNode.HasParentNodeNameRelation) | |
770 { | |
771 string relationalName = childNode.Parent
.GetRelationalName(); | |
772 string extension = childNode.GetRelation
NameExtension(); | |
773 newfilename = relationalName + extension
; | |
774 newfilename = Path.Combine(Path.GetDirec
toryName(childNode.Parent.GetMkDocument()), newfilename); | |
775 } | |
776 else | |
777 { | |
778 newfilename = Path.Combine(Path.GetDirec
toryName(childNode.Parent.GetMkDocument()), childNode.Caption); | |
779 } | |
780 | |
781 childNode.RenameDocument(childNode.GetMkDocument
(), newfilename); | |
782 | |
783 //We must update the DependsUpon property since
the rename operation will not do it if the childNode is not renamed | |
784 //which happens if the is no name relation betwe
en the parent and the child | |
785 string dependentOf = childNode.ItemNode.GetMetad
ata(ProjectFileConstants.DependentUpon); | |
786 if(!string.IsNullOrEmpty(dependentOf)) | |
787 { | |
788 childNode.ItemNode.SetMetadata(ProjectFi
leConstants.DependentUpon, childNode.Parent.ItemNode.GetMetadata(ProjectFileCons
tants.Include)); | |
789 } | |
790 } | |
791 } | |
792 | |
793 | |
794 /// <summary> | |
795 /// Tries recovering from a rename failure. | |
796 /// </summary> | |
797 /// <param name="fileThatFailed"> The file that failed to be ren
amed.</param> | |
798 /// <param name="originalFileName">The original filenamee</param
> | |
799 protected virtual void RecoverFromRenameFailure(string fileThatF
ailed, string originalFileName) | |
800 { | |
801 if(this.ItemNode != null && !String.IsNullOrEmpty(origin
alFileName)) | |
802 { | |
803 this.ItemNode.Rename(originalFileName); | |
804 } | |
805 } | |
806 | |
807 protected override bool CanDeleteItem(__VSDELETEITEMOPERATION de
leteOperation) | |
808 { | |
809 if(deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_
DeleteFromStorage) | |
810 { | |
811 return this.ProjectMgr.CanProjectDeleteItems; | |
812 } | |
813 return false; | |
814 } | |
815 | |
816 /// <summary> | |
817 /// This should be overriden for node that are not saved on disk | |
818 /// </summary> | |
819 /// <param name="oldName">Previous name in storage</param> | |
820 /// <param name="newName">New name in storage</param> | |
821 protected virtual void RenameInStorage(string oldName, string ne
wName) | |
822 { | |
823 File.Move(oldName, newName); | |
824 } | |
825 | |
826 /// <summary> | |
827 /// factory method for creating single file generators. | |
828 /// </summary> | |
829 /// <returns></returns> | |
830 protected virtual ISingleFileGenerator CreateSingleFileGenerator
() | |
831 { | |
832 return new SingleFileGenerator(this.ProjectMgr); | |
833 } | |
834 | |
835 /// <summary> | |
836 /// This method should be overridden to provide the list of spec
ial files and associated flags for source control. | |
837 /// </summary> | |
838 /// <param name="sccFile">One of the file associated to the node
.</param> | |
839 /// <param name="files">The list of files to be placed under sou
rce control.</param> | |
840 /// <param name="flags">The flags that are associated to the fil
es.</param> | |
841 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "Scc")] | |
842 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "scc")] | |
843 protected internal override void GetSccSpecialFiles(string sccFi
le, IList<string> files, IList<tagVsSccFilesFlags> flags) | |
844 { | |
845 if(this.ExcludeNodeFromScc) | |
846 { | |
847 return; | |
848 } | |
849 | |
850 if(files == null) | |
851 { | |
852 throw new ArgumentNullException("files"); | |
853 } | |
854 | |
855 if(flags == null) | |
856 { | |
857 throw new ArgumentNullException("flags"); | |
858 } | |
859 | |
860 foreach(HierarchyNode node in this.GetChildNodes()) | |
861 { | |
862 files.Add(node.GetMkDocument()); | |
863 } | |
864 } | |
865 | |
866 #endregion | |
867 | |
868 #region Helper methods | |
869 /// <summary> | |
870 /// Get's called to rename the eventually running document this
hierarchyitem points to | |
871 /// </summary> | |
872 /// returns FALSE if the doc can not be renamed | |
873 internal bool RenameDocument(string oldName, string newName) | |
874 { | |
875 IVsRunningDocumentTable pRDT = this.GetService(typeof(IV
sRunningDocumentTable)) as IVsRunningDocumentTable; | |
876 if(pRDT == null) return false; | |
877 IntPtr docData = IntPtr.Zero; | |
878 IVsHierarchy pIVsHierarchy; | |
879 uint itemId; | |
880 uint uiVsDocCookie; | |
881 | |
882 SuspendFileChanges sfc = new SuspendFileChanges(this.Pro
jectMgr.Site, oldName); | |
883 sfc.Suspend(); | |
884 | |
885 try | |
886 { | |
887 // Suspend ms build since during a rename operat
ion no msbuild re-evaluation should be performed until we have finished. | |
888 // Scenario that could fail if we do not suspend
. | |
889 // We have a project system relying on MPF that
triggers a Compile target build (re-evaluates itself) whenever the project chang
es. (example: a file is added, property changed.) | |
890 // 1. User renames a file in the above project
sytem relying on MPF | |
891 // 2. Our rename funstionality implemented in th
is method removes and readds the file and as a post step copies all msbuild entr
ies from the removed file to the added file. | |
892 // 3. The project system mentioned will trigger
an msbuild re-evaluate with the new item, because it was listening to OnItemAdde
d. | |
893 // The problem is that the item at the "add"
time is only partly added to the project, since the msbuild part has not yet bee
n copied over as mentioned in part 2 of the last step of the rename process. | |
894 // The result is that the project re-evaluate
s itself wrongly. | |
895 VSRENAMEFILEFLAGS renameflag = VSRENAMEFILEFLAGS
.VSRENAMEFILEFLAGS_NoFlags; | |
896 try | |
897 { | |
898 this.ProjectMgr.SuspendMSBuild(); | |
899 ErrorHandler.ThrowOnFailure(pRDT.FindAnd
LockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldName, out pIVsHierarchy, out itemI
d, out docData, out uiVsDocCookie)); | |
900 | |
901 if(pIVsHierarchy != null && !Utilities.I
sSameComObject(pIVsHierarchy, this.ProjectMgr)) | |
902 { | |
903 // Don't rename it if it wasn't
opened by us. | |
904 return false; | |
905 } | |
906 | |
907 // ask other potentially running package
s | |
908 if(!this.ProjectMgr.Tracker.CanRenameIte
m(oldName, newName, renameflag)) | |
909 { | |
910 return false; | |
911 } | |
912 // Allow the user to "fix" the project b
y renaming the item in the hierarchy | |
913 // to the real name of the file on disk. | |
914 if(IsFileOnDisk(oldName) || !IsFileOnDis
k(newName)) | |
915 { | |
916 RenameInStorage(oldName, newName
); | |
917 } | |
918 | |
919 string newFileName = Path.GetFileName(ne
wName); | |
920 DocumentManager.UpdateCaption(this.Proje
ctMgr.Site, newFileName, docData); | |
921 bool caseOnlyChange = NativeMethods.IsSa
mePath(oldName, newName); | |
922 if(!caseOnlyChange) | |
923 { | |
924 // Check out the project file if
necessary. | |
925 if(!this.ProjectMgr.QueryEditPro
jectFile(false)) | |
926 { | |
927 throw Marshal.GetExcepti
onForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); | |
928 } | |
929 | |
930 this.RenameFileNode(oldName, new
Name); | |
931 } | |
932 else | |
933 { | |
934 this.RenameCaseOnlyChange(newFil
eName); | |
935 } | |
936 } | |
937 finally | |
938 { | |
939 this.ProjectMgr.ResumeMSBuild(this.Proje
ctMgr.ReEvaluateProjectFileTargetName); | |
940 } | |
941 | |
942 this.ProjectMgr.Tracker.OnItemRenamed(oldName, n
ewName, renameflag); | |
943 } | |
944 finally | |
945 { | |
946 sfc.Resume(); | |
947 if(docData != IntPtr.Zero) | |
948 { | |
949 Marshal.Release(docData); | |
950 } | |
951 } | |
952 | |
953 return true; | |
954 } | |
955 | |
956 private FileNode RenameFileNode(string oldFileName, string newFi
leName) | |
957 { | |
958 return this.RenameFileNode(oldFileName, newFileName, thi
s.Parent.ID); | |
959 } | |
960 | |
961 /// <summary> | |
962 /// Renames the file node for a case only change. | |
963 /// </summary> | |
964 /// <param name="newFileName">The new file name.</param> | |
965 private void RenameCaseOnlyChange(string newFileName) | |
966 { | |
967 //Update the include for this item. | |
968 string include = this.ItemNode.Item.Include; | |
969 if(String.Compare(include, newFileName, StringComparison
.OrdinalIgnoreCase) == 0) | |
970 { | |
971 this.ItemNode.Item.Include = newFileName; | |
972 } | |
973 else | |
974 { | |
975 string includeDir = Path.GetDirectoryName(includ
e); | |
976 this.ItemNode.Item.Include = Path.Combine(includ
eDir, newFileName); | |
977 } | |
978 | |
979 this.ItemNode.RefreshProperties(); | |
980 | |
981 this.ReDraw(UIHierarchyElement.Caption); | |
982 this.RenameChildNodes(this); | |
983 | |
984 // Refresh the property browser. | |
985 IVsUIShell shell = this.ProjectMgr.Site.GetService(typeo
f(SVsUIShell)) as IVsUIShell; | |
986 Debug.Assert(shell != null, "Could not get the ui shell
from the project"); | |
987 if(shell == null) | |
988 { | |
989 throw new InvalidOperationException(); | |
990 } | |
991 | |
992 ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser
(0)); | |
993 | |
994 //Select the new node in the hierarchy | |
995 IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.Get
UIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); | |
996 ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.Pro
jectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); | |
997 } | |
998 | |
999 #endregion | |
1000 | |
1001 #region SingleFileGenerator Support methods | |
1002 /// <summary> | |
1003 /// Event handler for the Custom tool property changes | |
1004 /// </summary> | |
1005 /// <param name="sender">FileNode sending it</param> | |
1006 /// <param name="e">Node event args</param> | |
1007 internal virtual void OnCustomToolChanged(object sender, Hierarc
hyNodeEventArgs e) | |
1008 { | |
1009 this.RunGenerator(); | |
1010 } | |
1011 | |
1012 /// <summary> | |
1013 /// Event handler for the Custom tool namespce property changes | |
1014 /// </summary> | |
1015 /// <param name="sender">FileNode sending it</param> | |
1016 /// <param name="e">Node event args</param> | |
1017 internal virtual void OnCustomToolNameSpaceChanged(object sender
, HierarchyNodeEventArgs e) | |
1018 { | |
1019 this.RunGenerator(); | |
1020 } | |
1021 | |
1022 #endregion | |
1023 | |
1024 #region helpers | |
1025 /// <summary> | |
1026 /// Runs a generator. | |
1027 /// </summary> | |
1028 internal void RunGenerator() | |
1029 { | |
1030 ISingleFileGenerator generator = this.CreateSingleFileGe
nerator(); | |
1031 if(generator != null) | |
1032 { | |
1033 generator.RunGenerator(this.Url); | |
1034 } | |
1035 } | |
1036 | |
1037 /// <summary> | |
1038 /// Update the ChildNodes after the parent node has been renamed | |
1039 /// </summary> | |
1040 /// <param name="newFileNode">The new FileNode created as part o
f the rename of this node</param> | |
1041 private void SetNewParentOnChildNodes(FileNode newFileNode) | |
1042 { | |
1043 foreach(HierarchyNode childNode in GetChildNodes()) | |
1044 { | |
1045 childNode.Parent = newFileNode; | |
1046 } | |
1047 } | |
1048 | |
1049 private List<HierarchyNode> GetChildNodes() | |
1050 { | |
1051 List<HierarchyNode> childNodes = new List<HierarchyNode>
(); | |
1052 HierarchyNode childNode = this.FirstChild; | |
1053 while(childNode != null) | |
1054 { | |
1055 childNodes.Add(childNode); | |
1056 childNode = childNode.NextSibling; | |
1057 } | |
1058 return childNodes; | |
1059 } | |
1060 #endregion | |
1061 } | |
1062 } | |
OLD | NEW |