Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(500)

Side by Side Diff: experimental/visual_studio_plugin/third_party/Microsoft.VisualStudio.Project/FileNode.cs

Issue 10928195: First round of dead file removal (Closed) Base URL: https://github.com/samclegg/nativeclient-sdk.git@master
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698