changeset 4:3142e52cbe69

Lots more progress
author Brad Greco <brad@bgreco.net>
date Sun, 10 Feb 2019 20:51:26 -0500
parents 96f0b028176d
children b6fe203af9d5
files ServerMonitor/App.config ServerMonitor/Controls/CheckControl.cs ServerMonitor/Controls/ServerSummaryControl.Designer.cs ServerMonitor/Controls/ServerSummaryControl.cs ServerMonitor/Controls/ServerSummaryControl.resx ServerMonitor/Forms/CheckForm.Designer.cs ServerMonitor/Forms/CheckForm.cs ServerMonitor/Forms/ServerForm.Designer.cs ServerMonitor/Forms/ServerForm.cs ServerMonitor/Forms/ServerSummaryForm.Designer.cs ServerMonitor/Forms/ServerSummaryForm.cs ServerMonitor/Forms/ServerSummaryForm.resx ServerMonitor/Forms/SettingsForm.Designer.cs ServerMonitor/Forms/SettingsForm.cs ServerMonitor/Forms/SettingsForm.resx ServerMonitor/Helpers.cs ServerMonitor/Objects/CheckResult.cs ServerMonitor/Objects/Checks/Check.cs ServerMonitor/Objects/Checks/DiskSpaceCheck.cs ServerMonitor/Objects/Checks/FileCheck.cs ServerMonitor/Objects/Checks/SshCheck.cs ServerMonitor/Objects/Schedule.cs ServerMonitor/Objects/Server.cs ServerMonitor/Objects/ServerMonitor.cs ServerMonitor/Properties/Resources.Designer.cs ServerMonitor/Properties/Resources.resx ServerMonitor/Properties/Settings.Designer.cs ServerMonitor/Properties/Settings.settings ServerMonitor/Resources/disable.png ServerMonitor/Resources/error.png ServerMonitor/Resources/info.png ServerMonitor/Resources/pass.png ServerMonitor/Resources/settings.png ServerMonitor/Resources/warning.png ServerMonitor/ServerMonitor.csproj ServerMonitor/Win32Helpers.cs
diffstat 36 files changed, 1219 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/ServerMonitor/App.config	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/App.config	Sun Feb 10 20:51:26 2019 -0500
@@ -2,6 +2,7 @@
 <configuration>
     <configSections>
         <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+            <section name="ServerMonitorApp.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
             <section name="ServerMonitor.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
         </sectionGroup>
     </configSections>
@@ -9,6 +10,23 @@
         
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup>
     <userSettings>
+        <ServerMonitorApp.Properties.Settings>
+            <setting name="ConfirmDeleteCheck" serializeAs="String">
+                <value>True</value>
+            </setting>
+            <setting name="ErrorAction" serializeAs="String">
+                <value>FlashTaskbar</value>
+            </setting>
+            <setting name="WarningAction" serializeAs="String">
+                <value>NotificationBalloon</value>
+            </setting>
+            <setting name="InformationAction" serializeAs="String">
+                <value>NotificationBalloon</value>
+            </setting>
+            <setting name="MaxCheckId" serializeAs="String">
+                <value>0</value>
+            </setting>
+        </ServerMonitorApp.Properties.Settings>
         <ServerMonitor.Properties.Settings>
             <setting name="ConfirmDeleteCheck" serializeAs="String">
                 <value>True</value>
--- a/ServerMonitor/Controls/CheckControl.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Controls/CheckControl.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -13,7 +13,7 @@
     /// <remarks>This control should never be used directly, but marking it abstract causes problems with the designer.</remarks>
     public partial class CheckControl : UserControl
     {
-        public Type CheckType { get { return GetCheckType(GetType()); } }
+        public Type CheckType => GetCheckType(GetType());
 
         public CheckControl()
         {
--- a/ServerMonitor/Controls/ServerSummaryControl.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Controls/ServerSummaryControl.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -30,7 +30,9 @@
         {
             this.ServerNameLabel = new System.Windows.Forms.Label();
             this.ServerPictureBox = new System.Windows.Forms.PictureBox();
+            this.StatusPictureBox = new System.Windows.Forms.PictureBox();
             ((System.ComponentModel.ISupportInitialize)(this.ServerPictureBox)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.StatusPictureBox)).BeginInit();
             this.SuspendLayout();
             // 
             // ServerNameLabel
@@ -54,16 +56,29 @@
             this.ServerPictureBox.TabIndex = 0;
             this.ServerPictureBox.TabStop = false;
             // 
+            // StatusPictureBox
+            // 
+            this.StatusPictureBox.BackColor = System.Drawing.Color.Transparent;
+            this.StatusPictureBox.Image = global::ServerMonitorApp.Properties.Resources.pass;
+            this.StatusPictureBox.Location = new System.Drawing.Point(104, 92);
+            this.StatusPictureBox.Name = "StatusPictureBox";
+            this.StatusPictureBox.Size = new System.Drawing.Size(32, 32);
+            this.StatusPictureBox.TabIndex = 2;
+            this.StatusPictureBox.TabStop = false;
+            this.StatusPictureBox.Click += new System.EventHandler(this.Control_Click);
+            // 
             // ServerSummaryControl
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.BackColor = System.Drawing.SystemColors.Control;
+            this.Controls.Add(this.StatusPictureBox);
             this.Controls.Add(this.ServerNameLabel);
             this.Controls.Add(this.ServerPictureBox);
             this.Name = "ServerSummaryControl";
             this.Size = new System.Drawing.Size(192, 192);
             ((System.ComponentModel.ISupportInitialize)(this.ServerPictureBox)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.StatusPictureBox)).EndInit();
             this.ResumeLayout(false);
 
         }
@@ -72,5 +87,6 @@
 
         private System.Windows.Forms.PictureBox ServerPictureBox;
         private System.Windows.Forms.Label ServerNameLabel;
+        private System.Windows.Forms.PictureBox StatusPictureBox;
     }
 }
--- a/ServerMonitor/Controls/ServerSummaryControl.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Controls/ServerSummaryControl.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -26,6 +26,14 @@
             }
             Server = server;
             ServerNameLabel.Text = Server.Name;
+            StatusPictureBox.Parent = ServerPictureBox;
+            StatusPictureBox.Image = Server.Status.GetIcon();
+        }
+
+        public override void Refresh()
+        {
+            StatusPictureBox.Image = Server.Status.GetIcon();
+            base.Refresh();
         }
 
         private void Control_Click(object sender, EventArgs e)
--- a/ServerMonitor/Controls/ServerSummaryControl.resx	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Controls/ServerSummaryControl.resx	Sun Feb 10 20:51:26 2019 -0500
@@ -112,9 +112,9 @@
     <value>2.0</value>
   </resheader>
   <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
 </root>
\ No newline at end of file
--- a/ServerMonitor/Forms/CheckForm.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/CheckForm.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -33,6 +33,8 @@
             this.CheckTypePanel = new System.Windows.Forms.Panel();
             this.TypeHelpPictureBox = new System.Windows.Forms.PictureBox();
             this.GeneralGroupBox = new System.Windows.Forms.GroupBox();
+            this.SeverityComboBox = new System.Windows.Forms.ComboBox();
+            this.SeverityLabel = new System.Windows.Forms.Label();
             this.ScheduleAtPanel = new System.Windows.Forms.Panel();
             this.AtTimePicker = new System.Windows.Forms.DateTimePicker();
             this.ScheduleAtLabel = new System.Windows.Forms.Label();
@@ -93,7 +95,7 @@
             this.CheckTypePanel.Controls.Add(this.CheckTypeLabel);
             this.CheckTypePanel.Controls.Add(this.TypeHelpPictureBox);
             this.CheckTypePanel.Controls.Add(this.CheckTypeComboBox);
-            this.CheckTypePanel.Location = new System.Drawing.Point(162, 12);
+            this.CheckTypePanel.Location = new System.Drawing.Point(167, 12);
             this.CheckTypePanel.Name = "CheckTypePanel";
             this.CheckTypePanel.Size = new System.Drawing.Size(227, 26);
             this.CheckTypePanel.TabIndex = 1;
@@ -114,6 +116,8 @@
             // 
             this.GeneralGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
+            this.GeneralGroupBox.Controls.Add(this.SeverityComboBox);
+            this.GeneralGroupBox.Controls.Add(this.SeverityLabel);
             this.GeneralGroupBox.Controls.Add(this.ScheduleAtPanel);
             this.GeneralGroupBox.Controls.Add(this.ScheduleBetweenPanel);
             this.GeneralGroupBox.Controls.Add(this.FrequencyUnitsComboBox);
@@ -127,16 +131,34 @@
             this.GeneralGroupBox.Controls.Add(this.EnabledCheckBox);
             this.GeneralGroupBox.Location = new System.Drawing.Point(12, 39);
             this.GeneralGroupBox.Name = "GeneralGroupBox";
-            this.GeneralGroupBox.Size = new System.Drawing.Size(526, 115);
+            this.GeneralGroupBox.Size = new System.Drawing.Size(537, 135);
             this.GeneralGroupBox.TabIndex = 2;
             this.GeneralGroupBox.TabStop = false;
             this.GeneralGroupBox.Text = "General settings";
             // 
+            // SeverityComboBox
+            // 
+            this.SeverityComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.SeverityComboBox.FormattingEnabled = true;
+            this.SeverityComboBox.Location = new System.Drawing.Point(92, 106);
+            this.SeverityComboBox.Name = "SeverityComboBox";
+            this.SeverityComboBox.Size = new System.Drawing.Size(82, 21);
+            this.SeverityComboBox.TabIndex = 34;
+            // 
+            // SeverityLabel
+            // 
+            this.SeverityLabel.AutoSize = true;
+            this.SeverityLabel.Location = new System.Drawing.Point(6, 109);
+            this.SeverityLabel.Name = "SeverityLabel";
+            this.SeverityLabel.Size = new System.Drawing.Size(80, 13);
+            this.SeverityLabel.TabIndex = 33;
+            this.SeverityLabel.Text = "Failure severity:";
+            // 
             // ScheduleAtPanel
             // 
             this.ScheduleAtPanel.Controls.Add(this.AtTimePicker);
             this.ScheduleAtPanel.Controls.Add(this.ScheduleAtLabel);
-            this.ScheduleAtPanel.Location = new System.Drawing.Point(247, 76);
+            this.ScheduleAtPanel.Location = new System.Drawing.Point(258, 76);
             this.ScheduleAtPanel.Name = "ScheduleAtPanel";
             this.ScheduleAtPanel.Size = new System.Drawing.Size(270, 29);
             this.ScheduleAtPanel.TabIndex = 32;
@@ -167,7 +189,7 @@
             this.ScheduleBetweenPanel.Controls.Add(this.EndTimePicker);
             this.ScheduleBetweenPanel.Controls.Add(this.StartTimePicker);
             this.ScheduleBetweenPanel.Controls.Add(this.ScheduleBetweenLabel);
