unit SetupForm;
{*******************************************************************************
  ProcessChecker Demo
  Written by David Clegg, davidclegg@optusnet.com.au.

  Form used to configure and save any settings for the application.
*******************************************************************************}

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data, ProcessCheckerSettings, ProcessClasses, 
  System.Resources;

type
  TSetupForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container;
    btnEditDependant: System.Windows.Forms.Button;
    btnDeleteDependant: System.Windows.Forms.Button;
    btnAddDependant: System.Windows.Forms.Button;
    cmProcesses: System.Windows.Forms.ContextMenu;
    miAdd: System.Windows.Forms.MenuItem;
    miEdit: System.Windows.Forms.MenuItem;
    miDelete: System.Windows.Forms.MenuItem;
    btnDeleteProcess: System.Windows.Forms.Button;
    btnEditProcess: System.Windows.Forms.Button;
    columnHeader1: System.Windows.Forms.ColumnHeader;
    btnAddProcess: System.Windows.Forms.Button;
    cbRestartOneProcess: System.Windows.Forms.CheckBox;
    label4: System.Windows.Forms.Label;
    columnHeader3: System.Windows.Forms.ColumnHeader;
    label3: System.Windows.Forms.Label;
    nudFrequency: System.Windows.Forms.NumericUpDown;
    btnApply: System.Windows.Forms.Button;
    btnCancel: System.Windows.Forms.Button;
    btnOK: System.Windows.Forms.Button;
    label2: System.Windows.Forms.Label;
    lvDependants: System.Windows.Forms.ListView;
    columnHeader2: System.Windows.Forms.ColumnHeader;
    label1: System.Windows.Forms.Label;
    lvProcesses: System.Windows.Forms.ListView;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
    procedure TSetupForm_Load(sender: System.Object; e: System.EventArgs);
    procedure lvProcesses_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
    procedure miDelete_Click(sender: System.Object; e: System.EventArgs);
    procedure miAdd_Click(sender: System.Object; e: System.EventArgs);
    procedure miEdit_Click(sender: System.Object; e: System.EventArgs);
    procedure btnDeleteProcess_Click(sender: System.Object; e: System.EventArgs);
    procedure btnAddProcess_Click(sender: System.Object; e: System.EventArgs);
    procedure btnEditProcess_Click(sender: System.Object; e: System.EventArgs);
    procedure lvDependants_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
    procedure btnDeleteDependant_Click(sender: System.Object; e: System.EventArgs);
    procedure btnEditDependant_Click(sender: System.Object; e: System.EventArgs);
    procedure btnAddDependant_Click(sender: System.Object; e: System.EventArgs);
    procedure btnOK_Click(Sender: System.Object; e: System.EventArgs);
    procedure cbRestartOneProcess_Click(sender: System.Object; e: System.EventArgs);
    procedure nudFrequency_Click(sender: System.Object; e: System.EventArgs);
    procedure btnApply_Click(sender: System.Object; e: System.EventArgs);
    procedure btnCancel_Click(sender: System.Object; e: System.EventArgs);
    procedure cmProcesses_Popup(sender: System.Object; e: System.EventArgs);
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    FSettings: TProcessCheckerSettings;
    procedure PopulateControls;
    procedure AddProcess(pProcess: TProcess; pListView: ListView);
    function SelectedIndex(pListView: ListView): integer;
    function SelectedItem(pListView: ListView): ListViewItem;
    procedure EnableProcessButtons;
    procedure DoDelete(pListView: ListView);
    procedure DoAdd(pListView: ListView);
    procedure DoEdit(pListView: ListView);
    procedure EnableDependantButtons;
    procedure SaveSettings;
    procedure AddDependants(pProcess: TWatchedProcess);
  public
    class function ShowSetupForm(var pSettings: TProcessCheckerSettings): System.Windows.Forms.DialogResult;
    constructor Create;
  end;

implementation

uses
  System.Globalization, ProcessDetailsForm, SysUtils;

