comparison ServerMonitor/Forms/ServerSummaryForm.cs @ 16:7626b099aefd

More comments.
author Brad Greco <brad@bgreco.net>
date Tue, 30 Apr 2019 20:40:58 -0400
parents 75ca86e0862c
children b3128fe10d57
comparison
equal deleted inserted replaced
15:23f2e0da1094 16:7626b099aefd
1 using NAppUpdate.Framework; 1 using NAppUpdate.Framework;
2 using NAppUpdate.Framework.Sources; 2 using NAppUpdate.Framework.Sources;
3 using NAppUpdate.Framework.Tasks;
4 using ServerMonitorApp.Properties; 3 using ServerMonitorApp.Properties;
5 using System; 4 using System;
6 using System.Collections.Generic; 5 using System.Collections.Generic;
7 using System.ComponentModel; 6 using System.ComponentModel;
8 using System.Data; 7 using System.Data;
9 using System.Drawing; 8 using System.Drawing;
10 using System.Linq; 9 using System.Linq;
11 using System.Text;
12 using System.Windows.Forms; 10 using System.Windows.Forms;
13 11
14 namespace ServerMonitorApp 12 namespace ServerMonitorApp
15 { 13 {
14 /// <summary>Main application form that shows an overview of all servers.</summary>
16 public partial class ServerSummaryForm : Form 15 public partial class ServerSummaryForm : Form
17 { 16 {
18 private readonly Dictionary<Server, ServerForm> serverForms = new Dictionary<Server, ServerForm>(); 17 private readonly Dictionary<Server, ServerForm> serverForms = new Dictionary<Server, ServerForm>();
19 private ServerMonitor monitor; 18 private ServerMonitor monitor;
20 19
21 public ServerSummaryForm() 20 public ServerSummaryForm()
22 { 21 {
23 InitializeComponent(); 22 InitializeComponent();
24 } 23 }
25 24
26 public void AlertServerForm(Check check)
27 {
28 bool existingForm = serverForms.ContainsKey(check.Server);
29 ServerForm form = ShowServerForm(check.Server, false);
30 Win32Helpers.FlashWindowEx(form);
31 if (!existingForm)
32 {
33 form.ShowLog(check);
34 }
35 }
36
37 public void ShowBalloon(CheckResult result)
38 {
39 string title = string.Format("{0}: {1} failed", result.Check.Server.Name, result.Check.Name);
40 NotifyIcon.Tag = result;
41 NotifyIcon.ShowBalloonTip(30000, title, result.Message, GetToolTipIcon(result.CheckStatus));
42 }
43
44 private void ServerSummaryForm_Load(object sender, EventArgs e) 25 private void ServerSummaryForm_Load(object sender, EventArgs e)
45 { 26 {
27 // Restore the window size from the previous session.
46 Size size = Settings.Default.SummaryFormSize; 28 Size size = Settings.Default.SummaryFormSize;
47 if (size.Height > 0 && size.Width > 0) 29 if (size.Height > 0 && size.Width > 0)
48 Size = size; 30 Size = size;
49 31
32 // Resize the images in buttons to fit the button size.
50 Helpers.FormatImageButton(NewServerButton); 33 Helpers.FormatImageButton(NewServerButton);
51 Helpers.FormatImageButton(SettingsButton); 34 Helpers.FormatImageButton(SettingsButton);
35 // Create the global server monitor object.
52 monitor = new ServerMonitor(this); 36 monitor = new ServerMonitor(this);
37 // Load the server configuration file.
53 while (true) 38 while (true)
54 { 39 {
55 try 40 try
56 { 41 {
42 // If the configuration file is loaded successfully, proceed.
57 monitor.LoadServers(); 43 monitor.LoadServers();
58 break; 44 break;
59 } 45 }
60 catch (Exception ex) 46 catch (Exception ex)
61 { 47 {
48 // If there was an error loading the config file, show it.
62 DialogResult result = MessageBox.Show("Could not load servers. Please fix or delete the file " + monitor.ConfigFile + Environment.NewLine + Environment.NewLine 49 DialogResult result = MessageBox.Show("Could not load servers. Please fix or delete the file " + monitor.ConfigFile + Environment.NewLine + Environment.NewLine
63 + "Error details:" + Environment.NewLine + ex.GetAllMessages(), 50 + "Error details:" + Environment.NewLine + ex.GetAllMessages(),
64 "Error loading servers", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); 51 "Error loading servers", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
52 // If the error message was cancelled, exit. Otherwise, retry by continuing the loop.
65 if (result == DialogResult.Cancel) 53 if (result == DialogResult.Cancel)
66 { 54 {
67 Environment.Exit(1); 55 Environment.Exit(1);
68 } 56 }
69 } 57 }
70 } 58 }
59 // Listen to server monitor events.
71 monitor.CheckStatusChanged += Monitor_CheckStatusChanged; 60 monitor.CheckStatusChanged += Monitor_CheckStatusChanged;
61 // Show the servers.
72 RefreshDisplay(); 62 RefreshDisplay();
63 // If any servers have encrypted private keys, attempt to open them immediately
64 // rather than interrupting the user later when they are first used.
73 CollectPrivateKeyPasswords(); 65 CollectPrivateKeyPasswords();
74 CheckForUpdate(); 66 CheckForUpdate();
75 } 67 }
76 68
69 /// <summary>Shows a form to edit or create a server.</summary>
70 /// <param name="server">The server to edit. If null, a new server will be created.</param>
71 /// <param name="activate">Whether the server form should be activated.</param>
72 /// <returns>The created or existing server for for the server.</returns>
77 private ServerForm ShowServerForm(Server server, bool activate = true) 73 private ServerForm ShowServerForm(Server server, bool activate = true)
78 { 74 {
79 bool isNewServer = false; 75 bool isNewServer = false;
80 if (server == null) 76 if (server == null)
81 { 77 {
78 // Create a new server if none was given.
82 server = new Server(); 79 server = new Server();
80 // The server is added to the server monitor immediately so that
81 // checks can be created and run. If the server was created by
82 // mistake, it will automatically be removed when the form is closed
83 // as long as no information was entered into the form.
83 monitor.AddServer(server); 84 monitor.AddServer(server);
84 isNewServer = true; 85 isNewServer = true;
85 } 86 }
86 if (serverForms.TryGetValue(server, out ServerForm form)) 87 if (serverForms.TryGetValue(server, out ServerForm form))
87 { 88 {
89 // If the server form is already open, just activate it if requested.
88 if (activate) 90 if (activate)
89 form.Activate(); 91 form.Activate();
90 } 92 }
91 else 93 else
92 { 94 {
95 // Open a new server form for the server.
93 form = new ServerForm(monitor, server, isNewServer); 96 form = new ServerForm(monitor, server, isNewServer);
97 // Keep track of the form so it can be activated later if the server is clicked again.
94 serverForms[server] = form; 98 serverForms[server] = form;
95 form.FormClosing += ServerForm_FormClosing; 99 form.FormClosing += ServerForm_FormClosing;
96 form.Show(activate); 100 form.Show(activate);
97 } 101 }
98 return form; 102 return form;
99 } 103 }
100 104
105 /// <summary>Refreshes the server list with the server monitor data.</summary>
101 private void RefreshDisplay() 106 private void RefreshDisplay()
102 { 107 {
108 // Delete all server controls and recreate them.
103 ServerPanel.Controls.Clear(); 109 ServerPanel.Controls.Clear();
104 foreach (Server server in monitor.Servers) 110 foreach (Server server in monitor.Servers)
105 { 111 {
112 // Subscribe to server events.
106 server.EnabledChanged -= Server_EnabledChanged; 113 server.EnabledChanged -= Server_EnabledChanged;
107 server.EnabledChanged += Server_EnabledChanged; 114 server.EnabledChanged += Server_EnabledChanged;
115 // Create a server control and add it to the panel.
108 ServerSummaryControl control = new ServerSummaryControl(server); 116 ServerSummaryControl control = new ServerSummaryControl(server);
109 control.ContextMenuStrip = ServerContextMenu; 117 control.ContextMenuStrip = ServerContextMenu;
110 control.Click += ServerSummaryControl_Click; 118 control.Click += ServerSummaryControl_Click;
111 ServerPanel.Controls.Add(control); 119 ServerPanel.Controls.Add(control);
112 } 120 }
121 // Refresh the form icon that depends on the status of all servers.
113 UpdateIcon(); 122 UpdateIcon();
114 } 123 }
115 124
125 /// <summary>Refreshes a single server control.</summary>
126 /// <param name="server">The server to refresh.</param>
116 private void RefreshServer(Server server) 127 private void RefreshServer(Server server)
117 { 128 {
118 ServerPanel.Controls.Cast<ServerSummaryControl>().FirstOrDefault(c => c.Server == server).Refresh(); 129 ServerPanel.Controls.Cast<ServerSummaryControl>().FirstOrDefault(c => c.Server == server).Refresh();
130 // The server's status might have changed, so refresh the form icon.
119 UpdateIcon(); 131 UpdateIcon();
120 } 132 }
121 133
134 /// <summary>Flashes the taskbar button for a server form.</summary>
135 /// <param name="check">The check that needs attention.</param>
136 public void AlertServerForm(Check check)
137 {
138 // Show the form, but don't activate it since the user did not initiate this event.
139 ServerForm form = ShowServerForm(check.Server, false);
140 // Flash the taskbar button.
141 Win32Helpers.FlashWindowEx(form);
142 // If the form was not already open, focus the Log tab and display
143 // only the log entries for this check. Do not do this if the form
144 // was already open since the user might be in the middle of doing
145 // something with it.
146 if (!serverForms.ContainsKey(check.Server))
147 {
148 form.ShowLog(check);
149 }
150 }
151
152 /// <summary>Shows a balloon popup with the results of a failed check.</summary>
153 /// <param name="check">The check that failed.</param>
154 public void ShowBalloon(CheckResult result)
155 {
156 string title = string.Format("{0}: {1} failed", result.Check.Server.Name, result.Check.Name);
157 NotifyIcon.Tag = result;
158 NotifyIcon.ShowBalloonTip(30000, title, result.Message, GetToolTipIcon(result.CheckStatus));
159 }
160
161 /// <summary>Updates the form icon to reflect a summary of the status of all servers.</summary>
122 private void UpdateIcon() 162 private void UpdateIcon()
123 { 163 {
164 // The status for the summary icon is the most severe status of all servers.
165 // When performing the comparison:
166 // - Enabled servers use their current status.
167 // - If a server is disabled due to a locked private key, report a warning.
168 // Otherwise, report success to effectively ignore it.
169 // The integer value of the CheckStatus enum increases with the severity,
170 // so the maximum value of all servers gives the most severe status.
124 CheckStatus status = monitor.Servers 171 CheckStatus status = monitor.Servers
125 .Select(s => s.Enabled 172 .Select(s => s.Enabled
126 ? s.Status 173 ? s.Status
127 : s.KeyStatus == KeyStatus.NeedPassword ? CheckStatus.Warning : CheckStatus.Success) 174 : s.KeyStatus == KeyStatus.NeedPassword ? CheckStatus.Warning : CheckStatus.Success)
128 .DefaultIfEmpty(CheckStatus.Success) 175 .DefaultIfEmpty(CheckStatus.Success)
129 .Max(); 176 .Max();
130 Icon = status.GetIcon(); 177 Icon = status.GetIcon();
131 NotifyIcon.Icon = Icon; 178 NotifyIcon.Icon = Icon;
132 } 179 }
133 180
181 /// <summary>Prompts the user for the passwords to open all encrypted private keys.</summary>
134 private void CollectPrivateKeyPasswords() 182 private void CollectPrivateKeyPasswords()
135 { 183 {
184 // List of paths to keyfiles.
136 List<string> triedKeys = new List<string>(); 185 List<string> triedKeys = new List<string>();
137 foreach (Server server in monitor.Servers) 186 foreach (Server server in monitor.Servers)
138 { 187 {
188 // If the same private key is used for multiple servers, don't prompt
189 // the user multiple times to open the same keyfile.
139 if (triedKeys.Contains(server.KeyFile)) 190 if (triedKeys.Contains(server.KeyFile))
140 continue; 191 continue;
192 // Show the prompt.
141 ServerForm.OpenPrivateKey(monitor, server, this); 193 ServerForm.OpenPrivateKey(monitor, server, this);
194 // Keep track of the keyfile so we don't needlessly ask again.
142 triedKeys.Add(server.KeyFile); 195 triedKeys.Add(server.KeyFile);
143 } 196 }
144 } 197 }
145 198
199 /// <summary>Refreshes a server control when the server state changes.</summary>
146 private void Server_EnabledChanged(object sender, EventArgs e) 200 private void Server_EnabledChanged(object sender, EventArgs e)
147 { 201 {
148 RefreshServer((Server)sender); 202 RefreshServer((Server)sender);
149 } 203 }
150 204
205 /// <summary>Refreshes a server control when the server status might have changed.</summary>
151 private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e) 206 private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e)
152 { 207 {
153 if (e.CheckResult != null) 208 if (e.CheckResult != null)
154 { 209 {
155 RefreshServer(e.Check.Server); 210 RefreshServer(e.Check.Server);
156 } 211 }
157 } 212 }
158 213
214 /// <summary>Gets a Windows tooltip icon based on the severity of the message.</summary>
215 /// <param name="status">The status of the check that will be reported in the balloon tip.</param>
159 private ToolTipIcon GetToolTipIcon(CheckStatus status) 216 private ToolTipIcon GetToolTipIcon(CheckStatus status)
160 { 217 {
161 switch (status) 218 switch (status)
162 { 219 {
163 case CheckStatus.Error: return ToolTipIcon.Error; 220 case CheckStatus.Error: return ToolTipIcon.Error;
165 case CheckStatus.Information: return ToolTipIcon.Info; 222 case CheckStatus.Information: return ToolTipIcon.Info;
166 default: return ToolTipIcon.None; 223 default: return ToolTipIcon.None;
167 } 224 }
168 } 225 }
169 226
227 /// <summary>Shows a server form when a server control is clicked.</summary>
170 private void ServerSummaryControl_Click(object sender, EventArgs e) 228 private void ServerSummaryControl_Click(object sender, EventArgs e)
171 { 229 {
172 ShowServerForm(((ServerSummaryControl)sender).Server); 230 ShowServerForm(((ServerSummaryControl)sender).Server);
173 } 231 }
174 232
233 /// <summary>Handles the closing of a server form.</summary>
175 private void ServerForm_FormClosing(object sender, FormClosingEventArgs e) 234 private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
176 { 235 {
177 ServerForm form = (ServerForm)sender; 236 ServerForm form = (ServerForm)sender;
178 form.FormClosing -= ServerForm_FormClosing; 237 form.FormClosing -= ServerForm_FormClosing;
179 Server server = form.Server; 238 Server server = form.Server;
239 // Remove the closed form from the list of open forms.
180 serverForms.Remove(form.Server); 240 serverForms.Remove(form.Server);
241 // If there is no user data associated with the server, it can be deleted.
242 // This usually happens when the New Server button is clicked and the server form
243 // is closed without entering any information.
181 if (server.IsEmpty()) 244 if (server.IsEmpty())
182 { 245 {
183 monitor.DeleteServer(server); 246 monitor.DeleteServer(server);
184 } 247 }
185 RefreshDisplay(); 248 RefreshDisplay();
186 } 249 }
187 250
251 /// <summary>Shows a server form to create a new server.</summary>
188 private void NewServerButton_Click(object sender, EventArgs e) 252 private void NewServerButton_Click(object sender, EventArgs e)
189 { 253 {
190 ShowServerForm(null); 254 ShowServerForm(null);
191 } 255 }
192 256
257 /// <summary>Hides the main form instead of exiting the application based on user preferences.</summary>
258 /// <remarks>Allows the monitor to run in the background without being shown in the taskbar.</remarks>
193 private void ServerSummaryForm_FormClosing(object sender, FormClosingEventArgs e) 259 private void ServerSummaryForm_FormClosing(object sender, FormClosingEventArgs e)
194 { 260 {
195 if ((e.CloseReason == CloseReason.None || e.CloseReason == CloseReason.UserClosing) && Settings.Default.HideToNotificationArea) 261 if ((e.CloseReason == CloseReason.None || e.CloseReason == CloseReason.UserClosing) && Settings.Default.HideToNotificationArea)
196 { 262 {
197 Hide(); 263 Hide();
198 e.Cancel = true; 264 e.Cancel = true;
199 } 265 }
200 } 266 }
201 267
268 /// <summary>Shows the settings form.</summary>
202 private void SettingsButton_Click(object sender, EventArgs e) 269 private void SettingsButton_Click(object sender, EventArgs e)
203 { 270 {
204 new SettingsForm().Show(); 271 new SettingsForm().Show();
205 } 272 }
206 273
274 /// <summary>Shows the details of a failed check when the balloon notification is clicked.</summary>
207 private void NotifyIcon_BalloonTipClicked(object sender, EventArgs e) 275 private void NotifyIcon_BalloonTipClicked(object sender, EventArgs e)
208 { 276 {
209 CheckResult result = (CheckResult)(sender as NotifyIcon).Tag; 277 CheckResult result = (CheckResult)(sender as NotifyIcon).Tag;
210 ServerForm form = ShowServerForm(result.Check.Server); 278 ServerForm form = ShowServerForm(result.Check.Server);
211 form.ShowLog(result.Check); 279 form.ShowLog(result.Check);
212 form.WindowState = FormWindowState.Normal; 280 form.WindowState = FormWindowState.Normal;
213 } 281 }
214 282
283 /// <summary>Handles the server context menu.</summary>
215 private void ServerContextMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e) 284 private void ServerContextMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
216 { 285 {
217 Server server = GetClickedServer((ContextMenuStrip)e.ClickedItem.Owner); 286 Server server = GetClickedServer((ContextMenuStrip)e.ClickedItem.Owner);
218 if (e.ClickedItem == DeleteServerMenuItem) 287 if (e.ClickedItem == DeleteServerMenuItem)
219 { 288 {
289 // Close the menu immediately so it doesn't stay open while the messagebox is shown.
220 ServerContextMenu.Close(); 290 ServerContextMenu.Close();
291 // Show the server delete confirmation dialog. No option to not ask again
292 // since it's a rare and very destructive event.
221 DialogResult result = MessageBox.Show( 293 DialogResult result = MessageBox.Show(
222 string.Format("The server \"{0}\" and its {1} {2} will be deleted.", server.Name, server.Checks.Count, server.Checks.Count == 1 ? "check" : "checks"), 294 string.Format("The server \"{0}\" and its {1} {2} will be deleted.", server.Name, server.Checks.Count, server.Checks.Count == 1 ? "check" : "checks"),
223 "Delete server", 295 "Delete server",
224 MessageBoxButtons.OKCancel, 296 MessageBoxButtons.OKCancel,
225 MessageBoxIcon.Warning ); 297 MessageBoxIcon.Warning);
226 if (result == DialogResult.OK) 298 if (result == DialogResult.OK)
227 { 299 {
228 monitor.DeleteServer(server); 300 monitor.DeleteServer(server);
229 RefreshDisplay(); 301 RefreshDisplay();
230 } 302 }
232 else if (e.ClickedItem == ToggleEnableServerMenuItem) 304 else if (e.ClickedItem == ToggleEnableServerMenuItem)
233 { 305 {
234 bool enable = ToggleEnableServerMenuItem.Text == "Enable"; 306 bool enable = ToggleEnableServerMenuItem.Text == "Enable";
235 if (enable) 307 if (enable)
236 { 308 {
309 // Close the menu immediately so it doesn't stay open while the messagebox is shown.
237 ServerContextMenu.Close(); 310 ServerContextMenu.Close();
311 // Attempt to open the private key for the server immediately since it has not
312 // been opened yet.
238 ServerForm.OpenPrivateKey(monitor, server, this); 313 ServerForm.OpenPrivateKey(monitor, server, this);
239 } 314 }
240 server.Enabled = enable; 315 server.Enabled = enable;
241 RefreshDisplay(); 316 RefreshDisplay();
242 } 317 }
243 } 318 }
244 319
320 /// <summary>Activates the appropriate Enable/Disable menu option based on the server's current state.</summary>
245 private void ServerContextMenu_Opening(object sender, CancelEventArgs e) 321 private void ServerContextMenu_Opening(object sender, CancelEventArgs e)
246 { 322 {
247 Server server = GetClickedServer((ContextMenuStrip)sender); 323 Server server = GetClickedServer((ContextMenuStrip)sender);
248 ToggleEnableServerMenuItem.Text = server.Enabled ? "Disable" : "Enable"; 324 ToggleEnableServerMenuItem.Text = server.Enabled ? "Disable" : "Enable";
249 } 325 }
250 326
327 /// <summary>Gets the server corresponding to a server context menu.</summary>
251 private Server GetClickedServer(ContextMenuStrip menu) 328 private Server GetClickedServer(ContextMenuStrip menu)
252 { 329 {
253 return ((ServerSummaryControl)menu.SourceControl).Server; 330 return ((ServerSummaryControl)menu.SourceControl).Server;
254 } 331 }
255 332
333 /// <summary>Saves the window size after it is resized so it can be restored next time the program is run.</summary>
256 private void ServerSummaryForm_ResizeEnd(object sender, EventArgs e) 334 private void ServerSummaryForm_ResizeEnd(object sender, EventArgs e)
257 { 335 {
258 Settings.Default.SummaryFormSize = Size; 336 Settings.Default.SummaryFormSize = Size;
259 Settings.Default.Save(); 337 Settings.Default.Save();
260 } 338 }
261 339
340 /// <summary>Shows the main form when the WM_SHOWMONITOR message is received.</summary>
341 /// <remarks>
342 /// This is used to make this a single-instance application. When a second copy of the program
343 /// is launched, it sends this message to activate the first copy and then exits.
344 /// </remarks>
262 protected override void WndProc(ref Message m) 345 protected override void WndProc(ref Message m)
263 { 346 {
264 if (m.Msg == Win32Helpers.WM_SHOWMONITOR) 347 if (m.Msg == Win32Helpers.WM_SHOWMONITOR)
265 ShowWindow(); 348 ShowWindow();
266 base.WndProc(ref m); 349 base.WndProc(ref m);
267 } 350 }
268 351
352 /// <summary>Handles clicks on the notification area icon.</summary>
269 private void NotifyIcon_MouseClick(object sender, MouseEventArgs e) 353 private void NotifyIcon_MouseClick(object sender, MouseEventArgs e)
270 { 354 {
271 if (e.Button == MouseButtons.Left) 355 if (e.Button == MouseButtons.Left)
272 ShowWindow(); 356 ShowWindow();
273 else if (e.Button == MouseButtons.Right) 357 else if (e.Button == MouseButtons.Right)
274 NotificationIconMenu.Show(); 358 NotificationIconMenu.Show();
275 } 359 }
276 360
361 /// <summary>Shows the window.</summary>
277 private void ShowWindow() 362 private void ShowWindow()
278 { 363 {
279 if (WindowState == FormWindowState.Minimized) 364 if (WindowState == FormWindowState.Minimized)
280 WindowState = FormWindowState.Normal; 365 WindowState = FormWindowState.Normal;
366 // Do various things to try to get this window on top.
367 // We only do this as a result of user input, so it's ok. ;)
281 Show(); 368 Show();
282 TopMost = true; 369 TopMost = true;
283 TopMost = false; 370 TopMost = false;
284 Activate(); 371 Activate();
285 } 372 }
286 373
374 /// <summary>Handles clicks on the notification icon menu.</summary>
287 private void NotificationIconMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e) 375 private void NotificationIconMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
288 { 376 {
289 if (e.ClickedItem == ShowServerMonitorMenuItem) 377 if (e.ClickedItem == ShowServerMonitorMenuItem)
290 ShowWindow(); 378 ShowWindow();
291 else if (e.ClickedItem == ExitMenuItem) 379 else if (e.ClickedItem == ExitMenuItem)
292 Application.Exit(); 380 Application.Exit();
293 } 381 }
294 382
383 /// <summary>Begins checking for program updates in the background.</summary>
295 private void CheckForUpdate() 384 private void CheckForUpdate()
296 { 385 {
297 //System.Threading.Thread.Sleep(5000);
298 UpdateManager manager = UpdateManager.Instance; 386 UpdateManager manager = UpdateManager.Instance;
387 // Make the update manager happy if the program was just restarted to apply an update.
299 manager.ReinstateIfRestarted(); 388 manager.ReinstateIfRestarted();
300 manager.UpdateSource = new SimpleWebSource("https://www.bgreco.net/test/servermonitor.xml"); 389 manager.UpdateSource = new SimpleWebSource("https://www.bgreco.net/test/servermonitor.xml");
301 if (manager.State == UpdateManager.UpdateProcessState.NotChecked) 390 if (manager.State == UpdateManager.UpdateProcessState.NotChecked)
302 manager.BeginCheckForUpdates(CheckForUpdatesCallback, null); 391 manager.BeginCheckForUpdates(CheckForUpdatesCallback, null);
303 } 392 }
304 393
394 /// <summary>Callback after the program update check completes.</summary>
305 private void CheckForUpdatesCallback(IAsyncResult result) 395 private void CheckForUpdatesCallback(IAsyncResult result)
306 { 396 {
307 UpdateManager manager = UpdateManager.Instance; 397 UpdateManager manager = UpdateManager.Instance;
308 if (manager.UpdatesAvailable > 0) 398 if (manager.UpdatesAvailable > 0)
309 { 399 {
400 // Extract the new version number from the result.
310 GetUpdateInfo(out string version, out string _); 401 GetUpdateInfo(out string version, out string _);
402 // If the user has not chosen to ignore this update, show a notification.
311 if (Settings.Default.IgnoreUpdate != version) 403 if (Settings.Default.IgnoreUpdate != version)
312 Invoke((MethodInvoker)(() => UpdatePanel.Show())); 404 Invoke((MethodInvoker)(() => UpdatePanel.Show()));
313 } 405 }
314 } 406 }
315 407
408 /// <summary>Applies the program updates.</summary>
316 private void PrepareUpdatesCallback(IAsyncResult result) 409 private void PrepareUpdatesCallback(IAsyncResult result)
317 { 410 {
318 UpdateManager manager = UpdateManager.Instance; 411 UpdateManager manager = UpdateManager.Instance;
319 manager.EndCheckForUpdates(result); 412 manager.EndCheckForUpdates(result);
320 manager.ApplyUpdates(true); 413 manager.ApplyUpdates(true);
321 } 414 }
322 415
416 /// <summary>Shows information about a program update when the notification is clicked.</summary>
323 private void UpdateLabel_Click(object sender, EventArgs e) 417 private void UpdateLabel_Click(object sender, EventArgs e)
324 { 418 {
419 // Extract the update information from the update manager result.
325 GetUpdateInfo(out string version, out string changeMessage); 420 GetUpdateInfo(out string version, out string changeMessage);
326 string message = "Server Monitor version {0} is available for download." + Environment.NewLine 421 string message = "Server Monitor version {0} is available for download." + Environment.NewLine
327 + Environment.NewLine 422 + Environment.NewLine
328 + "What's new:" + Environment.NewLine 423 + "What's new:" + Environment.NewLine
329 + "{1}" + Environment.NewLine 424 + "{1}" + Environment.NewLine
330 + Environment.NewLine 425 + Environment.NewLine
331 + "Would you like to download and apply the update now?"; 426 + "Would you like to download and apply the update now?";
332 using (UpdateDialog dialog = new UpdateDialog { Message = string.Format(message, version, changeMessage) }) 427 using (UpdateDialog dialog = new UpdateDialog { Message = string.Format(message, version, changeMessage) })
333 { 428 {
334 DialogResult result = dialog.ShowDialog(); 429 DialogResult result = dialog.ShowDialog();
430 // If the user declined the update and asked not to be notified again,
431 // save the preference so they will not be asked again for this version.
335 if (dialog.Checked && result == DialogResult.Cancel) 432 if (dialog.Checked && result == DialogResult.Cancel)
336 { 433 {
337 Settings.Default.IgnoreUpdate = version; 434 Settings.Default.IgnoreUpdate = version;
338 Settings.Default.Save(); 435 Settings.Default.Save();
339 UpdatePanel.Hide(); 436 UpdatePanel.Hide();
340 } 437 }
438 // If Yes was not chosen, do not apply the update.
341 if (result != DialogResult.OK) 439 if (result != DialogResult.OK)
342 return; 440 return;
343 } 441 }
344 UpdateManager.Instance.BeginPrepareUpdates(PrepareUpdatesCallback, null); 442 UpdateManager.Instance.BeginPrepareUpdates(PrepareUpdatesCallback, null);
345 } 443 }
346 444
445 /// <summary>Extracts the update information from the update manager result.</summary>
347 private void GetUpdateInfo(out string version, out string changeMessage) 446 private void GetUpdateInfo(out string version, out string changeMessage)
348 { 447 {
448 // The update description is in the form {version}:{change message}.
349 string[] parts = UpdateManager.Instance.Tasks.First().Description.Split(new char[] { ':' }, 2); 449 string[] parts = UpdateManager.Instance.Tasks.First().Description.Split(new char[] { ':' }, 2);
350 version = parts[0]; 450 version = parts[0];
351 changeMessage = parts[1]; 451 changeMessage = parts[1];
352 } 452 }
353 } 453 }