diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj index 7816fe34..06283a08 100644 --- a/Source/Core/Builder.csproj +++ b/Source/Core/Builder.csproj @@ -143,6 +143,7 @@ + @@ -179,6 +180,12 @@ ArgumentBox.cs + + UserControl + + + ExternalCommandControl.cs + @@ -262,6 +269,18 @@ + + Form + + + PreAndPostCommandsForm.cs + + + Form + + + RunExternalCommandForm.cs + Form @@ -637,9 +656,18 @@ + + ExternalCommandControl.cs + PairedFloatControl.cs + + PreAndPostCommandsForm.cs + + + RunExternalCommandForm.cs + ThingStatisticsForm.cs diff --git a/Source/Core/BuilderMono.csproj b/Source/Core/BuilderMono.csproj index f1cd04af..f90f42ea 100644 --- a/Source/Core/BuilderMono.csproj +++ b/Source/Core/BuilderMono.csproj @@ -140,6 +140,7 @@ + @@ -176,6 +177,12 @@ ArgumentBox.cs + + UserControl + + + ExternalCommandControl.cs + @@ -254,6 +261,18 @@ + + Form + + + PreAndPostCommandsForm.cs + + + Form + + + RunExternalCommandForm.cs + Form @@ -629,9 +648,18 @@ + + ExternalCommandControl.cs + PairedFloatControl.cs + + PreAndPostCommandsForm.cs + + + RunExternalCommandForm.cs + ThingStatisticsForm.cs @@ -941,7 +969,7 @@ - + diff --git a/Source/Core/Config/ExternalCommandSettings.cs b/Source/Core/Config/ExternalCommandSettings.cs new file mode 100644 index 00000000..02f10287 --- /dev/null +++ b/Source/Core/Config/ExternalCommandSettings.cs @@ -0,0 +1,122 @@ +#region ================== Copyright (c) 2021 Boris Iwanski + +/* + * This program is free software: you can redistribute it and/or modify + * + * it under the terms of the GNU General Public License as published by + * + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.If not, see. + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CodeImp.DoomBuilder.IO; + +#endregion + +namespace CodeImp.DoomBuilder.Config +{ + public class ExternalCommandSettings + { + #region ================== Variables + + private string workingdirectory; + private string commands; + private bool autocloseonsuccess; + private bool exitcodeiserror; + private bool stderriserror; + + #endregion + + #region ================== Properties + + public string WorkingDirectory { get { return workingdirectory; } set { workingdirectory = value; } } + public string Commands { get { return commands; } set { commands = value; } } + public bool AutoCloseOnSuccess { get { return autocloseonsuccess; } set { autocloseonsuccess = value; } } + public bool ExitCodeIsError { get { return exitcodeiserror; } set { exitcodeiserror = value; } } + public bool StdErrIsError { get { return stderriserror; } set { stderriserror = value; } } + + #endregion + + #region ================== Constructors + + public ExternalCommandSettings() + { + WorkingDirectory = string.Empty; + Commands = string.Empty; + AutoCloseOnSuccess = true; + ExitCodeIsError = true; + StdErrIsError = true; + } + + /// + /// Initialize with the settings loaded from a given section in a configuration. + /// + /// The configuration + /// The section to load the settings from + public ExternalCommandSettings(Configuration cfg, string section) + { + LoadSettings(cfg, section); + } + + #endregion + + #region ================== Methods + + /// + /// Loads the settings from a given section in a configuration. + /// + /// The configuration + /// The section to load the settings from + public void LoadSettings(Configuration cfg, string section) + { + WorkingDirectory = cfg.ReadSetting(section + ".workingdirectory", string.Empty); + Commands = cfg.ReadSetting(section + ".commands", string.Empty); + AutoCloseOnSuccess = cfg.ReadSetting(section + ".autocloseonsuccess", true); + ExitCodeIsError = cfg.ReadSetting(section + ".exitcodeiserror", true); + StdErrIsError = cfg.ReadSetting(section + ".stderriserror", true); + } + + /// + /// Writes the settings to a given section in a configuration. + /// + /// The configuration + /// The section to write the settings to + public void WriteSettings(Configuration cfg, string section) + { + if (!string.IsNullOrWhiteSpace(Commands)) + cfg.WriteSetting(section + ".commands", Commands); + else + cfg.DeleteSetting(section + ".commands"); + + if (!string.IsNullOrWhiteSpace(WorkingDirectory)) + cfg.WriteSetting(section + ".workingdirectory", WorkingDirectory); + else + cfg.DeleteSetting(section + ".workingdirectory"); + + cfg.WriteSetting(section + ".autocloseonsuccess", AutoCloseOnSuccess); + cfg.WriteSetting(section + ".exitcodeiserror", ExitCodeIsError); + cfg.WriteSetting(section + ".stderriserror", StdErrIsError); + } + + #endregion + } +} \ No newline at end of file diff --git a/Source/Core/Controls/ExternalCommandControl.Designer.cs b/Source/Core/Controls/ExternalCommandControl.Designer.cs new file mode 100644 index 00000000..ca16d1ad --- /dev/null +++ b/Source/Core/Controls/ExternalCommandControl.Designer.cs @@ -0,0 +1,147 @@ +namespace CodeImp.DoomBuilder.Controls +{ + partial class ExternalCommandControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.tbFolder = new System.Windows.Forms.TextBox(); + this.tbCommand = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.btnBrowseFolder = new System.Windows.Forms.Button(); + this.cbAutoclose = new System.Windows.Forms.CheckBox(); + this.cbExitCode = new System.Windows.Forms.CheckBox(); + this.cbStdErr = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // tbFolder + // + this.tbFolder.Location = new System.Drawing.Point(101, 5); + this.tbFolder.Name = "tbFolder"; + this.tbFolder.Size = new System.Drawing.Size(498, 20); + this.tbFolder.TabIndex = 0; + // + // tbCommand + // + this.tbCommand.AcceptsReturn = true; + this.tbCommand.Location = new System.Drawing.Point(101, 31); + this.tbCommand.Multiline = true; + this.tbCommand.Name = "tbCommand"; + this.tbCommand.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.tbCommand.Size = new System.Drawing.Size(498, 160); + this.tbCommand.TabIndex = 1; + this.tbCommand.WordWrap = false; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(33, 31); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(62, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Commands:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(2, 8); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(93, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Working directory:"; + // + // btnBrowseFolder + // + this.btnBrowseFolder.Image = global::CodeImp.DoomBuilder.Properties.Resources.Folder; + this.btnBrowseFolder.Location = new System.Drawing.Point(605, 4); + this.btnBrowseFolder.Name = "btnBrowseFolder"; + this.btnBrowseFolder.Size = new System.Drawing.Size(23, 23); + this.btnBrowseFolder.TabIndex = 3; + this.btnBrowseFolder.UseVisualStyleBackColor = true; + this.btnBrowseFolder.Click += new System.EventHandler(this.button1_Click); + // + // cbAutoclose + // + this.cbAutoclose.AutoSize = true; + this.cbAutoclose.Location = new System.Drawing.Point(101, 197); + this.cbAutoclose.Name = "cbAutoclose"; + this.cbAutoclose.Size = new System.Drawing.Size(243, 17); + this.cbAutoclose.TabIndex = 4; + this.cbAutoclose.Text = "Automatically close status window on success"; + this.cbAutoclose.UseVisualStyleBackColor = true; + // + // cbExitCode + // + this.cbExitCode.AutoSize = true; + this.cbExitCode.Location = new System.Drawing.Point(101, 221); + this.cbExitCode.Name = "cbExitCode"; + this.cbExitCode.Size = new System.Drawing.Size(175, 17); + this.cbExitCode.TabIndex = 5; + this.cbExitCode.Text = "Exit code not equal 0 is an error"; + this.cbExitCode.UseVisualStyleBackColor = true; + // + // cbStdErr + // + this.cbStdErr.AutoSize = true; + this.cbStdErr.Location = new System.Drawing.Point(101, 244); + this.cbStdErr.Name = "cbStdErr"; + this.cbStdErr.Size = new System.Drawing.Size(149, 17); + this.cbStdErr.TabIndex = 5; + this.cbStdErr.Text = "Writing to stderr is an error"; + this.cbStdErr.UseVisualStyleBackColor = true; + // + // ExternalCommandControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.cbStdErr); + this.Controls.Add(this.cbExitCode); + this.Controls.Add(this.cbAutoclose); + this.Controls.Add(this.btnBrowseFolder); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.tbCommand); + this.Controls.Add(this.tbFolder); + this.Name = "ExternalCommandControl"; + this.Size = new System.Drawing.Size(633, 268); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox tbFolder; + private System.Windows.Forms.TextBox tbCommand; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btnBrowseFolder; + private System.Windows.Forms.CheckBox cbAutoclose; + private System.Windows.Forms.CheckBox cbExitCode; + private System.Windows.Forms.CheckBox cbStdErr; + } +} diff --git a/Source/Core/Controls/ExternalCommandControl.cs b/Source/Core/Controls/ExternalCommandControl.cs new file mode 100644 index 00000000..60e7649d --- /dev/null +++ b/Source/Core/Controls/ExternalCommandControl.cs @@ -0,0 +1,101 @@ +#region ================== Copyright (c) 2021 Boris Iwanski + +/* + * This program is free software: you can redistribute it and/or modify + * + * it under the terms of the GNU General Public License as published by + * + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.If not, see. + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Controls +{ + public partial class ExternalCommandControl : UserControl + { + #region ================== Constructors + + public ExternalCommandControl() + { + InitializeComponent(); + } + + #endregion + + #region ================== Methods + + /// + /// Fills the controls with the settings from the ExternalCommandSettings. + /// + /// The settings + public void Setup(ExternalCommandSettings ecs) + { + tbFolder.Text = ecs.WorkingDirectory; + tbCommand.Text = ecs.Commands; + cbAutoclose.Checked = ecs.AutoCloseOnSuccess; + cbExitCode.Checked = ecs.ExitCodeIsError; + cbStdErr.Checked = ecs.StdErrIsError; + } + + /// + /// Returns the external command settings. + /// + /// The external command settings + public ExternalCommandSettings GetSettings() + { + ExternalCommandSettings ecs = new ExternalCommandSettings(); + ecs.WorkingDirectory = tbFolder.Text.Trim(); + ecs.Commands = tbCommand.Text.Trim(); + ecs.AutoCloseOnSuccess = cbAutoclose.Checked; + ecs.ExitCodeIsError = cbExitCode.Checked; + ecs.StdErrIsError = cbStdErr.Checked; + + return ecs; + } + + #endregion + + #region ================== Events + + private void button1_Click(object sender, EventArgs e) + { + FolderSelectDialog dirdialog = new FolderSelectDialog(); + dirdialog.Title = "Select base folder"; + dirdialog.InitialDirectory = tbFolder.Text; + + if (dirdialog.ShowDialog(this.Handle)) + { + tbFolder.Text = dirdialog.FileName; + } + } + + #endregion + } +} diff --git a/Source/Core/Controls/ExternalCommandControl.resx b/Source/Core/Controls/ExternalCommandControl.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/Source/Core/Controls/ExternalCommandControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Core/General/Launcher.cs b/Source/Core/General/Launcher.cs index 9408e593..7ea6b076 100755 --- a/Source/Core/General/Launcher.cs +++ b/Source/Core/General/Launcher.cs @@ -327,40 +327,71 @@ namespace CodeImp.DoomBuilder General.Plugins.OnMapSaveBegin(SavePurpose.Testing); if(General.Map.SaveMap(tempwad, SavePurpose.Testing)) { + bool canceled = false; + // No compiler errors? - if(General.Map.Errors.Count == 0) + if (General.Map.Errors.Count == 0) { - // Make arguments - string args = ConvertParameters(General.Map.ConfigSettings.TestParameters, skill, General.Map.ConfigSettings.TestShortPaths, General.Map.ConfigSettings.TestLinuxPaths); - - // Setup process info - ProcessStartInfo processinfo = new ProcessStartInfo(); - processinfo.Arguments = args; - processinfo.FileName = General.Map.ConfigSettings.TestProgram; - processinfo.CreateNoWindow = false; - processinfo.ErrorDialog = false; - processinfo.UseShellExecute = true; - processinfo.WindowStyle = ProcessWindowStyle.Normal; - processinfo.WorkingDirectory = Path.GetDirectoryName(processinfo.FileName); - - // Output info - General.WriteLogLine("Running test program: " + processinfo.FileName); - General.WriteLogLine("Program parameters: " + processinfo.Arguments); - General.MainWindow.DisplayStatus(StatusType.Info, "Launching " + processinfo.FileName + "..."); - - try + // Check if there's a pre command to run, and try to execute it + if (!string.IsNullOrWhiteSpace(General.Map.Options.TestPreCommand.Commands)) { - // Start the program - Process process = Process.Start(processinfo); - process.EnableRaisingEvents = true; //mxd - process.Exited += ProcessOnExited; //mxd - processes.Add(process, tempwad); //mxd - Cursor.Current = oldcursor; //mxd + if (!General.Map.ExecuteExternalCommand(General.Map.Options.TestPreCommand, tempwad)) + { + General.WriteLogLine("Testing was canceled when executing the testing pre command."); + + // Reset status + General.MainWindow.DisplayStatus(StatusType.Warning, "Testing was canceled."); + canceled = true; + } } - catch(Exception e) + + if (!canceled) { - // Unable to start the program - General.ShowErrorMessage("Unable to start the test program, " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + // Make arguments + string args = ConvertParameters(General.Map.ConfigSettings.TestParameters, skill, General.Map.ConfigSettings.TestShortPaths, General.Map.ConfigSettings.TestLinuxPaths); + + // Setup process info + ProcessStartInfo processinfo = new ProcessStartInfo(); + processinfo.Arguments = args; + processinfo.FileName = General.Map.ConfigSettings.TestProgram; + processinfo.CreateNoWindow = false; + processinfo.ErrorDialog = false; + processinfo.UseShellExecute = true; + processinfo.WindowStyle = ProcessWindowStyle.Normal; + processinfo.WorkingDirectory = Path.GetDirectoryName(processinfo.FileName); + + // Output info + General.WriteLogLine("Running test program: " + processinfo.FileName); + General.WriteLogLine("Program parameters: " + processinfo.Arguments); + General.MainWindow.DisplayStatus(StatusType.Info, "Launching " + processinfo.FileName + "..."); + + try + { + // Start the program + Process process = Process.Start(processinfo); + process.EnableRaisingEvents = true; //mxd + process.Exited += ProcessOnExited; //mxd + processes.Add(process, tempwad); //mxd + Cursor.Current = oldcursor; //mxd + } + catch (Exception e) + { + // Unable to start the program + General.ShowErrorMessage("Unable to start the test program, " + e.GetType().Name + ": " + e.Message, MessageBoxButtons.OK); + } + + // Check if there's a post command to run, and try to execute it + // TODO: currently modifying the commands is disabled since it'd have to be executed after the test program ends, which is not + // the case in the current situation, since this code here is reached immediately after launching the test program + /* + if (!string.IsNullOrWhiteSpace(General.Map.Options.TestPostCommand.Commands)) + { + if (!General.Map.ExecuteExternalCommand(General.Map.Options.TestPostCommand, tempwad)) + { + General.WriteLogLine("Failed to execute the test post command successfully."); + } + } + */ } } else diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs index bf1bcb1e..d67f3732 100755 --- a/Source/Core/General/MapManager.cs +++ b/Source/Core/General/MapManager.cs @@ -1053,7 +1053,7 @@ namespace CodeImp.DoomBuilder filetitle = Path.GetFileName(filepathname); // Reload resources - ReloadResources(true); + ReloadResources(true, false); } try @@ -2300,7 +2300,7 @@ namespace CodeImp.DoomBuilder DebugConsole.Clear(); #endif - ReloadResources(true); + ReloadResources(true, true); if(General.ErrorLogger.IsErrorAdded) { @@ -2315,12 +2315,27 @@ namespace CodeImp.DoomBuilder } - internal void ReloadResources(bool clearerrors) //mxd. clearerrors flag + internal void ReloadResources(bool clearerrors, bool runprepostcommands) //mxd. clearerrors flag { // Keep old display info StatusInfo oldstatus = General.MainWindow.Status; Cursor oldcursor = Cursor.Current; + // Check if there's a pre command to run, and try to execute it + if (runprepostcommands && !string.IsNullOrWhiteSpace(options.ReloadResourcePreCommand.Commands)) + { + if(!ExecuteExternalCommand(options.ReloadResourcePreCommand)) + { + General.WriteLogLine("Reloading resources was canceled when executing the reload resource pre command."); + + // Reset status + General.MainWindow.DisplayStatus(StatusType.Warning, "Reloading resourcess was canceled."); + Cursor.Current = oldcursor; + + return; + } + } + // Show status General.MainWindow.DisplayStatus(StatusType.Busy, "Reloading data resources..."); Cursor.Current = Cursors.WaitCursor; @@ -2377,11 +2392,46 @@ namespace CodeImp.DoomBuilder //mxd. Update script names LoadACS(); + // Check if there's a post command to run, and try to execute it + if (runprepostcommands && !string.IsNullOrWhiteSpace(options.ReloadResourcePostCommand.Commands)) + { + if (!ExecuteExternalCommand(options.ReloadResourcePostCommand)) + { + General.WriteLogLine("Failed to execute the reload resource post command successfully."); + } + } + // Reset status General.MainWindow.DisplayStatus(oldstatus); Cursor.Current = oldcursor; } + public bool ExecuteExternalCommand(ExternalCommandSettings cmdsettings, string arguments = "") + { + string filename; + + do + { + filename = Path.ChangeExtension(Path.GetTempFileName(), ".cmd"); + } + while (File.Exists(filename)); + + File.WriteAllText(filename, cmdsettings.Commands); + + ProcessStartInfo startinfo = new ProcessStartInfo(); + startinfo.FileName = "cmd.exe"; + startinfo.Arguments = "/C " + filename + " " + arguments; + if(!string.IsNullOrWhiteSpace(cmdsettings.WorkingDirectory)) + startinfo.WorkingDirectory = cmdsettings.WorkingDirectory; + + RunExternalCommandForm f = new RunExternalCommandForm(startinfo, cmdsettings); + f.ShowDialog(); + + File.Delete(filename); + + return f.DialogResult == DialogResult.OK; + } + // Game Configuration action [BeginAction("mapoptions")] internal void ShowMapOptions() @@ -2475,7 +2525,7 @@ namespace CodeImp.DoomBuilder map.UpdateCustomLinedefColors(); // Reload resources - ReloadResources(false); + ReloadResources(false, false); // Update interface General.MainWindow.SetupInterface(); diff --git a/Source/Core/Map/MapOptions.cs b/Source/Core/Map/MapOptions.cs index e4085840..1cdc691e 100755 --- a/Source/Core/Map/MapOptions.cs +++ b/Source/Core/Map/MapOptions.cs @@ -89,6 +89,11 @@ namespace CodeImp.DoomBuilder.Map //mxd. Position and scale private readonly Vector2D viewposition; private readonly float viewscale; + + private ExternalCommandSettings reloadresourceprecommand; + private ExternalCommandSettings reloadresourcepostcommand; + private ExternalCommandSettings testprecommand; + private ExternalCommandSettings testpostcommand; #endregion @@ -147,6 +152,11 @@ namespace CodeImp.DoomBuilder.Map public Vector2D ViewPosition { get { return viewposition; } } public float ViewScale { get { return viewscale; } } + public ExternalCommandSettings ReloadResourcePreCommand { get { return reloadresourceprecommand; } internal set { reloadresourceprecommand = value; } } + public ExternalCommandSettings ReloadResourcePostCommand { get { return reloadresourcepostcommand; } internal set { reloadresourcepostcommand = value; } } + public ExternalCommandSettings TestPreCommand { get { return testprecommand; } internal set { testprecommand = value; } } + public ExternalCommandSettings TestPostCommand { get { return testpostcommand; } internal set { testpostcommand = value; } } + #endregion #region ================== Constructor / Disposer @@ -167,6 +177,11 @@ namespace CodeImp.DoomBuilder.Map this.viewposition = new Vector2D(float.NaN, float.NaN); //mxd this.viewscale = float.NaN; //mxd + reloadresourceprecommand = new ExternalCommandSettings(); + reloadresourcepostcommand = new ExternalCommandSettings(); + testprecommand = new ExternalCommandSettings(); + testpostcommand = new ExternalCommandSettings(); + //mxd. Sector drawing options this.custombrightness = 196; this.customceilheight = 128; @@ -235,6 +250,12 @@ namespace CodeImp.DoomBuilder.Map //mxd uselongtexturenames = longtexturenamessupported && this.mapconfig.ReadSetting("uselongtexturenames", false); + // Load the pre and post commands + reloadresourceprecommand = new ExternalCommandSettings(mapconfig, "reloadresourceprecommand"); + reloadresourcepostcommand= new ExternalCommandSettings(mapconfig, "reloadresourcepostcommand"); + testprecommand = new ExternalCommandSettings(mapconfig, "testprecommand"); + testpostcommand = new ExternalCommandSettings(mapconfig, "testpostcommand"); + //mxd. Position and scale float vpx = this.mapconfig.ReadSetting("viewpositionx", float.NaN); float vpy = this.mapconfig.ReadSetting("viewpositiony", float.NaN); @@ -386,8 +407,14 @@ namespace CodeImp.DoomBuilder.Map foreach(ScriptDocumentSettings settings in scriptsettings.Values) WriteScriptDocumentSettings(mapconfig, "scriptdocuments.document" + (sdcounter++), settings); + // Write pre and post commands + reloadresourceprecommand.WriteSettings(mapconfig, "reloadresourceprecommand"); + reloadresourcepostcommand.WriteSettings(mapconfig, "reloadresourcepostcommand"); + testprecommand.WriteSettings(mapconfig, "testprecommand"); + testpostcommand.WriteSettings(mapconfig, "testpostcommand"); + // Load the file or make a new file - if(File.Exists(settingsfile)) + if (File.Exists(settingsfile)) wadcfg = new Configuration(settingsfile, true); else wadcfg = new Configuration(true); diff --git a/Source/Core/Windows/MapOptionsForm.Designer.cs b/Source/Core/Windows/MapOptionsForm.Designer.cs index c06ee0a1..adf58dd8 100755 --- a/Source/Core/Windows/MapOptionsForm.Designer.cs +++ b/Source/Core/Windows/MapOptionsForm.Designer.cs @@ -47,6 +47,8 @@ namespace CodeImp.DoomBuilder.Windows this.strictpatches = new System.Windows.Forms.CheckBox(); this.datalocations = new CodeImp.DoomBuilder.Controls.ResourceListEditor(); this.tooltip = new System.Windows.Forms.ToolTip(this.components); + this.label5 = new System.Windows.Forms.Label(); + this.prepostcommands = new System.Windows.Forms.Button(); label3 = new System.Windows.Forms.Label(); label2 = new System.Windows.Forms.Label(); label1 = new System.Windows.Forms.Label(); @@ -165,7 +167,7 @@ namespace CodeImp.DoomBuilder.Windows // apply // this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.apply.Location = new System.Drawing.Point(179, 399); + this.apply.Location = new System.Drawing.Point(179, 429); this.apply.Name = "apply"; this.apply.Size = new System.Drawing.Size(112, 25); this.apply.TabIndex = 2; @@ -177,7 +179,7 @@ namespace CodeImp.DoomBuilder.Windows // this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancel.Location = new System.Drawing.Point(297, 399); + this.cancel.Location = new System.Drawing.Point(297, 429); this.cancel.Name = "cancel"; this.cancel.Size = new System.Drawing.Size(112, 25); this.cancel.TabIndex = 3; @@ -229,13 +231,33 @@ namespace CodeImp.DoomBuilder.Windows this.datalocations.Size = new System.Drawing.Size(368, 127); this.datalocations.TabIndex = 0; // + // label5 + // + this.label5.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.label5.Location = new System.Drawing.Point(13, 423); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(396, 2); + this.label5.TabIndex = 21; + // + // prepostcommands + // + this.prepostcommands.Location = new System.Drawing.Point(247, 395); + this.prepostcommands.Name = "prepostcommands"; + this.prepostcommands.Size = new System.Drawing.Size(162, 25); + this.prepostcommands.TabIndex = 20; + this.prepostcommands.Text = "Edit pre and post commands"; + this.prepostcommands.UseVisualStyleBackColor = true; + this.prepostcommands.Click += new System.EventHandler(this.prepostcommands_Click); + // // MapOptionsForm // this.AcceptButton = this.apply; this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.CancelButton = this.cancel; - this.ClientSize = new System.Drawing.Size(421, 436); + this.ClientSize = new System.Drawing.Size(421, 466); + this.Controls.Add(this.label5); + this.Controls.Add(this.prepostcommands); this.Controls.Add(this.panelres); this.Controls.Add(this.cancel); this.Controls.Add(this.apply); @@ -272,7 +294,7 @@ namespace CodeImp.DoomBuilder.Windows private System.Windows.Forms.Label examplelabel; private System.Windows.Forms.CheckBox longtexturenames; private System.Windows.Forms.ToolTip tooltip; - - + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button prepostcommands; } } \ No newline at end of file diff --git a/Source/Core/Windows/MapOptionsForm.cs b/Source/Core/Windows/MapOptionsForm.cs index 3bc512ed..825d5edb 100755 --- a/Source/Core/Windows/MapOptionsForm.cs +++ b/Source/Core/Windows/MapOptionsForm.cs @@ -34,6 +34,11 @@ namespace CodeImp.DoomBuilder.Windows // Variables private readonly MapOptions options; private readonly bool newmap; + private ExternalCommandSettings reloadresourceprecommand; + private ExternalCommandSettings reloadresourcepostcommand; + private ExternalCommandSettings testprecommand; + private ExternalCommandSettings testpostcommand; + private bool prepostcommandsmodified; // Properties public MapOptions Options { get { return options; } } @@ -49,8 +54,14 @@ namespace CodeImp.DoomBuilder.Windows // Keep settings this.options = options; + prepostcommandsmodified = false; + reloadresourceprecommand = options.ReloadResourcePreCommand ; + reloadresourcepostcommand = options.ReloadResourcePostCommand ; + testprecommand = options.TestPreCommand ; + testpostcommand = options.TestPostCommand; + //mxd. Add script compilers - foreach(KeyValuePair group in General.CompiledScriptConfigs) + foreach (KeyValuePair group in General.CompiledScriptConfigs) { scriptcompiler.Items.Add(group.Value); } @@ -110,6 +121,8 @@ namespace CodeImp.DoomBuilder.Windows // Fill the resources list datalocations.EditResourceLocationList(options.Resources); + + //reloadresourceprecmd.Text = options.ReloadResourcePreCommand; } // OK clicked @@ -247,6 +260,15 @@ namespace CodeImp.DoomBuilder.Windows options.StrictPatches = strictpatches.Checked; options.CopyResources(datalocations.GetResources()); + // Only store the pre and post commands in the map options if they were actually changed (i.e. the user pressed the OK button the the dialog) + if (prepostcommandsmodified) + { + options.ReloadResourcePreCommand = reloadresourceprecommand; + options.ReloadResourcePostCommand = reloadresourcepostcommand; + options.TestPreCommand = testprecommand; + options.TestPostCommand = testpostcommand; + } + //mxd. Store script compiler if(scriptcompiler.Enabled) { @@ -264,6 +286,8 @@ namespace CodeImp.DoomBuilder.Windows //mxd. Use long texture names? if(longtexturenames.Enabled) options.UseLongTextureNames = longtexturenames.Checked; + //options.ReloadResourcePreCommand = reloadresourceprecmd.Text; + // Hide window this.DialogResult = DialogResult.OK; this.Close(); @@ -347,5 +371,22 @@ namespace CodeImp.DoomBuilder.Windows General.ShowHelp("w_mapoptions.html"); hlpevent.Handled = true; } + + + private void prepostcommands_Click(object sender, EventArgs e) + { + PreAndPostCommandsForm papcf = new PreAndPostCommandsForm(reloadresourceprecommand, reloadresourcepostcommand, testprecommand, testpostcommand); + papcf.ShowDialog(); + + if (papcf.DialogResult == DialogResult.OK) + { + reloadresourceprecommand = papcf.GetReloadResourcePreCommand(); + reloadresourcepostcommand = papcf.GetReloadResourcePostCommand(); + testprecommand = papcf.GetTestPreCommand(); + testpostcommand = papcf.GetTestPostCommand(); + + prepostcommandsmodified = true; + } + } } } \ No newline at end of file diff --git a/Source/Core/Windows/OpenMapOptionsForm.Designer.cs b/Source/Core/Windows/OpenMapOptionsForm.Designer.cs index e3720437..cb1c44be 100755 --- a/Source/Core/Windows/OpenMapOptionsForm.Designer.cs +++ b/Source/Core/Windows/OpenMapOptionsForm.Designer.cs @@ -132,7 +132,7 @@ namespace CodeImp.DoomBuilder.Windows // apply // this.apply.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.apply.Location = new System.Drawing.Point(178, 513); + this.apply.Location = new System.Drawing.Point(178, 510); this.apply.Name = "apply"; this.apply.Size = new System.Drawing.Size(112, 25); this.apply.TabIndex = 3; @@ -144,7 +144,7 @@ namespace CodeImp.DoomBuilder.Windows // this.cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.cancel.Location = new System.Drawing.Point(296, 513); + this.cancel.Location = new System.Drawing.Point(296, 510); this.cancel.Name = "cancel"; this.cancel.Size = new System.Drawing.Size(112, 25); this.cancel.TabIndex = 4; @@ -214,7 +214,7 @@ namespace CodeImp.DoomBuilder.Windows this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.CancelButton = this.cancel; - this.ClientSize = new System.Drawing.Size(420, 544); + this.ClientSize = new System.Drawing.Size(420, 541); this.Controls.Add(this.scriptcompiler); this.Controls.Add(this.scriptcompilerlabel); this.Controls.Add(this.mapslist); @@ -255,7 +255,5 @@ namespace CodeImp.DoomBuilder.Windows private System.Windows.Forms.Label scriptcompilerlabel; private System.Windows.Forms.CheckBox longtexturenames; private System.Windows.Forms.ToolTip tooltip; - - } } \ No newline at end of file diff --git a/Source/Core/Windows/PreAndPostCommandsForm.Designer.cs b/Source/Core/Windows/PreAndPostCommandsForm.Designer.cs new file mode 100644 index 00000000..514e6259 --- /dev/null +++ b/Source/Core/Windows/PreAndPostCommandsForm.Designer.cs @@ -0,0 +1,248 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class PreAndPostCommandsForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.label2 = new System.Windows.Forms.Label(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.reloadpost = new CodeImp.DoomBuilder.Controls.ExternalCommandControl(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.reloadpre = new CodeImp.DoomBuilder.Controls.ExternalCommandControl(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.testpost = new CodeImp.DoomBuilder.Controls.ExternalCommandControl(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.testpre = new CodeImp.DoomBuilder.Controls.ExternalCommandControl(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.SuspendLayout(); + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.Location = new System.Drawing.Point(524, 678); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 3; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(605, 678); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Location = new System.Drawing.Point(12, 12); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(670, 657); + this.tabControl1.TabIndex = 4; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.label2); + this.tabPage1.Controls.Add(this.groupBox2); + this.tabPage1.Controls.Add(this.groupBox1); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(662, 631); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Reload resources commands"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(6, 15); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(431, 13); + this.label2.TabIndex = 8; + this.label2.Text = "The commands will be automatically stored in a temporary CMD file and executed as" + + " such."; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.reloadpost); + this.groupBox2.Location = new System.Drawing.Point(6, 336); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(649, 289); + this.groupBox2.TabIndex = 4; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Post commands"; + // + // reloadpost + // + this.reloadpost.Location = new System.Drawing.Point(6, 19); + this.reloadpost.Name = "reloadpost"; + this.reloadpost.Size = new System.Drawing.Size(633, 268); + this.reloadpost.TabIndex = 1; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.reloadpre); + this.groupBox1.Location = new System.Drawing.Point(6, 41); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(649, 289); + this.groupBox1.TabIndex = 3; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Pre commands"; + // + // reloadpre + // + this.reloadpre.Location = new System.Drawing.Point(7, 19); + this.reloadpre.Name = "reloadpre"; + this.reloadpre.Size = new System.Drawing.Size(633, 268); + this.reloadpre.TabIndex = 1; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.label1); + this.tabPage2.Controls.Add(this.groupBox3); + this.tabPage2.Controls.Add(this.groupBox4); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(662, 631); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Test map commands"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 15); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(637, 13); + this.label1.TabIndex = 7; + this.label1.Text = "The commands will be automatically stored in a temporary CMD file and executed as" + + " such. %1 is the full path to the temporary map file."; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.testpost); + this.groupBox3.Enabled = false; + this.groupBox3.Location = new System.Drawing.Point(6, 336); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(649, 289); + this.groupBox3.TabIndex = 6; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Post commands"; + // + // testpost + // + this.testpost.Location = new System.Drawing.Point(6, 19); + this.testpost.Name = "testpost"; + this.testpost.Size = new System.Drawing.Size(633, 268); + this.testpost.TabIndex = 1; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.testpre); + this.groupBox4.Location = new System.Drawing.Point(6, 41); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(649, 289); + this.groupBox4.TabIndex = 5; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "Pre commands"; + // + // testpre + // + this.testpre.Location = new System.Drawing.Point(7, 19); + this.testpre.Name = "testpre"; + this.testpre.Size = new System.Drawing.Size(633, 268); + this.testpre.TabIndex = 1; + // + // PreAndPostCommandsForm + // + this.AcceptButton = this.btnOK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(692, 713); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.btnCancel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PreAndPostCommandsForm"; + this.Text = "Pre and post commands"; + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox4.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.GroupBox groupBox2; + private Controls.ExternalCommandControl reloadpost; + private System.Windows.Forms.GroupBox groupBox1; + private Controls.ExternalCommandControl reloadpre; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.GroupBox groupBox3; + private Controls.ExternalCommandControl testpost; + private System.Windows.Forms.GroupBox groupBox4; + private Controls.ExternalCommandControl testpre; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/PreAndPostCommandsForm.cs b/Source/Core/Windows/PreAndPostCommandsForm.cs new file mode 100644 index 00000000..03e6688e --- /dev/null +++ b/Source/Core/Windows/PreAndPostCommandsForm.cs @@ -0,0 +1,97 @@ +#region ================== Copyright (c) 2021 Boris Iwanski + +/* + * This program is free software: you can redistribute it and/or modify + * + * it under the terms of the GNU General Public License as published by + * + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.If not, see. + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class PreAndPostCommandsForm : DelayedForm + { + #region ================== Constructors + + public PreAndPostCommandsForm(ExternalCommandSettings reloadresourceprecommand, ExternalCommandSettings reloadresourcepostcommand, ExternalCommandSettings testprecommand, ExternalCommandSettings testpostcommand) + { + InitializeComponent(); + + reloadpre.Setup(reloadresourceprecommand); + reloadpost.Setup(reloadresourcepostcommand); + testpre.Setup(testprecommand); + testpost.Setup(testpostcommand); + } + + #endregion + + #region ================== Methods + + public ExternalCommandSettings GetReloadResourcePreCommand() + { + return reloadpre.GetSettings(); + } + + public ExternalCommandSettings GetReloadResourcePostCommand() + { + return reloadpost.GetSettings(); + } + + public ExternalCommandSettings GetTestPreCommand() + { + return testpre.GetSettings(); + } + + public ExternalCommandSettings GetTestPostCommand() + { + return testpost.GetSettings(); + } + + #endregion + + #region ================== Events + + private void btnCancel_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.Cancel; + Close(); + } + + private void btnOK_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + + #endregion + } +} diff --git a/Source/Core/Windows/PreAndPostCommandsForm.resx b/Source/Core/Windows/PreAndPostCommandsForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/Source/Core/Windows/PreAndPostCommandsForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Source/Core/Windows/RunExternalCommandForm.Designer.cs b/Source/Core/Windows/RunExternalCommandForm.Designer.cs new file mode 100644 index 00000000..784735c1 --- /dev/null +++ b/Source/Core/Windows/RunExternalCommandForm.Designer.cs @@ -0,0 +1,114 @@ +namespace CodeImp.DoomBuilder.Windows +{ + partial class RunExternalCommandForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.btnContinue = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.rtbOutput = new System.Windows.Forms.RichTextBox(); + this.btnRetry = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // btnContinue + // + this.btnContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnContinue.Enabled = false; + this.btnContinue.Location = new System.Drawing.Point(596, 459); + this.btnContinue.Name = "btnContinue"; + this.btnContinue.Size = new System.Drawing.Size(75, 23); + this.btnContinue.TabIndex = 1; + this.btnContinue.Text = "Continue"; + this.toolTip1.SetToolTip(this.btnContinue, "Continues loading the resources"); + this.btnContinue.UseVisualStyleBackColor = true; + this.btnContinue.Click += new System.EventHandler(this.btnContinue_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.Location = new System.Drawing.Point(677, 459); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.toolTip1.SetToolTip(this.btnCancel, "Cancels the running command, or cancels loading the resources"); + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // rtbOutput + // + this.rtbOutput.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.rtbOutput.Location = new System.Drawing.Point(12, 12); + this.rtbOutput.Name = "rtbOutput"; + this.rtbOutput.ReadOnly = true; + this.rtbOutput.Size = new System.Drawing.Size(740, 441); + this.rtbOutput.TabIndex = 2; + this.rtbOutput.Text = ""; + // + // btnRetry + // + this.btnRetry.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnRetry.Enabled = false; + this.btnRetry.Location = new System.Drawing.Point(515, 459); + this.btnRetry.Name = "btnRetry"; + this.btnRetry.Size = new System.Drawing.Size(75, 23); + this.btnRetry.TabIndex = 1; + this.btnRetry.Text = "Run again"; + this.toolTip1.SetToolTip(this.btnRetry, "Runs the command again"); + this.btnRetry.UseVisualStyleBackColor = true; + this.btnRetry.Click += new System.EventHandler(this.btnRetry_Click); + // + // RunExternalCommandForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(766, 494); + this.Controls.Add(this.rtbOutput); + this.Controls.Add(this.btnRetry); + this.Controls.Add(this.btnContinue); + this.Controls.Add(this.btnCancel); + this.Name = "RunExternalCommandForm"; + this.ShowIcon = false; + this.Text = "Running external command"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.RunExternalCommandForm_FormClosing); + this.Shown += new System.EventHandler(this.RunExternalCommandForm_Shown); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnContinue; + private System.Windows.Forms.RichTextBox rtbOutput; + private System.Windows.Forms.Button btnRetry; + private System.Windows.Forms.ToolTip toolTip1; + } +} \ No newline at end of file diff --git a/Source/Core/Windows/RunExternalCommandForm.cs b/Source/Core/Windows/RunExternalCommandForm.cs new file mode 100644 index 00000000..b6a5343c --- /dev/null +++ b/Source/Core/Windows/RunExternalCommandForm.cs @@ -0,0 +1,290 @@ +#region ================== Copyright (c) 2021 Boris Iwanski + +/* + * This program is free software: you can redistribute it and/or modify + * + * it under the terms of the GNU General Public License as published by + * + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.If not, see. + */ + +#endregion + +#region ================== Namespaces + +using System; +using System.Diagnostics; +using System.Drawing; +using System.Management; +using System.Threading; +using System.Windows.Forms; +using CodeImp.DoomBuilder.Config; + +#endregion + +namespace CodeImp.DoomBuilder.Windows +{ + public partial class RunExternalCommandForm : DelayedForm + { + #region ================== Variables + + private Process process; + private ProcessStartInfo startinfo; + private Thread runthread; + private object lockobj; + private bool haserrors; + private ExternalCommandSettings settings; + + #endregion + + #region ================== Delegates + + private delegate void CallStringBoolMethodDeletage(string s, bool iserror); + private delegate void CallVoidMethodDeletage(); + + #endregion + + #region ================== Constructors + + public RunExternalCommandForm(ProcessStartInfo startinfo, ExternalCommandSettings settings) + { + InitializeComponent(); + + lockobj = new object(); + haserrors = false; + + rtbOutput.Font = new Font(FontFamily.GenericMonospace, rtbOutput.Font.Size); + + this.startinfo = startinfo; + this.settings = settings; + } + + #endregion + + #region ================== Methods + + /// + /// Starts execution of the external command + /// + private void Start() + { + rtbOutput.Clear(); + haserrors = false; + btnContinue.Enabled = false; + btnRetry.Enabled = false; + + runthread = new Thread(() => Run()); + runthread.Name = "Run external command"; + runthread.Priority = ThreadPriority.Normal; + runthread.Start(); + } + + /// + /// Stops execution of the external command + /// + private void Stop() + { + haserrors = true; + process.CancelOutputRead(); + process.CancelErrorRead(); + KillProcessAndChildren(process.Id); + FinishRun(); + } + + /// + /// Runs the external command + /// + private void Run() + { + process = new Process(); + process.StartInfo = startinfo; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + process.StartInfo.CreateNoWindow = true; + + process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler); + process.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler); + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + + if (process.ExitCode != 0 && settings.ExitCodeIsError) + haserrors = true; + + FinishRun(); + } + + /// + /// Finishes after running the external command. Closes the window if there were no errors + /// + private void FinishRun() + { + if(InvokeRequired) + { + CallVoidMethodDeletage d = FinishRun; + Invoke(d); + } + else + { + btnContinue.Enabled = true; + btnRetry.Enabled = true; + + if(haserrors) + { + AppendText(Environment.NewLine + Environment.NewLine + "There were errors during the execution of the external commands.", true); + AppendText(Environment.NewLine + "Exit code: " + process.ExitCode, true); + } + + if(!haserrors && settings.AutoCloseOnSuccess) + { + DialogResult = DialogResult.OK; + Close(); + } + } + } + + /// + /// Handles lines going to stdout + /// + /// Process the line is coming from + /// The received line event + private void OutputHandler(object sendingprocess, DataReceivedEventArgs outline) + { + if(outline.Data != null) + AppendText(outline.Data + Environment.NewLine, false); + } + + /// + /// handles lines going to stderr + /// + /// Process the line is coming from + /// The received line event + private void ErrorHandler(object sendingprocess, DataReceivedEventArgs outline) + { + if (outline.Data != null) + { + AppendText(outline.Data + Environment.NewLine, true); + + if(settings.StdErrIsError) + haserrors = true; + } + } + + /// + /// Adds a line to the output text box and scrolls to the bottom. Colors the text red if it's an error + /// + /// Text to append + /// If it's an error or not + private void AppendText(string text, bool iserror) + { + if(InvokeRequired) + { + CallStringBoolMethodDeletage d = AppendText; + Invoke(d, text, iserror); + } + else + { + lock (lockobj) + { + rtbOutput.AppendText(text); + + if(iserror) + { + rtbOutput.Select(rtbOutput.Text.Length - text.Length, text.Length); + rtbOutput.SelectionColor = Color.Red; + //haserrors = true; + } + + rtbOutput.Select(rtbOutput.Text.Length, 0); + rtbOutput.ScrollToCaret(); + } + } + } + + /// + /// Recursively kill a process and its childern. Taken from https://stackoverflow.com/a/40265540 + /// + /// Process id + private static void KillProcessAndChildren(int pid) + { + ManagementObjectSearcher processSearcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid); + ManagementObjectCollection processCollection = processSearcher.Get(); + + // We must kill child processes first! + if (processCollection != null) + { + foreach (ManagementObject mo in processCollection) + { + KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.) + } + } + + // Then kill parents. + try + { + Process proc = Process.GetProcessById(pid); + if (!proc.HasExited) proc.Kill(); + } + catch (ArgumentException) + { + // Process already exited. + } + } + + #endregion + + #region ================== Events + + private void RunExternalCommandForm_Shown(object sender, EventArgs e) + { + Start(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + if (process.HasExited) + { + DialogResult = DialogResult.Cancel; + Close(); + } + else + { + Stop(); + } + } + + private void btnRetry_Click(object sender, EventArgs e) + { + Start(); + } + + private void btnContinue_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + Close(); + } + + private void RunExternalCommandForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (!process.HasExited) + e.Cancel = true; + } + + #endregion + } +} diff --git a/Source/Core/Windows/RunExternalCommandForm.resx b/Source/Core/Windows/RunExternalCommandForm.resx new file mode 100644 index 00000000..d54ad1e4 --- /dev/null +++ b/Source/Core/Windows/RunExternalCommandForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 17, 17 + + \ No newline at end of file