{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TSetupForm.InitializeComponent;
type
  TSystem_Windows_Forms_MenuItemArray = array of System.Windows.Forms.MenuItem;
  TSystem_Windows_Forms_ColumnHeaderArray = array of System.Windows.Forms.ColumnHeader;
var
  resources: System.Resources.ResourceManager;
begin
  resources := System.Resources.ResourceManager.Create(TypeOf(TSetupForm));
  Self.btnEditDependant := System.Windows.Forms.Button.Create;
  Self.btnDeleteDependant := System.Windows.Forms.Button.Create;
  Self.btnAddDependant := System.Windows.Forms.Button.Create;
  Self.cmProcesses := System.Windows.Forms.ContextMenu.Create;
  Self.miAdd := System.Windows.Forms.MenuItem.Create;
  Self.miEdit := System.Windows.Forms.MenuItem.Create;
  Self.miDelete := System.Windows.Forms.MenuItem.Create;
  Self.btnDeleteProcess := System.Windows.Forms.Button.Create;
  Self.btnEditProcess := System.Windows.Forms.Button.Create;
  Self.columnHeader1 := System.Windows.Forms.ColumnHeader.Create;
  Self.btnAddProcess := System.Windows.Forms.Button.Create;
  Self.cbRestartOneProcess := System.Windows.Forms.CheckBox.Create;
  Self.label4 := System.Windows.Forms.Label.Create;
  Self.columnHeader3 := System.Windows.Forms.ColumnHeader.Create;
  Self.label3 := System.Windows.Forms.Label.Create;
  Self.nudFrequency := System.Windows.Forms.NumericUpDown.Create;
  Self.btnApply := System.Windows.Forms.Button.Create;
  Self.btnCancel := System.Windows.Forms.Button.Create;
  Self.btnOK := System.Windows.Forms.Button.Create;
  Self.label2 := System.Windows.Forms.Label.Create;
  Self.lvDependants := System.Windows.Forms.ListView.Create;
  Self.columnHeader2 := System.Windows.Forms.ColumnHeader.Create;
  Self.label1 := System.Windows.Forms.Label.Create;
  Self.lvProcesses := System.Windows.Forms.ListView.Create;
  (System.ComponentModel.ISupportInitialize(Self.nudFrequency)).BeginInit;
  Self.SuspendLayout;
  // 
  // btnEditDependant
  // 
  Self.btnEditDependant.Enabled := False;
  Self.btnEditDependant.Location := System.Drawing.Point.Create(464, 296);
  Self.btnEditDependant.Name := 'btnEditDependant';
  Self.btnEditDependant.TabIndex := 36;
  Self.btnEditDependant.Text := 'Edit';
  Include(Self.btnEditDependant.Click, Self.btnEditDependant_Click);
  // 
  // btnDeleteDependant
  // 
  Self.btnDeleteDependant.Enabled := False;
  Self.btnDeleteDependant.Location := System.Drawing.Point.Create(552, 296);
  Self.btnDeleteDependant.Name := 'btnDeleteDependant';
  Self.btnDeleteDependant.TabIndex := 35;
  Self.btnDeleteDependant.Text := 'Delete';
  Include(Self.btnDeleteDependant.Click, Self.btnDeleteDependant_Click);
  // 
  // btnAddDependant
  // 
  Self.btnAddDependant.Enabled := False;
  Self.btnAddDependant.Location := System.Drawing.Point.Create(376, 296);
  Self.btnAddDependant.Name := 'btnAddDependant';
  Self.btnAddDependant.TabIndex := 34;
  Self.btnAddDependant.Text := 'Add';
  Include(Self.btnAddDependant.Click, Self.btnAddDependant_Click);
  // 
  // cmProcesses
  // 
  Self.cmProcesses.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miAdd, Self.miEdit, Self.miDelete));
  Include(Self.cmProcesses.Popup, Self.cmProcesses_Popup);
  // 
  // miAdd
  // 
  Self.miAdd.Index := 0;
  Self.miAdd.Text := 'Add';
  Include(Self.miAdd.Click, Self.miAdd_Click);
  // 
  // miEdit
  // 
  Self.miEdit.Index := 1;
  Self.miEdit.Text := 'Edit';
  Include(Self.miEdit.Click, Self.miEdit_Click);
  // 
  // miDelete
  // 
  Self.miDelete.Index := 2;
  Self.miDelete.Text := 'Delete';
  Include(Self.miDelete.Click, Self.miDelete_Click);
  // 
  // btnDeleteProcess
  // 
  Self.btnDeleteProcess.Enabled := False;
  Self.btnDeleteProcess.Location := System.Drawing.Point.Create(192, 296);
  Self.btnDeleteProcess.Name := 'btnDeleteProcess';
  Self.btnDeleteProcess.TabIndex := 33;
  Self.btnDeleteProcess.Text := 'Delete';
  Include(Self.btnDeleteProcess.Click, Self.btnDeleteProcess_Click);
  // 
  // btnEditProcess
  // 
  Self.btnEditProcess.Enabled := False;
  Self.btnEditProcess.Location := System.Drawing.Point.Create(104, 296);
  Self.btnEditProcess.Name := 'btnEditProcess';
  Self.btnEditProcess.TabIndex := 32;
  Self.btnEditProcess.Text := 'Edit';
  Include(Self.btnEditProcess.Click, Self.btnEditProcess_Click);
  // 
  // columnHeader1
  // 
  Self.columnHeader1.Text := 'Process Name';
  Self.columnHeader1.Width := 213;
  // 
  // btnAddProcess
  // 
  Self.btnAddProcess.Location := System.Drawing.Point.Create(16, 296);
  Self.btnAddProcess.Name := 'btnAddProcess';
  Self.btnAddProcess.TabIndex := 31;
  Self.btnAddProcess.Text := 'Add';
  Include(Self.btnAddProcess.Click, Self.btnAddProcess_Click);
  // 
  // cbRestartOneProcess
  // 
  Self.cbRestartOneProcess.Location := System.Drawing.Point.Create(16, 376);
  Self.cbRestartOneProcess.Name := 'cbRestartOneProcess';
  Self.cbRestartOneProcess.Size := System.Drawing.Size.Create(232, 24);
  Self.cbRestartOneProcess.TabIndex := 30;
  Self.cbRestartOneProcess.Text := 'Restart Only One Process Per Check';
  Include(Self.cbRestartOneProcess.Click, Self.cbRestartOneProcess_Click);
  // 
  // label4
  // 
  Self.label4.Location := System.Drawing.Point.Create(176, 352);
  Self.label4.Name := 'label4';
  Self.label4.Size := System.Drawing.Size.Create(112, 16);
  Self.label4.TabIndex := 29;
  Self.label4.Text := 'seconds';
  // 
  // columnHeader3
  // 
  Self.columnHeader3.Text := 'Check Responding';
  Self.columnHeader3.TextAlign := System.Windows.Forms.HorizontalAlignment.Center;
  Self.columnHeader3.Width := 106;
  // 
  // label3
  // 
  Self.label3.Location := System.Drawing.Point.Create(16, 352);
  Self.label3.Name := 'label3';
  Self.label3.Size := System.Drawing.Size.Create(112, 16);
  Self.label3.TabIndex := 28;
  Self.label3.Text := 'Checking Frequency';
  // 
  // nudFrequency
  // 
  Self.nudFrequency.Location := System.Drawing.Point.Create(128, 352);
  Self.nudFrequency.Name := 'nudFrequency';
  Self.nudFrequency.Size := System.Drawing.Size.Create(40, 20);
  Self.nudFrequency.TabIndex := 27;
  Include(Self.nudFrequency.Click, Self.nudFrequency_Click);
  // 
  // btnApply
  // 
  Self.btnApply.Enabled := False;
  Self.btnApply.Location := System.Drawing.Point.Create(592, 384);
  Self.btnApply.Name := 'btnApply';
  Self.btnApply.TabIndex := 26;
  Self.btnApply.Text := 'Apply';
  Include(Self.btnApply.Click, Self.btnApply_Click);
  // 
  // btnCancel
  // 
  Self.btnCancel.DialogResult := System.Windows.Forms.DialogResult.Cancel;
  Self.btnCancel.Location := System.Drawing.Point.Create(504, 384);
  Self.btnCancel.Name := 'btnCancel';
  Self.btnCancel.TabIndex := 25;
  Self.btnCancel.Text := 'Cancel';
  Include(Self.btnCancel.Click, Self.btnCancel_Click);
  // 
  // btnOK
  // 
  Self.btnOK.DialogResult := System.Windows.Forms.DialogResult.OK;
  Self.btnOK.Location := System.Drawing.Point.Create(416, 384);
  Self.btnOK.Name := 'btnOK';
  Self.btnOK.TabIndex := 24;
  Self.btnOK.Text := 'OK';
  Include(Self.btnOK.Click, Self.btnOK_Click);
  // 
  // label2
  // 
  Self.label2.Location := System.Drawing.Point.Create(376, 8);
  Self.label2.Name := 'label2';
  Self.label2.Size := System.Drawing.Size.Create(120, 16);
  Self.label2.TabIndex := 23;
  Self.label2.Text := 'Dependant Processes';
  // 
  // lvDependants
  // 
  Self.lvDependants.Columns.AddRange(TSystem_Windows_Forms_ColumnHeaderArray.Create(Self.columnHeader2));
  Self.lvDependants.ContextMenu := Self.cmProcesses;
  Self.lvDependants.FullRowSelect := True;
  Self.lvDependants.HideSelection := False;
  Self.lvDependants.Location := System.Drawing.Point.Create(376, 24);
  Self.lvDependants.MultiSelect := False;
  Self.lvDependants.Name := 'lvDependants';
  Self.lvDependants.Size := System.Drawing.Size.Create(296, 264);
  Self.lvDependants.TabIndex := 22;
  Self.lvDependants.View := System.Windows.Forms.View.Details;
  Include(Self.lvDependants.DoubleClick, Self.btnEditDependant_Click);
  Include(Self.lvDependants.SelectedIndexChanged, Self.lvDependants_SelectedIndexChanged);
  // 
  // columnHeader2
  // 
  Self.columnHeader2.Text := 'Process Name';
  Self.columnHeader2.Width := 260;
  // 
  // label1
  // 
  Self.label1.Location := System.Drawing.Point.Create(16, 8);
  Self.label1.Name := 'label1';
  Self.label1.Size := System.Drawing.Size.Create(100, 16);
  Self.label1.TabIndex := 21;
  Self.label1.Text := 'Processes';
  // 
  // lvProcesses
  // 
  Self.lvProcesses.Columns.AddRange(TSystem_Windows_Forms_ColumnHeaderArray.Create(Self.columnHeader1, Self.columnHeader3));
  Self.lvProcesses.ContextMenu := Self.cmProcesses;
  Self.lvProcesses.FullRowSelect := True;
  Self.lvProcesses.HideSelection := False;
  Self.lvProcesses.Location := System.Drawing.Point.Create(16, 24);
  Self.lvProcesses.MultiSelect := False;
  Self.lvProcesses.Name := 'lvProcesses';
  Self.lvProcesses.Size := System.Drawing.Size.Create(344, 264);
  Self.lvProcesses.TabIndex := 20;
  Self.lvProcesses.View := System.Windows.Forms.View.Details;
  Include(Self.lvProcesses.DoubleClick, Self.btnEditProcess_Click);
  Include(Self.lvProcesses.SelectedIndexChanged, Self.lvProcesses_SelectedIndexChanged);
  // 
  // TSetupForm
  // 
  Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13);
  Self.ClientSize := System.Drawing.Size.Create(688, 418);
  Self.Controls.Add(Self.btnDeleteDependant);
  Self.Controls.Add(Self.btnAddDependant);
  Self.Controls.Add(Self.btnDeleteProcess);
  Self.Controls.Add(Self.btnEditProcess);
  Self.Controls.Add(Self.btnAddProcess);
  Self.Controls.Add(Self.cbRestartOneProcess);
  Self.Controls.Add(Self.label4);
  Self.Controls.Add(Self.label3);
  Self.Controls.Add(Self.nudFrequency);
  Self.Controls.Add(Self.btnApply);
  Self.Controls.Add(Self.btnCancel);
  Self.Controls.Add(Self.btnOK);
  Self.Controls.Add(Self.label2);
  Self.Controls.Add(Self.lvDependants);
  Self.Controls.Add(Self.label1);
  Self.Controls.Add(Self.lvProcesses);
  Self.Controls.Add(Self.btnEditDependant);
  Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedSingle;
  Self.Icon := (System.Drawing.Icon(resources.GetObject('$this.Icon')));
  Self.MaximizeBox := False;
  Self.MinimizeBox := False;
  Self.Name := 'TSetupForm';
  Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen;
  Self.Text := 'Process Checker Setup';
  Include(Self.Load, Self.TSetupForm_Load);
  (System.ComponentModel.ISupportInitialize(Self.nudFrequency)).EndInit;
  Self.ResumeLayout(False);
end;
{$ENDREGION}

procedure TSetupForm.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

class function TSetupForm.ShowSetupForm(var pSettings: TProcessCheckerSettings): System.Windows.Forms.DialogResult;
var
  lSetupForm: TSetupForm;
begin
  lSetupForm := TSetupForm.Create;
  lSetupForm.FSettings := pSettings;
  Result := lSetupForm.ShowDialog;
  pSettings := lSetupForm.FSettings;
end;

constructor TSetupForm.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;
end;

/// <summary>
/// Need to ensure that we are only enabling Edit and Delete menu items
/// if a ListViewItem is selected, and that we only enable the Add
/// menu item for the dependant processes ListView if a watched process
/// has been selected.
/// </summary>
procedure TSetupForm.cmProcesses_Popup(sender: System.Object; e: System.EventArgs);
var
  lListView: ListView;
begin
  //Get a reference to the ListView the context menu is associated with
  lListView := ListView(System.Windows.Forms.ContextMenu(Sender).SourceControl);

  //Only allow editing or deleting if a ListViewItem is selected
  miEdit.Enabled := (lListView.SelectedItems.Count <> 0);
  miDelete.Enabled := miEdit.Enabled;

  //Only allow adding of dependant processes if a watched process is
  //selected
  if (lListView = lvDependants) then
    miAdd.Enabled := (lvProcesses.SelectedItems.Count <> 0);
end;


/// <summary>
/// Populate the Dependant Process ListView for the currently selected item in
/// the Watched Process ListView.
/// </summary>
procedure TSetupForm.lvProcesses_SelectedIndexChanged(sender: System.Object;
    e: System.EventArgs);
var
  lIndex: integer;
begin
  //As MultiSelect is false, there will only ever be at most one item in
  //the SelectedItems collection
  lIndex := SelectedIndex(lvProcesses);
  if (lIndex <> -1) then
    AddDependants(TWatchedProcess(FSettings.WatchedProcesses[lIndex]));
  EnableProcessButtons;
  EnableDependantButtons;
end;

procedure TSetupForm.TSetupForm_Load(sender: System.Object; e: System.EventArgs);
begin
  PopulateControls;
end;

/// <summary>
/// Populate the GUI controls based on the details contained in the
/// FProcessCheckerSettings reference.
/// </summary>
procedure TSetupForm.PopulateControls;
var
  i: integer;
begin
  nudFrequency.Value := FSettings.CheckFrequency;
  cbRestartOneProcess.Checked := FSettings.RestartOneProcess;

  //Populate the Process and Dependant Process list views
  for i := 0 to FSettings.WatchedProcesses.Count -1 do
    AddProcess(FSettings.WatchedProcesses[i], lvProcesses);
end;

/// <summary>
/// Add details of a watched or dependant process to the specified ListView.
/// </summary>
procedure TSetupForm.AddProcess(pProcess: TProcess; pListView: ListView);
var
  lItem: ListViewItem;
begin
  lItem := ListViewItem.Create(pProcess.Path);
  lItem.Tag := pProcess;
  pListView.Items.Add(lItem);
  if pProcess is TWatchedProcess then
    lItem.SubItems.Add(TWatchedProcess(pProcess).CheckResponding.ToString);
end;

/// <summary>
/// Populate the Dependants Process ListView based on the passed in
/// WatchedProcess reference.
/// </summary>
procedure TSetupForm.AddDependants(pProcess: TWatchedProcess);
var
  i: integer;
begin
  lvDependants.Items.Clear;
  for i := 0 to pProcess.DependantProcesses.Count -1 do
    AddProcess(pProcess.DependantProcesses[i], lvDependants);
end;

/// <summary>
/// Ensure we can only Edit or Delete if a process is selected.
/// </summary>
procedure TSetupForm.EnableProcessButtons;
begin
  //Ensure we can only Edit or Delete if a process is selected
  btnEditProcess.Enabled := (SelectedIndex(lvProcesses) <> -1);
  btnDeleteProcess.Enabled := btnEditProcess.Enabled;
end;

/// <summary>
/// Returns the index for the last of the currently selected items in a
/// ListView.
/// </summary>
function TSetupForm.SelectedIndex(pListView: ListView): Integer;
begin
  if pListView.SelectedIndices.Count <> 0 then
    Result := pListView.SelectedIndices[pListView.SelectedIndices.Count -1]
  else
    Result := -1;