-            this.ScheduleBetweenPanel.Location = new System.Drawing.Point(247, 76);
+            this.ScheduleBetweenPanel.Location = new System.Drawing.Point(258, 76);
             this.ScheduleBetweenPanel.Name = "ScheduleBetweenPanel";
             this.ScheduleBetweenPanel.Size = new System.Drawing.Size(270, 29);
             this.ScheduleBetweenPanel.TabIndex = 31;
@@ -214,7 +236,7 @@
             // 
             this.FrequencyUnitsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
             this.FrequencyUnitsComboBox.FormattingEnabled = true;
-            this.FrequencyUnitsComboBox.Location = new System.Drawing.Point(169, 79);
+            this.FrequencyUnitsComboBox.Location = new System.Drawing.Point(180, 79);
             this.FrequencyUnitsComboBox.Name = "FrequencyUnitsComboBox";
             this.FrequencyUnitsComboBox.Size = new System.Drawing.Size(75, 21);
             this.FrequencyUnitsComboBox.TabIndex = 30;
@@ -224,7 +246,7 @@
             // ScheduleEveryLabel
             // 
             this.ScheduleEveryLabel.AutoSize = true;
-            this.ScheduleEveryLabel.Location = new System.Drawing.Point(78, 82);
+            this.ScheduleEveryLabel.Location = new System.Drawing.Point(89, 82);
             this.ScheduleEveryLabel.Name = "ScheduleEveryLabel";
             this.ScheduleEveryLabel.Size = new System.Drawing.Size(34, 13);
             this.ScheduleEveryLabel.TabIndex = 29;
@@ -232,7 +254,7 @@
             // 
             // FrequencyUpDown
             // 
-            this.FrequencyUpDown.Location = new System.Drawing.Point(114, 80);
+            this.FrequencyUpDown.Location = new System.Drawing.Point(125, 80);
             this.FrequencyUpDown.Maximum = new decimal(new int[] {
             59,
             0,
@@ -254,7 +276,7 @@
             0,
             0,
             0});
-            this.TimeoutInput.Location = new System.Drawing.Point(81, 54);
+            this.TimeoutInput.Location = new System.Drawing.Point(92, 54);
             this.TimeoutInput.Maximum = new decimal(new int[] {
             60000,
             0,
@@ -277,7 +299,7 @@
             // PingTimeoutLabel
             // 
             this.PingTimeoutLabel.AutoSize = true;
-            this.PingTimeoutLabel.Location = new System.Drawing.Point(5, 56);
+            this.PingTimeoutLabel.Location = new System.Drawing.Point(6, 56);
             this.PingTimeoutLabel.Name = "PingTimeoutLabel";
             this.PingTimeoutLabel.Size = new System.Drawing.Size(70, 13);
             this.PingTimeoutLabel.TabIndex = 26;
@@ -303,7 +325,7 @@
             // 
             // NameTextBox
             // 
-            this.NameTextBox.Location = new System.Drawing.Point(81, 28);
+            this.NameTextBox.Location = new System.Drawing.Point(92, 28);
             this.NameTextBox.Name = "NameTextBox";
             this.NameTextBox.Size = new System.Drawing.Size(181, 20);
             this.NameTextBox.TabIndex = 22;
@@ -313,7 +335,7 @@
             this.EnabledCheckBox.AutoSize = true;
             this.EnabledCheckBox.Checked = true;
             this.EnabledCheckBox.CheckState = System.Windows.Forms.CheckState.Checked;
-            this.EnabledCheckBox.Location = new System.Drawing.Point(268, 31);
+            this.EnabledCheckBox.Location = new System.Drawing.Point(279, 31);
             this.EnabledCheckBox.Name = "EnabledCheckBox";
             this.EnabledCheckBox.Size = new System.Drawing.Size(65, 17);
             this.EnabledCheckBox.TabIndex = 23;
@@ -324,7 +346,7 @@
             // 
             this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
             this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK;
-            this.OkButton.Location = new System.Drawing.Point(382, 397);
+            this.OkButton.Location = new System.Drawing.Point(393, 397);
             this.OkButton.Name = "OkButton";
             this.OkButton.Size = new System.Drawing.Size(75, 23);
             this.OkButton.TabIndex = 20;
@@ -336,7 +358,7 @@
             // 
             this.CancelCheckButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
             this.CancelCheckButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
-            this.CancelCheckButton.Location = new System.Drawing.Point(463, 397);
+            this.CancelCheckButton.Location = new System.Drawing.Point(474, 397);
             this.CancelCheckButton.Name = "CancelCheckButton";
             this.CancelCheckButton.Size = new System.Drawing.Size(75, 23);
             this.CancelCheckButton.TabIndex = 19;
@@ -349,9 +371,9 @@
             this.CheckSettingsPanel.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.CheckSettingsPanel.Location = new System.Drawing.Point(12, 160);
+            this.CheckSettingsPanel.Location = new System.Drawing.Point(12, 180);
             this.CheckSettingsPanel.Name = "CheckSettingsPanel";
-            this.CheckSettingsPanel.Size = new System.Drawing.Size(526, 231);
+            this.CheckSettingsPanel.Size = new System.Drawing.Size(537, 211);
             this.CheckSettingsPanel.TabIndex = 27;
             // 
             // ResultLabel
@@ -361,7 +383,7 @@
             this.ResultLabel.AutoEllipsis = true;
             this.ResultLabel.Location = new System.Drawing.Point(115, 394);
             this.ResultLabel.Name = "ResultLabel";
-            this.ResultLabel.Size = new System.Drawing.Size(261, 29);
+            this.ResultLabel.Size = new System.Drawing.Size(272, 29);
             this.ResultLabel.TabIndex = 29;
             this.ResultLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
             this.ResultLabel.Visible = false;
@@ -410,7 +432,7 @@
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.CancelButton = this.CancelCheckButton;
-            this.ClientSize = new System.Drawing.Size(550, 432);
+            this.ClientSize = new System.Drawing.Size(561, 432);
             this.Controls.Add(this.ResultIconPictureBox);
             this.Controls.Add(this.ResultLabel);
             this.Controls.Add(this.CancelRunButton);
@@ -472,5 +494,7 @@
         private System.Windows.Forms.Panel ScheduleAtPanel;
         private System.Windows.Forms.DateTimePicker AtTimePicker;
         private System.Windows.Forms.Label ScheduleAtLabel;
+        private System.Windows.Forms.ComboBox SeverityComboBox;
+        private System.Windows.Forms.Label SeverityLabel;
     }
 }
\ No newline at end of file
--- a/ServerMonitor/Forms/CheckForm.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/CheckForm.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -28,7 +28,7 @@
 
         public int CheckId { get; private set; }
 
-        public Point HelpLocation { get { return TypeHelpPictureBox.PointToScreen(Point.Empty); } }
+        public Point HelpLocation => TypeHelpPictureBox.PointToScreen(Point.Empty);
 
         public CheckForm(ServerMonitor monitor, Check check)
         {
@@ -50,6 +50,8 @@
         private void CheckForm_Load(object sender, EventArgs e)
         {
             CheckTypeComboBox.Items.AddRange(Check.CheckTypes);
+            SeverityComboBox.Items.AddRange(new object[] { CheckStatus.Error, CheckStatus.Warning, CheckStatus.Information });
+            SeverityComboBox.SelectedIndex = 0;
             FrequencyUnitsComboBox.DataSource = Enum.GetValues(typeof(FrequencyUnits));
             Helpers.FormatImageButton(RunButton);
             Helpers.FormatImageButton(CancelRunButton);
@@ -115,6 +117,7 @@
             NameTextBox.Text = Check.Name;
             EnabledCheckBox.Checked = check.Enabled;
             TimeoutInput.Value = check.Timeout;
+            SeverityComboBox.SelectedItem = check.FailStatus;
             FrequencyUnitsComboBox.SelectedItem = check.Schedule.Units;
             FrequencyUpDown.Value = check.Schedule.Frequency;
             StartTimePicker.Value = new DateTime(1970, 1, 1) + check.Schedule.StartTime;
@@ -136,6 +139,7 @@
                 check.Name = NameTextBox.Text;
                 check.Enabled = EnabledCheckBox.Checked;
                 check.Timeout = (int)TimeoutInput.Value;
+                check.FailStatus = (CheckStatus)SeverityComboBox.SelectedItem;
                 check.Schedule = new Schedule((FrequencyUnits)FrequencyUnitsComboBox.SelectedItem, (int)FrequencyUpDown.Value, StartTimePicker.Value.TimeOfDay, EndTimePicker.Value.TimeOfDay);
                 try
                 {
--- a/ServerMonitor/Forms/ServerForm.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/ServerForm.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -47,6 +47,12 @@
             this.KeyBrowseButton = new System.Windows.Forms.Button();
             this.PasswordTextBox = new System.Windows.Forms.TextBox();
             this.CheckGrid = new System.Windows.Forms.DataGridView();
+            this.StatusColumn = new System.Windows.Forms.DataGridViewImageColumn();
+            this.NameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.ScheduleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.LastRunTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.EnabledColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn();
+            this.CheckBindingSource = new System.Windows.Forms.BindingSource(this.components);
             this.CheckActionsDividerLabel = new System.Windows.Forms.Label();
             this.RunAllButton = new System.Windows.Forms.Button();
             this.RunButton = new System.Windows.Forms.Button();
@@ -63,33 +69,27 @@
             this.LogCheckLabel = new System.Windows.Forms.Label();
             this.LogCheckComboBox = new System.Windows.Forms.ComboBox();
             this.LogGrid = new System.Windows.Forms.DataGridView();
-            this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
-            this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.ServerInfoPanel = new System.Windows.Forms.Panel();
-            this.dataGridViewImageColumn2 = new System.Windows.Forms.DataGridViewImageColumn();
-            this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.LastRunTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.EnabledColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn();
-            this.dataGridViewImageColumn3 = new System.Windows.Forms.DataGridViewImageColumn();
-            this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.StatusColumn = new System.Windows.Forms.DataGridViewImageColumn();
-            this.NameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.ScheduleColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
-            this.CheckBindingSource = new System.Windows.Forms.BindingSource(this.components);
             this.LogStatusColumn = new System.Windows.Forms.DataGridViewImageColumn();
             this.LogNameColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
             this.LogMessageColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
             this.LogStartTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
             this.LogEndTimeColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
             this.CheckResultBindingSource = new System.Windows.Forms.BindingSource(this.components);
+            this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.ServerInfoPanel = new System.Windows.Forms.Panel();
+            this.dataGridViewImageColumn2 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
+            this.dataGridViewImageColumn3 = new System.Windows.Forms.DataGridViewImageColumn();
+            this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn();
             ((System.ComponentModel.ISupportInitialize)(this.CheckGrid)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).BeginInit();
             this.CheckTabControl.SuspendLayout();
             this.CheckTabPage.SuspendLayout();
             this.LogTabPage.SuspendLayout();
             ((System.ComponentModel.ISupportInitialize)(this.LogGrid)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).BeginInit();
             this.ServerInfoPanel.SuspendLayout();
-            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).BeginInit();
-            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).BeginInit();
             this.SuspendLayout();
             // 
             // TitleLabel
@@ -188,7 +188,7 @@
             // 
             // KeyTextBox
             // 
-            this.KeyTextBox.Location = new System.Drawing.Point(143, 82);
+            this.KeyTextBox.Location = new System.Drawing.Point(147, 82);
             this.KeyTextBox.Name = "KeyTextBox";
             this.KeyTextBox.Size = new System.Drawing.Size(202, 20);
             this.KeyTextBox.TabIndex = 13;
@@ -211,6 +211,7 @@
             this.KeyBrowseButton.TabIndex = 15;
             this.KeyBrowseButton.Text = "Browse...";
             this.KeyBrowseButton.UseVisualStyleBackColor = true;
+            this.KeyBrowseButton.Click += new System.EventHandler(this.KeyBrowseButton_Click);
             // 
             // PasswordTextBox
             // 
@@ -257,6 +258,57 @@
             this.CheckGrid.SelectionChanged += new System.EventHandler(this.CheckGrid_SelectionChanged);
             this.CheckGrid.KeyDown += new System.Windows.Forms.KeyEventHandler(this.CheckGrid_KeyDown);
             // 
+            // StatusColumn
+            // 
+            this.StatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.StatusColumn.DataPropertyName = "Status";
+            this.StatusColumn.HeaderText = "";
+            this.StatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.StatusColumn.Name = "StatusColumn";
+            this.StatusColumn.ReadOnly = true;
+            this.StatusColumn.ToolTipText = "Last Run Status";
+            this.StatusColumn.Width = 25;
+            // 
+            // NameColumn
+            // 
+            this.NameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
+            this.NameColumn.DataPropertyName = "Name";
+            this.NameColumn.HeaderText = "Name";
+            this.NameColumn.Name = "NameColumn";
+            this.NameColumn.ReadOnly = true;
+            this.NameColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            this.NameColumn.Width = 41;
+            // 
+            // ScheduleColumn
+            // 
+            this.ScheduleColumn.DataPropertyName = "Schedule";
+            this.ScheduleColumn.HeaderText = "Schedule";
+            this.ScheduleColumn.Name = "ScheduleColumn";
+            this.ScheduleColumn.ReadOnly = true;
+            this.ScheduleColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            // 
+            // LastRunTimeColumn
+            // 
+            this.LastRunTimeColumn.DataPropertyName = "LastRunTime";
+            this.LastRunTimeColumn.HeaderText = "LastRunTime";
+            this.LastRunTimeColumn.Name = "LastRunTimeColumn";
+            this.LastRunTimeColumn.ReadOnly = true;
+            this.LastRunTimeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
+            // 
+            // EnabledColumn
+            // 
+            this.EnabledColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.EnabledColumn.DataPropertyName = "Enabled";
+            this.EnabledColumn.HeaderText = "Enabled";
+            this.EnabledColumn.Name = "EnabledColumn";
+            this.EnabledColumn.ReadOnly = true;
+            this.EnabledColumn.Width = 50;
+            // 
+            // CheckBindingSource
+            // 
+            this.CheckBindingSource.DataSource = typeof(ServerMonitorApp.Check);
+            this.CheckBindingSource.ListChanged += new System.ComponentModel.ListChangedEventHandler(this.CheckBindingSource_ListChanged);
+            // 
             // CheckActionsDividerLabel
             // 
             this.CheckActionsDividerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
@@ -490,6 +542,57 @@
             this.LogGrid.TabIndex = 28;
             this.LogGrid.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.LogGrid_CellFormatting);
             // 
+            // LogStatusColumn
+            // 
+            this.LogStatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
+            this.LogStatusColumn.DataPropertyName = "CheckStatus";
+            this.LogStatusColumn.HeaderText = "";
+            this.LogStatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
+            this.LogStatusColumn.Name = "LogStatusColumn";
+            this.LogStatusColumn.ReadOnly = true;
+            this.LogStatusColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
+            this.LogStatusColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
+            this.LogStatusColumn.ToolTipText = "Status";
+            this.LogStatusColumn.Width = 25;
+            // 
+            // LogNameColumn
+            // 
+            this.LogNameColumn.DataPropertyName = "Check";
+            this.LogNameColumn.HeaderText = "Check";
+            this.LogNameColumn.Name = "LogNameColumn";
+            this.LogNameColumn.ReadOnly = true;
+            // 
+            // LogMessageColumn
+            // 
+            this.LogMessageColumn.DataPropertyName = "Message";
+            this.LogMessageColumn.HeaderText = "Message";
+            this.LogMessageColumn.Name = "LogMessageColumn";
+            this.LogMessageColumn.ReadOnly = true;
+            // 
+            // LogStartTimeColumn
+            // 
+            this.LogStartTimeColumn.DataPropertyName = "StartTime";
+            dataGridViewCellStyle1.Format = "G";
+            dataGridViewCellStyle1.NullValue = null;
+            this.LogStartTimeColumn.DefaultCellStyle = dataGridViewCellStyle1;
+            this.LogStartTimeColumn.HeaderText = "StartTime";
+            this.LogStartTimeColumn.Name = "LogStartTimeColumn";
+            this.LogStartTimeColumn.ReadOnly = true;
+            // 
+            // LogEndTimeColumn
+            // 
+            this.LogEndTimeColumn.DataPropertyName = "EndTime";
+            dataGridViewCellStyle2.Format = "G";
+            dataGridViewCellStyle2.NullValue = null;
+            this.LogEndTimeColumn.DefaultCellStyle = dataGridViewCellStyle2;
+            this.LogEndTimeColumn.HeaderText = "EndTime";
+            this.LogEndTimeColumn.Name = "LogEndTimeColumn";
+            this.LogEndTimeColumn.ReadOnly = true;
+            // 
+            // CheckResultBindingSource
+            // 
+            this.CheckResultBindingSource.DataSource = typeof(ServerMonitorApp.CheckResult);
+            // 
             // dataGridViewImageColumn1
             // 
             this.dataGridViewImageColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
@@ -550,23 +653,6 @@
             this.dataGridViewTextBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
             this.dataGridViewTextBoxColumn2.Width = 267;
             // 
-            // LastRunTimeColumn
-            // 
-            this.LastRunTimeColumn.DataPropertyName = "LastRunTime";
-            this.LastRunTimeColumn.HeaderText = "LastRunTime";
-            this.LastRunTimeColumn.Name = "LastRunTimeColumn";
-            this.LastRunTimeColumn.ReadOnly = true;
-            this.LastRunTimeColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-            // 
-            // EnabledColumn
-            // 
-            this.EnabledColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
-            this.EnabledColumn.DataPropertyName = "Enabled";
-            this.EnabledColumn.HeaderText = "Enabled";
-            this.EnabledColumn.Name = "EnabledColumn";
-            this.EnabledColumn.ReadOnly = true;
-            this.EnabledColumn.Width = 50;
-            // 
             // dataGridViewImageColumn3
             // 
             this.dataGridViewImageColumn3.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
@@ -587,91 +673,6 @@
             this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
             this.dataGridViewTextBoxColumn3.Width = 267;
             // 
-            // StatusColumn
-            // 
-            this.StatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
-            this.StatusColumn.DataPropertyName = "Status";
-            this.StatusColumn.HeaderText = "";
-            this.StatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
-            this.StatusColumn.Name = "StatusColumn";
-            this.StatusColumn.ReadOnly = true;
-            this.StatusColumn.ToolTipText = "Last Run Status";
-            this.StatusColumn.Width = 25;
-            // 
-            // NameColumn
-            // 
-            this.NameColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells;
-            this.NameColumn.DataPropertyName = "Name";
-            this.NameColumn.HeaderText = "Name";
-            this.NameColumn.Name = "NameColumn";
-            this.NameColumn.ReadOnly = true;
-            this.NameColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-            this.NameColumn.Width = 41;
-            // 
-            // ScheduleColumn
-            // 
-            this.ScheduleColumn.DataPropertyName = "Schedule";
-            this.ScheduleColumn.HeaderText = "Schedule";
-            this.ScheduleColumn.Name = "ScheduleColumn";
-            this.ScheduleColumn.ReadOnly = true;
-            this.ScheduleColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable;
-            // 
-            // CheckBindingSource
-            // 
-            this.CheckBindingSource.DataSource = typeof(ServerMonitorApp.Check);
-            this.CheckBindingSource.ListChanged += new System.ComponentModel.ListChangedEventHandler(this.CheckBindingSource_ListChanged);
-            // 
-            // LogStatusColumn
-            // 
-            this.LogStatusColumn.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.None;
-            this.LogStatusColumn.DataPropertyName = "CheckStatus";
-            this.LogStatusColumn.HeaderText = "";
-            this.LogStatusColumn.ImageLayout = System.Windows.Forms.DataGridViewImageCellLayout.Zoom;
-            this.LogStatusColumn.Name = "LogStatusColumn";
-            this.LogStatusColumn.ReadOnly = true;
-            this.LogStatusColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True;
-            this.LogStatusColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
-            this.LogStatusColumn.ToolTipText = "Status";
-            this.LogStatusColumn.Width = 25;
-            // 
-            // LogNameColumn
-            // 
-            this.LogNameColumn.DataPropertyName = "Check";
-            this.LogNameColumn.HeaderText = "Check";
-            this.LogNameColumn.Name = "LogNameColumn";
-            this.LogNameColumn.ReadOnly = true;
-            // 
-            // LogMessageColumn
-            // 
-            this.LogMessageColumn.DataPropertyName = "Message";
-            this.LogMessageColumn.HeaderText = "Message";
-            this.LogMessageColumn.Name = "LogMessageColumn";
-            this.LogMessageColumn.ReadOnly = true;
-            // 
-            // LogStartTimeColumn
-            // 
-            this.LogStartTimeColumn.DataPropertyName = "StartTime";
-            dataGridViewCellStyle1.Format = "G";
-            dataGridViewCellStyle1.NullValue = null;
-            this.LogStartTimeColumn.DefaultCellStyle = dataGridViewCellStyle1;
-            this.LogStartTimeColumn.HeaderText = "StartTime";
-            this.LogStartTimeColumn.Name = "LogStartTimeColumn";
-            this.LogStartTimeColumn.ReadOnly = true;
-            // 
-            // LogEndTimeColumn
-            // 
-            this.LogEndTimeColumn.DataPropertyName = "EndTime";
-            dataGridViewCellStyle2.Format = "G";
-            dataGridViewCellStyle2.NullValue = null;
-            this.LogEndTimeColumn.DefaultCellStyle = dataGridViewCellStyle2;
-            this.LogEndTimeColumn.HeaderText = "EndTime";
-            this.LogEndTimeColumn.Name = "LogEndTimeColumn";
-            this.LogEndTimeColumn.ReadOnly = true;
-            // 
-            // CheckResultBindingSource
-            // 
-            this.CheckResultBindingSource.DataSource = typeof(ServerMonitorApp.CheckResult);
-            // 
             // ServerForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@@ -683,18 +684,19 @@
             this.MinimumSize = new System.Drawing.Size(515, 38);
             this.Name = "ServerForm";
             this.Text = "New Server";
