using System; using System.Drawing; using System.IO; using System.Windows.Forms; namespace TeslaConsole; /// /// Defines a new catalog product (a single launch entry) and, optionally, an install /// package to deploy. The validated result is exposed as ; the /// caller persists it to the catalog and (optionally) pushes it to selected pods. /// internal class dlgAddProduct : Form { private TextBox txtName; private TextBox txtInstallDir; private TextBox txtExe; private TextBox txtArgs; private TextBox txtPackage; private CheckBox chkAutoRestart; private Button cmdOK; private Button cmdCancel; /// The product the operator defined. Valid only after the dialog returns . public ProductDefinition Product { get; private set; } /// Optional install package (zip). If set, the product is deployed to the /// selected pods after it is added; if null/empty, it is only added to the catalog. public string PackagePath => string.IsNullOrWhiteSpace(txtPackage.Text) ? null : txtPackage.Text.Trim(); public dlgAddProduct() { InitializeComponent(); } private void InitializeComponent() { this.txtName = new TextBox(); this.txtInstallDir = new TextBox(); this.txtExe = new TextBox(); this.txtArgs = new TextBox(); this.txtPackage = new TextBox(); this.chkAutoRestart = new CheckBox(); this.cmdOK = new Button(); this.cmdCancel = new Button(); base.SuspendLayout(); int labelX = 12; int fieldX = 130; int fieldW = 300; int browseX = fieldX + fieldW + 6; int y = 15; int rowH = 28; AddLabel("Product name:", labelX, y); this.txtName.SetBounds(fieldX, y - 3, fieldW, 20); y += rowH; AddLabel("Install directory:", labelX, y); this.txtInstallDir.SetBounds(fieldX, y - 3, fieldW, 20); AddBrowseButton(browseX, y - 4, BrowseInstallDir); y += rowH; AddLabel("Executable:", labelX, y); this.txtExe.SetBounds(fieldX, y - 3, fieldW, 20); AddBrowseButton(browseX, y - 4, BrowseExe); y += rowH; AddLabel("Arguments:", labelX, y); this.txtArgs.SetBounds(fieldX, y - 3, fieldW, 20); y += rowH; AddLabel("Install package (optional):", labelX, y); this.txtPackage.SetBounds(fieldX, y - 3, fieldW, 20); AddBrowseButton(browseX, y - 4, BrowsePackage); y += rowH; this.chkAutoRestart.SetBounds(fieldX, y, 200, 20); this.chkAutoRestart.Text = "Auto-restart if it exits"; this.chkAutoRestart.Checked = true; y += 36; this.cmdOK.SetBounds(browseX - 169, y, 80, 24); this.cmdOK.Text = "OK"; this.cmdOK.Click += cmdOK_Click; this.cmdCancel.SetBounds(browseX - 83, y, 80, 24); this.cmdCancel.Text = "Cancel"; this.cmdCancel.DialogResult = DialogResult.Cancel; base.AcceptButton = this.cmdOK; base.CancelButton = this.cmdCancel; base.FormBorderStyle = FormBorderStyle.FixedDialog; base.MaximizeBox = false; base.MinimizeBox = false; base.StartPosition = FormStartPosition.CenterParent; base.ClientSize = new Size(browseX + 40, y + 36); base.Controls.Add(this.txtName); base.Controls.Add(this.txtInstallDir); base.Controls.Add(this.txtExe); base.Controls.Add(this.txtArgs); base.Controls.Add(this.txtPackage); base.Controls.Add(this.chkAutoRestart); base.Controls.Add(this.cmdOK); base.Controls.Add(this.cmdCancel); base.Name = "dlgAddProduct"; this.Text = "Add Product"; base.ResumeLayout(false); base.PerformLayout(); } private void AddLabel(string text, int x, int y) { Label label = new Label { Text = text, AutoSize = true }; label.SetBounds(x, y, 110, 16); base.Controls.Add(label); } private void AddBrowseButton(int x, int y, EventHandler onClick) { Button button = new Button { Text = "..." }; button.SetBounds(x, y, 30, 22); button.Click += onClick; base.Controls.Add(button); } private void BrowseInstallDir(object sender, EventArgs e) { using FolderBrowserDialog dialog = new FolderBrowserDialog(); if (!string.IsNullOrWhiteSpace(txtInstallDir.Text)) { dialog.SelectedPath = txtInstallDir.Text; } if (dialog.ShowDialog(this) == DialogResult.OK) { txtInstallDir.Text = dialog.SelectedPath; } } private void BrowseExe(object sender, EventArgs e) { using OpenFileDialog dialog = new OpenFileDialog { Filter = "Executables (*.exe)|*.exe|All Files (*.*)|*.*", Title = "Select Executable" }; if (dialog.ShowDialog(this) == DialogResult.OK) { txtExe.Text = dialog.FileName; } } private void BrowsePackage(object sender, EventArgs e) { using OpenFileDialog dialog = new OpenFileDialog { Filter = "VWE Install Archive (*.zip)|*.zip|All Files (*.*)|*.*", Title = "Select Install Package" }; if (dialog.ShowDialog(this) == DialogResult.OK) { txtPackage.Text = dialog.FileName; } } private void cmdOK_Click(object sender, EventArgs e) { string name = txtName.Text.Trim(); string installDir = txtInstallDir.Text.Trim(); string exe = txtExe.Text.Trim(); if (name.Length == 0) { Warn("Please enter a product name."); return; } if (exe.Length == 0) { Warn("Please specify the executable path."); return; } if (installDir.Length == 0) { Warn("Please specify the install directory."); return; } string fullExe, fullDir; try { fullExe = Path.GetFullPath(exe); fullDir = Path.GetFullPath(installDir); } catch (Exception) { Warn("The executable or install directory path is not valid."); return; } // Invariant: the exe must live under the install directory the package extracts to. string dirWithSep = fullDir.TrimEnd('\\', '/') + "\\"; if (!fullExe.StartsWith(dirWithSep, StringComparison.OrdinalIgnoreCase)) { Warn("The executable must be located inside the install directory:\n" + fullDir); return; } Guid id = Guid.NewGuid(); Product = new ProductDefinition { Id = id, Name = name, MenuText = name + "...", HostTypeDialog = false }; Product.Entries.Add(new LaunchEntryDefinition { LaunchKey = id, DisplayName = name, Exe = fullExe, Args = txtArgs.Text.Trim(), WorkingDirectory = fullDir, AutoRestart = chkAutoRestart.Checked, HostType = AppHostType.None }); base.DialogResult = DialogResult.OK; Close(); } private void Warn(string message) { MessageBox.Show(this, message, "Add Product", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }