using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Beefy; using Beefy.gfx; using Beefy.theme.dark; using Beefy.widgets; using System.IO; using System.Diagnostics; namespace IDE.ui { public class LaunchDialog : IDEDialog { const String cBrowseCmd = "< Browse ... >"; const String cCurProjectCmd = "< Current Debug Setting >"; PathComboBox mTargetCombo; DarkComboBox mArgumentsCombo; DarkComboBox mWorkingDirCombo; DarkComboBox mEnvironmentCombo; DarkCheckBox mDebugCheckbox; DarkCheckBox mPausedCheckbox; DarkCheckBox mProfileCheckbox; bool mIgnoreContentChanged; public this() { mSettingHistoryManager = gApp.mLaunchHistoryManager; mTitle = new .("Launch Executable"); mWindowFlags = .ClientSized | .TopMost | .Caption | .Border | .SysMenu | .Resizable; AddOkCancelButtons(new (evt) => { evt.mCloseDialog = false; Launch(); }, null, 0, 1); mButtonBottomMargin = GS!(6); mButtonRightMargin = GS!(6); mTargetCombo = new PathComboBox(); mTargetCombo.MakeEditable(new PathEditWidget()); mTargetCombo.Label = ""; mTargetCombo.mPopulateMenuAction.Add(new => PopulateTargetMenu); AddWidget(mTargetCombo); AddEdit(mTargetCombo.mEditWidget); mArgumentsCombo = new DarkComboBox(); mArgumentsCombo.MakeEditable(); mArgumentsCombo.Label = ""; mArgumentsCombo.mPopulateMenuAction.Add(new => PopulateArgumentsMenu); AddWidget(mArgumentsCombo); AddEdit(mArgumentsCombo.mEditWidget); mWorkingDirCombo = new PathComboBox(); mWorkingDirCombo.MakeEditable(new PathEditWidget()); mWorkingDirCombo.Label = ""; mWorkingDirCombo.mPopulateMenuAction.Add(new => PopulateWorkingDirMenu); AddWidget(mWorkingDirCombo); AddEdit(mWorkingDirCombo.mEditWidget); mEnvironmentCombo = new DarkComboBox(); mEnvironmentCombo.MakeEditable(); mEnvironmentCombo.Label = ""; mEnvironmentCombo.mPopulateMenuAction.Add(new => PopulateEnvironmentMenu); AddWidget(mEnvironmentCombo); AddEdit(mEnvironmentCombo.mEditWidget); mDebugCheckbox = new DarkCheckBox(); mDebugCheckbox.Label = "&Debug"; mDebugCheckbox.Checked = true; AddDialogComponent(mDebugCheckbox); mPausedCheckbox = new DarkCheckBox(); mPausedCheckbox.Label = "&Start Paused"; mPausedCheckbox.Checked = false; AddDialogComponent(mPausedCheckbox); mProfileCheckbox = new DarkCheckBox(); mProfileCheckbox.Label = "P&rofile"; mProfileCheckbox.Checked = false; AddDialogComponent(mProfileCheckbox); //mPrevButton = new DarkButton(); //mPrevButton.Label = "< Prev"; CreatePrevNextButtons(); } protected override void UseProps(PropertyBag propBag) { mTargetCombo.Label = propBag.Get("Target") ?? ""; mArgumentsCombo.Label = propBag.Get("Arguments") ?? ""; mWorkingDirCombo.Label = propBag.Get("WorkingDir") ?? ""; mEnvironmentCombo.Label = propBag.Get("EnvVars") ?? ""; mDebugCheckbox.Checked = propBag.Get("Debug", true); mPausedCheckbox.Checked = propBag.Get("StartPaused"); mProfileCheckbox.Checked = propBag.Get("Profile"); } void SaveProps() { PropertyBag propBag = new PropertyBag(); propBag.Add("Target", mTargetCombo.Label); propBag.Add("Arguments", mArgumentsCombo.Label); propBag.Add("WorkingDir", mWorkingDirCombo.Label); propBag.Add("EnvVars", mEnvironmentCombo.Label); propBag.Add("Debug", mDebugCheckbox.Checked); propBag.Add("StartPaused", mPausedCheckbox.Checked); propBag.Add("Profile", mProfileCheckbox.Checked); mSettingHistoryManager.Add(propBag); } bool GetStartupOption(delegate String (Project.Options options) dlg, String outString) { if (gApp.mWorkspace.mStartupProject != null) { let options = gApp.GetCurProjectOptions(gApp.mWorkspace.mStartupProject); let workspaceOptions = gApp.GetCurWorkspaceOptions(); var inStr = dlg(options); if (inStr == null) return false; gApp.ResolveConfigString(workspaceOptions, gApp.mWorkspace.mStartupProject, options, inStr, "", outString); return true; } return false; } void DoBrowse() { #if !CLI String str = scope String(); GetStartupOption(scope (options) => options.mDebugOptions.mCommand, str); String dir = scope String(); Path.GetDirectoryPath(str, dir); IDEUtils.FixFilePath(dir); var fileDialog = scope System.IO.OpenFileDialog(); fileDialog.ShowReadOnly = false; fileDialog.Title = "Select Executable"; fileDialog.Multiselect = true; if (!dir.IsEmpty) fileDialog.InitialDirectory = dir; fileDialog.ValidateNames = true; fileDialog.DefaultExt = ".exe"; fileDialog.SetFilter("Executables (*.exe)|*.exe|All files (*.*)|*.*"); mWidgetWindow.PreModalChild(); if (fileDialog.ShowDialog(gApp.GetActiveWindow()) case .Ok) { var fileNames = fileDialog.FileNames; if (!fileNames.IsEmpty) mTargetCombo.Label = fileNames[0]; } #endif } void PopulateTargetMenu(Menu menu) { var item = menu.AddItem(cBrowseCmd); item.mOnMenuItemSelected.Add(new (dlg) => { DoBrowse(); }); item = menu.AddItem(cCurProjectCmd); item.mOnMenuItemSelected.Add(new (dlg) => { mIgnoreContentChanged = true; String str = scope String(); GetStartupOption(scope (options) => options.mDebugOptions.mCommand, str); mTargetCombo.Label = str; mIgnoreContentChanged = false; }); } void PopulateArgumentsMenu(Menu menu) { var item = menu.AddItem(cCurProjectCmd); item.mOnMenuItemSelected.Add(new (dlg) => { mIgnoreContentChanged = true; String str = scope String(); GetStartupOption(scope (options) => options.mDebugOptions.mCommandArguments, str); mArgumentsCombo.Label = str; mIgnoreContentChanged = false; }); } void PopulateWorkingDirMenu(Menu menu) { var item = menu.AddItem(cCurProjectCmd); item.mOnMenuItemSelected.Add(new (dlg) => { mIgnoreContentChanged = true; String str = scope String(); GetStartupOption(scope (options) => options.mDebugOptions.mWorkingDirectory, str); mWorkingDirCombo.Label = str; mIgnoreContentChanged = false; }); } void PopulateEnvironmentMenu(Menu menu) { var item = menu.AddItem(cCurProjectCmd); item.mOnMenuItemSelected.Add(new (dlg) => { mIgnoreContentChanged = true; String str = scope String(); for (int i = 0; true; i++) { var curStr = scope String(); if (!GetStartupOption(scope (options) => { if (i < options.mDebugOptions.mEnvironmentVars.Count) return options.mDebugOptions.mEnvironmentVars[i]; return null; }, curStr)) break; if (!str.IsEmpty) str.Append(";"); str.Append(curStr); } mEnvironmentCombo.Label = str; mIgnoreContentChanged = false; }); } public static void DoLaunch(LaunchDialog dialog, String targetPath, String arguments, String workingDir, String envVarStr, bool startPaused, bool debug) { var workingDir; if (!File.Exists(targetPath)) { dialog?.mTargetCombo.SetFocus(); gApp.Fail(scope String()..AppendF("Unable to locate target file '{0}'", targetPath)); return; } if (workingDir.IsWhiteSpace) { workingDir = scope:: .(@workingDir); Path.GetDirectoryPath(targetPath, workingDir); } if (workingDir.EndsWith('\\') || workingDir.EndsWith('/')) workingDir.RemoveFromEnd(1); if (!Directory.Exists(workingDir)) { dialog?.mWorkingDirCombo.SetFocus(); gApp.Fail(scope String()..AppendF("Unable to locate working directory '{0}'", workingDir)); return; } if (gApp.mDebugger.mIsRunning) { gApp.Fail("An executable is already being debugged"); return; } gApp.mProfilePanel.Clear(); gApp.mDebugger.ClearInvalidBreakpoints(); gApp.mExecutionPaused = false; gApp.mTargetDidInitBreak = false; gApp.mTargetHadFirstBreak = false; if (!debug) { ProcessStartInfo procInfo = scope ProcessStartInfo(); procInfo.UseShellExecute = false; procInfo.SetWorkingDirectory(workingDir); procInfo.SetArguments(arguments); for (var envVar in envVarStr.Split(';')) { envVar.Trim(); if (envVar.IsEmpty) continue; int eqPos = envVar.IndexOf('=', 1); if (eqPos == -1) { gApp.OutputErrorLine("Invalid environment variable: {0}", envVar); } else { procInfo.AddEnvironmentVariable(StringView(envVar, 0, eqPos), StringView(envVar, eqPos + 1)); } } procInfo.SetFileName(targetPath); var spawnedProcess = scope SpawnedProcess(); if (spawnedProcess.Start(procInfo) case .Err) { gApp.Fail(scope String()..AppendF("Unable to start executable: {0}", targetPath)); } return; } if (startPaused) { gApp.mTargetDidInitBreak = true; gApp.mTargetHadFirstBreak = true; } var envVars = scope Dictionary(); defer { for (var kv in envVars) { delete kv.key; delete kv.value; } } Environment.GetEnvironmentVariables(envVars); for (var envVar in envVarStr.Split(';')) { envVar.Trim(); if (envVar.IsEmpty) continue; int eqPos = envVar.IndexOf('=', 1); if (eqPos == -1) { gApp.OutputErrorLine("Invalid environment variable: {0}", envVar); } else { Environment.SetEnvironmentVariable(envVars, StringView(envVar, 0, eqPos), StringView(envVar, eqPos + 1)); } } var envBlock = scope List(); Environment.EncodeEnvironmentVariables(envVars, envBlock); if (!gApp.mDebugger.OpenFile(targetPath, arguments, workingDir, envBlock, false)) { gApp.Fail(scope String()..AppendF("Unable to open executable for debugging: {0}", targetPath)); return; } gApp.[Friend]CheckDebugVisualizers(); gApp.mDebugger.mIsRunning = true; gApp.mDebugger.RehupBreakpoints(true); gApp.mDebugger.Run(); } void Launch() { var targetPath = scope String(); targetPath.Append(mTargetCombo.Label); IDEUtils.FixFilePath(targetPath); String workingDir = scope String(); workingDir.Append(mWorkingDirCombo.Label); if (Path.IsPathRooted(workingDir)) { defer targetPath.Remove(0, targetPath.Length); Path.GetAbsolutePath(targetPath, workingDir, targetPath); } else { String targetDir = scope .(); Path.GetDirectoryPath(targetPath, targetDir); if (!targetDir.IsWhiteSpace) { defer workingDir.Remove(0, workingDir.Length); Path.GetAbsolutePath(workingDir, targetDir, workingDir); } } String envVarStr = scope String(); envVarStr.Append(mEnvironmentCombo.Label); String arguments = scope String(); arguments.Append(mArgumentsCombo.Label); DoLaunch(this, targetPath, arguments, workingDir, envVarStr, mPausedCheckbox.Checked, mDebugCheckbox.Checked); SaveProps(); Close(); } public override void ResizeComponents() { base.ResizeComponents(); float curY = GS!(30); mTargetCombo.Resize(GS!(6), curY, mWidth - GS!(6) - GS!(6), GS!(22)); curY += GS!(42); mArgumentsCombo.Resize(GS!(6), curY, mWidth - GS!(6) - GS!(6), GS!(22)); curY += GS!(42); mWorkingDirCombo.Resize(GS!(6), curY, mWidth - GS!(6) - GS!(6), GS!(22)); curY += GS!(42); mEnvironmentCombo.Resize(GS!(6), curY, mWidth - GS!(6) - GS!(6), GS!(22)); curY += GS!(28); mDebugCheckbox.Resize(GS!(6), curY, mProfileCheckbox.CalcWidth(), GS!(22)); curY += GS!(22); mPausedCheckbox.Resize(GS!(6), curY, mPausedCheckbox.CalcWidth(), GS!(22)); curY += GS!(22); mProfileCheckbox.Resize(GS!(6), curY, mProfileCheckbox.CalcWidth(), GS!(22)); } public override void CalcSize() { mWidth = GS!(400); mHeight = GS!(260); } public override void AddedToParent() { base.AddedToParent(); RehupMinSize(); } void RehupMinSize() { mWidgetWindow.SetMinimumSize(GS!(280), GS!(280), true); } public override void RehupScale(float oldScale, float newScale) { base.RehupScale(oldScale, newScale); RehupMinSize(); } public override void Resize(float x, float y, float width, float height) { base.Resize(x, y, width, height); ResizeComponents(); } public override void Draw(Graphics g) { base.Draw(g); g.DrawString("Target Path:", GS!(6), mTargetCombo.mY - GS!(18)); g.DrawString("Arguments:", GS!(6), mArgumentsCombo.mY - GS!(18)); g.DrawString("Working Directory:", GS!(6), mWorkingDirCombo.mY - GS!(18)); g.DrawString("Environment Variables:", GS!(6), mEnvironmentCombo.mY - GS!(18)); } public override void Update() { base.Update(); } } }