+            this.Activated += new System.EventHandler(this.ServerForm_Activated);
             this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerForm_FormClosing);
             this.Load += new System.EventHandler(this.ServerForm_Load);
             ((System.ComponentModel.ISupportInitialize)(this.CheckGrid)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).EndInit();
             this.CheckTabControl.ResumeLayout(false);
             this.CheckTabPage.ResumeLayout(false);
             this.LogTabPage.ResumeLayout(false);
             this.LogTabPage.PerformLayout();
             ((System.ComponentModel.ISupportInitialize)(this.LogGrid)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).EndInit();
             this.ServerInfoPanel.ResumeLayout(false);
             this.ServerInfoPanel.PerformLayout();
-            ((System.ComponentModel.ISupportInitialize)(this.CheckBindingSource)).EndInit();
-            ((System.ComponentModel.ISupportInitialize)(this.CheckResultBindingSource)).EndInit();
             this.ResumeLayout(false);
 
         }
--- a/ServerMonitor/Forms/ServerForm.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/ServerForm.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -1,4 +1,5 @@
-using System;
+using ServerMonitorApp.Properties;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
@@ -23,9 +24,9 @@
 
         public Server Server { get; private set; }
 
-        private IEnumerable<Check> SelectedChecks { get { return CheckGrid.SelectedRows.Cast<DataGridViewRow>().Select(r => r.DataBoundItem).Cast<Check>(); } }
+        private IEnumerable<Check> SelectedChecks => CheckGrid.SelectedRows.Cast<DataGridViewRow>().Select(r => r.DataBoundItem).Cast<Check>();
 
-        private Check SelectedCheck { get { return SelectedChecks.FirstOrDefault(); } }
+        private Check SelectedCheck => SelectedChecks.FirstOrDefault();
 
         public ServerForm(ServerMonitor monitor, Server server, bool isNewServer = false)
         {
@@ -70,8 +71,17 @@
             UpdateCheckButtons();
         }
 
+        public void Show(bool activate)
+        {
+            if (!activate)
+                WindowState = FormWindowState.Minimized;
+            Show();
+        }
+
         private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e)
         {
+            if (e.Check.Server != Server)
+                return;
             CheckGrid.Refresh();
             if (e.CheckResult != null && logResults != null)
             {
@@ -175,7 +185,7 @@
 
         private void DeleteSelectedChecks()
         {
-            if (Properties.Settings.Default.ConfirmDeleteCheck)
+            if (Settings.Default.ConfirmDeleteCheck)
             {
                 string message = "Delete " + (SelectedChecks.Count() == 1 ? "\"" + SelectedCheck.Name + "\"" : "selected checks") + "?";
                 using (CheckBoxDialog dialog = new CheckBoxDialog { Message = message })
@@ -183,8 +193,8 @@
                     DialogResult result = dialog.ShowDialog();
                     if (dialog.Checked && result == DialogResult.OK)
                     {
-                        Properties.Settings.Default.ConfirmDeleteCheck = false;
-                        Properties.Settings.Default.Save();
+                        Settings.Default.ConfirmDeleteCheck = false;
+                        Settings.Default.Save();
                     }
                     if (result != DialogResult.OK)
                         return;
@@ -199,10 +209,10 @@
             await Task.WhenAll(checks.Select(c => monitor.ExecuteCheckAsync(c)));
         }
 
-        private void ShowLog(Check check)
+        public void ShowLog(Check check)
         {
+            CheckTabControl.SelectedTab = LogTabPage;
             LogCheckComboBox.SelectedItem = check;
-            CheckTabControl.SelectedTab = LogTabPage;
         }
 
         private void UpdateCheckButtons()
@@ -223,7 +233,7 @@
         {
             foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is TextBox))
                 control.TextChanged += (sender, e) => UpdateServer(false);
-            foreach (Control control in Controls.OfType<Control>().Where(c => c is ComboBox))
+            foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is ComboBox))
                 control.TextChanged += (sender, e) => UpdateServer();
             foreach (CheckBox control in LogTabPage.Controls.OfType<CheckBox>())
                 control.CheckedChanged += FilterChanged;
@@ -354,6 +364,21 @@
             }
         }
 
+        private void ServerForm_Activated(object sender, EventArgs e)
+        {
+            Win32Helpers.StopFlashWindowEx(this);
+        }
+
+        private void KeyBrowseButton_Click(object sender, EventArgs e)
+        {
+            OpenFileDialog dialog = new OpenFileDialog() { Title = "Select private key file" };
+            if (dialog.ShowDialog() == DialogResult.OK)
+            {
+                KeyTextBox.Text = dialog.FileName;
+                UpdateServer();
+            }
+        }
+
         private bool MatchesFilter(CheckResult result)
         {
             return filteredStatuses.Contains(result.CheckStatus) && (LogCheckComboBox.SelectedIndex == 0 || LogCheckComboBox.SelectedItem == result.Check);
--- a/ServerMonitor/Forms/ServerSummaryForm.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/ServerSummaryForm.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -28,17 +28,61 @@
         /// </summary>
         private void InitializeComponent()
         {
+            this.components = new System.ComponentModel.Container();
             this.ServerPanel = new System.Windows.Forms.FlowLayoutPanel();
+            this.NotifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
+            this.ServerContextMenu = new System.Windows.Forms.ContextMenuStrip(this.components);
+            this.DeleteServerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.SettingsButton = new System.Windows.Forms.Button();
             this.NewServerButton = new System.Windows.Forms.Button();
+            this.ToggleEnableServerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.ServerContextMenu.SuspendLayout();
             this.SuspendLayout();
             // 
             // ServerPanel
             // 
+            this.ServerPanel.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.ServerPanel.Location = new System.Drawing.Point(12, 12);
             this.ServerPanel.Name = "ServerPanel";
-            this.ServerPanel.Size = new System.Drawing.Size(560, 308);
+            this.ServerPanel.Size = new System.Drawing.Size(767, 391);
             this.ServerPanel.TabIndex = 0;
             // 
+            // NotifyIcon
+            // 
+            this.NotifyIcon.Text = "Server Monitor";
+            this.NotifyIcon.Visible = true;
+            this.NotifyIcon.BalloonTipClicked += new System.EventHandler(this.NotifyIcon_BalloonTipClicked);
+            // 
+            // ServerContextMenu
+            // 
+            this.ServerContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+            this.ToggleEnableServerMenuItem,
+            this.DeleteServerMenuItem});
+            this.ServerContextMenu.Name = "ServerContextMenu";
+            this.ServerContextMenu.Size = new System.Drawing.Size(181, 70);
+            this.ServerContextMenu.Opening += new System.ComponentModel.CancelEventHandler(this.ServerContextMenu_Opening);
+            this.ServerContextMenu.ItemClicked += new System.Windows.Forms.ToolStripItemClickedEventHandler(this.ServerContextMenu_ItemClicked);
+            // 
+            // DeleteServerMenuItem
+            // 
+            this.DeleteServerMenuItem.Name = "DeleteServerMenuItem";
+            this.DeleteServerMenuItem.Size = new System.Drawing.Size(180, 22);
+            this.DeleteServerMenuItem.Text = "Delete server";
+            // 
+            // SettingsButton
+            // 
+            this.SettingsButton.Image = global::ServerMonitorApp.Properties.Resources.settings;
+            this.SettingsButton.Location = new System.Drawing.Point(264, 409);
+            this.SettingsButton.Name = "SettingsButton";
+            this.SettingsButton.Size = new System.Drawing.Size(92, 29);
+            this.SettingsButton.TabIndex = 1;
+            this.SettingsButton.Text = "Settings";
+            this.SettingsButton.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+            this.SettingsButton.UseVisualStyleBackColor = true;
+            this.SettingsButton.Click += new System.EventHandler(this.SettingsButton_Click);
+            // 
             // NewServerButton
             // 
             this.NewServerButton.Image = global::ServerMonitorApp.Properties.Resources.add;
@@ -51,16 +95,25 @@
             this.NewServerButton.UseVisualStyleBackColor = true;
             this.NewServerButton.Click += new System.EventHandler(this.NewServerButton_Click);
             // 
+            // ToggleEnableServerMenuItem
+            // 
+            this.ToggleEnableServerMenuItem.Name = "ToggleEnableServerMenuItem";
+            this.ToggleEnableServerMenuItem.Size = new System.Drawing.Size(180, 22);
+            this.ToggleEnableServerMenuItem.Text = "Disable";
+            // 
             // ServerSummaryForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
             this.ClientSize = new System.Drawing.Size(800, 450);
+            this.Controls.Add(this.SettingsButton);
             this.Controls.Add(this.NewServerButton);
             this.Controls.Add(this.ServerPanel);
             this.Name = "ServerSummaryForm";
             this.Text = "Form1";
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerSummaryForm_FormClosing);
             this.Load += new System.EventHandler(this.ServerSummaryForm_Load);
+            this.ServerContextMenu.ResumeLayout(false);
             this.ResumeLayout(false);
 
         }
@@ -69,6 +122,11 @@
 
         private System.Windows.Forms.FlowLayoutPanel ServerPanel;
         private System.Windows.Forms.Button NewServerButton;
+        private System.Windows.Forms.NotifyIcon NotifyIcon;
+        private System.Windows.Forms.Button SettingsButton;
+        private System.Windows.Forms.ContextMenuStrip ServerContextMenu;
+        private System.Windows.Forms.ToolStripMenuItem DeleteServerMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem ToggleEnableServerMenuItem;
     }
 }
 
--- a/ServerMonitor/Forms/ServerSummaryForm.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/ServerSummaryForm.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -1,4 +1,5 @@
-using System;
+using ServerMonitorApp.Properties;
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
@@ -19,10 +20,29 @@
             InitializeComponent();
         }
 
