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

Side by Side Diff: obsolete/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
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 }
OLDNEW
« no previous file with comments | « obsolete/Microsoft.VisualStudio.Project/FileDocumentManager.cs ('k') | obsolete/Microsoft.VisualStudio.Project/FolderNode.cs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698