end;

/// <summary>
/// Returns the ListViewItem reference for the last of the currently selected
/// items in a ListView.
/// </summary>
function TSetupForm.SelectedItem(pListView: ListView): ListViewItem;
begin
  if pListView.SelectedItems.Count <> 0 then
    Result := pListView.SelectedItems[pListView.SelectedItems.Count -1]
  else
    Result := nil;
end;

procedure TSetupForm.miDelete_Click(sender: System.Object; e: System.EventArgs);
var
  lListView: ListView;
begin
  //Get a reference to the ListView the MenuItem is assocated
  //with. The sender parameter is a reference to the MenuItem object,
  //who's parent will be a ContextMenu. From the ContextMenu reference
  //we can determine the SourceControl (in this case, which ListView
  //initiated the request)
  lListView := ListView(System.Windows.Forms.ContextMenu(MenuItem(Sender).Parent).SourceControl);
  DoDelete(lListView);
end;

/// <summary>
/// Delete the selected ListViewItem from the ListView (and associated
/// ProcessList reference).
/// </summary>
procedure TSetupForm.DoDelete(pListView: ListView);
var
  lSelectedItem: ListViewItem;
  lWatchedItem: ListViewItem;
begin
  //Index to selected ListViewItem
  lSelectedItem := SelectedItem(pListView);

  if (pListView = lvProcesses) then
  begin
    //Delete watched process
    if Assigned(lSelectedItem) then
    begin
      //Delete from ProcessCheckerSettings object
      FSettings.WatchedProcesses.Remove(TWatchedProcess(lSelectedItem.Tag));
      //Remove from ListView
      pListView.Items.RemoveAt(lSelectedItem.Index);
      //Clear the dependant processes ListView
      lvDependants.Items.Clear;
    end
  end
  else
  begin
    //Delete dependant process
    if Assigned(lSelectedItem) then
    begin
      //Reference
      lWatchedItem := SelectedItem(lvProcesses);
      if Assigned(lWatchedItem) then
      begin
        TWatchedProcess(lWatchedItem.Tag).DependantProcesses.Remove(TDependantProcess(lSelectedItem.Tag));
        pListView.Items.RemoveAt(lSelectedItem.Index);
      end;
    end;
  end;
  btnApply.Enabled := true;
end;

procedure TSetupForm.miAdd_Click(sender: System.Object; e: System.EventArgs);
var
  lListView: ListView;
begin
  //Get a reference to the ListView the MenuItem is assocated
  //with. The sender parameter is a reference to the MenuItem object,
  //who's parent will be a ContextMenu. From the ContextMenu reference
  //we can determine the SourceControl (in this case, which ListView
  //initiated the request)
  lListView := ListView(System.Windows.Forms.ContextMenu(MenuItem(Sender).Parent).SourceControl);
  DoAdd(lListView);
end;

/// <summary>
/// Add a process to the selected ListView (and associated ProcessList
/// reference).
/// </summary>
procedure TSetupForm.DoAdd(pListView: ListView);
var
  lProcesses: TProcesses;
  lProcess: TProcess;
  lSelectedItem: ListViewItem;
  lWatchedProcess: TProcess;
begin
  //selected ListViewItem
  lSelectedItem := SelectedItem(pListView);

  if (pListView = lvProcesses) then
  begin
    //Adding a new watched process
    lProcesses := FSettings.WatchedProcesses;
    lProcess := TWatchedProcess.Create('');
  end
  else
  begin
    //Adding a new dependant process
    lProcesses := TWatchedProcess(FSettings.WatchedProcesses[SelectedIndex(lvProcesses)]).DependantProcesses;
    lWatchedProcess := TWatchedProcess(SelectedItem(lvProcesses).Tag);
    lProcess := TDependantProcess.Create('', lWatchedProcess);
  end;

  if (TProcessDetailsForm.ShowProcessDetails(lProcess) = System.Windows.Forms.DialogResult.OK) then
  begin
    lProcesses.Add(lProcess);
    AddProcess(lProcess, pListView);
  end;
  btnApply.Enabled := true;
end;

procedure TSetupForm.miEdit_Click(sender: System.Object; e: System.EventArgs);
var
  lListView: ListView;