+        public void AlertServerForm(Check check)
+        {
+            bool existingForm = serverForms.ContainsKey(check.Server);
+            ServerForm form = ShowServerForm(check.Server, false);
+            Win32Helpers.FlashWindowEx(form);
+            if (!existingForm)
+            {
+                form.ShowLog(check);
+            }
+        }
+
+        public void ShowBalloon(CheckResult result)
+        {
+            string title = string.Format("{0}: {1} failed", result.Check.Server.Name, result.Check.Name);
+            NotifyIcon.Tag = result;
+            NotifyIcon.ShowBalloonTip(30000, title, result.Message, GetToolTipIcon(result.CheckStatus));
+        }
+
         private void ServerSummaryForm_Load(object sender, EventArgs e)
         {
             Helpers.FormatImageButton(NewServerButton);
-            monitor = new ServerMonitor();
+            Helpers.FormatImageButton(SettingsButton);
+            monitor = new ServerMonitor(this);
             while (true)
             {
                 try
@@ -41,21 +61,12 @@
                     }
                 }
             }
+            NotifyIcon.Icon = new Icon(Icon, 16, 16);
+            monitor.CheckStatusChanged += Monitor_CheckStatusChanged;
             RefreshDisplay();
         }
 
-        private void RefreshDisplay()
-        {
-            ServerPanel.Controls.Clear();
-            foreach (Server server in monitor.Servers)
-            {
-                ServerSummaryControl control = new ServerSummaryControl(server);
-                control.Click += ServerSummaryControl_Click;
-                ServerPanel.Controls.Add(control);
-            }
-        }
-
-        private void ShowServerForm(Server server)
+        private ServerForm ShowServerForm(Server server, bool activate = true)
         {
             bool isNewServer = false;
             if (server == null)
@@ -66,14 +77,45 @@
             }
             if (serverForms.TryGetValue(server, out ServerForm form))
             {
-                form.Activate();
+                if (activate)
+                    form.Activate();
             }
             else
             {
                 form = new ServerForm(monitor, server, isNewServer);
                 serverForms[server] = form;
                 form.FormClosing += ServerForm_FormClosing;
-                form.Show();
+                form.Show(activate);
+            }
+            return form;
+        }
+
+        private void RefreshDisplay()
+        {
+            ServerPanel.Controls.Clear();
+            foreach (Server server in monitor.Servers)
+            {
+                ServerSummaryControl control = new ServerSummaryControl(server);
+                control.ContextMenuStrip = ServerContextMenu;
+                control.Click += ServerSummaryControl_Click;
+                ServerPanel.Controls.Add(control);
+            }
+        }
+
+        private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e)
+        {
+            if (e.CheckResult != null)
+                ServerPanel.Controls.Cast<ServerSummaryControl>().FirstOrDefault(c => c.Server == e.Check.Server).Refresh();
+        }
+
+        private ToolTipIcon GetToolTipIcon(CheckStatus status)
+        {
+            switch (status)
+            {
+                case CheckStatus.Error: return ToolTipIcon.Error;
+                case CheckStatus.Warning: return ToolTipIcon.Warning;
+                case CheckStatus.Information: return ToolTipIcon.Info;
+                default: return ToolTipIcon.None;
             }
         }
 
@@ -86,7 +128,12 @@
         {
             ServerForm form = (ServerForm)sender;
             form.FormClosing -= ServerForm_FormClosing;
+            Server server = form.Server;
             serverForms.Remove(form.Server);
+            if (server.IsEmpty())
+            {
+                monitor.DeleteServer(server);
+            }
             RefreshDisplay();
         }
 
@@ -94,5 +141,63 @@
         {
             ShowServerForm(null);
         }
+
+        private void ServerSummaryForm_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            if (e.CloseReason == CloseReason.None || e.CloseReason == CloseReason.UserClosing)
+            {
+                Hide();
+                e.Cancel = true;
+            }
+        }
+
+        private void SettingsButton_Click(object sender, EventArgs e)
+        {
+            new SettingsForm().Show();
+        }
+
+        private void NotifyIcon_BalloonTipClicked(object sender, EventArgs e)
+        {
+            CheckResult result = (CheckResult)(sender as NotifyIcon).Tag;
+            ServerForm form = ShowServerForm(result.Check.Server);
+            form.ShowLog(result.Check);
+            form.WindowState = FormWindowState.Normal;
+        }
+
+        private void ServerContextMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
+        {
+            Server server = getClickedServer((ContextMenuStrip)e.ClickedItem.Owner);
+            if (e.ClickedItem == DeleteServerMenuItem)
+            {
+                ServerContextMenu.Close();
+                DialogResult result = MessageBox.Show(
+                    string.Format("The server \"{0}\" and its {1} {2} will be deleted.", server.Name, server.Checks.Count, server.Checks.Count == 1 ? "check" : "checks"),
+                    "Delete server",
+                    MessageBoxButtons.OKCancel,
+                    MessageBoxIcon.Warning  );
+                if (result == DialogResult.OK)
+                {
+                    monitor.DeleteServer(server);
+                    RefreshDisplay();
+                }
+            }
+            else if (e.ClickedItem == ToggleEnableServerMenuItem)
+            {
+                bool enable = ToggleEnableServerMenuItem.Text == "Enable";
+                server.Enabled = enable;
+                RefreshDisplay();
+            }
+        }
+
+        private void ServerContextMenu_Opening(object sender, CancelEventArgs e)
+        {
+            Server server = getClickedServer((ContextMenuStrip)sender);
+            ToggleEnableServerMenuItem.Text = server.Enabled ? "Disable" : "Enable";
+        }
+
+        private Server getClickedServer(ContextMenuStrip menu)
+        {
+            return ((ServerSummaryControl)menu.SourceControl).Server;
+        }
     }
 }
--- a/ServerMonitor/Forms/ServerSummaryForm.resx	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Forms/ServerSummaryForm.resx	Sun Feb 10 20:51:26 2019 -0500
@@ -117,4 +117,10 @@
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
+  <metadata name="NotifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <metadata name="ServerContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>125, 17</value>
+  </metadata>
 </root>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/SettingsForm.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -0,0 +1,225 @@
