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 FolderNode : HierarchyNode | |
22 { | |
23 #region ctors | |
24 /// <summary> | |
25 /// Constructor for the FolderNode | |
26 /// </summary> | |
27 /// <param name="root">Root node of the hierarchy</param> | |
28 /// <param name="relativePath">relative path from root i.e.: "Ne
wFolder1\\NewFolder2\\NewFolder3</param> | |
29 /// <param name="element">Associated project element</param> | |
30 public FolderNode(ProjectNode root, string relativePath, Project
Element element) | |
31 : base(root, element) | |
32 { | |
33 this.VirtualNodeName = relativePath.TrimEnd('\\'); | |
34 } | |
35 #endregion | |
36 | |
37 #region overridden properties | |
38 public override int SortPriority | |
39 { | |
40 get { return DefaultSortOrderNode.FolderNode; } | |
41 } | |
42 | |
43 /// <summary> | |
44 /// This relates to the SCC glyph | |
45 /// </summary> | |
46 public override VsStateIcon StateIconIndex | |
47 { | |
48 get | |
49 { | |
50 // The SCC manager does not support being asked
for the state icon of a folder (result of the operation is undefined) | |
51 return VsStateIcon.STATEICON_NOSTATEICON; | |
52 } | |
53 } | |
54 #endregion | |
55 | |
56 #region overridden methods | |
57 protected override NodeProperties CreatePropertiesObject() | |
58 { | |
59 return new FolderNodeProperties(this); | |
60 } | |
61 | |
62 protected internal override void DeleteFromStorage(string path) | |
63 { | |
64 this.DeleteFolder(path); | |
65 } | |
66 | |
67 /// <summary> | |
68 /// Get the automation object for the FolderNode | |
69 /// </summary> | |
70 /// <returns>An instance of the Automation.OAFolderNode type if
succeeded</returns> | |
71 public override object GetAutomationObject() | |
72 { | |
73 if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) | |
74 { | |
75 return null; | |
76 } | |
77 | |
78 return new Automation.OAFolderItem(this.ProjectMgr.GetAu
tomationObject() as Automation.OAProject, this); | |
79 } | |
80 | |
81 public override object GetIconHandle(bool open) | |
82 { | |
83 return this.ProjectMgr.ImageHandler.GetIconHandle(open ?
(int)ProjectNode.ImageName.OpenFolder : (int)ProjectNode.ImageName.Folder); | |
84 } | |
85 | |
86 /// <summary> | |
87 /// Rename Folder | |
88 /// </summary> | |
89 /// <param name="label">new Name of Folder</param> | |
90 /// <returns>VSConstants.S_OK, if succeeded</returns> | |
91 public override int SetEditLabel(string label) | |
92 { | |
93 if(String.Compare(Path.GetFileName(this.Url.TrimEnd('\\'
)), label, StringComparison.Ordinal) == 0) | |
94 { | |
95 // Label matches current Name | |
96 return VSConstants.S_OK; | |
97 } | |
98 | |
99 string newPath = Path.Combine(new DirectoryInfo(this.Url
).Parent.FullName, label); | |
100 | |
101 // Verify that No Directory/file already exists with the
new name among current children | |
102 for(HierarchyNode n = Parent.FirstChild; n != null; n =
n.NextSibling) | |
103 { | |
104 if(n != this && String.Compare(n.Caption, label,
StringComparison.OrdinalIgnoreCase) == 0) | |
105 { | |
106 return ShowFileOrFolderAlreadExistsError
Message(newPath); | |
107 } | |
108 } | |
109 | |
110 // Verify that No Directory/file already exists with the
new name on disk | |
111 if(Directory.Exists(newPath) || File.Exists(newPath)) | |
112 { | |
113 return ShowFileOrFolderAlreadExistsErrorMessage(
newPath); | |
114 } | |
115 | |
116 try | |
117 { | |
118 RenameFolder(label); | |
119 | |
120 //Refresh the properties in the properties windo
w | |
121 IVsUIShell shell = this.ProjectMgr.GetService(ty
peof(SVsUIShell)) as IVsUIShell; | |
122 Debug.Assert(shell != null, "Could not get the u
i shell from the project"); | |
123 ErrorHandler.ThrowOnFailure(shell.RefreshPropert
yBrowser(0)); | |
124 | |
125 // Notify the listeners that the name of this fo
lder is changed. This will | |
126 // also force a refresh of the SolutionExplorer'
s node. | |
127 this.OnPropertyChanged(this, (int)__VSHPROPID.VS
HPROPID_Caption, 0); | |
128 } | |
129 catch(Exception e) | |
130 { | |
131 throw new InvalidOperationException(String.Forma
t(CultureInfo.CurrentCulture, SR.GetString(SR.RenameFolder, CultureInfo.CurrentU
ICulture), e.Message)); | |
132 } | |
133 return VSConstants.S_OK; | |
134 } | |
135 | |
136 | |
137 public override int MenuCommandId | |
138 { | |
139 get { return VsMenus.IDM_VS_CTXT_FOLDERNODE; } | |
140 } | |
141 | |
142 public override Guid ItemTypeGuid | |
143 { | |
144 get | |
145 { | |
146 return VSConstants.GUID_ItemType_PhysicalFolder; | |
147 } | |
148 } | |
149 | |
150 public override string Url | |
151 { | |
152 get | |
153 { | |
154 return Path.Combine(Path.GetDirectoryName(this.P
rojectMgr.Url), this.VirtualNodeName) + "\\"; | |
155 } | |
156 } | |
157 | |
158 public override string Caption | |
159 { | |
160 get | |
161 { | |
162 // it might have a backslash at the end... | |
163 // and it might consist of Grandparent\parent\th
is\ | |
164 string caption = this.VirtualNodeName; | |
165 string[] parts; | |
166 parts = caption.Split(Path.DirectorySeparatorCha
r); | |
167 caption = parts[parts.GetUpperBound(0)]; | |
168 return caption; | |
169 } | |
170 } | |
171 | |
172 public override string GetMkDocument() | |
173 { | |
174 Debug.Assert(this.Url != null, "No url sepcified for thi
s node"); | |
175 | |
176 return this.Url; | |
177 } | |
178 | |
179 /// <summary> | |
180 /// Enumerate the files associated with this node. | |
181 /// A folder node is not a file and as such no file to enumerate
. | |
182 /// </summary> | |
183 /// <param name="files">The list of files to be placed under sou
rce control.</param> | |
184 /// <param name="flags">The flags that are associated to the fil
es.</param> | |
185 protected internal override void GetSccFiles(System.Collections.
Generic.IList<string> files, System.Collections.Generic.IList<tagVsSccFilesFlags
> flags) | |
186 { | |
187 return; | |
188 } | |
189 | |
190 /// <summary> | |
191 /// This method should be overridden to provide the list of spec
ial files and associated flags for source control. | |
192 /// </summary> | |
193 /// <param name="sccFile">One of the file associated to the node
.</param> | |
194 /// <param name="files">The list of files to be placed under sou
rce control.</param> | |
195 /// <param name="flags">The flags that are associated to the fil
es.</param> | |
196 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "Scc")] | |
197 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBe
SpelledCorrectly", MessageId = "scc")] | |
198 protected internal override void GetSccSpecialFiles(string sccFi
le, IList<string> files, IList<tagVsSccFilesFlags> flags) | |
199 { | |
200 if(this.ExcludeNodeFromScc) | |
201 { | |
202 return; | |
203 } | |
204 | |
205 if(files == null) | |
206 { | |
207 throw new ArgumentNullException("files"); | |
208 } | |
209 | |
210 if(flags == null) | |
211 { | |
212 throw new ArgumentNullException("flags"); | |
213 } | |
214 | |
215 if(string.IsNullOrEmpty(sccFile)) | |
216 { | |
217 throw new ArgumentException(SR.GetString(SR.Inva
lidParameter, CultureInfo.CurrentUICulture), "sccFile"); | |
218 } | |
219 | |
220 // Get the file node for the file passed in. | |
221 FileNode node = this.FindChild(sccFile) as FileNode; | |
222 | |
223 // Dependents do not participate directly in scc. | |
224 if(node != null && !(node is DependentFileNode)) | |
225 { | |
226 node.GetSccSpecialFiles(sccFile, files, flags); | |
227 } | |
228 } | |
229 | |
230 /// <summary> | |
231 /// Recursevily walks the folder nodes and redraws the state ico
ns | |
232 /// </summary> | |
233 protected internal override void UpdateSccStateIcons() | |
234 { | |
235 for(HierarchyNode child = this.FirstChild; child != null
; child = child.NextSibling) | |
236 { | |
237 child.UpdateSccStateIcons(); | |
238 } | |
239 } | |
240 | |
241 protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd
, IntPtr pCmdText, ref QueryStatusResult result) | |
242 { | |
243 if(cmdGroup == VsMenus.guidStandardCommandSet97) | |
244 { | |
245 switch((VsCommands)cmd) | |
246 { | |
247 case VsCommands.Copy: | |
248 case VsCommands.Paste: | |
249 case VsCommands.Cut: | |
250 case VsCommands.Rename: | |
251 result |= QueryStatusResult.SUPP
ORTED | QueryStatusResult.ENABLED; | |
252 return VSConstants.S_OK; | |
253 | |
254 case VsCommands.NewFolder: | |
255 case VsCommands.AddNewItem: | |
256 case VsCommands.AddExistingItem: | |
257 result |= QueryStatusResult.SUPP
ORTED | QueryStatusResult.ENABLED; | |
258 return VSConstants.S_OK; | |
259 } | |
260 } | |
261 else if(cmdGroup == VsMenus.guidStandardCommandSet2K) | |
262 { | |
263 if((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROM
PROJECT) | |
264 { | |
265 result |= QueryStatusResult.SUPPORTED |
QueryStatusResult.ENABLED; | |
266 return VSConstants.S_OK; | |
267 } | |
268 } | |
269 else | |
270 { | |
271 return (int)OleConstants.OLECMDERR_E_UNKNOWNGROU
P; | |
272 } | |
273 return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, r
ef result); | |
274 } | |
275 | |
276 protected override bool CanDeleteItem(__VSDELETEITEMOPERATION de
leteOperation) | |
277 { | |
278 if(deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_
DeleteFromStorage) | |
279 { | |
280 return this.ProjectMgr.CanProjectDeleteItems; | |
281 } | |
282 return false; | |
283 } | |
284 | |
285 #endregion | |
286 | |
287 #region virtual methods | |
288 /// <summary> | |
289 /// Override if your node is not a file system folder so that | |
290 /// it does nothing or it deletes it from your storage location. | |
291 /// </summary> | |
292 /// <param name="path">Path to the folder to delete</param> | |
293 public virtual void DeleteFolder(string path) | |
294 { | |
295 if(Directory.Exists(path)) | |
296 Directory.Delete(path, true); | |
297 } | |
298 | |
299 /// <summary> | |
300 /// creates the physical directory for a folder node | |
301 /// Override if your node does not use file system folder | |
302 /// </summary> | |
303 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1804:RemoveUnusedLocals", MessageId = "e")] | |
304 public virtual void CreateDirectory() | |
305 { | |
306 try | |
307 { | |
308 if(Directory.Exists(this.Url) == false) | |
309 { | |
310 Directory.CreateDirectory(this.Url); | |
311 } | |
312 } | |
313 //TODO - this should not digest all exceptions. | |
314 catch(System.Exception e) | |
315 { | |
316 CCITracing.Trace(e); | |
317 throw; | |
318 } | |
319 } | |
320 /// <summary> | |
321 /// Creates a folder nodes physical directory | |
322 /// Override if your node does not use file system folder | |
323 /// </summary> | |
324 /// <param name="newName"></param> | |
325 /// <returns></returns> | |
326 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Perf
ormance", "CA1804:RemoveUnusedLocals", MessageId = "e")] | |
327 public virtual void CreateDirectory(string newName) | |
328 { | |
329 if(String.IsNullOrEmpty(newName)) | |
330 { | |
331 throw new ArgumentException(SR.GetString(SR.Para
meterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "newName"); | |
332 } | |
333 | |
334 try | |
335 { | |
336 // on a new dir && enter, we get called with the
same name (so do nothing if name is the same | |
337 char[] dummy = new char[1]; | |
338 dummy[0] = Path.DirectorySeparatorChar; | |
339 string oldDir = this.Url; | |
340 oldDir = oldDir.TrimEnd(dummy); | |
341 string strNewDir = Path.Combine(Path.GetDirector
yName(oldDir), newName); | |
342 | |
343 if(String.Compare(strNewDir, oldDir, StringCompa
rison.OrdinalIgnoreCase) != 0) | |
344 { | |
345 if(Directory.Exists(strNewDir)) | |
346 { | |
347 throw new InvalidOperationExcept
ion(SR.GetString(SR.DirectoryExistError, CultureInfo.CurrentUICulture)); | |
348 } | |
349 Directory.CreateDirectory(strNewDir); | |
350 } | |
351 } | |
352 //TODO - this should not digest all exceptions. | |
353 catch(System.Exception e) | |
354 { | |
355 CCITracing.Trace(e); | |
356 throw; | |
357 } | |
358 } | |
359 | |
360 /// <summary> | |
361 /// Rename the physical directory for a folder node | |
362 /// Override if your node does not use file system folder | |
363 /// </summary> | |
364 /// <returns></returns> | |
365 public virtual void RenameDirectory(string newPath) | |
366 { | |
367 if(Directory.Exists(this.Url)) | |
368 { | |
369 if(Directory.Exists(newPath)) | |
370 { | |
371 ShowFileOrFolderAlreadExistsErrorMessage
(newPath); | |
372 } | |
373 | |
374 Directory.Move(this.Url, newPath); | |
375 } | |
376 } | |
377 #endregion | |
378 | |
379 #region helper methods | |
380 private void RenameFolder(string newName) | |
381 { | |
382 // Do the rename (note that we only do the physical rena
me if the leaf name changed) | |
383 string newPath = Path.Combine(this.Parent.VirtualNodeNam
e, newName); | |
384 if(String.Compare(Path.GetFileName(VirtualNodeName), new
Name, StringComparison.Ordinal) != 0) | |
385 { | |
386 this.RenameDirectory(Path.Combine(this.ProjectMg
r.ProjectFolder, newPath)); | |
387 } | |
388 this.VirtualNodeName = newPath; | |
389 | |
390 this.ItemNode.Rename(VirtualNodeName); | |
391 | |
392 // Let all children know of the new path | |
393 for(HierarchyNode child = this.FirstChild; child != null
; child = child.NextSibling) | |
394 { | |
395 FolderNode node = child as FolderNode; | |
396 | |
397 if(node == null) | |
398 { | |
399 child.SetEditLabel(child.Caption); | |
400 } | |
401 else | |
402 { | |
403 node.RenameFolder(node.Caption); | |
404 } | |
405 } | |
406 | |
407 // Some of the previous operation may have changed the s
election so set it back to us | |
408 IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.Get
UIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); | |
409 ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.Pro
jectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); | |
410 | |
411 } | |
412 | |
413 /// <summary> | |
414 /// Show error message if not in automation mode, otherwise thro
w exception | |
415 /// </summary> | |
416 /// <param name="newPath">path of file or folder already existin
g on disk</param> | |
417 /// <returns>S_OK</returns> | |
418 private int ShowFileOrFolderAlreadExistsErrorMessage(string newP
ath) | |
419 { | |
420 //A file or folder with the name '{0}' already exists on
disk at this location. Please choose another name. | |
421 //If this file or folder does not appear in the Solution
Explorer, then it is not currently part of your project. To view files which ex
ist on disk, but are not in the project, select Show All Files from the Project
menu. | |
422 string errorMessage = (String.Format(CultureInfo.Current
Culture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture
), newPath)); | |
423 if(!Utilities.IsInAutomationFunction(this.ProjectMgr.Sit
e)) | |
424 { | |
425 string title = null; | |
426 OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL
; | |
427 OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON
_OK; | |
428 OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.
OLEMSGDEFBUTTON_FIRST; | |
429 VsShellUtilities.ShowMessageBox(this.ProjectMgr.
Site, title, errorMessage, icon, buttons, defaultButton); | |
430 return VSConstants.S_OK; | |
431 } | |
432 else | |
433 { | |
434 throw new InvalidOperationException(errorMessage
); | |
435 } | |
436 } | |
437 | |
438 #endregion | |
439 } | |
440 } | |
OLD | NEW |