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