+namespace ServerMonitorApp
+{
+    partial class SettingsForm
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #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>
+        private void InitializeComponent()
+        {
+            this.SeverityOptionsGroupBox = new System.Windows.Forms.GroupBox();
+            this.ErrorComboBox = new System.Windows.Forms.ComboBox();
+            this.WarningComboBox = new System.Windows.Forms.ComboBox();
+            this.InformationComboBox = new System.Windows.Forms.ComboBox();
+            this.InformationLabel = new System.Windows.Forms.Label();
+            this.InformationIcon = new System.Windows.Forms.PictureBox();
+            this.WarningLabel = new System.Windows.Forms.Label();
+            this.WarningIcon = new System.Windows.Forms.PictureBox();
+            this.ErrorLabel = new System.Windows.Forms.Label();
+            this.ErrorIcon = new System.Windows.Forms.PictureBox();
+            this.label1 = new System.Windows.Forms.Label();
+            this.OkButton = new System.Windows.Forms.Button();
+            this.CancelSettingsButton = new System.Windows.Forms.Button();
+            this.SeverityOptionsGroupBox.SuspendLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.InformationIcon)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.WarningIcon)).BeginInit();
+            ((System.ComponentModel.ISupportInitialize)(this.ErrorIcon)).BeginInit();
+            this.SuspendLayout();
+            // 
+            // SeverityOptionsGroupBox
+            // 
+            this.SeverityOptionsGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.SeverityOptionsGroupBox.Controls.Add(this.ErrorComboBox);
+            this.SeverityOptionsGroupBox.Controls.Add(this.WarningComboBox);
+            this.SeverityOptionsGroupBox.Controls.Add(this.InformationComboBox);
+            this.SeverityOptionsGroupBox.Controls.Add(this.InformationLabel);
+            this.SeverityOptionsGroupBox.Controls.Add(this.InformationIcon);
+            this.SeverityOptionsGroupBox.Controls.Add(this.WarningLabel);
+            this.SeverityOptionsGroupBox.Controls.Add(this.WarningIcon);
+            this.SeverityOptionsGroupBox.Controls.Add(this.ErrorLabel);
+            this.SeverityOptionsGroupBox.Controls.Add(this.ErrorIcon);
+            this.SeverityOptionsGroupBox.Location = new System.Drawing.Point(12, 57);
+            this.SeverityOptionsGroupBox.Name = "SeverityOptionsGroupBox";
+            this.SeverityOptionsGroupBox.Size = new System.Drawing.Size(337, 104);
+            this.SeverityOptionsGroupBox.TabIndex = 0;
+            this.SeverityOptionsGroupBox.TabStop = false;
+            this.SeverityOptionsGroupBox.Text = "Severity settings";
+            // 
+            // ErrorComboBox
+            // 
+            this.ErrorComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.ErrorComboBox.FormattingEnabled = true;
+            this.ErrorComboBox.Location = new System.Drawing.Point(125, 17);
+            this.ErrorComboBox.Name = "ErrorComboBox";
+            this.ErrorComboBox.Size = new System.Drawing.Size(121, 21);
+            this.ErrorComboBox.TabIndex = 8;
+            // 
+            // WarningComboBox
+            // 
+            this.WarningComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.WarningComboBox.FormattingEnabled = true;
+            this.WarningComboBox.Location = new System.Drawing.Point(125, 44);
+            this.WarningComboBox.Name = "WarningComboBox";
+            this.WarningComboBox.Size = new System.Drawing.Size(121, 21);
+            this.WarningComboBox.TabIndex = 7;
+            // 
+            // InformationComboBox
+            // 
+            this.InformationComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.InformationComboBox.FormattingEnabled = true;
+            this.InformationComboBox.Location = new System.Drawing.Point(125, 71);
+            this.InformationComboBox.Name = "InformationComboBox";
+            this.InformationComboBox.Size = new System.Drawing.Size(121, 21);
+            this.InformationComboBox.TabIndex = 6;
+            // 
+            // InformationLabel
+            // 
+            this.InformationLabel.AutoSize = true;
+            this.InformationLabel.Location = new System.Drawing.Point(25, 74);
+            this.InformationLabel.Name = "InformationLabel";
+            this.InformationLabel.Size = new System.Drawing.Size(94, 13);
+            this.InformationLabel.TabIndex = 5;
+            this.InformationLabel.Text = "Information action:";
+            // 
+            // InformationIcon
+            // 
+            this.InformationIcon.Image = global::ServerMonitorApp.Properties.Resources.info;
+            this.InformationIcon.Location = new System.Drawing.Point(6, 73);
+            this.InformationIcon.Name = "InformationIcon";
+            this.InformationIcon.Size = new System.Drawing.Size(16, 16);
+            this.InformationIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.InformationIcon.TabIndex = 4;
+            this.InformationIcon.TabStop = false;
+            // 
+            // WarningLabel
+            // 
+            this.WarningLabel.AutoSize = true;
+            this.WarningLabel.Location = new System.Drawing.Point(25, 47);
+            this.WarningLabel.Name = "WarningLabel";
+            this.WarningLabel.Size = new System.Drawing.Size(82, 13);
+            this.WarningLabel.TabIndex = 3;
+            this.WarningLabel.Text = "Warning action:";
+            // 
+            // WarningIcon
+            // 
+            this.WarningIcon.Image = global::ServerMonitorApp.Properties.Resources.warning;
+            this.WarningIcon.Location = new System.Drawing.Point(6, 46);
+            this.WarningIcon.Name = "WarningIcon";
+            this.WarningIcon.Size = new System.Drawing.Size(16, 16);
+            this.WarningIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.WarningIcon.TabIndex = 2;
+            this.WarningIcon.TabStop = false;
+            // 
+            // ErrorLabel
+            // 
+            this.ErrorLabel.AutoSize = true;
+            this.ErrorLabel.Location = new System.Drawing.Point(25, 20);
+            this.ErrorLabel.Name = "ErrorLabel";
+            this.ErrorLabel.Size = new System.Drawing.Size(64, 13);
+            this.ErrorLabel.TabIndex = 1;
+            this.ErrorLabel.Text = "Error action:";
+            // 
+            // ErrorIcon
+            // 
+            this.ErrorIcon.Image = global::ServerMonitorApp.Properties.Resources.error;
+            this.ErrorIcon.Location = new System.Drawing.Point(6, 19);
+            this.ErrorIcon.Name = "ErrorIcon";
+            this.ErrorIcon.Size = new System.Drawing.Size(16, 16);
+            this.ErrorIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
+            this.ErrorIcon.TabIndex = 0;
+            this.ErrorIcon.TabStop = false;
+            // 
+            // label1
+            // 
+            this.label1.AutoSize = true;
+            this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+            this.label1.Location = new System.Drawing.Point(9, 9);
+            this.label1.Name = "label1";
+            this.label1.Size = new System.Drawing.Size(50, 13);
+            this.label1.TabIndex = 1;
+            this.label1.Text = "Options";
+            // 
+            // OkButton
+            // 
+            this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.OkButton.Location = new System.Drawing.Point(193, 170);
+            this.OkButton.Name = "OkButton";
+            this.OkButton.Size = new System.Drawing.Size(75, 23);
+            this.OkButton.TabIndex = 2;
+            this.OkButton.Text = "OK";
+            this.OkButton.UseVisualStyleBackColor = true;
+            this.OkButton.Click += new System.EventHandler(this.OkButton_Click);
+            // 
+            // CancelSettingsButton
+            // 
+            this.CancelSettingsButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+            this.CancelSettingsButton.Location = new System.Drawing.Point(274, 170);
+            this.CancelSettingsButton.Name = "CancelSettingsButton";
+            this.CancelSettingsButton.Size = new System.Drawing.Size(75, 23);
+            this.CancelSettingsButton.TabIndex = 3;
+            this.CancelSettingsButton.Text = "Cancel";
+            this.CancelSettingsButton.UseVisualStyleBackColor = true;
+            this.CancelSettingsButton.Click += new System.EventHandler(this.CancelSettingsButton_Click);
+            // 
+            // SettingsForm
+            // 
+            this.AcceptButton = this.OkButton;
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(361, 205);
+            this.Controls.Add(this.CancelSettingsButton);
+            this.Controls.Add(this.OkButton);
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.SeverityOptionsGroupBox);
+            this.Name = "SettingsForm";
+            this.Text = "Options";
+            this.Load += new System.EventHandler(this.SettingsForm_Load);
+            this.SeverityOptionsGroupBox.ResumeLayout(false);
+            this.SeverityOptionsGroupBox.PerformLayout();
+            ((System.ComponentModel.ISupportInitialize)(this.InformationIcon)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.WarningIcon)).EndInit();
+            ((System.ComponentModel.ISupportInitialize)(this.ErrorIcon)).EndInit();
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.GroupBox SeverityOptionsGroupBox;
+        private System.Windows.Forms.PictureBox ErrorIcon;
+        private System.Windows.Forms.Label label1;
+        private System.Windows.Forms.Label InformationLabel;
+        private System.Windows.Forms.PictureBox InformationIcon;
+        private System.Windows.Forms.Label WarningLabel;
+        private System.Windows.Forms.PictureBox WarningIcon;
+        private System.Windows.Forms.Label ErrorLabel;
+        private System.Windows.Forms.ComboBox ErrorComboBox;
+        private System.Windows.Forms.ComboBox WarningComboBox;
+        private System.Windows.Forms.ComboBox InformationComboBox;
+        private System.Windows.Forms.Button OkButton;
+        private System.Windows.Forms.Button CancelSettingsButton;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/SettingsForm.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -0,0 +1,53 @@
+using ServerMonitorApp.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    public partial class SettingsForm : Form
+    {
+        public SettingsForm()
+        {
+            InitializeComponent();
+        }
+
+        private void SettingsForm_Load(object sender, EventArgs e)
+        {
+            foreach (ComboBox comboBox in new object[] { ErrorComboBox, WarningComboBox, InformationComboBox })
+            {
+                comboBox.DataSource = Enum.GetValues(typeof(FailAction));
+                comboBox.Format += FailActionComboBox_Format;
+            }
+            ErrorComboBox.SelectedItem = Settings.Default.ErrorAction;
+            WarningComboBox.SelectedItem = Settings.Default.WarningAction;
+            InformationComboBox.SelectedItem = Settings.Default.InformationAction;
+        }
+
+        private void FailActionComboBox_Format(object sender, ListControlConvertEventArgs e)
+        {
+            e.Value = e.Value.ToString().Substring(0, 1) + Regex.Replace(e.Value.ToString(), "(\\B[A-Z])", " $1").ToLower().Substring(1);
+        }
+
+        private void OkButton_Click(object sender, EventArgs e)
+        {
+            Settings.Default.ErrorAction = (FailAction)ErrorComboBox.SelectedItem;
+            Settings.Default.WarningAction = (FailAction)WarningComboBox.SelectedItem;
+            Settings.Default.InformationAction = (FailAction)InformationComboBox.SelectedItem;
+            Settings.Default.Save();
+            Close();
+        }
+
+        private void CancelSettingsButton_Click(object sender, EventArgs e)
+        {
+            Close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Forms/SettingsForm.resx	Sun Feb 10 20:51:26 2019 -0500
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>
\ No newline at end of file
--- a/ServerMonitor/Helpers.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Helpers.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -4,6 +4,7 @@
 using System.ComponentModel;
 using System.Drawing;
 using System.Linq;
+using System.Net.NetworkInformation;
 using System.Text;
 using System.Windows.Forms;
 
@@ -50,6 +51,7 @@
                 case CheckStatus.Information: return Resources.info;
                 case CheckStatus.Success: return Resources.pass;
                 case CheckStatus.Running: return Resources.run;
+                case CheckStatus.Disabled: return Resources.disable;
                 default: return null;
             }
         }
@@ -57,5 +59,40 @@
         public static bool In(this Enum value, params Enum[] values) {
             return values.Contains(value);
         }
+
+        // https://stackoverflow.com/a/8345173
+        /// <summary>
+        /// Indicates whether any network connection is available.
+        /// Filter connections below a specified speed, as well as virtual network cards.
+        /// </summary>
+        /// <returns>
+        ///     <c>true</c> if a network connection is available; otherwise, <c>false</c>.
+        /// </returns>
+        public static bool IsNetworkAvailable()
+        {
+            if (!NetworkInterface.GetIsNetworkAvailable())
+                return false;
+
+            foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
+            {
+                // discard because of standard reasons
+                if ((ni.OperationalStatus != OperationalStatus.Up) ||
+                    (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback) ||
+                    (ni.NetworkInterfaceType == NetworkInterfaceType.Tunnel))
+                    continue;
+
+                // discard virtual cards (virtual box, virtual pc, etc.)
+                if ((ni.Description.IndexOf("virtual", StringComparison.OrdinalIgnoreCase) >= 0) ||
+                    (ni.Name.IndexOf("virtual", StringComparison.OrdinalIgnoreCase) >= 0))
+                    continue;
+
+                // discard "Microsoft Loopback Adapter", it will not show as NetworkInterfaceType.Loopback but as Ethernet Card.
+                if (ni.Description.Equals("Microsoft Loopback Adapter", StringComparison.OrdinalIgnoreCase))
+                    continue;
+
+                return true;
+            }
+            return false;
+        }
     }
 }
--- a/ServerMonitor/Objects/CheckResult.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/CheckResult.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -18,6 +18,24 @@
 
         public DateTime EndTime { get; set; }
 