begin
  //Get a reference to the ListView the MenuItem is assocated
  //with. The sender parameter is a reference to the MenuItem object,
  //who's parent will be a ContextMenu. From the ContextMenu reference
  //we can determine the SourceControl (in this case, which ListView
  //initiated the request)
  lListView := ListView(System.Windows.Forms.ContextMenu(MenuItem(Sender).Parent).SourceControl);
  DoEdit(lListView);
end;

/// <summary>
/// Edit the details of the selected ListViewItem (and associated TProcess
/// reference).
/// </summary>
procedure TSetupForm.DoEdit(pListView: ListView);
var
  lSelectedItem: ListViewItem;
  lProcess: TProcess;
begin
  //selected ListViewItem
  lSelectedItem := SelectedItem(pListView);
  if Assigned(lSelectedItem) then
  begin
    //Reference to ProcessDetails object
    lProcess := TProcess(lSelectedItem.Tag);

    if (TProcessDetailsForm.ShowProcessDetails(lProcess) = System.Windows.Forms.DialogResult.OK) then
    begin
      //Update the ListViewItem to display the updated process details
      lSelectedItem.Text := lProcess.Path;
      if (lProcess is TWatchedProcess) then
        lSelectedItem.SubItems[1].Text := TWatchedProcess(lProcess).CheckResponding.ToString;
      btnApply.Enabled := True;
    end;
  end;
end;

procedure TSetupForm.btnAddProcess_Click(sender: System.Object; e: System.EventArgs);
begin
  DoAdd(lvProcesses);
end;

procedure TSetupForm.btnEditProcess_Click(sender: System.Object; e: System.EventArgs);
begin
  DoEdit(lvProcesses);
end;

procedure TSetupForm.btnDeleteProcess_Click(sender: System.Object; e: System.EventArgs);
begin
  DoDelete(lvProcesses);
end;

procedure TSetupForm.lvDependants_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
begin
  EnableDependantButtons();
end;

/// <summary>
/// Ensure we can only Edit or Delete if a dependant process is selected,
/// and can only Add if a watched process is selected.
/// </summary>
procedure TSetupForm.EnableDependantButtons;
begin
  //Ensure we can only Add if a watched process is selected
  btnAddDependant.Enabled := (SelectedIndex(lvProcesses) <> -1);

  //Ensure we can only Edit or Delete if a dependant process is selected
  btnEditDependant.Enabled := (SelectedIndex(lvDependants) <> -1);
  btnDeleteDependant.Enabled := btnEditDependant.Enabled;
end;

procedure TSetupForm.btnAddDependant_Click(sender: System.Object; e: System.EventArgs);
begin
  DoAdd(lvDependants);
end;

procedure TSetupForm.btnEditDependant_Click(sender: System.Object; e: System.EventArgs);
begin
  DoEdit(lvDependants);
end;

procedure TSetupForm.btnDeleteDependant_Click(sender: System.Object; e: System.EventArgs);
begin
  DoDelete(lvDependants);
end;

procedure TSetupForm.btnOK_Click(sender: System.Object; e: System.EventArgs);
begin
  SaveSettings;
end;

/// <summary>
/// Save the settings to disk (in .\ProcessChecker.dat)
/// </summary>
procedure TSetupForm.SaveSettings;
begin
  //Update the TProcessCheckerSettings instance. The WatchedProcesses
  //list would have already been updated, so we only need to
  //update any additional properties.
  FSettings.CheckFrequency := integer(nudFrequency.Value);
  FSettings.RestartOneProcess := cbRestartOneProcess.Checked;

  //Save to disk
  try
    FSettings.Save;
  except on E: Exception do
    MessageBox.Show(Format('There was an error saving settings to disk: %s',
      [e.Message]));
  end;
end;

procedure TSetupForm.nudFrequency_Click(sender: System.Object; e: System.EventArgs);
begin
  btnApply.Enabled := true;
end;

procedure TSetupForm.cbRestartOneProcess_Click(sender: System.Object; e: System.EventArgs);
begin
  btnApply.Enabled := true;
end;

procedure TSetupForm.btnApply_Click(sender: System.Object; e: System.EventArgs);
begin
  SaveSettings;
  btnApply.Enabled := false;
end;

procedure TSetupForm.btnCancel_Click(sender: System.Object; e: System.EventArgs);
begin
  //Serialize the old settings back from disk
  FSettings := TProcessCheckerSettings.Load;
end;

end.
