2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Diagnostics ;
using System.ComponentModel.Design ;
using System.IO ;
using System.Linq ;
using Microsoft.VisualStudio ;
using Microsoft.VisualStudio.Shell ;
using Microsoft.VisualStudio.Shell.Interop ;
using Microsoft.VisualStudio.OLE.Interop ;
using EnvDTE ;
using System.Runtime.InteropServices ;
using System.Collections.Generic ;
2014-04-02 18:09:23 -04:00
using EnvDTE80 ;
2014-03-14 14:13:41 -04:00
namespace UnrealVS
{
internal class CommandLineEditor
{
/** constants */
private const int ComboID = 0x1030 ;
private const int ComboListID = 0x1040 ;
private const string InvalidProjectString = "<No Command-line>" ;
private const int ComboListCountMax = 32 ;
/** methods */
public CommandLineEditor ( )
{
// Create the handlers for our commands
{
// CommandLineCombo
var ComboCommandID = new CommandID ( GuidList . UnrealVSCmdSet , ComboID ) ;
ComboCommand = new OleMenuCommand ( new EventHandler ( ComboHandler ) , ComboCommandID ) ;
UnrealVSPackage . Instance . MenuCommandService . AddCommand ( ComboCommand ) ;
// CommandLineComboList
var ComboListCommandID = new CommandID ( GuidList . UnrealVSCmdSet , ComboListID ) ;
2014-04-24 08:29:14 -04:00
ComboCommandList = new OleMenuCommand ( new EventHandler ( ComboListHandler ) , ComboListCommandID ) ;
UnrealVSPackage . Instance . MenuCommandService . AddCommand ( ComboCommandList ) ;
2014-03-14 14:13:41 -04:00
}
// Register for events that we care about
2014-04-24 08:29:14 -04:00
UnrealVSPackage . Instance . OnSolutionOpened + = UpdateCommandLineCombo ;
UnrealVSPackage . Instance . OnSolutionClosed + = UpdateCommandLineCombo ;
UnrealVSPackage . Instance . OnStartupProjectChanged + = ( p ) = > UpdateCommandLineCombo ( ) ;
2014-03-14 14:13:41 -04:00
UnrealVSPackage . Instance . OnStartupProjectPropertyChanged + = OnStartupProjectPropertyChanged ;
UnrealVSPackage . Instance . OnStartupProjectConfigChanged + = OnStartupProjectConfigChanged ;
UpdateCommandLineCombo ( ) ;
}
/// <summary>
/// Called when a startup project property has changed. We'll refresh our interface.
/// </summary>
public void OnStartupProjectPropertyChanged ( UInt32 itemid , Int32 propid , UInt32 flags )
{
IVsHierarchy ProjectHierarchy ;
UnrealVSPackage . Instance . SolutionBuildManager . get_StartupProject ( out ProjectHierarchy ) ;
if ( ProjectHierarchy ! = null )
{
// @todo: filter this so that we only respond to changes in the command line property
UpdateCommandLineCombo ( ) ;
}
}
/// <summary>
/// Reads options out of the solution file.
/// </summary>
public void LoadOptions ( Stream Stream )
{
using ( BinaryReader Reader = new BinaryReader ( Stream ) )
{
List < string > CommandLines = new List < string > ( ) ;
int Count = Reader . ReadInt32 ( ) ;
for ( int CommandLineIdx = 0 ; CommandLineIdx < Count ; CommandLineIdx + + )
{
CommandLines . Add ( Reader . ReadString ( ) ) ;
}
ComboList . Clear ( ) ;
ComboList . AddRange ( CommandLines . ToArray ( ) ) ;
}
}
/// <summary>
/// Writes options to the solution file.
/// </summary>
public void SaveOptions ( Stream Stream )
{
using ( BinaryWriter Writer = new BinaryWriter ( Stream ) )
{
string [ ] CommandLines = ComboList . ToArray ( ) ;
Writer . Write ( CommandLines . Length ) ;
for ( int CommandLineIdx = 0 ; CommandLineIdx < CommandLines . Length ; CommandLineIdx + + )
{
Writer . Write ( CommandLines [ CommandLineIdx ] ) ;
}
}
}
/// <summary>
/// Called when the startup project active config has changed.
/// </summary>
private void OnStartupProjectConfigChanged ( Project Project )
{
UpdateCommandLineCombo ( ) ;
}
/// <summary>
/// Updates the command-line combo box after the project loaded state has changed
/// </summary>
private void UpdateCommandLineCombo ( )
{
// Enable or disable our command-line selector
DesiredCommandLine = null ; // clear this state var used by the combo box handler
IVsHierarchy ProjectHierarchy ;
UnrealVSPackage . Instance . SolutionBuildManager . get_StartupProject ( out ProjectHierarchy ) ;
if ( ProjectHierarchy ! = null )
{
ComboCommand . Enabled = true ;
2014-04-24 08:29:14 -04:00
ComboCommandList . Enabled = true ;
2014-03-14 14:13:41 -04:00
}
else
{
ComboCommand . Enabled = false ;
2014-04-24 08:29:14 -04:00
ComboCommandList . Enabled = false ;
2014-03-14 14:13:41 -04:00
}
string CommandLine = MakeCommandLineComboText ( ) ;
CommitCommandLineToMRU ( CommandLine ) ;
}
/// <summary>
/// Returns a string to display in the command-line combo box, based on project state
/// </summary>
/// <returns>String to display</returns>
private string MakeCommandLineComboText ( )
{
string Text = "" ;
IVsHierarchy ProjectHierarchy ;
UnrealVSPackage . Instance . SolutionBuildManager . get_StartupProject ( out ProjectHierarchy ) ;
if ( ProjectHierarchy ! = null )
{
var SelectedStartupProject = Utils . HierarchyObjectToProject ( ProjectHierarchy ) ;
if ( SelectedStartupProject ! = null )
{
var CommandLineArgumentsProperty = GetProjectCommandLineProperty ( SelectedStartupProject ) ;
if ( CommandLineArgumentsProperty ! = null )
{
var CommandLineArguments = ( string ) CommandLineArgumentsProperty . Value ;
2014-04-02 18:09:23 -04:00
// for "Game" projects automatically remove the game project filename from the start of the command line
var ActiveConfiguration = ( SolutionConfiguration2 ) UnrealVSPackage . Instance . DTE . Solution . SolutionBuild . ActiveConfiguration ;
if ( UnrealVSPackage . Instance . IsUE4Loaded & & Utils . IsGameProject ( SelectedStartupProject ) & & Utils . HasUProjectCommandLineArg ( ActiveConfiguration . Name ) )
{
string UProjectFileName = Utils . GetUProjectFileName ( SelectedStartupProject ) ;
2015-03-05 07:17:31 -05:00
if ( CommandLineArguments . Trim ( ) . StartsWith ( UProjectFileName , StringComparison . OrdinalIgnoreCase ) )
2014-04-02 18:09:23 -04:00
{
CommandLineArguments = CommandLineArguments . Trim ( ) . Substring ( UProjectFileName . Length ) . Trim ( ) ;
}
2015-03-05 07:17:31 -05:00
else if ( CommandLineArguments . Trim ( ) . StartsWith ( SelectedStartupProject . Name , StringComparison . OrdinalIgnoreCase ) )
{
CommandLineArguments = CommandLineArguments . Trim ( ) . Substring ( SelectedStartupProject . Name . Length ) . Trim ( ) ;
}
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
Text = CommandLineArguments ;
}
else
{
Text = InvalidProjectString ;
}
}
}
return Text ;
}
/// Called by combo control to query the text to display or to apply newly-entered text
private void ComboHandler ( object Sender , EventArgs Args )
{
try
{
var OleArgs = ( OleMenuCmdEventArgs ) Args ;
string InString = OleArgs . InValue as string ;
if ( InString ! = null )
{
// New text set on the combo - set the command line property
DesiredCommandLine = null ;
CommitCommandLineText ( InString ) ;
}
else if ( OleArgs . OutValue ! = IntPtr . Zero )
{
string EditingString = null ;
if ( OleArgs . InValue ! = null )
{
object [ ] InArray = OleArgs . InValue as object [ ] ;
if ( InArray ! = null & & 0 < InArray . Length )
{
EditingString = InArray . Last ( ) as string ;
}
}
string TextToDisplay = string . Empty ;
if ( EditingString ! = null )
{
// The control wants to put EditingString in the box
TextToDisplay = DesiredCommandLine = EditingString ;
}
else
{
// This is always hit at the end of interaction with the combo
if ( DesiredCommandLine ! = null )
{
TextToDisplay = DesiredCommandLine ;
DesiredCommandLine = null ;
CommitCommandLineText ( TextToDisplay ) ;
}
else
{
TextToDisplay = MakeCommandLineComboText ( ) ;
}
}
Marshal . GetNativeVariantForObject ( TextToDisplay , OleArgs . OutValue ) ;
}
}
catch ( Exception ex )
{
Exception AppEx = new ApplicationException ( "CommandLineEditor threw an exception in ComboHandler()" , ex ) ;
Logging . WriteLine ( AppEx . ToString ( ) ) ;
throw AppEx ;
}
}
private void CommitCommandLineText ( string CommandLine )
{
2014-04-02 18:09:23 -04:00
string FullCommandLine = CommandLine ;
2014-03-14 14:13:41 -04:00
IVsHierarchy ProjectHierarchy ;
UnrealVSPackage . Instance . SolutionBuildManager . get_StartupProject ( out ProjectHierarchy ) ;
if ( ProjectHierarchy ! = null )
{
var SelectedStartupProject = Utils . HierarchyObjectToProject ( ProjectHierarchy ) ;
if ( SelectedStartupProject ! = null )
{
2014-04-02 18:09:23 -04:00
// for "Game" projects automatically remove the game project filename from the start of the command line
var ActiveConfiguration = ( SolutionConfiguration2 ) UnrealVSPackage . Instance . DTE . Solution . SolutionBuild . ActiveConfiguration ;
if ( UnrealVSPackage . Instance . IsUE4Loaded & & Utils . IsGameProject ( SelectedStartupProject ) & & Utils . HasUProjectCommandLineArg ( ActiveConfiguration . Name ) )
{
string UProjectFileName = Utils . GetUProjectFileName ( SelectedStartupProject ) ;
if ( FullCommandLine . Trim ( ) . StartsWith ( UProjectFileName , StringComparison . InvariantCultureIgnoreCase ) )
{
VsShellUtilities . ShowMessageBox ( ServiceProvider . GlobalProvider ,
2015-03-05 07:17:31 -05:00
string . Format ( "INFORMATION: The project filename {0} has been removed from the command line because it is included automatically for 'Game' projects." , UProjectFileName ) ,
"UnrealVS" ,
OLEMSGICON . OLEMSGICON_INFO ,
OLEMSGBUTTON . OLEMSGBUTTON_OK ,
OLEMSGDEFBUTTON . OLEMSGDEFBUTTON_FIRST ) ;
}
else if ( FullCommandLine . Trim ( ) . StartsWith ( SelectedStartupProject . Name , StringComparison . OrdinalIgnoreCase ) )
{
VsShellUtilities . ShowMessageBox ( ServiceProvider . GlobalProvider ,
string . Format ( "INFORMATION: The project name {0} has been removed from the command line because it is included automatically for 'Game' projects." , SelectedStartupProject . Name ) ,
2014-04-02 18:09:23 -04:00
"UnrealVS" ,
OLEMSGICON . OLEMSGICON_INFO ,
OLEMSGBUTTON . OLEMSGBUTTON_OK ,
OLEMSGDEFBUTTON . OLEMSGDEFBUTTON_FIRST ) ;
}
else
{
FullCommandLine = UProjectFileName + " " + FullCommandLine ;
}
}
2014-03-14 14:13:41 -04:00
var CommandLineArgumentsProperty = GetProjectCommandLineProperty ( SelectedStartupProject ) ;
if ( CommandLineArgumentsProperty ! = null )
{
2014-04-02 18:09:23 -04:00
Utils . SetPropertyValue ( CommandLineArgumentsProperty , FullCommandLine ) ;
2014-03-14 14:13:41 -04:00
}
}
}
CommitCommandLineToMRU ( CommandLine ) ;
}
private void CommitCommandLineToMRU ( string CommandLine )
{
if ( 0 < CommandLine . Length )
{
// Maintain the MRU history
// Adds the entered command line to the top of the list
// Moves it to the top if it's already in the list
// Trims the list to the max length if it's too big
ComboList . RemoveAll ( s = > 0 = = string . Compare ( s , CommandLine ) ) ;
ComboList . Insert ( 0 , CommandLine ) ;
if ( ComboList . Count > ComboListCountMax )
{
ComboList . RemoveAt ( ComboList . Count - 1 ) ;
}
}
}
/// Called by combo control to populate the drop-down list
private void ComboListHandler ( object Sender , EventArgs Args )
{
var OleArgs = ( OleMenuCmdEventArgs ) Args ;
Marshal . GetNativeVariantForObject ( ComboList . ToArray ( ) , OleArgs . OutValue ) ;
}
/// <summary>
/// Helper function to get the correct property from a project that represents its command line arguments
/// </summary>
private static Property GetProjectCommandLineProperty ( Project InProject )
{
// C++ projects use "CommandArguments" as the property name
var CommandLineArgumentsProperty = Utils . GetProjectConfigProperty ( InProject , null , "CommandArguments" ) ;
// C# projects use "StartArguments" as the property name
if ( CommandLineArgumentsProperty = = null )
{
CommandLineArgumentsProperty = Utils . GetProjectConfigProperty ( InProject , null , "StartArguments" ) ;
}
return CommandLineArgumentsProperty ;
}
/// List of MRU strings to show in the combobox drop-down list
private readonly List < string > ComboList = new List < string > ( ) ;
/// Combo control for command-line editor
private OleMenuCommand ComboCommand ;
2014-04-24 08:29:14 -04:00
private OleMenuCommand ComboCommandList ;
2014-03-14 14:13:41 -04:00
/// Used to store the user edited commandline mid-edit, in the combo handler
private string DesiredCommandLine ;
}
}