+        public bool Failed => CheckStatus != CheckStatus.Success;
+
+        public FailAction FailAction
+        {
+            get
+            {
+                switch (CheckStatus)
+                {
+                    case CheckStatus.Error: return Settings.Default.ErrorAction;
+                    case CheckStatus.Warning: return Settings.Default.WarningAction;
+                    case CheckStatus.Information: return Settings.Default.InformationAction;
+                    default: return FailAction.None;
+                }
+            }
+        }
+
+        public bool FlashTaskbar => FailAction == FailAction.FlashTaskbar;
+
         public CheckResult(Check check, CheckStatus status, string message)
         {
             Check = check;
--- a/ServerMonitor/Objects/Checks/Check.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Checks/Check.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -59,6 +59,8 @@
 
         public CheckStatus Status { get; set; }
 
+        public CheckStatus LastRunStatus { get; set; }
+
         public CheckStatus FailStatus { get; set; }
 
         [XmlIgnore]
@@ -90,7 +92,9 @@
 
         public async Task<CheckResult> ExecuteAsync(CancellationToken token = default(CancellationToken), bool update = true)
         {
-            //TODO check cancellation token before proceeding
+            if (token.IsCancellationRequested)
+                return null;
+
             CheckResult result;
             DateTime startTime = DateTime.Now;
             try
@@ -122,6 +126,7 @@
             if (update)
             {
                 Status = result.CheckStatus;
+                LastRunStatus = result.CheckStatus;
                 LastMessage = result.Message;
                 LastRunTime = result.EndTime;
             }
@@ -222,7 +227,7 @@
             {
                 if (result == null)
                     continue;
-                if (result.CheckStatus != CheckStatus.Success)
+                if (result.Failed)
                     failed = true;
                 message.AppendLine(result.Message);
             }
--- a/ServerMonitor/Objects/Checks/DiskSpaceCheck.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Checks/DiskSpaceCheck.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -10,7 +10,7 @@
     [DisplayName("Disk space check"), Description("Check the remaining free disk space"), DisplayWeight(11)]
     public class DiskSpaceCheck : SshCheck
     {
-        public override string Command { get => string.Format(base.Command, Device); }
+        public override string Command => string.Format(base.Command, Device);
 
         public string Device { get; set; }
 
@@ -29,7 +29,7 @@
         protected override List<CheckResult> ProcessCommandResult(string output, int exitCode)
         {
             List<CheckResult> results = base.ProcessCommandResult(output, exitCode);
-            if (results.Any(r => r.CheckStatus != CheckStatus.Success))
+            if (results.Any(r => r.Failed))
                 return results;
 
             List<string> lines = output.Split('\n').ToList();
--- a/ServerMonitor/Objects/Checks/FileCheck.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Checks/FileCheck.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -11,7 +11,7 @@
     [DisplayName("File check"), Description("Check file size or modified time"), DisplayWeight(12)]
     public class FileCheck : SshCheck
     {
-        public override string Command { get => string.Format(base.Command, Regex.Replace(File, "^~", "$HOME")); }
+        public override string Command => string.Format(base.Command, Regex.Replace(File, "^~", "$HOME"));
 
         public string File { get; set; }
 
@@ -19,12 +19,12 @@
 
         public bool FileSizeLessThan { get; set; }
 
-        public double FileSize { get; set; }
+        public int FileSize { get; set; }
 
         public double FileSizeInSelectedUnits
         {
-            get { return FileSize / Math.Pow(1024, (double)FileSizeUnits); }
-            set { FileSize = value * Math.Pow(1024, (double)FileSizeUnits); }
+            get => Math.Round(ConvertBytesToSelectedUnits(FileSize), 1);
+            set => FileSize = ConvertSelectedUnitsToBytes(value);
         }
 
         public SizeUnits FileSizeUnits { get; set; }
@@ -48,7 +48,7 @@
         protected override List<CheckResult> ProcessCommandResult(string output, int exitCode)
         {
             List<CheckResult> results = base.ProcessCommandResult(output, exitCode);
-            if (results.Any(r => r.CheckStatus != CheckStatus.Success))
+            if (results.Any(r => r.Failed))
                 return results;
 
             if (output.Split('\n').Length > 1)
@@ -62,7 +62,7 @@
                 {
                     if (int.TryParse(tokens[4], out int bytes))
                     {
-                        string message = string.Format("File size is {0} {1}", bytes / Math.Pow(1024, (double)FileSizeUnits), FileSizeUnits);
+                        string message = string.Format("File size is {0} {1}", Math.Round(ConvertBytesToSelectedUnits(bytes), 1), FileSizeUnits);
                         if ((bytes < FileSize && FileSizeLessThan) || (bytes > FileSize && !FileSizeLessThan))
                             results.Add(Pass(message));
                         else
@@ -110,5 +110,15 @@
                 message += "Date modified must be greater than 0." + Environment.NewLine;
             return message;
         }
+
+        private double ConvertBytesToSelectedUnits(int sizeInBytes)
+        {
+            return sizeInBytes / Math.Pow(1024, (double)FileSizeUnits);
+        }
+
+        private int ConvertSelectedUnitsToBytes(double sizeInSelectedUnits)
+        {
+            return (int)(sizeInSelectedUnits * Math.Pow(1024, (int)FileSizeUnits));
+        }
     }
 }
\ No newline at end of file
--- a/ServerMonitor/Objects/Checks/SshCheck.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Checks/SshCheck.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -74,7 +74,7 @@
             if (CheckExitCode)
             {
                 CheckResult result = GetIntResult(ExitCode, exitCode, "Exit code");
-                if (result.CheckStatus != CheckStatus.Success)
+                if (result.Failed)
                     result.Message += ". Command output: " + output;
                 results.Add(result);
             }
--- a/ServerMonitor/Objects/Schedule.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Schedule.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -41,7 +41,6 @@
                 // If the last run time was more than a day ago, fast-forward to reduce the number of loops
                 if (nextTime < minStartTime.AddHours(-24))
                     nextTime = minStartTime.Date.Add(StartTime).AddHours(-24);
-                //TODO handle start time and end time
                 while (nextTime < minStartTime)
                 {
                     switch (Units)
--- a/ServerMonitor/Objects/Server.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/Server.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -19,9 +19,11 @@
         private LoginType _loginType;
         private string _keyFile;
         private SshClient _sshClient;
+        private bool _enabled = true;
         private byte[] passwordHash;
 
         public event EventHandler CheckModified;
+        public event EventHandler EnabledChanged;
 
         public readonly BindingList<Check> Checks = new BindingList<Check>();
 
@@ -73,7 +75,17 @@
             }
         }
 
-        public bool Enabled { get; set; } = true;
+        public bool Enabled
+        {
+            get { return _enabled; }
+            set { _enabled = value; EnabledChanged?.Invoke(this, new EventArgs()); }
+        }
+
+        public CheckStatus Status => !Enabled ? CheckStatus.Disabled : Checks
+            .Where(c => c.Enabled)
+            .Select(c => c.LastRunStatus)
+            .DefaultIfEmpty(CheckStatus.Success)
+            .Max();
 
         public SshClient SshClient
         {
@@ -85,7 +97,7 @@
                     if (LoginType == LoginType.Password)
                         auth = new PasswordAuthenticationMethod(Username, Password);
                     else
-                        new PrivateKeyAuthenticationMethod(Username, new PrivateKeyFile(KeyFile));
+                        auth = new PrivateKeyAuthenticationMethod(Username, new PrivateKeyFile(KeyFile));
                     ConnectionInfo info = new ConnectionInfo(Host, Port, Username, auth);
                     _sshClient = new SshClient(info);
                 }
@@ -137,6 +149,13 @@
             CheckModified?.Invoke(check, new EventArgs());
         }
 
+        public bool IsEmpty()
+        {
+            return Name.IsNullOrEmpty()
+                && Host.IsNullOrEmpty()
+                && Checks.Count == 0;
+        }
+
         private void InvalidateSshConnection()
         {
             _sshClient?.Dispose();
--- a/ServerMonitor/Objects/ServerMonitor.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Objects/ServerMonitor.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -1,11 +1,14 @@
-using System;
+using ServerMonitorApp.Properties;
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
+using System.Net.NetworkInformation;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Windows.Forms;
 using System.Xml.Serialization;
 
 namespace ServerMonitorApp
@@ -15,20 +18,24 @@
         private readonly string configFileDir;
         private readonly Logger logger;
         private readonly Dictionary<int, CancellationTokenSource> tokens = new Dictionary<int, CancellationTokenSource>();
-        private bool running;
+        private readonly List<int> pausedChecks = new List<int>();
+        private bool running, networkAvailable;
         private Dictionary<Task<CheckResult>, int> tasks;
+        private ServerSummaryForm mainForm;
+
         //private List<Task<CheckResult>> tasks;
 
         public event EventHandler<CheckStatusChangedEventArgs> CheckStatusChanged;
 
         public List<Server> Servers { get; private set; } = new List<Server>();
 
-        public IEnumerable<Check> Checks { get { return Servers.SelectMany(s => s.Checks); } }
+        public IEnumerable<Check> Checks => Servers.SelectMany(s => s.Checks);
 
         public string ConfigFile { get; private set; }
 
-        public ServerMonitor()
+        public ServerMonitor(ServerSummaryForm mainForm)
         {
+            this.mainForm = mainForm;
             configFileDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ServerMonitor");
             ConfigFile = Path.Combine(configFileDir, "servers.xml");
             logger = new Logger(Path.Combine(configFileDir, "monitor.log"));
@@ -37,6 +44,13 @@
         public void AddServer(Server server)
         {
             Servers.Add(server);
+            SaveServers();
+        }
+
+        public void DeleteServer(Server server)
+        {
+            Servers.Remove(server);
+            SaveServers();
         }
 
         public void LoadServers()
@@ -56,8 +70,11 @@
                     foreach (Check check in server.Checks)
                     {
                         check.Server = server;
+                        if (check.Status == CheckStatus.Running)
+                            check.Status = check.LastRunStatus;
                     }
                     server.CheckModified += Server_CheckModified;
+                    server.EnabledChanged += Server_EnabledChanged;
                 }
             }
             // If the file doesn't exist, no special handling is needed. It will be created later.
@@ -72,6 +89,7 @@
             {
                 reader?.Close();
             }
+            NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
             Run();
         }
 
@@ -109,6 +127,15 @@
             if (running)
                 return;
             running = true;
+            networkAvailable = Helpers.IsNetworkAvailable();
+            if (networkAvailable)
+            {
+                foreach (int id in pausedChecks)
+                {
+                    await ExecuteCheckAsync(Checks.FirstOrDefault(c => c.Id == id));
+                }
+                pausedChecks.Clear();
+            }
             //TODO subscribe to power events. Find any check's NextExecutionTime is in the past. Cancel waiting task and run immediately (or after short delay).
             //tasks = Checks.Select(c => ScheduleExecuteCheckAsync(c)).ToList();
             tasks = Checks.ToDictionary(c => ScheduleExecuteCheckAsync(c), c => c.Id);
@@ -137,8 +164,17 @@
             OnCheckStatusChanged(check);
             CheckResult result = await check.ExecuteAsync(token);
             OnCheckStatusChanged(check, result);
+            HandleResultAsync(result);
+            return result;
+        }
+
+        private void HandleResultAsync(CheckResult result)
+        {
             logger.Log(result);
-            return result;
+            if (result.FailAction == FailAction.FlashTaskbar)
+                mainForm.AlertServerForm(result.Check);
+            if (result.FailAction.In(FailAction.FlashTaskbar, FailAction.NotificationBalloon))
+                mainForm.ShowBalloon(result);
         }
 
         public IList<CheckResult> GetLog(Server server)
@@ -166,9 +202,7 @@
                 else
                 {
                     // Check was modified or deleted, so remove any waiting tasks
-                    tasks.Remove(task);
-                    if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts))
-                        cts.Cancel();
+                    CancelCheck(check);
                     if (check.Server != null)
                     {
                         // If the check was not deleted, schedule the new check.
@@ -183,9 +217,34 @@
             Run();
         }
 
+        private void Server_EnabledChanged(object sender, EventArgs e)
+        {
+            Server server = (Server)sender;
+            if (server.Enabled)
+            {
+                Run();
+            }
+            else
+            {
+                foreach (Check check in server.Checks)
+                {
+                    CancelCheck(check);
+                }
+            }
+        }
+
+        private void CancelCheck(Check check)
+        {
+            Task<CheckResult> task = tasks.FirstOrDefault(kvp => kvp.Value == check.Id).Key;
+            tasks.Remove(task);
+            pausedChecks.RemoveAll(id => id == check.Id);
+            if (tokens.TryGetValue(check.Id, out CancellationTokenSource cts))
+                cts.Cancel();
+        }
+
         private async Task<CheckResult> ScheduleExecuteCheckAsync(Check check)
         {
-            if (!check.Enabled)
+            if (!check.Enabled || !check.Server.Enabled)
                 return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null));
 
             CancellationTokenSource cts = new CancellationTokenSource();
