Mercurial > servermonitor
comparison ServerMonitor/Forms/ServerForm.cs @ 14:2db36ab759de
Add comments.
author | Brad Greco <brad@bgreco.net> |
---|---|
date | Mon, 22 Apr 2019 21:10:42 -0400 |
parents | 052aa62cb42a |
children | 7645122aa7a9 |
comparison
equal
deleted
inserted
replaced
13:a36cc5c123f4 | 14:2db36ab759de |
---|---|
1 using Renci.SshNet; | 1 using ServerMonitorApp.Properties; |
2 using Renci.SshNet.Common; | |
3 using ServerMonitorApp.Properties; | |
4 using System; | 2 using System; |
5 using System.Collections.Generic; | 3 using System.Collections.Generic; |
6 using System.ComponentModel; | 4 using System.ComponentModel; |
7 using System.Data; | 5 using System.Data; |
8 using System.Drawing; | 6 using System.Drawing; |
9 using System.IO; | |
10 using System.Linq; | 7 using System.Linq; |
11 using System.Text; | |
12 using System.Threading.Tasks; | 8 using System.Threading.Tasks; |
13 using System.Windows.Forms; | 9 using System.Windows.Forms; |
14 | 10 |
15 namespace ServerMonitorApp | 11 namespace ServerMonitorApp |
16 { | 12 { |
13 /// <summary>Form for adding or editing a server and managing its checks.</summary> | |
17 public partial class ServerForm : Form | 14 public partial class ServerForm : Form |
18 { | 15 { |
19 private bool isNewServer; | 16 private bool isNewServer; |
20 private bool changedPassword; | 17 private bool changedPassword; |
21 private DateTime lastSaveTime; | 18 private DateTime lastSaveTime; |
23 private BindingList<CheckResult> logResults, logResultsFiltered; | 20 private BindingList<CheckResult> logResults, logResultsFiltered; |
24 private CheckStatus[] filteredStatuses; | 21 private CheckStatus[] filteredStatuses; |
25 private readonly Dictionary<int, CheckForm> checkForms = new Dictionary<int, CheckForm>(); | 22 private readonly Dictionary<int, CheckForm> checkForms = new Dictionary<int, CheckForm>(); |
26 private readonly Dictionary<CheckBox, CheckStatus> filterChecks; | 23 private readonly Dictionary<CheckBox, CheckStatus> filterChecks; |
27 | 24 |
25 /// <summary>The server being edited.</summary> | |
28 public Server Server { get; private set; } | 26 public Server Server { get; private set; } |
29 | 27 |
28 /// <summary>The checks currently selected by the user.</summary> | |
30 private IEnumerable<Check> SelectedChecks => CheckGrid.SelectedRows.Cast<DataGridViewRow>().Select(r => r.DataBoundItem).Cast<Check>(); | 29 private IEnumerable<Check> SelectedChecks => CheckGrid.SelectedRows.Cast<DataGridViewRow>().Select(r => r.DataBoundItem).Cast<Check>(); |
31 | 30 |
31 /// <summary>The first check currently selected by the user.</summary> | |
32 private Check SelectedCheck => SelectedChecks.FirstOrDefault(); | 32 private Check SelectedCheck => SelectedChecks.FirstOrDefault(); |
33 | 33 |
34 /// <summary>ServerForm constructor.</summary> | |
35 /// <param name="monitor">The global server monitor object.</param> | |
36 /// <param name="server">The server to edit.</param> | |
37 /// <param name="isNewServer">Whether an existing server is being edited or a new server is being created.</param> | |
34 public ServerForm(ServerMonitor monitor, Server server, bool isNewServer = false) | 38 public ServerForm(ServerMonitor monitor, Server server, bool isNewServer = false) |
35 { | 39 { |
36 InitializeComponent(); | 40 InitializeComponent(); |
37 this.monitor = monitor; | 41 this.monitor = monitor; |
38 this.isNewServer = isNewServer; | 42 this.isNewServer = isNewServer; |
39 Server = server; | 43 Server = server; |
44 // Associates filter check boxes with their corresponding check statuses. | |
40 filterChecks = new Dictionary<CheckBox, CheckStatus> | 45 filterChecks = new Dictionary<CheckBox, CheckStatus> |
41 { | 46 { |
42 { LogSuccessCheckBox, CheckStatus.Success }, | 47 { LogSuccessCheckBox, CheckStatus.Success }, |
43 { LogInformationCheckBox, CheckStatus.Information }, | 48 { LogInformationCheckBox, CheckStatus.Information }, |
44 { LogWarningCheckBox, CheckStatus.Warning }, | 49 { LogWarningCheckBox, CheckStatus.Warning }, |
46 }; | 51 }; |
47 } | 52 } |
48 | 53 |
49 private void ServerForm_Load(object sender, EventArgs e) | 54 private void ServerForm_Load(object sender, EventArgs e) |
50 { | 55 { |
56 // Bind the Check grid to the server's checks. | |
51 CheckBindingSource.DataSource = Server.Checks; | 57 CheckBindingSource.DataSource = Server.Checks; |
52 monitor.CheckStatusChanged += Monitor_CheckStatusChanged; | 58 monitor.CheckStatusChanged += Monitor_CheckStatusChanged; |
59 // Deselect the default first row selection. | |
53 CheckGrid.ClearSelection(); | 60 CheckGrid.ClearSelection(); |
54 if (isNewServer) | 61 if (isNewServer) |
55 { | 62 { |
63 // Set defaults for a new server. | |
56 LoginComboBox.SelectedIndex = 0; | 64 LoginComboBox.SelectedIndex = 0; |
57 Icon = CheckStatus.Success.GetIcon(); | 65 Icon = CheckStatus.Success.GetIcon(); |
58 } | 66 } |
59 else | 67 else |
60 { | 68 { |
69 // Update inputs with the server information. | |
61 SetTitle(); | 70 SetTitle(); |
62 SetIcon(); | 71 SetIcon(); |
63 NameTextBox.Text = Server.Name; | 72 NameTextBox.Text = Server.Name; |
64 HostTextBox.Text = Server.Host; | 73 HostTextBox.Text = Server.Host; |
65 EnabledCheckBox.Checked = Server.Enabled; | 74 EnabledCheckBox.Checked = Server.Enabled; |
69 LoginComboBox.SelectedIndex = (int)Server.LoginType; | 78 LoginComboBox.SelectedIndex = (int)Server.LoginType; |
70 KeyTextBox.Text = Server.KeyFile; | 79 KeyTextBox.Text = Server.KeyFile; |
71 changedPassword = false; | 80 changedPassword = false; |
72 } | 81 } |
73 | 82 |
83 // After the input controls have been initialized, bind change listeners to them. | |
74 BindChangeListeners(); | 84 BindChangeListeners(); |
85 // Resize the images in buttons to fit the button size. | |
75 FormatImageButtons(); | 86 FormatImageButtons(); |
87 // Set the Run and Edit buttons to their default state. | |
76 UpdateCheckButtons(); | 88 UpdateCheckButtons(); |
77 | 89 |
90 // Focus the name text box if it is empty so the user can start typing immediately. | |
78 if (NameTextBox.Text == string.Empty) | 91 if (NameTextBox.Text == string.Empty) |
79 ActiveControl = NameTextBox; | 92 ActiveControl = NameTextBox; |
80 } | 93 } |
81 | 94 |
95 /// <summary>Shows the form.</summary> | |
96 /// <param name="activate">Whether the form should be activated. Otherwise, it will be shown minimized.</param> | |
82 public void Show(bool activate) | 97 public void Show(bool activate) |
83 { | 98 { |
84 if (!activate) | 99 if (!activate) |
85 WindowState = FormWindowState.Minimized; | 100 WindowState = FormWindowState.Minimized; |
86 Show(); | 101 Show(); |
87 } | 102 } |
88 | 103 |
104 /// <summary>Updates the form when the status of a check changes.</summary> | |
89 private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e) | 105 private void Monitor_CheckStatusChanged(object sender, CheckStatusChangedEventArgs e) |
90 { | 106 { |
107 // Ignore events for checks that belong to other servers. | |
91 if (e.Check.Server != Server) | 108 if (e.Check.Server != Server) |
92 return; | 109 return; |
110 // Refresh the check display with the updated values. | |
93 CheckGrid.Refresh(); | 111 CheckGrid.Refresh(); |
94 SetIcon(); | 112 SetIcon(); |
113 // If there is a result, and the log grid has been initialized, append the log entry. | |
95 if (e.CheckResult != null && logResults != null) | 114 if (e.CheckResult != null && logResults != null) |
96 { | 115 { |
97 logResults.Insert(0, e.CheckResult); | 116 logResults.Insert(0, e.CheckResult); |
117 // If a filter is applied, also append the log to the filtered list if it matches. | |
98 if (logResultsFiltered != null && MatchesFilter(e.CheckResult)) | 118 if (logResultsFiltered != null && MatchesFilter(e.CheckResult)) |
99 logResultsFiltered.Insert(0, e.CheckResult); | 119 logResultsFiltered.Insert(0, e.CheckResult); |
100 } | 120 } |
101 } | 121 } |
102 | 122 |
103 /// <summary>Update the server with the current input values</summary> | 123 /// <summary>Updates the server with the current input values.</summary> |
104 /// <param name="forceSave"> | 124 /// <param name="forceSave"> |
105 /// If true, immediately update the config file on disk. | 125 /// If true, immediately update the config file on disk. |
106 /// If false, only update the config file if it has not been recently updated. | 126 /// If false, only update the config file if it has not been recently updated. |
107 /// </param> | 127 /// </param> |
108 private void UpdateServer(bool forceSave = true) | 128 private void UpdateServer(bool forceSave = true) |
115 Server.LoginType = (LoginType)LoginComboBox.SelectedIndex; | 135 Server.LoginType = (LoginType)LoginComboBox.SelectedIndex; |
116 Server.KeyFile = KeyTextBox.Text.Trim(); | 136 Server.KeyFile = KeyTextBox.Text.Trim(); |
117 if (changedPassword) | 137 if (changedPassword) |
118 Server.Password = PasswordTextBox.Text; | 138 Server.Password = PasswordTextBox.Text; |
119 | 139 |
140 // If a force save is not requested, only save if the last save time is | |
141 // old enough so we don't end up writing out the file on every keystroke. | |
120 if (forceSave || lastSaveTime < DateTime.Now.AddSeconds(-5)) | 142 if (forceSave || lastSaveTime < DateTime.Now.AddSeconds(-5)) |
121 { | 143 { |
122 lastSaveTime = DateTime.Now; | 144 lastSaveTime = DateTime.Now; |
123 monitor.SaveServers(); | 145 monitor.SaveServers(); |
124 } | 146 } |
125 } | 147 } |
126 | 148 |
149 /// <summary>Sets the window title and header based on the server name and state.</summary> | |
127 private void SetTitle(string title = null) | 150 private void SetTitle(string title = null) |
128 { | 151 { |
129 title = (title ?? Server.Name) + (Server.Enabled ? "" : " (disabled)"); | 152 title = (title ?? Server.Name) + (Server.Enabled ? "" : " (disabled)"); |
130 Text = title; | 153 Text = title; |
131 TitleLabel.Text = title; | 154 TitleLabel.Text = title; |
132 } | 155 } |
133 | 156 |
157 /// <summary>Sets the window icon based on the status of the server.</summary> | |
134 private void SetIcon() | 158 private void SetIcon() |
135 { | 159 { |
136 if (Server != null) | 160 if (Server != null) |
137 Icon = Server.Status.GetIcon(); | 161 Icon = Server.Status.GetIcon(); |
138 } | 162 } |
139 | 163 |
164 /// <summary>Updates the window title when the server name changes.</summary> | |
140 private void NameTextBox_TextChanged(object sender, EventArgs e) | 165 private void NameTextBox_TextChanged(object sender, EventArgs e) |
141 { | 166 { |
142 SetTitle(NameTextBox.Text); | 167 SetTitle(NameTextBox.Text); |
143 } | 168 } |
144 | 169 |
170 /// <summary>Shows the password or private key controls when the login type changes.</summary> | |
145 private void LoginComboBox_SelectedIndexChanged(object sender, EventArgs e) | 171 private void LoginComboBox_SelectedIndexChanged(object sender, EventArgs e) |
146 { | 172 { |
147 if (LoginComboBox.SelectedIndex == (int)LoginType.PrivateKey) | 173 if (LoginComboBox.SelectedIndex == (int)LoginType.PrivateKey) |
148 { | 174 { |
175 // Show private key controls. | |
149 PasswordTextBox.Visible = false; | 176 PasswordTextBox.Visible = false; |
150 KeyTextBox.Visible = true; | 177 KeyTextBox.Visible = true; |
151 KeyBrowseButton.Visible = true; | 178 KeyBrowseButton.Visible = true; |
152 } | 179 } |
153 else | 180 else |
154 { | 181 { |
182 // Show password controls. | |
155 PasswordTextBox.Visible = true; | 183 PasswordTextBox.Visible = true; |
156 KeyTextBox.Visible = false; | 184 KeyTextBox.Visible = false; |
157 KeyBrowseButton.Visible = false; | 185 KeyBrowseButton.Visible = false; |
158 } | 186 } |
159 } | 187 } |
160 | 188 |
189 /// <summary>Shows a form to create a new check.</summary> | |
161 private void NewCheckButton_Click(object sender, EventArgs e) | 190 private void NewCheckButton_Click(object sender, EventArgs e) |
162 { | 191 { |
163 ShowCheckForm(null); | 192 ShowCheckForm(null); |
164 } | 193 } |
165 | 194 |
195 /// <summary>Shows a form to edit the selected check.</summary> | |
166 private void EditCheckButton_Click(object sender, EventArgs e) | 196 private void EditCheckButton_Click(object sender, EventArgs e) |
167 { | 197 { |
168 EditSelectedCheck(); | 198 EditSelectedCheck(); |
169 } | 199 } |
170 | 200 |
201 /// <summary>Executes the selected checks.</summary> | |
171 private void RunButton_Click(object sender, EventArgs e) | 202 private void RunButton_Click(object sender, EventArgs e) |
172 { | 203 { |
173 ExecuteChecks(SelectedChecks); | 204 ExecuteChecks(SelectedChecks); |
174 } | 205 } |
175 | 206 |
207 /// <summary>Executes all checks for the server.</summary> | |
176 private void RunAllButton_Click(object sender, EventArgs e) | 208 private void RunAllButton_Click(object sender, EventArgs e) |
177 { | 209 { |
178 ExecuteChecks(Server.Checks); | 210 ExecuteChecks(Server.Checks); |
179 } | 211 } |
180 | 212 |
213 /// <summary>Shows a form to create or edit a check.</summary> | |
214 /// <param name="check">The check to edit, or null to create a new check.</param> | |
181 private void ShowCheckForm(Check check) | 215 private void ShowCheckForm(Check check) |
182 { | 216 { |
183 if (check != null) | 217 if (check != null) |
184 { | 218 { |
219 // Activate the form to edit the check if it is already open. | |
220 // Otherwise, open a new form. | |
185 if (!checkForms.TryGetValue(check.Id, out CheckForm form)) | 221 if (!checkForms.TryGetValue(check.Id, out CheckForm form)) |
186 { | 222 { |
187 form = new CheckForm(monitor, check); | 223 form = new CheckForm(monitor, check); |
188 checkForms[check.Id] = form; | 224 checkForms[check.Id] = form; |
189 form.FormClosing += CheckForm_FormClosing; | 225 form.FormClosing += CheckForm_FormClosing; |
198 { | 234 { |
199 new CheckForm(monitor, Server).Show(); | 235 new CheckForm(monitor, Server).Show(); |
200 } | 236 } |
201 } | 237 } |
202 | 238 |
239 /// <summary>Shows a form to edit the selected check.</summary> | |
203 private void EditSelectedCheck() | 240 private void EditSelectedCheck() |
204 { | 241 { |
205 ShowCheckForm(SelectedCheck); | 242 ShowCheckForm(SelectedCheck); |
206 } | 243 } |
207 | 244 |
245 /// <summary>Deletes the selected checks.</summary> | |
208 private void DeleteSelectedChecks() | 246 private void DeleteSelectedChecks() |
209 { | 247 { |
248 // Prompt to delete unless the "Do not ask again" setting has been set. | |
210 if (Settings.Default.ConfirmDeleteCheck) | 249 if (Settings.Default.ConfirmDeleteCheck) |
211 { | 250 { |
212 string message = "Delete " + (SelectedChecks.Count() == 1 ? "\"" + SelectedCheck.Name + "\"" : "selected checks") + "?"; | 251 string message = "Delete " + (SelectedChecks.Count() == 1 ? "\"" + SelectedCheck.Name + "\"" : "selected checks") + "?"; |
213 using (CheckBoxDialog dialog = new CheckBoxDialog { Message = message }) | 252 using (CheckBoxDialog dialog = new CheckBoxDialog { Message = message }) |
214 { | 253 { |
215 DialogResult result = dialog.ShowDialog(); | 254 DialogResult result = dialog.ShowDialog(); |
255 // Save the "Do not ask again" setting only if OK was clicked. | |
216 if (dialog.Checked && result == DialogResult.OK) | 256 if (dialog.Checked && result == DialogResult.OK) |
217 { | 257 { |
218 Settings.Default.ConfirmDeleteCheck = false; | 258 Settings.Default.ConfirmDeleteCheck = false; |
219 Settings.Default.Save(); | 259 Settings.Default.Save(); |
220 } | 260 } |
261 // Do nothing if Cancel was clicked. | |
221 if (result != DialogResult.OK) | 262 if (result != DialogResult.OK) |
222 return; | 263 return; |
223 } | 264 } |
224 } | 265 } |
266 // If OK was clicked or no confirmation was shown, delete the checks. | |
225 foreach (Check check in SelectedChecks) | 267 foreach (Check check in SelectedChecks) |
226 Server.DeleteCheck(check); | 268 Server.DeleteCheck(check); |
227 } | 269 } |
228 | 270 |
271 /// <summary>Executes the selected checks.</summary> | |
229 private async void ExecuteChecks(IEnumerable<Check> checks) | 272 private async void ExecuteChecks(IEnumerable<Check> checks) |
230 { | 273 { |
231 await Task.WhenAll(checks.Select(c => monitor.ExecuteCheckAsync(c))); | 274 await Task.WhenAll(checks.Select(c => monitor.ExecuteCheckAsync(c))); |
232 } | 275 } |
233 | 276 |
277 /// <summary>Shows the execution history for a check.</summary> | |
278 /// <param name="check">The check to show execution history for.</param> | |
234 public void ShowLog(Check check) | 279 public void ShowLog(Check check) |
235 { | 280 { |
281 // Switch to the Log tab. | |
236 CheckTabControl.SelectedTab = LogTabPage; | 282 CheckTabControl.SelectedTab = LogTabPage; |
283 // Filter the list to only show history for this check. | |
237 LogCheckComboBox.SelectedItem = check; | 284 LogCheckComboBox.SelectedItem = check; |
238 } | 285 } |
239 | 286 |
287 /// <summary>Sets the enabled state of buttons based on whether checks are selected.</summary> | |
240 private void UpdateCheckButtons() | 288 private void UpdateCheckButtons() |
241 { | 289 { |
242 RunAllButton.Enabled = CheckGrid.RowCount > 0; | 290 RunAllButton.Enabled = CheckGrid.RowCount > 0; |
243 RunButton.Enabled = DeleteCheckButton.Enabled = CheckGrid.SelectedRows.Count > 0; | 291 RunButton.Enabled = DeleteCheckButton.Enabled = CheckGrid.SelectedRows.Count > 0; |
244 EditCheckButton.Enabled = CheckGrid.SelectedRows.Count == 1; | 292 EditCheckButton.Enabled = CheckGrid.SelectedRows.Count == 1; |
245 } | 293 } |
246 | 294 |
295 /// <summary>Resizes the images in buttons to fit the button size.</summary> | |
247 private void FormatImageButtons() | 296 private void FormatImageButtons() |
248 { | 297 { |
249 Button[] buttons = new Button[] { NewCheckButton, RunAllButton, RunButton, EditCheckButton, DeleteCheckButton }; | 298 Button[] buttons = new Button[] { NewCheckButton, RunAllButton, RunButton, EditCheckButton, DeleteCheckButton }; |
250 foreach (Button button in buttons) | 299 foreach (Button button in buttons) |
251 Helpers.FormatImageButton(button); | 300 Helpers.FormatImageButton(button); |
252 } | 301 } |
253 | 302 |
303 /// <summary>Binds change listeners to most input controls.</summary> | |
254 private void BindChangeListeners() | 304 private void BindChangeListeners() |
255 { | 305 { |
256 Server.EnabledChanged += Server_EnabledChanged; | 306 Server.EnabledChanged += Server_EnabledChanged; |
307 // Update the server with text box values. | |
257 foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is TextBox)) | 308 foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is TextBox)) |
258 control.TextChanged += (sender, e) => UpdateServer(false); | 309 control.TextChanged += (sender, e) => UpdateServer(false); |
310 // Update the server with combo box values. | |
259 foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is ComboBox)) | 311 foreach (Control control in ServerInfoPanel.Controls.OfType<Control>().Where(c => c is ComboBox)) |
260 control.TextChanged += (sender, e) => UpdateServer(); | 312 control.TextChanged += (sender, e) => UpdateServer(); |
313 // Apply the log filter when the filter checkboxes are changed. | |
261 foreach (CheckBox control in LogTabPage.Controls.OfType<CheckBox>()) | 314 foreach (CheckBox control in LogTabPage.Controls.OfType<CheckBox>()) |
262 control.CheckedChanged += FilterChanged; | 315 control.CheckedChanged += FilterChanged; |
263 } | 316 } |
264 | 317 |
318 /// <summary>Handles the closing of a check form.</summary> | |
265 private void CheckForm_FormClosing(object sender, FormClosingEventArgs e) | 319 private void CheckForm_FormClosing(object sender, FormClosingEventArgs e) |
266 { | 320 { |
321 // Remove the form from the list of open forms. | |
267 CheckForm form = (CheckForm)sender; | 322 CheckForm form = (CheckForm)sender; |
268 form.FormClosing -= CheckForm_FormClosing; | 323 form.FormClosing -= CheckForm_FormClosing; |
269 checkForms.Remove(form.CheckId); | 324 checkForms.Remove(form.CheckId); |
325 // Refresh the check grid if the check was modified. | |
270 if (form.DialogResult == DialogResult.OK) | 326 if (form.DialogResult == DialogResult.OK) |
271 CheckGrid.Refresh(); | 327 CheckGrid.Refresh(); |
272 } | 328 } |
273 | 329 |
330 /// <summary>Updates the state of buttons based on the check selection.</summary> | |
274 private void CheckGrid_SelectionChanged(object sender, EventArgs e) | 331 private void CheckGrid_SelectionChanged(object sender, EventArgs e) |
275 { | 332 { |
276 UpdateCheckButtons(); | 333 UpdateCheckButtons(); |
277 } | 334 } |
278 | 335 |
336 /// <summary>Sets a flag indicating the password text box contains a real password that should be saved.</summary> | |
337 /// <remarks>When the form is loaded, the password text box is populated with literal asterisks, not the saved password.</remarks> | |
279 private void PasswordTextBox_TextChanged(object sender, EventArgs e) | 338 private void PasswordTextBox_TextChanged(object sender, EventArgs e) |
280 { | 339 { |
281 changedPassword = true; | 340 changedPassword = true; |
282 } | 341 } |
283 | 342 |
343 /// <summary>Saves the server when the form is closed.</summary> | |
284 private void ServerForm_FormClosing(object sender, FormClosingEventArgs e) | 344 private void ServerForm_FormClosing(object sender, FormClosingEventArgs e) |
285 { | 345 { |
286 UpdateServer(); | 346 UpdateServer(); |
287 } | 347 } |
288 | 348 |
349 /// <summary>Edits the selected check when it is double clicked.</summary> | |
289 private void CheckGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e) | 350 private void CheckGrid_CellDoubleClick(object sender, DataGridViewCellEventArgs e) |
290 { | 351 { |
291 EditSelectedCheck(); | 352 EditSelectedCheck(); |
292 } | 353 } |
293 | 354 |
355 /// <summary>Edits the selected check when ENTER is pressed.</summary> | |
294 private void CheckGrid_KeyDown(object sender, KeyEventArgs e) | 356 private void CheckGrid_KeyDown(object sender, KeyEventArgs e) |
295 { | 357 { |
296 if (e.KeyCode == Keys.Enter) | 358 if (e.KeyCode == Keys.Enter) |
297 { | 359 { |
298 EditSelectedCheck(); | 360 EditSelectedCheck(); |
299 e.Handled = true; | 361 e.Handled = true; |
300 } | 362 } |
301 } | 363 } |
302 | 364 |
365 /// <summary>Deletes the selected checks.</summary> | |
303 private void DeleteCheckButton_Click(object sender, EventArgs e) | 366 private void DeleteCheckButton_Click(object sender, EventArgs e) |
304 { | 367 { |
305 DeleteSelectedChecks(); | 368 DeleteSelectedChecks(); |
306 UpdateServer(); | 369 UpdateServer(); |
307 } | 370 } |
308 | 371 |
372 /// <summary>Shows an icon next to each check indicating the last execution status.</summary> | |
309 private void CheckGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) | 373 private void CheckGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) |
310 { | 374 { |
311 if (e.ColumnIndex == StatusColumn.Index) | 375 if (e.ColumnIndex == StatusColumn.Index) |
312 { | 376 { |
313 e.Value = ((CheckStatus)e.Value).GetImage(); | 377 e.Value = ((CheckStatus)e.Value).GetImage(); |
314 } | 378 } |
315 } | 379 } |
316 | 380 |
381 /// <summary>Shows an icon next to each log entry indicating its execution status.</summary> | |
317 private void LogGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) | 382 private void LogGrid_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) |
318 { | 383 { |
319 if (e.ColumnIndex == LogStatusColumn.Index) | 384 if (e.ColumnIndex == LogStatusColumn.Index) |
320 { | 385 { |
321 e.Value = ((CheckStatus)e.Value).GetImage(); | 386 e.Value = ((CheckStatus)e.Value).GetImage(); |
322 } | 387 } |
323 } | 388 } |
324 | 389 |
390 /// <summary>Refreshes the check filter combo box when the list of checks changes.</summary> | |
325 private void CheckBindingSource_ListChanged(object sender, ListChangedEventArgs e) | 391 private void CheckBindingSource_ListChanged(object sender, ListChangedEventArgs e) |
326 { | 392 { |
327 if (Server?.Checks != null) | 393 if (Server?.Checks != null) |
328 { | 394 { |
329 LogCheckComboBox.Items.Clear(); | 395 LogCheckComboBox.Items.Clear(); |
331 LogCheckComboBox.Items.AddRange(Server.Checks.ToArray()); | 397 LogCheckComboBox.Items.AddRange(Server.Checks.ToArray()); |
332 LogCheckComboBox.SelectedIndex = 0; | 398 LogCheckComboBox.SelectedIndex = 0; |
333 } | 399 } |
334 } | 400 } |
335 | 401 |
402 /// <summary>Handles showing the check execution log when the Log tab is selected.</summary> | |
336 private void CheckTabControl_SelectedIndexChanged(object sender, EventArgs e) | 403 private void CheckTabControl_SelectedIndexChanged(object sender, EventArgs e) |
337 { | 404 { |
405 // The results grid is not always used, and so is initialized just in time. | |
338 if (logResults == null && CheckTabControl.SelectedTab == LogTabPage) | 406 if (logResults == null && CheckTabControl.SelectedTab == LogTabPage) |
339 { | 407 { |
340 logResults = new BindingList<CheckResult>(monitor.GetLog(Server)); | 408 logResults = new BindingList<CheckResult>(monitor.GetLog(Server)); |
341 LogGrid.DataSource = logResults; | 409 LogGrid.DataSource = logResults; |
342 } | 410 } |
343 } | 411 } |
344 | 412 |
413 /// <summary>Shows a hand cursor over the status column as a hint that it can be clicked to jump to the log.</summary> | |
345 private void CheckGrid_CellMouseEnter(object sender, DataGridViewCellEventArgs e) | 414 private void CheckGrid_CellMouseEnter(object sender, DataGridViewCellEventArgs e) |
346 { | 415 { |
347 if (e.ColumnIndex == StatusColumn.Index) | 416 if (e.ColumnIndex == StatusColumn.Index) |
348 CheckGrid.Cursor = Cursors.Hand; | 417 CheckGrid.Cursor = Cursors.Hand; |
349 } | 418 } |
350 | 419 |
420 /// <summary>Restores the cursor to its default when leaving the status column.</summary> | |
351 private void CheckGrid_CellMouseLeave(object sender, DataGridViewCellEventArgs e) | 421 private void CheckGrid_CellMouseLeave(object sender, DataGridViewCellEventArgs e) |
352 { | 422 { |
353 if (e.ColumnIndex == StatusColumn.Index) | 423 if (e.ColumnIndex == StatusColumn.Index) |
354 CheckGrid.Cursor = Cursors.Default; | 424 CheckGrid.Cursor = Cursors.Default; |
355 } | 425 } |
356 | 426 |
427 /// <summary>Jumps to the check log when the status icon is clicked.</summary> | |
357 private void CheckGrid_CellClick(object sender, DataGridViewCellEventArgs e) | 428 private void CheckGrid_CellClick(object sender, DataGridViewCellEventArgs e) |
358 { | 429 { |
359 if (e.ColumnIndex == StatusColumn.Index) | 430 if (e.ColumnIndex == StatusColumn.Index) |
360 ShowLog((Check)CheckBindingSource[e.RowIndex]); | 431 ShowLog((Check)CheckBindingSource[e.RowIndex]); |
361 } | 432 } |
362 | 433 |
434 /// <summary>Enables or disables a check when the enable column is clicked.</summary> | |
363 private void CheckGrid_CellContentClick(object sender, DataGridViewCellEventArgs e) | 435 private void CheckGrid_CellContentClick(object sender, DataGridViewCellEventArgs e) |
364 { | 436 { |
365 // The status column is set to read-only, and updates are manually done here. | 437 // The status column is set to read-only, and updates are manually done here. |
366 // Otherwise, the change doesn't take effect until the cell loses focus. | 438 // Otherwise, the change doesn't take effect until the cell loses focus. |
367 if (e.ColumnIndex == EnabledColumn.Index) | 439 if (e.ColumnIndex == EnabledColumn.Index) |
368 { | 440 { |
369 Check check = (Check)CheckBindingSource[e.RowIndex]; | 441 Check check = (Check)CheckBindingSource[e.RowIndex]; |
370 check.Enabled = !(bool)CheckGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; ; | 442 check.Enabled = !(bool)CheckGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value; |
371 Server.UpdateCheck(check); | 443 Server.UpdateCheck(check); |
372 } | 444 } |
373 } | 445 } |
374 | 446 |
447 /// <summary>Refreshes the log when a log filter control is changed.</summary> | |
375 private void FilterChanged(object sender, EventArgs e) | 448 private void FilterChanged(object sender, EventArgs e) |
376 { | 449 { |
450 // Determine which check statuses to show. | |
377 filteredStatuses = filterChecks.Where(fc => fc.Key.Checked).Select(fc => fc.Value).ToArray(); | 451 filteredStatuses = filterChecks.Where(fc => fc.Key.Checked).Select(fc => fc.Value).ToArray(); |
378 if (filteredStatuses.Length == filterChecks.Count && LogCheckComboBox.SelectedIndex == 0) { | 452 if (filteredStatuses.Length == filterChecks.Count && LogCheckComboBox.SelectedIndex == 0) { |
453 // If all statuses are shown and no check is selected, show all log entries. | |
379 LogGrid.DataSource = logResults; | 454 LogGrid.DataSource = logResults; |
455 // Unset the filtered list so it can be garbage collected. | |
380 logResultsFiltered = null; | 456 logResultsFiltered = null; |
381 } | 457 } |
382 else | 458 else |
383 { | 459 { |
460 // If any filter is applied, create and display a new list with the filtered log entries. | |
384 logResultsFiltered = new BindingList<CheckResult>(logResults.Where(result => MatchesFilter(result)).ToList()); | 461 logResultsFiltered = new BindingList<CheckResult>(logResults.Where(result => MatchesFilter(result)).ToList()); |
385 LogGrid.DataSource = logResultsFiltered; | 462 LogGrid.DataSource = logResultsFiltered; |
386 } | 463 } |
387 } | 464 } |
388 | 465 |
466 /// <summary>Stops the taskbar button flashing when the window receives focus.</summary> | |
389 private void ServerForm_Activated(object sender, EventArgs e) | 467 private void ServerForm_Activated(object sender, EventArgs e) |
390 { | 468 { |
391 Win32Helpers.StopFlashWindowEx(this); | 469 Win32Helpers.StopFlashWindowEx(this); |
392 } | 470 } |
393 | 471 |
472 /// <summary>Opens a file browser to select a private key file.</summary> | |
394 private void KeyBrowseButton_Click(object sender, EventArgs e) | 473 private void KeyBrowseButton_Click(object sender, EventArgs e) |
395 { | 474 { |
396 OpenFileDialog dialog = new OpenFileDialog() { Title = "Select private key file" }; | 475 OpenFileDialog dialog = new OpenFileDialog() { Title = "Select private key file" }; |
397 if (dialog.ShowDialog() == DialogResult.OK) | 476 if (dialog.ShowDialog() == DialogResult.OK) |
398 { | 477 { |
399 KeyTextBox.Text = dialog.FileName; | 478 KeyTextBox.Text = dialog.FileName; |
400 UpdateServer(); | 479 UpdateServer(); |
401 } | 480 } |
402 } | 481 } |
403 | 482 |
483 /// <summary>Tests whether a log entry matches the active filter.</summary> | |
484 /// <param name="result">The log entry to test agains the active filter.</param> | |
404 private bool MatchesFilter(CheckResult result) | 485 private bool MatchesFilter(CheckResult result) |
405 { | 486 { |
406 return filteredStatuses.Contains(result.CheckStatus) && (LogCheckComboBox.SelectedIndex == 0 || LogCheckComboBox.SelectedItem == result.Check); | 487 return filteredStatuses.Contains(result.CheckStatus) && (LogCheckComboBox.SelectedIndex == 0 || LogCheckComboBox.SelectedItem == result.Check); |
407 } | 488 } |
408 | 489 |
490 /// <summary>Attempts to open the private key when the private key textbox loses focus.</summary> | |
409 private void KeyTextBox_Leave(object sender, EventArgs e) | 491 private void KeyTextBox_Leave(object sender, EventArgs e) |
410 { | 492 { |
411 OpenPrivateKey(monitor, Server, this); | 493 OpenPrivateKey(monitor, Server, this); |
412 } | 494 } |
413 | 495 |
496 /// <summary>Attempts to open the private key, collecting a password if necessary, and displays a message if it cannot be opened.</summary> | |
497 /// <param name="monitor">The global server monitor object.</param> | |
498 /// <param name="server">The server associated with the keyfile to open.</param> | |
499 /// <param name="owner">The window to use as the owner for password and message boxes.</param> | |
414 public static void OpenPrivateKey(ServerMonitor monitor, Server server, IWin32Window owner) | 500 public static void OpenPrivateKey(ServerMonitor monitor, Server server, IWin32Window owner) |
415 { | 501 { |
502 // Nothing to do if the server does not use a private key or one has not been set up yet. | |
416 if (server.LoginType != LoginType.PrivateKey || server.KeyFile.IsNullOrEmpty()) | 503 if (server.LoginType != LoginType.PrivateKey || server.KeyFile.IsNullOrEmpty()) |
417 return; | 504 return; |
418 | 505 |
506 // Attempt to open the keyfile. | |
419 KeyStatus keyStatus = monitor.OpenPrivateKey(server.KeyFile); | 507 KeyStatus keyStatus = monitor.OpenPrivateKey(server.KeyFile); |
508 // If the key is encrypted and has not been opened yet, ask for the password. | |
420 if (keyStatus == KeyStatus.NeedPassword) | 509 if (keyStatus == KeyStatus.NeedPassword) |
421 { | 510 { |
422 string message = "Private key password for " + server.Name + ":"; | 511 string message = "Private key password for " + server.Name + ":"; |
423 Icon icon = SystemIcons.Question; | 512 Icon icon = SystemIcons.Question; |
513 // Attempt to open the keyfile until the correct password is entered or Cancel is clicked. | |
424 while (keyStatus != KeyStatus.Open) | 514 while (keyStatus != KeyStatus.Open) |
425 { | 515 { |
516 // Collect the password. | |
426 string password = InputDialog.ShowDialog(message, icon, owner); | 517 string password = InputDialog.ShowDialog(message, icon, owner); |
518 // Stop asking if Cancel was clicked. | |
427 if (password == null) | 519 if (password == null) |
428 return; | 520 return; |
521 // Try to open the key using the collected password. | |
429 keyStatus = monitor.OpenPrivateKey(server.KeyFile, password); | 522 keyStatus = monitor.OpenPrivateKey(server.KeyFile, password); |
523 // If the password was incorrect, try again with a message saying so. | |
430 if (keyStatus == KeyStatus.NeedPassword) | 524 if (keyStatus == KeyStatus.NeedPassword) |
431 { | 525 { |
432 message = "Incorrect private key password for " + server.Name + ", please try again:"; | 526 message = "Incorrect private key password for " + server.Name + ", please try again:"; |
433 icon = SystemIcons.Error; | 527 icon = SystemIcons.Error; |
434 } | 528 } |
435 } | 529 } |
436 } | 530 } |
437 else if (keyStatus == KeyStatus.NotAccessible) | 531 else if (keyStatus == KeyStatus.NotAccessible) |
438 { | 532 { |
533 // If the private key is not accessible, there is nothing we can do but let the user know. | |
439 MessageBox.Show("The private key file " + server.KeyFile + " is not accessible or does not exist.", "Cannot open private key", MessageBoxButtons.OK, MessageBoxIcon.Error); | 534 MessageBox.Show("The private key file " + server.KeyFile + " is not accessible or does not exist.", "Cannot open private key", MessageBoxButtons.OK, MessageBoxIcon.Error); |
440 return; | 535 } |
441 } | 536 } |
442 } | 537 |
443 | 538 /// <summary>Enables or disables the server when the Enabled box is clicked.</summary> |
444 private void EnabledCheckBox_Click(object sender, EventArgs e) | 539 private void EnabledCheckBox_Click(object sender, EventArgs e) |
445 { | 540 { |
446 bool enabled = EnabledCheckBox.Checked; | 541 bool enabled = EnabledCheckBox.Checked; |
542 // The private key may not be open yet when enabling a server, so do that now. | |
447 if (enabled) | 543 if (enabled) |
448 OpenPrivateKey(monitor, Server, this); | 544 OpenPrivateKey(monitor, Server, this); |
449 Server.Enabled = enabled; | 545 Server.Enabled = enabled; |
450 EnabledCheckBox.Checked = Server.Enabled; | 546 EnabledCheckBox.Checked = Server.Enabled; |
451 } | 547 } |
452 | 548 |
453 //private void EnabledCheckBox_CheckedChanged(object sender, EventArgs e) | 549 /// <summary>Updates the title and enabled check box when the server is enabled or disabled.</summary> |
454 //{ | |
455 // bool enabled = EnabledCheckBox.Checked; | |
456 // if (enabled) | |
457 // OpenPrivateKey(monitor, Server, this); | |
458 // EnabledCheckBox.Checked = Server.Enabled; | |
459 //} | |
460 | |
461 private void Server_EnabledChanged(object sender, EventArgs e) | 550 private void Server_EnabledChanged(object sender, EventArgs e) |
462 { | 551 { |
463 SetTitle(); | 552 SetTitle(); |
553 // The server can also be enabled or disabled from the main server | |
554 // summary form, so update the checkbox when that happens. | |
464 EnabledCheckBox.Checked = Server.Enabled; | 555 EnabledCheckBox.Checked = Server.Enabled; |
465 } | 556 } |
466 } | 557 } |
467 } | 558 } |