namespace SolutionOpen { using System; using Microsoft.VisualStudio.CommandBars; using Extensibility; using EnvDTE; using EnvDTE80; using System.Windows.Forms; using System.Collections.Specialized; /// <summary>The object for implementing an Add-in.</summary> /// <seealso class='IDTExtensibility2' /> public class Connect : Object, IDTExtensibility2, IDTCommandTarget { /// <summary>Implements the constructor for the Add-in object. Place your initialization code within this method.</summary> public Connect() { } /// <summary>Implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.</summary> /// <param term='application'>Root object of the host application.</param> /// <param term='connectMode'>Describes how the Add-in is being loaded.</param> /// <param term='addInInst'>Object representing this Add-in.</param> /// <seealso class='IDTExtensibility2' /> public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { applicationObject = (DTE2)application; addInInstance = (AddIn)addInInst; if (connectMode == ext_ConnectMode.ext_cm_UISetup) { object[] contextGUIDS = new object[] { }; Commands2 commands = (Commands2)applicationObject.Commands; try { //Add a command to the Commands collection: Command command = commands.AddNamedCommand2(addInInstance, "SolutionOpen", "SolutionOpen", "Open files from anywhere within the solution", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton); Command command2 = commands.AddNamedCommand2(addInInstance, "HeaderFlip", "HeaderFlip", "Flips through all the files with the same base name", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton); Command command3 = commands.AddNamedCommand2(addInInstance, "RefreshFileList", "RefreshFileList", "Refreshes the list of files in the solution from the project files", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton); } catch (Exception) { } } } /// <summary>Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.</summary> /// <param term='disconnectMode'>Describes how the Add-in is being unloaded.</param> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) { } /// <summary>Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnAddInsUpdate(ref Array custom) { } /// <summary>Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnStartupComplete(ref Array custom) { } /// <summary>Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.</summary> /// <param term='custom'>Array of parameters that are host application specific.</param> /// <seealso class='IDTExtensibility2' /> public void OnBeginShutdown(ref Array custom) { } /// <summary>Implements the QueryStatus method of the IDTCommandTarget interface. This is called when the command's availability is updated</summary> /// <param term='commandName'>The name of the command to determine state for.</param> /// <param term='neededText'>Text that is needed for the command.</param> /// <param term='status'>The state of the command in the user interface.</param> /// <param term='commandText'>Text requested by the neededText parameter.</param> /// <seealso class='Exec' /> public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText) { if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone) { if (commandName == "SolutionOpen.Connect.SolutionOpen" || commandName == "SolutionOpen.Connect.HeaderFlip" || commandName == "SolutionOpen.Connect.RefreshFileList") { status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled; } } } private String BaseName(string fullName) { int fileNameStart = fullName.LastIndexOfAny("\\/".ToCharArray()) + 1; int firstDot = fullName.IndexOf('.', fileNameStart); if (firstDot > 0) return fullName.Substring(fileNameStart, (firstDot - fileNameStart)); return fullName; } /// <summary>Implements the Exec method of the IDTCommandTarget interface. This is called when the command is invoked.</summary> /// <param term='commandName'>The name of the command to execute.</param> /// <param term='executeOption'>Describes how the command should be run.</param> /// <param term='varIn'>Parameters passed from the caller to the command handler.</param> /// <param term='varOut'>Parameters passed from the command handler to the caller.</param> /// <param term='handled'>Informs the caller if the command was handled or not.</param> /// <seealso class='Exec' /> public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled) { handled = false; if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { bool refreshFileList = false; if (commandName == "SolutionOpen.Connect.RefreshFileList") { handled = true; refreshFileList = true; } if (refreshFileList || filePaths.Count == 0) { // get the list of files in the project filePaths.Clear(); EnvDTE.Projects projects = applicationObject.Solution.Projects; for (int i = 1; i <= projects.Count; ++i) FindFiles(projects.Item(i).ProjectItems, ref filePaths); // return if all that was requested was a file list refresh if (handled) return; } if (commandName == "SolutionOpen.Connect.SolutionOpen") { // show the dialog box and handle what the user wants to do ChooseFile chooseFile = new ChooseFile(ref applicationObject, ref filePaths); if (chooseFile.ShowDialog() == DialogResult.OK) chooseFile.OpenSelectedFiles(); // clean up chooseFile.Dispose(); handled = true; return; } else if (commandName == "SolutionOpen.Connect.HeaderFlip") { // make sure there is an active document if (applicationObject.ActiveDocument != null) { // Get the current active document and its base name String activeDocumentFullName = applicationObject.ActiveDocument.FullName; String activeDocumentBaseName = BaseName(activeDocumentFullName); // find the files with the same base name StringCollection matchingFilePaths = new StringCollection(); foreach (String fullName in filePaths.Values) { String baseName = BaseName(fullName); if (String.Compare(activeDocumentBaseName, baseName, true) == 0) matchingFilePaths.Add(fullName); } // make sure that we have more than one of them open before we try to switch if (matchingFilePaths.Count > 1) { // figure out which document in the list (if any) is the current document int activeDocumentIndex = -1; for (int i = 0; activeDocumentIndex < 0 && i < matchingFilePaths.Count; ++i) { if (String.Compare(activeDocumentFullName, matchingFilePaths[i], true) == 0) activeDocumentIndex = i; } // make sure we found this document if (activeDocumentIndex >= 0) { // figure out which document comes next int nextDocument = activeDocumentIndex + 1; if (nextDocument >= matchingFilePaths.Count) nextDocument = 0; // open and display that document applicationObject.ItemOperations.OpenFile(matchingFilePaths[nextDocument], EnvDTE.Constants.vsViewKindAny); } } handled = true; return; } } } } private void FindFiles(EnvDTE.ProjectItems projectItems, ref StringDictionary filePaths) { foreach (EnvDTE.ProjectItem projectItem in projectItems) { // check all the files in this project item for (int i = 0; i < projectItem.FileCount; ++i) { // Make sure the project item is a physical file if (projectItem.Kind == "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}") { String fileName = projectItem.get_FileNames(1); if (fileName != null) filePaths[fileName] = fileName; } } // recurse into any project items within this project item if (projectItem.ProjectItems != null) FindFiles(projectItem.ProjectItems, ref filePaths); // handle sub-projects if (projectItem.SubProject != null) FindFiles(projectItem.SubProject.ProjectItems, ref filePaths); } } private DTE2 applicationObject; private AddIn addInInstance; private StringDictionary filePaths = new StringDictionary(); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#6 | 6444 | Jeff Grills |
Improve performance on very large projects by persisting the file list between command invocations. Add a new command to refresh the cached file list. At some point it would be nice to detect changes and automatically update, but I needed something to solve this problem quickly. |
||
#5 | 6275 | Jeff Grills | Fix header flip now that the solution traversal uses a dictionary | ||
#4 | 6274 | Jeff Grills |
Fix minor bug when a solution contains the same source file in multiple projects. Fix occasional focusing issue with the first element. Properly preserve the case of the file names and paths in the selection window. |
||
#3 | 6273 | Jeff Grills |
Add display limit to alleviate performance problems for very large solutions. Minor code clean up. |
||
#2 | 5230 | Jeff Grills |
Fix bug where the wrong window would have input focus. Bind child windows to the edges so resizing works sensibly. Add release build version of the DLL. |
||
#1 | 5042 | Jeff Grills |
Add first pass of SolutionOpen project, which can open a file listed anywhere within a visual studio solution. This is supposed to be very similar to the WorkspaceWhiz workspace open functionality. |