@@ -193,7 +252,21 @@
             check.NextRunTime = check.Schedule.GetNextTime(check.LastScheduledRunTime);
             await Task.Delay(check.NextRunTime - DateTime.Now, cts.Token);
             check.LastScheduledRunTime = check.NextRunTime;
-            return await ExecuteCheckAsync(check, cts.Token);
+            if (networkAvailable)
+                return await ExecuteCheckAsync(check, cts.Token);
+            else
+            {
+                if (!pausedChecks.Contains(check.Id))
+                    pausedChecks.Add(check.Id);
+                return await Task.FromResult(new CheckResult(check, CheckStatus.Disabled, null));
+            }
+        }
+
+        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
+        {
+            networkAvailable = Helpers.IsNetworkAvailable();
+            if (networkAvailable)
+                mainForm.Invoke((MethodInvoker)(() => Run()));
         }
 
         private void GenerateIds()
@@ -208,15 +281,16 @@
                 }
             }
 
-            //TODO if a check is deleted, there might be old results in the log file that share an ID with a new one
             if (Checks.Any())
             {
-                int id = Checks.Max(c => c.Id);
+                int id = Math.Max(Settings.Default.MaxCheckId, Checks.Max(c => c.Id));
                 foreach (Check check in Checks)
                 {
                     if (check.Id == 0)
                         check.Id = ++id;
                 }
+                Settings.Default.MaxCheckId = id;
+                Settings.Default.Save();
             }
         }
 
@@ -237,6 +311,7 @@
             Check = check;
             CheckResult = result;
         }
+    }
 
-    }
+    public enum FailAction { FlashTaskbar = 0, NotificationBalloon = 1, None = 10 }
 }
--- a/ServerMonitor/Properties/Resources.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Properties/Resources.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -83,6 +83,16 @@
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
+        internal static System.Drawing.Bitmap disable {
+            get {
+                object obj = ResourceManager.GetObject("disable", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
         internal static System.Drawing.Bitmap edit {
             get {
                 object obj = ResourceManager.GetObject("edit", resourceCulture);
@@ -163,6 +173,16 @@
         /// <summary>
         ///   Looks up a localized resource of type System.Drawing.Bitmap.
         /// </summary>
+        internal static System.Drawing.Bitmap settings {
+            get {
+                object obj = ResourceManager.GetObject("settings", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized resource of type System.Drawing.Bitmap.
+        /// </summary>
         internal static System.Drawing.Bitmap warning {
             get {
                 object obj = ResourceManager.GetObject("warning", resourceCulture);
--- a/ServerMonitor/Properties/Resources.resx	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Properties/Resources.resx	Sun Feb 10 20:51:26 2019 -0500
@@ -124,6 +124,9 @@
   <data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="disable" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\disable.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
@@ -148,6 +151,9 @@
   <data name="server" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\server.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
+  <data name="settings" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>..\Resources\settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
   <data name="warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>..\Resources\warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
   </data>
--- a/ServerMonitor/Properties/Settings.Designer.cs	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Properties/Settings.Designer.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -12,7 +12,7 @@
     
     
     [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
     internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
         
         private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -34,5 +34,53 @@
                 this["ConfirmDeleteCheck"] = value;
             }
         }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("FlashTaskbar")]
+        public global::ServerMonitorApp.FailAction ErrorAction {
+            get {
+                return ((global::ServerMonitorApp.FailAction)(this["ErrorAction"]));
+            }
+            set {
+                this["ErrorAction"] = value;
+            }
+        }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("NotificationBalloon")]
+        public global::ServerMonitorApp.FailAction WarningAction {
+            get {
+                return ((global::ServerMonitorApp.FailAction)(this["WarningAction"]));
+            }
+            set {
+                this["WarningAction"] = value;
+            }
+        }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("NotificationBalloon")]
+        public global::ServerMonitorApp.FailAction InformationAction {
+            get {
+                return ((global::ServerMonitorApp.FailAction)(this["InformationAction"]));
+            }
+            set {
+                this["InformationAction"] = value;
+            }
+        }
+        
+        [global::System.Configuration.UserScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("0")]
+        public int MaxCheckId {
+            get {
+                return ((int)(this["MaxCheckId"]));
+            }
+            set {
+                this["MaxCheckId"] = value;
+            }
+        }
     }
 }
--- a/ServerMonitor/Properties/Settings.settings	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/Properties/Settings.settings	Sun Feb 10 20:51:26 2019 -0500
@@ -1,9 +1,21 @@
 <?xml version='1.0' encoding='utf-8'?>
-<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ServerMonitor.Properties" GeneratedClassName="Settings">
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ServerMonitorApp.Properties" GeneratedClassName="Settings">
   <Profiles />
   <Settings>
     <Setting Name="ConfirmDeleteCheck" Type="System.Boolean" Scope="User">
       <Value Profile="(Default)">True</Value>
     </Setting>
+    <Setting Name="ErrorAction" Type="ServerMonitorApp.FailAction" Scope="User">
+      <Value Profile="(Default)">FlashTaskbar</Value>
+    </Setting>
+    <Setting Name="WarningAction" Type="ServerMonitorApp.FailAction" Scope="User">
+      <Value Profile="(Default)">NotificationBalloon</Value>
+    </Setting>
+    <Setting Name="InformationAction" Type="ServerMonitorApp.FailAction" Scope="User">
+      <Value Profile="(Default)">NotificationBalloon</Value>
+    </Setting>
+    <Setting Name="MaxCheckId" Type="System.Int32" Scope="User">
+      <Value Profile="(Default)">0</Value>
+    </Setting>
   </Settings>
 </SettingsFile>
\ No newline at end of file
Binary file ServerMonitor/Resources/disable.png has changed
Binary file ServerMonitor/Resources/error.png has changed
Binary file ServerMonitor/Resources/info.png has changed
Binary file ServerMonitor/Resources/pass.png has changed
Binary file ServerMonitor/Resources/settings.png has changed
Binary file ServerMonitor/Resources/warning.png has changed
--- a/ServerMonitor/ServerMonitor.csproj	Fri Jan 11 22:34:18 2019 -0500
+++ b/ServerMonitor/ServerMonitor.csproj	Sun Feb 10 20:51:26 2019 -0500
@@ -95,6 +95,12 @@
     <Compile Include="Controls\MatchComboBox.cs">
       <SubType>Component</SubType>
     </Compile>
+    <Compile Include="Forms\SettingsForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Forms\SettingsForm.Designer.cs">
+      <DependentUpon>SettingsForm.cs</DependentUpon>
+    </Compile>
     <Compile Include="Objects\Checks\FileCheck.cs" />
     <Compile Include="Objects\Checks\DiskSpaceCheck.cs" />
     <Compile Include="Objects\UpdateCheckException.cs" />
@@ -146,6 +152,12 @@
     <Compile Include="Controls\ServerSummaryControl.Designer.cs">
       <DependentUpon>ServerSummaryControl.cs</DependentUpon>
     </Compile>
+    <Compile Include="Properties\Resources.Designer.cs">
+      <DesignTime>True</DesignTime>
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Win32Helpers.cs" />
     <EmbeddedResource Include="Controls\CheckControl.resx">
       <DependentUpon>CheckControl.cs</DependentUpon>
     </EmbeddedResource>
@@ -179,16 +191,14 @@
     <EmbeddedResource Include="Forms\ServerSummaryForm.resx">
       <DependentUpon>ServerSummaryForm.cs</DependentUpon>
     </EmbeddedResource>
+    <EmbeddedResource Include="Forms\SettingsForm.resx">
+      <DependentUpon>SettingsForm.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Properties\Resources.resx">
       <Generator>ResXFileCodeGenerator</Generator>
+      <SubType>Designer</SubType>
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
-      <SubType>Designer</SubType>
     </EmbeddedResource>
-    <Compile Include="Properties\Resources.Designer.cs">
-      <AutoGen>True</AutoGen>
-      <DependentUpon>Resources.resx</DependentUpon>
-      <DesignTime>True</DesignTime>
-    </Compile>
     <None Include="packages.config" />
     <None Include="Properties\DataSources\CheckResult.datasource" />
     <None Include="Properties\Settings.settings">
@@ -243,6 +253,11 @@
   <ItemGroup>
     <None Include="Resources\pass.png" />
   </ItemGroup>
-  <ItemGroup />
+  <ItemGroup>
+    <None Include="Resources\settings.png" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Resources\disable.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ServerMonitor/Win32Helpers.cs	Sun Feb 10 20:51:26 2019 -0500
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace ServerMonitorApp
+{
+    class Win32Helpers
+    {
+        [DllImport("user32.dll")]
+        [return: MarshalAs(UnmanagedType.Bool)]
+        static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct FLASHWINFO
+        {
+            public UInt32 cbSize;
+            public IntPtr hwnd;
+            public UInt32 dwFlags;
+            public UInt32 uCount;
+            public UInt32 dwTimeout;
+        }
+
+        public enum FlashWindowFlags : uint
+        {
+            /// <summary>
+            /// Stop flashing. The system restores the window to its original state.
+            /// </summary>    
+            FLASHW_STOP = 0,
+
+            /// <summary>
+            /// Flash the window caption
+            /// </summary>
+            FLASHW_CAPTION = 1,
+
+            /// <summary>
+            /// Flash the taskbar button.
+            /// </summary>
+            FLASHW_TRAY = 2,
+
+            /// <summary>
+            /// Flash both the window caption and taskbar button.
+            /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
+            /// </summary>
+            FLASHW_ALL = 3,
+
+            /// <summary>
+            /// Flash continuously, until the FLASHW_STOP flag is set.
+            /// </summary>
+            FLASHW_TIMER = 4,
+
+            /// <summary>
+            /// Flash continuously until the window comes to the foreground.
+            /// </summary>
+            FLASHW_TIMERNOFG = 12
+        }
+
+        public static bool FlashWindowEx(Form form)
+        {
+            IntPtr hWnd = form.Handle;
+            FLASHWINFO fInfo = new FLASHWINFO();
+
+            fInfo.cbSize = Convert.ToUInt32(Marshal.SizeOf(fInfo));
+            fInfo.hwnd = hWnd;
+            fInfo.dwFlags = (uint)FlashWindowFlags.FLASHW_TRAY;
+            fInfo.uCount = 10;
+            fInfo.dwTimeout = 0;
+
+            return FlashWindowEx(ref fInfo);
+        }
+
+        public static bool StopFlashWindowEx(Form form)
+        {
+            IntPtr hWnd = form.Handle;
+            FLASHWINFO fInfo = new FLASHWINFO();
+
+            fInfo.cbSize = Convert.ToUInt32(Marshal.SizeOf(fInfo));
+            fInfo.hwnd = hWnd;
+            fInfo.dwFlags = (uint)FlashWindowFlags.FLASHW_STOP;
+            fInfo.dwTimeout = 0;
+
+            return FlashWindowEx(ref fInfo);
+        }
+    }
+}