Horizon – Diff between revs 24 and 27

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 24 Rev 27
1 using System; 1 using System;
2 using System.Collections.Concurrent; 2 using System.Collections.Concurrent;
3 using System.Collections.Generic; 3 using System.Collections.Generic;
4 using System.ComponentModel; 4 using System.ComponentModel;
5 using System.Data.SQLite; 5 using System.Data.SQLite;
6 using System.Diagnostics; 6 using System.Diagnostics;
7 using System.Drawing; 7 using System.Drawing;
8 using System.IO; 8 using System.IO;
9 using System.IO.Ports; 9 using System.IO.Ports;
10 using System.Linq; 10 using System.Linq;
11 using System.Net.Sockets; 11 using System.Net.Sockets;
12 using System.Security.Cryptography; 12 using System.Security.Cryptography;
13 using System.Text; 13 using System.Text;
14 using System.Threading; 14 using System.Threading;
15 using System.Threading.Tasks; 15 using System.Threading.Tasks;
16 using System.Windows.Forms; 16 using System.Windows.Forms;
17 using Horizon.Database; 17 using Horizon.Database;
18 using Horizon.Utilities; 18 using Horizon.Utilities;
19 using Microsoft.WindowsAPICodePack.Dialogs; 19 using Microsoft.WindowsAPICodePack.Dialogs;
20 using Mono.Zeroconf; 20 using Mono.Zeroconf;
21 using Mono.Zeroconf.Providers.Bonjour; 21 using Mono.Zeroconf.Providers.Bonjour;
22 using Newtonsoft.Json; 22 using Newtonsoft.Json;
23 using Org.BouncyCastle.Crypto; 23 using Org.BouncyCastle.Crypto;
24 using Org.BouncyCastle.Utilities.Net; 24 using Org.BouncyCastle.Utilities.Net;
25 using Serilog; 25 using Serilog;
26 using WatsonTcp; 26 using WatsonTcp;
27   27  
28 namespace Horizon.Snapshots 28 namespace Horizon.Snapshots
29 { 29 {
30 public partial class SnapshotManagerForm : Form 30 public partial class SnapshotManagerForm : Form
31 { 31 {
32 #region Static Fields and Constants 32 #region Static Fields and Constants
33   33  
34 private static ScheduledContinuation _searchTextBoxChangedContinuation; 34 private static ScheduledContinuation _searchTextBoxChangedContinuation;
35   35  
36 #endregion 36 #endregion
37   -  
38 #region Public Events & Delegates -  
39   -  
40 public event EventHandler<PreviewRetrievedEventArgs> PreviewRetrieved; -  
41   -  
42 #endregion -  
43   37  
44 #region Private Delegates, Events, Enums, Properties, Indexers and Fields 38 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
45   39  
46 private readonly MainForm _mainForm; 40 private readonly MainForm _mainForm;
47   41  
48 private readonly SnapshotDatabase _snapshotDatabase; 42 private readonly SnapshotDatabase _snapshotDatabase;
49   43  
50 private HexViewForm _hexViewForm; 44 private HexViewForm _hexViewForm;
51   45  
52 private SnapshotNoteForm _snapshotNote; 46 private SnapshotNoteForm _snapshotNoteForm;
53   47  
54 private SnapshotPreviewForm _snapshotPreviewForm; 48 private SnapshotPreviewForm _snapshotPreviewForm;
55   49  
56 private readonly object _mouseMoveLock = new object(); 50 private readonly object _mouseMoveLock = new object();
57   51  
58 private readonly CancellationTokenSource _cancellationTokenSource; 52 private readonly CancellationTokenSource _cancellationTokenSource;
59   53  
60 private readonly CancellationToken _cancellationToken; 54 private readonly CancellationToken _cancellationToken;
61   55  
62 private readonly CancellationTokenSource _localCancellationTokenSource; 56 private readonly CancellationTokenSource _localCancellationTokenSource;
63   57  
64 private readonly CancellationToken _localCancellationToken; 58 private readonly CancellationToken _localCancellationToken;
65   59  
66 private readonly Mono.Zeroconf.ServiceBrowser _horizonServiceBrowser; 60 private readonly Mono.Zeroconf.ServiceBrowser _horizonServiceBrowser;
67   61  
68 private IResolvableService _resolvedService; 62 private IResolvableService _resolvedService;
69   63  
70 private readonly ConcurrentDictionary<string, Service> _discoveredHorizonNetworkShares; 64 private readonly ConcurrentDictionary<string, Service> _discoveredHorizonNetworkShares;
71   65  
72 #endregion 66 #endregion
73   67  
74 #region Constructors, Destructors and Finalizers 68 #region Constructors, Destructors and Finalizers
75   69  
76 private SnapshotManagerForm() 70 private SnapshotManagerForm()
77 { 71 {
78 InitializeComponent(); 72 InitializeComponent();
79   73  
80 dataGridView1.Columns["TimeColumn"].ValueType = typeof(DateTime); 74 dataGridView1.Columns["TimeColumn"].ValueType = typeof(DateTime);
81   75  
82 _searchTextBoxChangedContinuation = new ScheduledContinuation(); 76 _searchTextBoxChangedContinuation = new ScheduledContinuation();
83   77  
84 _localCancellationTokenSource = new CancellationTokenSource(); 78 _localCancellationTokenSource = new CancellationTokenSource();
85 _localCancellationToken = _localCancellationTokenSource.Token; 79 _localCancellationToken = _localCancellationTokenSource.Token;
86   80  
87 _discoveredHorizonNetworkShares = new ConcurrentDictionary<string, Service>(); 81 _discoveredHorizonNetworkShares = new ConcurrentDictionary<string, Service>();
88 _horizonServiceBrowser = new Mono.Zeroconf.ServiceBrowser(); 82 _horizonServiceBrowser = new Mono.Zeroconf.ServiceBrowser();
89 _horizonServiceBrowser.ServiceAdded += OnHorizonServiceBrowserOnServiceAdded; 83 _horizonServiceBrowser.ServiceAdded += OnHorizonServiceBrowserOnServiceAdded;
90 } 84 }
91   85  
92 public SnapshotManagerForm(MainForm mainForm, SnapshotDatabase snapshotDatabase, 86 public SnapshotManagerForm(MainForm mainForm, SnapshotDatabase snapshotDatabase,
93 CancellationToken cancellationToken) : this() 87 CancellationToken cancellationToken) : this()
94 { 88 {
95 _mainForm = mainForm; 89 _mainForm = mainForm;
96 _snapshotDatabase = snapshotDatabase; 90 _snapshotDatabase = snapshotDatabase;
97 _snapshotDatabase.SnapshotCreate += SnapshotManager_SnapshotCreate; 91 _snapshotDatabase.SnapshotCreate += SnapshotManager_SnapshotCreate;
98 _snapshotDatabase.SnapshotTransferReceived += _snapshotDatabase_SnapshotTransferReceived; 92 _snapshotDatabase.SnapshotTransferReceived += _snapshotDatabase_SnapshotTransferReceived;
99   93  
100 _cancellationTokenSource = 94 _cancellationTokenSource =
101 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, cancellationToken); 95 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, cancellationToken);
102 _cancellationToken = _cancellationTokenSource.Token; 96 _cancellationToken = _cancellationTokenSource.Token;
103 } 97 }
104   98  
105 /// <summary> 99 /// <summary>
106 /// Clean up any resources being used. 100 /// Clean up any resources being used.
107 /// </summary> 101 /// </summary>
108 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 102 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
109 protected override void Dispose(bool disposing) 103 protected override void Dispose(bool disposing)
110 { 104 {
111 if (disposing && components != null) 105 if (disposing && components != null)
112 { 106 {
113 components.Dispose(); 107 components.Dispose();
114 } 108 }
115   109  
116 _snapshotDatabase.SnapshotCreate -= SnapshotManager_SnapshotCreate; 110 _snapshotDatabase.SnapshotCreate -= SnapshotManager_SnapshotCreate;
117 _snapshotDatabase.SnapshotTransferReceived -= _snapshotDatabase_SnapshotTransferReceived; 111 _snapshotDatabase.SnapshotTransferReceived -= _snapshotDatabase_SnapshotTransferReceived;
118   112  
119 _horizonServiceBrowser.ServiceAdded -= OnHorizonServiceBrowserOnServiceAdded; 113 _horizonServiceBrowser.ServiceAdded -= OnHorizonServiceBrowserOnServiceAdded;
120 _horizonServiceBrowser.Dispose(); 114 _horizonServiceBrowser.Dispose();
121 _localCancellationTokenSource.Cancel(); 115 _localCancellationTokenSource.Cancel();
122   116  
123 base.Dispose(disposing); 117 base.Dispose(disposing);
124 } 118 }
125   119  
126 #endregion 120 #endregion
127   121  
128 #region Event Handlers 122 #region Event Handlers
129 private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) 123 private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
130 { 124 {
131 } 125 }
132   126  
133 private void contextMenuStrip1_Opened(object sender, EventArgs e) 127 private void contextMenuStrip1_Opened(object sender, EventArgs e)
134 { 128 {
135 } 129 }
136   130  
137 private void DataGridView1_MouseDown(object sender, MouseEventArgs e) 131 private void DataGridView1_MouseDown(object sender, MouseEventArgs e)
138 { 132 {
139 var dataGridView = (DataGridView)sender; 133 var dataGridView = (DataGridView)sender;
140   134  
141 var index = dataGridView.HitTest(e.X, e.Y).RowIndex; 135 var index = dataGridView.HitTest(e.X, e.Y).RowIndex;
142   136  
143 if (index == -1) 137 if (index == -1)
144 { 138 {
145 base.OnMouseDown(e); 139 base.OnMouseDown(e);
146 return; 140 return;
147 } 141 }
148   142  
149 if (!dataGridView.SelectedRows.Contains(dataGridView.Rows[index])) 143 if (!dataGridView.SelectedRows.Contains(dataGridView.Rows[index]))
150 { 144 {
151 base.OnMouseDown(e); 145 base.OnMouseDown(e);
152 } 146 }
153 } 147 }
154   148  
155 private async void DataGridView1_MouseMove(object sender, MouseEventArgs e) 149 private async void DataGridView1_MouseMove(object sender, MouseEventArgs e)
156 { 150 {
157 var dataGridView = (DataGridView)sender; 151 var dataGridView = (DataGridView)sender;
158   152  
159 // Only accept dragging with left mouse button. 153 // Only accept dragging with left mouse button.
160 switch (e.Button) 154 switch (e.Button)
161 { 155 {
162 case MouseButtons.Left: 156 case MouseButtons.Left:
163   157  
164 if (!Monitor.TryEnter(_mouseMoveLock)) 158 if (!Monitor.TryEnter(_mouseMoveLock))
165 { 159 {
166 break; 160 break;
167 } 161 }
168   162  
169 try 163 try
170 { 164 {
171 var index = dataGridView.HitTest(e.X, e.Y).RowIndex; 165 var index = dataGridView.HitTest(e.X, e.Y).RowIndex;
172   166  
173 if (index == -1) 167 if (index == -1)
174 { 168 {
175 base.OnMouseMove(e); 169 base.OnMouseMove(e);
176 return; 170 return;
177 } 171 }
178   172  
179 var rows = GetSelectedDataGridViewRows(dataGridView); 173 var rows = GetSelectedDataGridViewRows(dataGridView);
180   174  
181 var count = rows.Count; 175 var count = rows.Count;
182   176  
183 if (count == 0) 177 if (count == 0)
184 { 178 {
185 base.OnMouseMove(e); 179 base.OnMouseMove(e);
186 break; 180 break;
187 } 181 }
188   182  
189 toolStripProgressBar1.Minimum = 0; 183 toolStripProgressBar1.Minimum = 0;
190 toolStripProgressBar1.Maximum = count; 184 toolStripProgressBar1.Maximum = count;
191   185  
192 var virtualFileDataObject = new VirtualFileDataObject.VirtualFileDataObject(); 186 var virtualFileDataObject = new VirtualFileDataObject.VirtualFileDataObject();
193 var fileDescriptors = 187 var fileDescriptors =
194 new List<VirtualFileDataObject.VirtualFileDataObject.FileDescriptor>(count); 188 new List<VirtualFileDataObject.VirtualFileDataObject.FileDescriptor>(count);
195   189  
196 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 190 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
197 { 191 {
198 if (_cancellationToken.IsCancellationRequested) 192 if (_cancellationToken.IsCancellationRequested)
199 { 193 {
200 return; 194 return;
201 } 195 }
202   196  
203 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 197 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
204 { 198 {
205 Log.Error(rowProgressFailure.Exception, "Unable to retrieve data for row."); 199 Log.Error(rowProgressFailure.Exception, "Unable to retrieve data for row.");
206   200  
207 toolStripStatusLabel1.Text = 201 toolStripStatusLabel1.Text =
208 $"Could not read file data {rowProgress.Row.Cells["NameColumn"].Value}..."; 202 $"Could not read file data {rowProgress.Row.Cells["NameColumn"].Value}...";
209 toolStripProgressBar1.Value = rowProgress.Index + 1; 203 toolStripProgressBar1.Value = rowProgress.Index + 1;
210   204  
211 statusStrip1.Update(); 205 statusStrip1.Update();
212   206  
213 return; 207 return;
214 } 208 }
215   209  
216 if (rowProgress is DataGridViewRowProgressSuccessRetrieveFileStream 210 if (rowProgress is DataGridViewRowProgressSuccessRetrieveFileStream
217 rowProgressSuccessRetrieveFileStream) 211 rowProgressSuccessRetrieveFileStream)
218 { 212 {
219 toolStripStatusLabel1.Text = 213 toolStripStatusLabel1.Text =
220 $"Got {rowProgress.Row.Cells["NameColumn"].Value} file stream..."; 214 $"Got {rowProgress.Row.Cells["NameColumn"].Value} file stream...";
221 toolStripProgressBar1.Value = rowProgress.Index + 1; 215 toolStripProgressBar1.Value = rowProgress.Index + 1;
222   216  
223 statusStrip1.Update(); 217 statusStrip1.Update();
224   218  
225 var hash = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["HashColumn"].Value; 219 var hash = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["HashColumn"].Value;
226 var name = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["NameColumn"].Value; 220 var name = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["NameColumn"].Value;
227   221  
228 var fileDescriptor = new VirtualFileDataObject.VirtualFileDataObject.FileDescriptor 222 var fileDescriptor = new VirtualFileDataObject.VirtualFileDataObject.FileDescriptor
229 { 223 {
230 Name = name, 224 Name = name,
231 StreamContents = stream => 225 StreamContents = stream =>
232 { 226 {
233 rowProgressSuccessRetrieveFileStream.MemoryStream.Seek(0, SeekOrigin.Begin); 227 rowProgressSuccessRetrieveFileStream.MemoryStream.Seek(0, SeekOrigin.Begin);
234   228  
235 rowProgressSuccessRetrieveFileStream.MemoryStream.CopyTo(stream); 229 rowProgressSuccessRetrieveFileStream.MemoryStream.CopyTo(stream);
236 } 230 }
237 }; 231 };
238   232  
239 fileDescriptors.Add(fileDescriptor); 233 fileDescriptors.Add(fileDescriptor);
240 } 234 }
241 }); 235 });
242   236  
243 await Task.Run(() => RetrieveFileStream(rows, progress, _cancellationToken), _cancellationToken); 237 await Task.Run(() => RetrieveFileStream(rows, progress, _cancellationToken), _cancellationToken);
244   238  
245 if (_cancellationToken.IsCancellationRequested) 239 if (_cancellationToken.IsCancellationRequested)
246 { 240 {
247 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 241 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
248 toolStripStatusLabel1.Text = "Done."; 242 toolStripStatusLabel1.Text = "Done.";
249 } 243 }
250   244  
251 virtualFileDataObject.SetData(fileDescriptors); 245 virtualFileDataObject.SetData(fileDescriptors);
252   246  
253 dataGridView1.DoDragDrop(virtualFileDataObject, DragDropEffects.Copy); 247 dataGridView1.DoDragDrop(virtualFileDataObject, DragDropEffects.Copy);
254 } 248 }
255 finally 249 finally
256 { 250 {
257 Monitor.Exit(_mouseMoveLock); 251 Monitor.Exit(_mouseMoveLock);
258 } 252 }
259   253  
260 break; 254 break;
261 } 255 }
262 } 256 }
263   257  
264 private async Task RetrieveFileStream(IReadOnlyList<DataGridViewRow> rows, 258 private async Task RetrieveFileStream(IReadOnlyList<DataGridViewRow> rows,
265 IProgress<DataGridViewRowProgress> progress, 259 IProgress<DataGridViewRowProgress> progress,
266 CancellationToken cancellationToken) 260 CancellationToken cancellationToken)
267 { 261 {
268 var count = rows.Count; 262 var count = rows.Count;
269   263  
270 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 264 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
271 { 265 {
272 try 266 try
273 { 267 {
274 var fileStream = await _snapshotDatabase.RetrieveFileStreamAsync( -  
275 (string)rows[i].Cells["HashColumn"].Value, 268 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   269  
276 cancellationToken); 270 var fileStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, cancellationToken);
277   271  
278 progress.Report(new DataGridViewRowProgressSuccessRetrieveFileStream(rows[i], i, fileStream)); 272 progress.Report(new DataGridViewRowProgressSuccessRetrieveFileStream(rows[i], i, fileStream));
279 } 273 }
280 catch (Exception exception) 274 catch (Exception exception)
281 { 275 {
282 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 276 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
283 } 277 }
284 } 278 }
285 } 279 }
286   280  
287 private void SnapshotManagerForm_Resize(object sender, EventArgs e) 281 private void SnapshotManagerForm_Resize(object sender, EventArgs e)
288 { 282 {
289 if (_snapshotPreviewForm != null) 283 if (_snapshotPreviewForm is { Visible: true })
290 { 284 {
291 _snapshotPreviewForm.WindowState = WindowState; 285 _snapshotPreviewForm.WindowState = WindowState;
292 } 286 }
293 } 287 }
294   288  
295 private void OpenInExplorerToolStripMenuItem_Click(object sender, EventArgs e) 289 private void OpenInExplorerToolStripMenuItem_Click(object sender, EventArgs e)
296 { 290 {
297 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 291 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
298 if (row == null) 292 if (row == null)
299 { 293 {
300 return; 294 return;
301 } 295 }
302   296  
303 Process.Start("explorer.exe", $"/select, \"{(string)row.Cells["PathColumn"].Value}\""); 297 Process.Start("explorer.exe", $"/select, \"{(string)row.Cells["PathColumn"].Value}\"");
304 } 298 }
305   299  
306 private async void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) 300 private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
307 { 301 {
308 var dataGridView = (DataGridView)sender; 302 var dataGridView = (DataGridView)sender;
309   -  
310 if (_snapshotPreviewForm == null) -  
311 { -  
312 _snapshotPreviewForm = new SnapshotPreviewForm(this, _snapshotDatabase); -  
313 _snapshotPreviewForm.Owner = this; -  
314 _snapshotPreviewForm.Closing += SnapshotPreviewForm_Closing; -  
315 _snapshotPreviewForm.Show(); -  
316 } -  
317   303  
318 var row = GetSelectedDataGridViewRows(dataGridView).FirstOrDefault(); 304 var row = GetSelectedDataGridViewRows(dataGridView).FirstOrDefault();
319 if (row == null) 305 if (row == null)
320 { 306 {
321 return; 307 return;
322 } 308 }
323   309  
324 var hash = (string)row.Cells["HashColumn"].Value; 310 var hash = (string)row.Cells["HashColumn"].Value;
325   -  
326 var snapshotPreview = -  
327 await Task.Run(async () => await _snapshotDatabase.RetrievePreviewAsync(hash, _cancellationToken), -  
328 _cancellationToken); -  
329   311  
330 if (snapshotPreview == null) 312 if (_snapshotPreviewForm is { Visible: true })
331 { 313 {
332 return; 314 _snapshotPreviewForm.Close();
333 } 315 }
-   316  
-   317 _snapshotPreviewForm = new SnapshotPreviewForm(this, hash, _snapshotDatabase, _cancellationToken);
-   318 _snapshotPreviewForm.Owner = this;
334   319 _snapshotPreviewForm.Closing += SnapshotPreviewForm_Closing;
335 PreviewRetrieved?.Invoke(this, new PreviewRetrievedEventArgs(snapshotPreview)); 320 _snapshotPreviewForm.Show();
336 } 321 }
337   322  
338 private void SnapshotPreviewForm_Closing(object sender, CancelEventArgs e) 323 private void SnapshotPreviewForm_Closing(object sender, CancelEventArgs e)
339 { 324 {
340 if (_snapshotPreviewForm == null) 325 if (_snapshotPreviewForm is { Visible: false })
341 { 326 {
342 return; 327 return;
343 } 328 }
-   329  
344   330 _snapshotPreviewForm.Closing -= SnapshotPreviewForm_Closing;
345 _snapshotPreviewForm.Dispose(); -  
346 _snapshotPreviewForm = null; 331 _snapshotPreviewForm.Dispose();
347 } 332 }
348   333  
349 private async void NoneToolStripMenuItem_Click(object sender, EventArgs e) 334 private async void NoneToolStripMenuItem_Click(object sender, EventArgs e)
350 { 335 {
351 var rows = GetSelectedDataGridViewRows(dataGridView1); 336 var rows = GetSelectedDataGridViewRows(dataGridView1);
352   337  
353 var count = rows.Count; 338 var count = rows.Count;
354   339  
355 toolStripProgressBar1.Minimum = 0; 340 toolStripProgressBar1.Minimum = 0;
356 toolStripProgressBar1.Maximum = count; 341 toolStripProgressBar1.Maximum = count;
357   342  
358 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 343 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
359 { 344 {
360 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 345 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
361 { 346 {
362 Log.Error(rowProgressFailure.Exception, "Failed to remove color from row."); 347 Log.Error(rowProgressFailure.Exception, "Failed to remove color from row.");
363   348  
364 toolStripStatusLabel1.Text = 349 toolStripStatusLabel1.Text =
365 $"Could not remove color from {rowProgress.Row.Cells["NameColumn"].Value}..."; 350 $"Could not remove color from {rowProgress.Row.Cells["NameColumn"].Value}...";
366 toolStripProgressBar1.Value = rowProgress.Index + 1; 351 toolStripProgressBar1.Value = rowProgress.Index + 1;
367   352  
368 statusStrip1.Update(); 353 statusStrip1.Update();
369   354  
370 return; 355 return;
371 } 356 }
372   357  
373 rowProgress.Row.DefaultCellStyle.BackColor = Color.Empty; 358 rowProgress.Row.DefaultCellStyle.BackColor = Color.Empty;
374   359  
375 toolStripStatusLabel1.Text = 360 toolStripStatusLabel1.Text =
376 $"Removed color from {rowProgress.Row.Cells["NameColumn"].Value}..."; 361 $"Removed color from {rowProgress.Row.Cells["NameColumn"].Value}...";
377 toolStripProgressBar1.Value = rowProgress.Index + 1; 362 toolStripProgressBar1.Value = rowProgress.Index + 1;
378   363  
379 statusStrip1.Update(); 364 statusStrip1.Update();
380 }); 365 });
381   366  
382 await Task.Run(() => RemoveColorFiles(rows, progress, _cancellationToken), _cancellationToken); 367 await Task.Run(() => RemoveColorFiles(rows, progress, _cancellationToken), _cancellationToken);
383   368  
384 if (_cancellationToken.IsCancellationRequested) 369 if (_cancellationToken.IsCancellationRequested)
385 { 370 {
386 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 371 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
387 toolStripStatusLabel1.Text = "Done."; 372 toolStripStatusLabel1.Text = "Done.";
388 } 373 }
389 } 374 }
390   375  
391 private async void ColorToolStripMenuItem_Click(object sender, EventArgs e) 376 private async void ColorToolStripMenuItem_Click(object sender, EventArgs e)
392 { 377 {
393 var toolStripMenuItem = (ToolStripMenuItem)sender; 378 var toolStripMenuItem = (ToolStripMenuItem)sender;
394 var color = toolStripMenuItem.BackColor; 379 var color = toolStripMenuItem.BackColor;
395   380  
396 var rows = GetSelectedDataGridViewRows(dataGridView1); 381 var rows = GetSelectedDataGridViewRows(dataGridView1);
397   382  
398 var count = rows.Count; 383 var count = rows.Count;
399   384  
400 toolStripProgressBar1.Minimum = 0; 385 toolStripProgressBar1.Minimum = 0;
401 toolStripProgressBar1.Maximum = count; 386 toolStripProgressBar1.Maximum = count;
402   387  
403 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 388 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
404 { 389 {
405 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 390 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
406 { 391 {
407 Log.Error(rowProgressFailure.Exception, "Unable to color row."); 392 Log.Error(rowProgressFailure.Exception, "Unable to color row.");
408   393  
409 toolStripStatusLabel1.Text = 394 toolStripStatusLabel1.Text =
410 $"Could not color {rowProgress.Row.Cells["NameColumn"].Value}..."; 395 $"Could not color {rowProgress.Row.Cells["NameColumn"].Value}...";
411 toolStripProgressBar1.Value = rowProgress.Index + 1; 396 toolStripProgressBar1.Value = rowProgress.Index + 1;
412   397  
413 statusStrip1.Update(); 398 statusStrip1.Update();
414   399  
415 return; 400 return;
416 } 401 }
417   402  
418 rowProgress.Row.DefaultCellStyle.BackColor = color; 403 rowProgress.Row.DefaultCellStyle.BackColor = color;
419   404  
420 toolStripStatusLabel1.Text = 405 toolStripStatusLabel1.Text =
421 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}..."; 406 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
422 toolStripProgressBar1.Value = rowProgress.Index + 1; 407 toolStripProgressBar1.Value = rowProgress.Index + 1;
423   408  
424 statusStrip1.Update(); 409 statusStrip1.Update();
425 }); 410 });
426   411  
427 await Task.Run(() => ColorFiles(rows, color, progress, _cancellationToken), _cancellationToken); 412 await Task.Run(() => ColorFiles(rows, color, progress, _cancellationToken), _cancellationToken);
428   413  
429 if (_cancellationToken.IsCancellationRequested) 414 if (_cancellationToken.IsCancellationRequested)
430 { 415 {
431 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 416 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
432 toolStripStatusLabel1.Text = "Done."; 417 toolStripStatusLabel1.Text = "Done.";
433 } 418 }
434 } 419 }
435   420  
436 private async void DeleteToolStripMenuItem_Click(object sender, EventArgs e) 421 private async void DeleteToolStripMenuItem_Click(object sender, EventArgs e)
437 { 422 {
438 var rows = GetSelectedDataGridViewRows(dataGridView1); 423 var rows = GetSelectedDataGridViewRows(dataGridView1);
439   424  
440 var count = rows.Count; 425 var count = rows.Count;
441   426  
442 toolStripProgressBar1.Minimum = 0; 427 toolStripProgressBar1.Minimum = 0;
443 toolStripProgressBar1.Maximum = count; 428 toolStripProgressBar1.Maximum = count;
444   429  
445 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 430 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
446 { 431 {
447 if (_cancellationToken.IsCancellationRequested) 432 if (_cancellationToken.IsCancellationRequested)
448 { 433 {
449 return; 434 return;
450 } 435 }
451   436  
452 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 437 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
453 { 438 {
454 Log.Error(rowProgressFailure.Exception, "Unable to delete row."); 439 Log.Error(rowProgressFailure.Exception, "Unable to delete row.");
455   440  
456 toolStripStatusLabel1.Text = 441 toolStripStatusLabel1.Text =
457 $"Could not remove {rowProgress.Row.Cells["NameColumn"].Value}..."; 442 $"Could not remove {rowProgress.Row.Cells["NameColumn"].Value}...";
458 toolStripProgressBar1.Value = rowProgress.Index + 1; 443 toolStripProgressBar1.Value = rowProgress.Index + 1;
459   444  
460 statusStrip1.Update(); 445 statusStrip1.Update();
461   446  
462 return; 447 return;
463 } 448 }
464   449  
465 toolStripStatusLabel1.Text = 450 toolStripStatusLabel1.Text =
466 $"Removed {rowProgress.Row.Cells["NameColumn"].Value}..."; 451 $"Removed {rowProgress.Row.Cells["NameColumn"].Value}...";
467 toolStripProgressBar1.Value = rowProgress.Index + 1; 452 toolStripProgressBar1.Value = rowProgress.Index + 1;
468   453  
469 statusStrip1.Update(); 454 statusStrip1.Update();
470   455  
471 dataGridView1.Rows.Remove(rowProgress.Row); 456 dataGridView1.Rows.Remove(rowProgress.Row);
472 }); 457 });
473   458  
474 await Task.Run(() => DeleteFiles(rows, progress, _cancellationToken), _cancellationToken); 459 await Task.Run(() => DeleteFiles(rows, progress, _cancellationToken), _cancellationToken);
475   460  
476 if (_cancellationToken.IsCancellationRequested) 461 if (_cancellationToken.IsCancellationRequested)
477 { 462 {
478 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 463 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
479 toolStripStatusLabel1.Text = "Done."; 464 toolStripStatusLabel1.Text = "Done.";
480 } 465 }
481 } 466 }
482   467  
483 private async void DeleteFastToolStripMenuItem_Click(object sender, EventArgs e) 468 private async void DeleteFastToolStripMenuItem_Click(object sender, EventArgs e)
484 { 469 {
485 var rows = GetSelectedDataGridViewRows(dataGridView1); 470 var rows = GetSelectedDataGridViewRows(dataGridView1);
486   471  
487 try 472 try
488 { 473 {
489 await DeleteFilesFast(rows, _cancellationToken); 474 await DeleteFilesFast(rows, _cancellationToken);
490   475  
491 foreach (var row in rows) 476 foreach (var row in rows)
492 { 477 {
493 dataGridView1.Rows.Remove(row); 478 dataGridView1.Rows.Remove(row);
494 } 479 }
495 } 480 }
496 catch (Exception exception) 481 catch (Exception exception)
497 { 482 {
498 Log.Error(exception, "Unable to remove rows."); 483 Log.Error(exception, "Unable to remove rows.");
499 } 484 }
500 } 485 }
501   486  
502 private void SnapshotManager_SnapshotCreate(object sender, SnapshotCreateEventArgs e) 487 private void SnapshotManager_SnapshotCreate(object sender, SnapshotCreateEventArgs e)
503 { 488 {
504 switch (e) 489 switch (e)
505 { 490 {
506 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs: 491 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
507 dataGridView1.InvokeIfRequired(dataGridView => 492 dataGridView1.InvokeIfRequired(dataGridView =>
508 { 493 {
509 var index = dataGridView.Rows.Add(); 494 var index = dataGridView.Rows.Add();
510   495  
511 dataGridView.Rows[index].Cells["TimeColumn"].Value = 496 dataGridView.Rows[index].Cells["TimeColumn"].Value =
512 DateTime.Parse(snapshotCreateSuccessEventArgs.Time); 497 DateTime.Parse(snapshotCreateSuccessEventArgs.Time);
513 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name; 498 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name;
514 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path; 499 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path;
515 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash; 500 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash;
516 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color; 501 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color;
517   502  
518 dataGridView.Rows[index].Selected = true; 503 dataGridView.Rows[index].Selected = true;
519 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending); 504 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending);
520 }); 505 });
521 break; 506 break;
522 case SnapshotCreateFailureEventArgs snapshotCreateFailure: 507 case SnapshotCreateFailureEventArgs snapshotCreateFailure:
523 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot."); 508 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot.");
524 break; 509 break;
525 } 510 }
526 } 511 }
527   512  
528   513  
529 private void _snapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e) 514 private void _snapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e)
530 { 515 {
531 switch (e) 516 switch (e)
532 { 517 {
533 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs: 518 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
534 dataGridView1.InvokeIfRequired(dataGridView => 519 dataGridView1.InvokeIfRequired(dataGridView =>
535 { 520 {
536 var index = dataGridView.Rows.Add(); 521 var index = dataGridView.Rows.Add();
537   522  
538 dataGridView.Rows[index].Cells["TimeColumn"].Value = 523 dataGridView.Rows[index].Cells["TimeColumn"].Value =
539 DateTime.Parse(snapshotCreateSuccessEventArgs.Time); 524 DateTime.Parse(snapshotCreateSuccessEventArgs.Time);
540 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name; 525 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name;
541 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path; 526 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path;
542 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash; 527 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash;
543 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color; 528 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color;
544   529  
545 dataGridView.Rows[index].Selected = true; 530 dataGridView.Rows[index].Selected = true;
546 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending); 531 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending);
547 }); 532 });
548 break; 533 break;
549 case SnapshotCreateFailureEventArgs snapshotCreateFailure: 534 case SnapshotCreateFailureEventArgs snapshotCreateFailure:
550 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot."); 535 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot.");
551 break; 536 break;
552 } 537 }
553 } 538 }
554   539  
555 private void RevertToThisToolStripMenuItem_Click(object sender, EventArgs e) 540 private void RevertToThisToolStripMenuItem_Click(object sender, EventArgs e)
556 { 541 {
557 _mainForm.InvokeIfRequired(async form => 542 _mainForm.InvokeIfRequired(async form =>
558 { 543 {
559 var fileSystemWatchers = new List<FileSystemWatcherState>(); 544 var fileSystemWatchers = new List<FileSystemWatcherState>();
560 var watchPaths = new HashSet<string>(); 545 var watchPaths = new HashSet<string>();
561 // Temporary disable all filesystem watchers that are watching the selected file directory. 546 // Temporary disable all filesystem watchers that are watching the selected file directory.
562 foreach (var row in GetSelectedDataGridViewRows(dataGridView1)) 547 foreach (var row in GetSelectedDataGridViewRows(dataGridView1))
563 { 548 {
564 var path = (string)row.Cells["PathColumn"].Value; 549 var path = (string)row.Cells["PathColumn"].Value;
565   550  
566 foreach (var fileSystemWatcher in form.FileSystemWatchers) 551 foreach (var fileSystemWatcher in form.FileSystemWatchers)
567 { 552 {
568 if (!path.IsPathEqual(fileSystemWatcher.Path) && 553 if (!path.IsPathEqual(fileSystemWatcher.Path) &&
569 !path.IsSubPathOf(fileSystemWatcher.Path)) 554 !path.IsSubPathOf(fileSystemWatcher.Path))
570 { 555 {
571 continue; 556 continue;
572 } 557 }
573   558  
574 if (watchPaths.Contains(fileSystemWatcher.Path)) 559 if (watchPaths.Contains(fileSystemWatcher.Path))
575 { 560 {
576 continue; 561 continue;
577 } 562 }
578   563  
579 fileSystemWatchers.Add(new FileSystemWatcherState(fileSystemWatcher)); 564 fileSystemWatchers.Add(new FileSystemWatcherState(fileSystemWatcher));
580   565  
581 fileSystemWatcher.EnableRaisingEvents = false; 566 fileSystemWatcher.EnableRaisingEvents = false;
582   567  
583 watchPaths.Add(fileSystemWatcher.Path); 568 watchPaths.Add(fileSystemWatcher.Path);
584 } 569 }
585 } 570 }
586   571  
587 try 572 try
588 { 573 {
589 var rows = GetSelectedDataGridViewRows(dataGridView1); 574 var rows = GetSelectedDataGridViewRows(dataGridView1);
590   575  
591 var count = rows.Count; 576 var count = rows.Count;
592   577  
593 toolStripProgressBar1.Minimum = 0; 578 toolStripProgressBar1.Minimum = 0;
594 toolStripProgressBar1.Maximum = count; 579 toolStripProgressBar1.Maximum = count;
595   580  
596 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 581 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
597 { 582 {
598 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 583 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
599 { 584 {
600 Log.Error(rowProgressFailure.Exception, "Could not revert to snapshot."); 585 Log.Error(rowProgressFailure.Exception, "Could not revert to snapshot.");
601   586  
602 toolStripStatusLabel1.Text = 587 toolStripStatusLabel1.Text =
603 $"Could not revert {rowProgress.Row.Cells["NameColumn"].Value}..."; 588 $"Could not revert {rowProgress.Row.Cells["NameColumn"].Value}...";
604 toolStripProgressBar1.Value = rowProgress.Index + 1; 589 toolStripProgressBar1.Value = rowProgress.Index + 1;
605   590  
606 statusStrip1.Update(); 591 statusStrip1.Update();
607   592  
608 return; 593 return;
609 } 594 }
610   595  
611 toolStripStatusLabel1.Text = 596 toolStripStatusLabel1.Text =
612 $"Reverted {rowProgress.Row.Cells["NameColumn"].Value}..."; 597 $"Reverted {rowProgress.Row.Cells["NameColumn"].Value}...";
613 toolStripProgressBar1.Value = rowProgress.Index + 1; 598 toolStripProgressBar1.Value = rowProgress.Index + 1;
614   599  
615 statusStrip1.Update(); 600 statusStrip1.Update();
616 }); 601 });
617   602  
618 await Task.Run(() => RevertFile(rows, progress, _cancellationToken), _cancellationToken); 603 await Task.Run(() => RevertFile(rows, progress, _cancellationToken), _cancellationToken);
619   604  
620 if (_cancellationToken.IsCancellationRequested) 605 if (_cancellationToken.IsCancellationRequested)
621 { 606 {
622 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 607 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
623 toolStripStatusLabel1.Text = "Done."; 608 toolStripStatusLabel1.Text = "Done.";
624 } 609 }
625 } 610 }
626 catch (Exception exception) 611 catch (Exception exception)
627 { 612 {
628 Log.Error(exception, "Could not update data grid view."); 613 Log.Error(exception, "Could not update data grid view.");
629 } 614 }
630 finally 615 finally
631 { 616 {
632 // Restore initial state. 617 // Restore initial state.
633 foreach (var fileSystemWatcherState in fileSystemWatchers) 618 foreach (var fileSystemWatcherState in fileSystemWatchers)
634 { 619 {
635 foreach (var fileSystemWatcher in form.FileSystemWatchers) 620 foreach (var fileSystemWatcher in form.FileSystemWatchers)
636 { 621 {
637 if (fileSystemWatcherState.FileSystemWatcher == fileSystemWatcher) 622 if (fileSystemWatcherState.FileSystemWatcher == fileSystemWatcher)
638 { 623 {
639 fileSystemWatcher.EnableRaisingEvents = fileSystemWatcherState.State; 624 fileSystemWatcher.EnableRaisingEvents = fileSystemWatcherState.State;
640 } 625 }
641 } 626 }
642 } 627 }
643 } 628 }
644 }); 629 });
645 } 630 }
646   631  
647 private async void SnapshotManagerForm_Load(object sender, EventArgs e) 632 private async void SnapshotManagerForm_Load(object sender, EventArgs e)
648 { 633 {
649 // Start browsing for network services. 634 // Start browsing for network services.
650 _horizonServiceBrowser.Browse("_horizon._tcp", "local"); 635 _horizonServiceBrowser.Browse("_horizon._tcp", "local");
-   636  
-   637 int count;
-   638 try
-   639 {
-   640 count = (int)await _snapshotDatabase.CountSnapshotsAsync(_cancellationToken);
-   641 }
-   642 catch (Exception exception)
-   643 {
-   644 Log.Error(exception, "Could not count snapshots.");
-   645 return;
-   646 }
-   647  
-   648 if (count == 0)
-   649 {
-   650 return;
-   651 }
651   652  
652 // Load snapshots. 653 // Load snapshots.
653 toolStripProgressBar1.Minimum = 0; 654 toolStripProgressBar1.Minimum = 0;
654 toolStripProgressBar1.Maximum = (int)await _snapshotDatabase.CountSnapshotsAsync(_cancellationToken); 655 toolStripProgressBar1.Maximum = count;
655   656  
656 var snapshotQueue = new ConcurrentQueue<Snapshot>(); 657 var snapshotQueue = new ConcurrentQueue<Snapshot>();
657   658  
658 void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs) 659 void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
659 { 660 {
660 try 661 try
661 { 662 {
662 if (snapshotQueue.IsEmpty) 663 if (snapshotQueue.IsEmpty)
663 { 664 {
664 Application.Idle -= IdleHandler; 665 Application.Idle -= IdleHandler;
665   666  
666 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending); 667 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending);
667 toolStripStatusLabel1.Text = "Done."; 668 toolStripStatusLabel1.Text = "Done.";
668 } 669 }
669   670  
670 if (!snapshotQueue.TryDequeue(out var snapshot)) 671 if (!snapshotQueue.TryDequeue(out var snapshot))
671 { 672 {
672 return; 673 return;
673 } 674 }
674   675  
675 var index = dataGridView1.Rows.Add(); 676 var index = dataGridView1.Rows.Add();
676   677  
677 dataGridView1.Rows[index].Cells["TimeColumn"].Value = DateTime.Parse(snapshot.Time); 678 dataGridView1.Rows[index].Cells["TimeColumn"].Value = DateTime.Parse(snapshot.Time);
678 dataGridView1.Rows[index].Cells["NameColumn"].Value = snapshot.Name; 679 dataGridView1.Rows[index].Cells["NameColumn"].Value = snapshot.Name;
679 dataGridView1.Rows[index].Cells["PathColumn"].Value = snapshot.Path; 680 dataGridView1.Rows[index].Cells["PathColumn"].Value = snapshot.Path;
680 dataGridView1.Rows[index].Cells["HashColumn"].Value = snapshot.Hash; 681 dataGridView1.Rows[index].Cells["HashColumn"].Value = snapshot.Hash;
681 dataGridView1.Rows[index].DefaultCellStyle.BackColor = snapshot.Color; 682 dataGridView1.Rows[index].DefaultCellStyle.BackColor = snapshot.Color;
682   683  
683 toolStripStatusLabel1.Text = $"Loaded {snapshot.Name}..."; 684 toolStripStatusLabel1.Text = $"Loaded {snapshot.Name}...";
684   685  
685 toolStripProgressBar1.Increment(1); 686 toolStripProgressBar1.Increment(1);
686   687  
687 statusStrip1.Update(); 688 statusStrip1.Update();
688 } 689 }
689 catch (Exception exception) 690 catch (Exception exception)
690 { 691 {
691 Log.Error(exception, "Could not update data grid view."); 692 Log.Error(exception, "Could not update data grid view.");
692 } 693 }
693 } 694 }
694 695
695 try 696 try
696 { 697 {
697 await foreach (var snapshot in _snapshotDatabase.LoadSnapshotsAsync(_cancellationToken).WithCancellation(_cancellationToken)) 698 await foreach (var snapshot in _snapshotDatabase.LoadSnapshotsAsync(_cancellationToken).WithCancellation(_cancellationToken))
698 { 699 {
699 snapshotQueue.Enqueue(snapshot); 700 snapshotQueue.Enqueue(snapshot);
700 } 701 }
701   702  
702 Application.Idle += IdleHandler; 703 Application.Idle += IdleHandler;
703 } 704 }
704 catch (Exception exception) 705 catch (Exception exception)
705 { 706 {
706 Application.Idle -= IdleHandler; 707 Application.Idle -= IdleHandler;
707   708  
708 Log.Error(exception, "Unable to load snapshots."); 709 Log.Error(exception, "Unable to load snapshots.");
709 } 710 }
710 } 711 }
711   712  
712 private void OnHorizonServiceBrowserOnServiceAdded(object o, Mono.Zeroconf.ServiceBrowseEventArgs args) 713 private void OnHorizonServiceBrowserOnServiceAdded(object o, Mono.Zeroconf.ServiceBrowseEventArgs args)
713 { 714 {
714 _resolvedService = args.Service; 715 _resolvedService = args.Service;
715   716  
716 Log.Information("Found Service: {0}", _resolvedService.Name); 717 Log.Information("Found Service: {0}", _resolvedService.Name);
717   718  
718 _resolvedService.Resolved += OnServiceOnResolved; 719 _resolvedService.Resolved += OnServiceOnResolved;
719   720  
720 _resolvedService.Resolve(); 721 _resolvedService.Resolve();
721   722  
722 _resolvedService.Resolved -= OnServiceOnResolved; 723 _resolvedService.Resolved -= OnServiceOnResolved;
723 } 724 }
724   725  
725 private void OnServiceOnResolved(object o, ServiceResolvedEventArgs args) 726 private void OnServiceOnResolved(object o, ServiceResolvedEventArgs args)
726 { 727 {
727 var service = (Service) args.Service; 728 var service = (Service) args.Service;
728   729  
729 Log.Information("Resolved Service: {0} - {1}:{2} ({3} TXT record entries)", service.FullName, service.HostEntry.AddressList[0], service.UPort, service.TxtRecord.Count); 730 Log.Information("Resolved Service: {0} - {1}:{2} ({3} TXT record entries)", service.FullName, service.HostEntry.AddressList[0], service.UPort, service.TxtRecord.Count);
730   731  
731 // Do not add discovered services more than once. 732 // Do not add discovered services more than once.
732 if (!_discoveredHorizonNetworkShares.TryAdd(service.Name, service)) 733 if (!_discoveredHorizonNetworkShares.TryAdd(service.Name, service))
733 { 734 {
734 return; 735 return;
735 } 736 }
736 737
737 var discoveredServiceMenuItem = new ToolStripMenuItem(service.Name, null, OnShareWithNodeClicked); 738 var discoveredServiceMenuItem = new ToolStripMenuItem(service.Name, null, OnShareWithNodeClicked);
738 discoveredServiceMenuItem.Tag = service; 739 discoveredServiceMenuItem.Tag = service;
739   740  
740 this.InvokeIfRequired(form => 741 this.InvokeIfRequired(form =>
741 { 742 {
742 form.shareToToolStripMenuItem.DropDownItems.Add(discoveredServiceMenuItem); 743 form.shareToToolStripMenuItem.DropDownItems.Add(discoveredServiceMenuItem);
743 }); 744 });
744 } 745 }
745   746  
746 private async void OnShareWithNodeClicked(object sender, EventArgs e) 747 private async void OnShareWithNodeClicked(object sender, EventArgs e)
747 { 748 {
748 var toolStripMenuItem = (ToolStripMenuItem)sender; 749 var toolStripMenuItem = (ToolStripMenuItem)sender;
749   750  
750 var service = (Service)toolStripMenuItem.Tag; 751 var service = (Service)toolStripMenuItem.Tag;
751   752  
752 var rows = GetSelectedDataGridViewRows(dataGridView1); 753 var rows = GetSelectedDataGridViewRows(dataGridView1);
753   754  
754 var count = rows.Count; 755 var count = rows.Count;
755   756  
756 toolStripProgressBar1.Minimum = 0; 757 toolStripProgressBar1.Minimum = 0;
757 toolStripProgressBar1.Maximum = count; 758 toolStripProgressBar1.Maximum = count;
758   759  
759 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 760 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
760 { 761 {
761 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 762 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
762 { 763 {
763 Log.Error(rowProgressFailure.Exception, "Unable to transfer data."); 764 Log.Error(rowProgressFailure.Exception, "Unable to transfer data.");
764   765  
765 toolStripStatusLabel1.Text = 766 toolStripStatusLabel1.Text =
766 $"Could not transfer {rowProgress.Row.Cells["NameColumn"].Value}..."; 767 $"Could not transfer {rowProgress.Row.Cells["NameColumn"].Value}...";
767 toolStripProgressBar1.Value = rowProgress.Index + 1; 768 toolStripProgressBar1.Value = rowProgress.Index + 1;
768   769  
769 statusStrip1.Update(); 770 statusStrip1.Update();
770   771  
771 return; 772 return;
772 } 773 }
773   774  
774 toolStripStatusLabel1.Text = 775 toolStripStatusLabel1.Text =
775 $"Transferred {rowProgress.Row.Cells["NameColumn"].Value}..."; 776 $"Transferred {rowProgress.Row.Cells["NameColumn"].Value}...";
776 toolStripProgressBar1.Value = rowProgress.Index + 1; 777 toolStripProgressBar1.Value = rowProgress.Index + 1;
777   778  
778 statusStrip1.Update(); 779 statusStrip1.Update();
779 }); 780 });
780   781  
781 await Task.Run(() => TransferFiles(rows, service, progress, _cancellationToken), _cancellationToken); 782 await Task.Run(() => TransferFiles(rows, service, progress, _cancellationToken), _cancellationToken);
782   783  
783 if (_cancellationToken.IsCancellationRequested) 784 if (_cancellationToken.IsCancellationRequested)
784 { 785 {
785 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 786 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
786 toolStripStatusLabel1.Text = "Done."; 787 toolStripStatusLabel1.Text = "Done.";
787 } 788 }
788 } 789 }
789   790  
790 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e) 791 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e)
791 { 792 {
792 _cancellationTokenSource.Cancel(); 793 _cancellationTokenSource.Cancel();
793   794  
794 if (_snapshotPreviewForm != null) 795 if (_snapshotPreviewForm is { Visible: true })
795 { 796 {
796 _snapshotPreviewForm.Close(); -  
797 _snapshotPreviewForm = null; 797 _snapshotPreviewForm.Close();
798 } 798 }
799   799  
800 if (_hexViewForm != null) 800 if (_hexViewForm is { Visible: true })
801 { -  
802 _hexViewForm.Close(); 801 {
803 _hexViewForm = null; 802 _hexViewForm.Close();
804 } 803 }
805 } 804 }
806   805  
807 private void DataGridView1_DragEnter(object sender, DragEventArgs e) 806 private void DataGridView1_DragEnter(object sender, DragEventArgs e)
808 { 807 {
809 if (e.Data.GetDataPresent(DataFormats.FileDrop)) 808 if (e.Data.GetDataPresent(DataFormats.FileDrop))
810 { 809 {
811 e.Effect = DragDropEffects.Copy; 810 e.Effect = DragDropEffects.Copy;
812 return; 811 return;
813 } 812 }
814   813  
815 e.Effect = DragDropEffects.None; 814 e.Effect = DragDropEffects.None;
816 } 815 }
817   816  
818 private void CreateSnapshots(IReadOnlyList<string> files, Bitmap screenCapture, TrackedFolders.TrackedFolders trackedFolders, IProgress<CreateSnapshotProgress> progress, CancellationToken cancellationToken) 817 private void CreateSnapshots(IReadOnlyList<string> files, Bitmap screenCapture, TrackedFolders.TrackedFolders trackedFolders, IProgress<CreateSnapshotProgress> progress, CancellationToken cancellationToken)
819 { 818 {
820 Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 512 }, async file => 819 Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 512 }, async file =>
821 { 820 {
822 var color = Color.Empty; 821 var color = Color.Empty;
823 if (_mainForm.TrackedFolders.TryGet(file, out var folder)) 822 if (_mainForm.TrackedFolders.TryGet(file, out var folder))
824 { 823 {
825 color = folder.Color; 824 color = folder.Color;
826 } 825 }
827   826  
828 var fileInfo = File.GetAttributes(file); 827 var fileInfo = File.GetAttributes(file);
829 if (fileInfo.HasFlag(FileAttributes.Directory)) 828 if (fileInfo.HasFlag(FileAttributes.Directory))
830 { 829 {
831 foreach (var directoryFile in Directory.GetFiles(file, "*.*", SearchOption.AllDirectories)) 830 foreach (var directoryFile in Directory.GetFiles(file, "*.*", SearchOption.AllDirectories))
832 { 831 {
833 var name = Path.GetFileName(directoryFile); 832 var name = Path.GetFileName(directoryFile);
834 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name); 833 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name);
835   834  
836 try 835 try
837 { 836 {
838 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, 837 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken);
839 _cancellationToken); -  
840   838  
841 progress.Report(new CreateSnapshotProgressSuccess(file)); 839 progress.Report(new CreateSnapshotProgressSuccess(file));
842 } 840 }
843 catch (Exception exception) 841 catch (Exception exception)
844 { 842 {
845 progress.Report(new CreateSnapshotProgressFailure(file, exception)); 843 progress.Report(new CreateSnapshotProgressFailure(file, exception));
846 } 844 }
847 } 845 }
848   846  
849 return; 847 return;
850 } 848 }
851   849  
852 var fileName = Path.GetFileName(file); 850 var fileName = Path.GetFileName(file);
-   851  
853 var pathName = Path.Combine(Path.GetDirectoryName(file), fileName); 852 var pathName = Path.Combine(Path.GetDirectoryName(file), fileName);
854   853  
855 try 854 try
856 { 855 {
857 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color, 856 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
858 _cancellationToken); 857 _cancellationToken);
859   858  
860 progress.Report(new CreateSnapshotProgressSuccess(file)); 859 progress.Report(new CreateSnapshotProgressSuccess(file));
861 } 860 }
862 catch (Exception exception) 861 catch (Exception exception)
863 { 862 {
864 progress.Report(new CreateSnapshotProgressFailure(file, exception)); 863 progress.Report(new CreateSnapshotProgressFailure(file, exception));
865 } 864 }
866 }); 865 });
867 } 866 }
868 private async void DataGridView1_DragDrop(object sender, DragEventArgs e) 867 private async void DataGridView1_DragDrop(object sender, DragEventArgs e)
869 { 868 {
870 if (!e.Data.GetDataPresent(DataFormats.FileDrop)) 869 if (!e.Data.GetDataPresent(DataFormats.FileDrop))
871 { 870 {
872 return; 871 return;
873 } 872 }
874   873  
875 var files = (string[])e.Data.GetData(DataFormats.FileDrop); 874 var files = (string[])e.Data.GetData(DataFormats.FileDrop);
876   875  
877 toolStripProgressBar1.Minimum = 0; 876 toolStripProgressBar1.Minimum = 0;
878 toolStripProgressBar1.Maximum = files.Length; 877 toolStripProgressBar1.Maximum = files.Length;
879 toolStripStatusLabel1.Text = "Snapshotting files..."; 878 toolStripStatusLabel1.Text = "Snapshotting files...";
880   879  
881 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 880 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
882   881  
883 var progress = new Progress<CreateSnapshotProgress>(createSnapshotProgress => 882 var progress = new Progress<CreateSnapshotProgress>(createSnapshotProgress =>
884 { 883 {
885 switch (createSnapshotProgress) 884 switch (createSnapshotProgress)
886 { 885 {
887 case CreateSnapshotProgressSuccess createSnapshotProgressSuccess: 886 case CreateSnapshotProgressSuccess createSnapshotProgressSuccess:
888 toolStripStatusLabel1.Text = $"Snapshot taken of {createSnapshotProgressSuccess.File}."; 887 toolStripStatusLabel1.Text = $"Snapshot taken of {createSnapshotProgressSuccess.File}.";
889 break; 888 break;
890 case CreateSnapshotProgressFailure createSnapshotProgressFailure: 889 case CreateSnapshotProgressFailure createSnapshotProgressFailure:
891 if (createSnapshotProgressFailure.Exception is SQLiteException { ResultCode: SQLiteErrorCode.Constraint }) 890 if (createSnapshotProgressFailure.Exception is SQLiteException { ResultCode: SQLiteErrorCode.Constraint })
892 { 891 {
893 toolStripStatusLabel1.Text = $"Snapshot of file {createSnapshotProgressFailure.File} already exists."; 892 toolStripStatusLabel1.Text = $"Snapshot of file {createSnapshotProgressFailure.File} already exists.";
894 break; 893 break;
895 } 894 }
896   895  
897 toolStripStatusLabel1.Text = $"Could not snapshot file {createSnapshotProgressFailure.File}"; 896 toolStripStatusLabel1.Text = $"Could not snapshot file {createSnapshotProgressFailure.File}";
898 Log.Warning(createSnapshotProgressFailure.Exception, $"Could not snapshot file {createSnapshotProgressFailure.File}"); 897 Log.Warning(createSnapshotProgressFailure.Exception, $"Could not snapshot file {createSnapshotProgressFailure.File}");
899 break; 898 break;
900 } 899 }
901   900  
902 toolStripProgressBar1.Increment(1); 901 toolStripProgressBar1.Increment(1);
903 statusStrip1.Update(); 902 statusStrip1.Update();
904 }); 903 });
905   904  
906 await Task.Factory.StartNew( () => 905 await Task.Factory.StartNew( () =>
907 { 906 {
908 CreateSnapshots(files, screenCapture, _mainForm.TrackedFolders, progress, _cancellationToken); 907 CreateSnapshots(files, screenCapture, _mainForm.TrackedFolders, progress, _cancellationToken);
909 }, _cancellationToken); 908 }, _cancellationToken);
910 } 909 }
911   910  
912 private async void FileToolStripMenuItem_Click(object sender, EventArgs e) 911 private async void FileToolStripMenuItem_Click(object sender, EventArgs e)
913 { 912 {
914 var dialog = new CommonOpenFileDialog(); 913 var dialog = new CommonOpenFileDialog();
915 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 914 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
916 { 915 {
917 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 916 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
918   917  
919 var fileName = Path.GetFileName(dialog.FileName); 918 var fileName = Path.GetFileName(dialog.FileName);
920 var directory = Path.GetDirectoryName(dialog.FileName); 919 var directory = Path.GetDirectoryName(dialog.FileName);
921 var pathName = Path.Combine(directory, fileName); 920 var pathName = Path.Combine(directory, fileName);
922   921  
923 var color = Color.Empty; 922 var color = Color.Empty;
924 if (_mainForm.TrackedFolders.TryGet(directory, out var folder)) 923 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
925 { 924 {
926 color = folder.Color; 925 color = folder.Color;
927 } 926 }
928   927  
929 try 928 try
930 { 929 {
931 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color, 930 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
932 _cancellationToken); 931 _cancellationToken);
933 } 932 }
934 catch (SQLiteException exception) 933 catch (SQLiteException exception)
935 { 934 {
936 if (exception.ResultCode == SQLiteErrorCode.Constraint) 935 if (exception.ResultCode == SQLiteErrorCode.Constraint)
937 { 936 {
938 Log.Information(exception, "Snapshot already exists."); 937 Log.Information(exception, "Snapshot already exists.");
939 } 938 }
940 } 939 }
941 catch (Exception exception) 940 catch (Exception exception)
942 { 941 {
943 Log.Warning(exception, "Could not create snapshot."); 942 Log.Warning(exception, "Could not create snapshot.");
944 } 943 }
945 } 944 }
946 } 945 }
947   946  
948 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e) 947 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e)
949 { 948 {
950 var dialog = new CommonOpenFileDialog { IsFolderPicker = true }; 949 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
951 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 950 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
952 { 951 {
953 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 952 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
954 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories)) 953 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories))
955 { 954 {
956 var name = Path.GetFileName(directoryFile); 955 var name = Path.GetFileName(directoryFile);
957 var directory = Path.GetDirectoryName(directoryFile); 956 var directory = Path.GetDirectoryName(directoryFile);
958 var path = Path.Combine(directory, name); 957 var path = Path.Combine(directory, name);
959   958  
960 var color = Color.Empty; 959 var color = Color.Empty;
961 if (_mainForm.TrackedFolders.TryGet(directory, out var folder)) 960 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
962 { 961 {
963 color = folder.Color; 962 color = folder.Color;
964 } 963 }
965   964  
966 try 965 try
967 { 966 {
968 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken); 967 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken);
969 } 968 }
970 catch (SQLiteException exception) 969 catch (SQLiteException exception)
971 { 970 {
972 if (exception.ResultCode == SQLiteErrorCode.Constraint) 971 if (exception.ResultCode == SQLiteErrorCode.Constraint)
973 { 972 {
974 Log.Information(exception, "Snapshot already exists."); 973 Log.Information(exception, "Snapshot already exists.");
975 } 974 }
976 } 975 }
977 catch (Exception exception) 976 catch (Exception exception)
978 { 977 {
979 Log.Warning(exception, "Could not create snapshot."); 978 Log.Warning(exception, "Could not create snapshot.");
980 } 979 }
981 } 980 }
982 } 981 }
983 } 982 }
984   983  
985 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e) 984 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e)
986 { 985 {
987 var commonOpenFileDialog = new CommonOpenFileDialog 986 var commonOpenFileDialog = new CommonOpenFileDialog
988 { 987 {
989 InitialDirectory = _mainForm.Configuration.LastFolder, 988 InitialDirectory = _mainForm.Configuration.LastFolder,
990 IsFolderPicker = true 989 IsFolderPicker = true
991 }; 990 };
992   991  
993 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok) 992 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok)
994 { 993 {
995 return; 994 return;
996 } 995 }
997   996  
998 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName; 997 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
999 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 998 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1000 async () => await _mainForm.SaveConfiguration(), _cancellationToken); 999 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1001   1000  
1002 var directory = commonOpenFileDialog.FileName; 1001 var directory = commonOpenFileDialog.FileName;
1003   1002  
1004 var rows = GetSelectedDataGridViewRows(dataGridView1); 1003 var rows = GetSelectedDataGridViewRows(dataGridView1);
1005   1004  
1006 var count = rows.Count; 1005 var count = rows.Count;
1007   1006  
1008 toolStripProgressBar1.Minimum = 0; 1007 toolStripProgressBar1.Minimum = 0;
1009 toolStripProgressBar1.Maximum = count; 1008 toolStripProgressBar1.Maximum = count;
1010   1009  
1011 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1010 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1012 { 1011 {
1013 var path = Path.Combine(directory, 1012 var path = Path.Combine(directory,
1014 (string)rowProgress.Row.Cells["NameColumn"].Value); 1013 (string)rowProgress.Row.Cells["NameColumn"].Value);
1015   1014  
1016 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1015 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1017 { 1016 {
1018 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot."); 1017 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot.");
1019   1018  
1020 toolStripStatusLabel1.Text = 1019 toolStripStatusLabel1.Text =
1021 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1020 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1022 toolStripProgressBar1.Value = rowProgress.Index + 1; 1021 toolStripProgressBar1.Value = rowProgress.Index + 1;
1023   1022  
1024 statusStrip1.Update(); 1023 statusStrip1.Update();
1025   1024  
1026 return; 1025 return;
1027 } 1026 }
1028   1027  
1029 rowProgress.Row.Cells["PathColumn"].Value = path; 1028 rowProgress.Row.Cells["PathColumn"].Value = path;
1030   1029  
1031 toolStripStatusLabel1.Text = 1030 toolStripStatusLabel1.Text =
1032 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1031 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1033 toolStripProgressBar1.Value = rowProgress.Index + 1; 1032 toolStripProgressBar1.Value = rowProgress.Index + 1;
1034   1033  
1035 statusStrip1.Update(); 1034 statusStrip1.Update();
1036 }); 1035 });
1037   1036  
1038 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken); 1037 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken);
1039   1038  
1040 if (_cancellationToken.IsCancellationRequested) 1039 if (_cancellationToken.IsCancellationRequested)
1041 { 1040 {
1042 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1041 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1043 toolStripStatusLabel1.Text = "Done."; 1042 toolStripStatusLabel1.Text = "Done.";
1044 } 1043 }
1045 } 1044 }
1046   1045  
1047 private async void NoteToolStripMenuItem_Click(object sender, EventArgs e) 1046 private void NoteToolStripMenuItem_Click(object sender, EventArgs e)
1048 { -  
1049 if (_snapshotNote != null) -  
1050 { -  
1051 return; -  
1052 } -  
1053   1047 {
1054 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 1048 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1055 if (row == null) 1049 if (row == null)
1056 { 1050 {
1057 return; 1051 return;
1058 } 1052 }
1059   -  
1060 try -  
1061 { -  
1062 var snapshotPreview = await _snapshotDatabase.RetrievePreviewAsync( 1053  
1063 (string)row.Cells["HashColumn"].Value, _cancellationToken); -  
1064   -  
1065 if (snapshotPreview == null) -  
1066 { -  
1067 return; -  
1068 } -  
1069   1054 var hash = (string)row.Cells["HashColumn"].Value;
1070 _snapshotNote = new SnapshotNoteForm(this, snapshotPreview); -  
1071 _snapshotNote.Owner = this; -  
1072 _snapshotNote.SaveNote += SnapshotNote_SaveNote; -  
1073 _snapshotNote.Closing += SnapshotNote_Closing; -  
1074 _snapshotNote.Show(); -  
1075 } 1055  
1076 catch (Exception exception) 1056 if (_snapshotNoteForm is { Visible: true })
1077 { 1057 {
-   1058 _snapshotNoteForm.Close();
-   1059 }
-   1060  
-   1061 _snapshotNoteForm = new SnapshotNoteForm(hash, _snapshotDatabase, _cancellationToken);
-   1062 _snapshotNoteForm.Owner = this;
-   1063 _snapshotNoteForm.SaveNote += SnapshotNoteFormSaveNoteForm;
1078 Log.Error(exception, "Could not open notes form."); 1064 _snapshotNoteForm.Closing += SnapshotNoteForm_Closing;
1079 } 1065 _snapshotNoteForm.Show();
1080 } 1066 }
1081   1067  
1082 private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e) 1068 private async void SnapshotNoteFormSaveNoteForm(object sender, SaveNoteEventArgs e)
1083 { 1069 {
1084 var rows = GetSelectedDataGridViewRows(dataGridView1); 1070 var rows = GetSelectedDataGridViewRows(dataGridView1);
1085   1071  
1086 var count = rows.Count; 1072 var count = rows.Count;
1087   1073  
1088 toolStripProgressBar1.Minimum = 0; 1074 toolStripProgressBar1.Minimum = 0;
1089 toolStripProgressBar1.Maximum = count; 1075 toolStripProgressBar1.Maximum = count;
1090   1076  
1091 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1077 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1092 { 1078 {
1093 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1079 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1094 { 1080 {
1095 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot."); 1081 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot.");
1096   1082  
1097 toolStripStatusLabel1.Text = 1083 toolStripStatusLabel1.Text =
1098 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1084 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1099 toolStripProgressBar1.Value = rowProgress.Index + 1; 1085 toolStripProgressBar1.Value = rowProgress.Index + 1;
1100   1086  
1101 statusStrip1.Update(); 1087 statusStrip1.Update();
1102   1088  
1103 return; 1089 return;
1104 } 1090 }
1105   1091  
1106 toolStripStatusLabel1.Text = 1092 toolStripStatusLabel1.Text =
1107 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1093 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1108 toolStripProgressBar1.Value = rowProgress.Index + 1; 1094 toolStripProgressBar1.Value = rowProgress.Index + 1;
1109   1095  
1110 statusStrip1.Update(); 1096 statusStrip1.Update();
1111 }); 1097 });
1112   1098  
1113 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken); 1099 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken);
1114   1100  
1115 if (_cancellationToken.IsCancellationRequested) 1101 if (_cancellationToken.IsCancellationRequested)
1116 { 1102 {
1117 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1103 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1118 toolStripStatusLabel1.Text = "Done."; 1104 toolStripStatusLabel1.Text = "Done.";
1119 } 1105 }
1120 } 1106 }
1121   1107  
1122 private void SnapshotNote_Closing(object sender, CancelEventArgs e) 1108 private void SnapshotNoteForm_Closing(object sender, CancelEventArgs e)
1123 { 1109 {
1124 if (_snapshotNote == null) 1110 if (_snapshotNoteForm is { Visible: false })
1125 { 1111 {
1126 return; 1112 return;
1127 } 1113 }
1128   1114  
1129 _snapshotNote.Closing -= SnapshotNote_Closing; 1115 _snapshotNoteForm.SaveNote -= SnapshotNoteFormSaveNoteForm;
1130 _snapshotNote.Close(); 1116 _snapshotNoteForm.Closing -= SnapshotNoteForm_Closing;
1131 _snapshotNote = null; 1117 _snapshotNoteForm.Dispose();
1132 } 1118 }
1133   1119  
1134 private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e) 1120 private void ViewHexToolStripMenuItem_Click(object sender, EventArgs e)
1135 { 1121 {
1136 var rows = GetSelectedDataGridViewRows(dataGridView1); 1122 var rows = GetSelectedDataGridViewRows(dataGridView1);
1137 var row = rows.FirstOrDefault(); 1123 var row = rows.FirstOrDefault();
1138 if (row == null) 1124 if (row == null)
1139 { 1125 {
1140 return; 1126 return;
1141 } 1127 }
1142   1128  
1143 var hash = (string)row.Cells["HashColumn"].Value; 1129 var hash = (string)row.Cells["HashColumn"].Value;
1144   1130  
1145 using (var memoryStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, _cancellationToken)) 1131 if (_hexViewForm is { Visible: true })
1146 { -  
1147 if (memoryStream == null) -  
1148 { -  
1149 return; -  
1150 } -  
1151   -  
1152 if (_hexViewForm != null) -  
1153 { -  
1154 _hexViewForm.UpdateData(memoryStream.ToArray()); -  
1155 _hexViewForm.Activate(); -  
1156 return; -  
1157 } -  
1158   -  
1159 _hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray()); -  
1160 _hexViewForm.Owner = this; -  
1161 _hexViewForm.Closing += HexViewForm_Closing; -  
1162 _hexViewForm.SaveData += HexViewForm_SaveData; -  
1163   1132 {
1164 _hexViewForm.Show(); 1133 _hexViewForm.Close();
-   1134 }
-   1135  
-   1136 _hexViewForm = new HexViewForm(hash, _snapshotDatabase, _cancellationToken);
-   1137 _hexViewForm.Owner = this;
-   1138 _hexViewForm.Closing += HexViewForm_Closing;
-   1139 _hexViewForm.SaveData += HexViewForm_SaveData;
1165 } 1140 _hexViewForm.Show();
1166 } 1141 }
1167   1142  
1168 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e) 1143 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e)
1169 { 1144 {
-   1145 string hash;
-   1146  
-   1147 try
-   1148 {
1170 var hash = await _snapshotDatabase.UpdateFileAsync(e.Hash, e.Data, _cancellationToken); 1149 hash = await _snapshotDatabase.UpdateFileAsync(e.Hash, e.Data, _cancellationToken);
-   1150 }
-   1151 catch (Exception exception)
-   1152 {
-   1153 Log.Error(exception, "Could not update snapshot data.");
-   1154 return;
-   1155 }
1171   1156  
1172 if (string.IsNullOrEmpty(hash)) 1157 if (string.IsNullOrEmpty(hash))
1173 { 1158 {
1174 return; 1159 return;
1175 } 1160 }
1176   1161  
1177 dataGridView1.InvokeIfRequired(dataGridView => 1162 dataGridView1.InvokeIfRequired(dataGridView =>
1178 { 1163 {
1179 // Update the hash in the datagridview. 1164 // Update the hash in the datagridview.
1180 var removeRows = new List<DataGridViewRow>(); 1165 var removeRows = new List<DataGridViewRow>();
1181 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>()) 1166 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>())
1182 { 1167 {
1183 if ((string)row.Cells["HashColumn"].Value == hash) 1168 if ((string)row.Cells["HashColumn"].Value == hash)
1184 { 1169 {
1185 removeRows.Add(row); 1170 removeRows.Add(row);
1186 } 1171 }
1187   1172  
1188 if ((string)row.Cells["HashColumn"].Value != e.Hash) 1173 if ((string)row.Cells["HashColumn"].Value != e.Hash)
1189 { 1174 {
1190 continue; 1175 continue;
1191 } 1176 }
1192   1177  
1193 row.Cells["HashColumn"].Value = hash; 1178 row.Cells["HashColumn"].Value = hash;
1194 } 1179 }
1195   1180  
1196 // Remove rows that might have the same hash. 1181 // Remove rows that might have the same hash.
1197 foreach (var row in removeRows) 1182 foreach (var row in removeRows)
1198 { 1183 {
1199 dataGridView.Rows.Remove(row); 1184 dataGridView.Rows.Remove(row);
1200 } 1185 }
1201 }); 1186 });
1202 } 1187 }
1203   1188  
1204 private void HexViewForm_Closing(object sender, CancelEventArgs e) 1189 private void HexViewForm_Closing(object sender, CancelEventArgs e)
1205 { 1190 {
1206 if (_hexViewForm == null) 1191 if (_hexViewForm is { Visible: false })
1207 { 1192 {
1208 return; 1193 return;
1209 } 1194 }
1210   1195  
1211 _hexViewForm.SaveData -= HexViewForm_SaveData; 1196 _hexViewForm.SaveData -= HexViewForm_SaveData;
1212 _hexViewForm.Closing -= HexViewForm_Closing; 1197 _hexViewForm.Closing -= HexViewForm_Closing;
1213 _hexViewForm.Close(); 1198 _hexViewForm.Dispose();
1214 _hexViewForm = null; -  
1215 } 1199 }
1216   1200  
1217 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e) 1201 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e)
1218 { 1202 {
1219 var commonOpenFileDialog = new CommonOpenFileDialog 1203 var commonOpenFileDialog = new CommonOpenFileDialog
1220 { 1204 {
1221 InitialDirectory = _mainForm.Configuration.LastFolder, 1205 InitialDirectory = _mainForm.Configuration.LastFolder,
1222 IsFolderPicker = true 1206 IsFolderPicker = true
1223 }; 1207 };
1224   1208  
1225 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok) 1209 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok)
1226 { 1210 {
1227 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName; 1211 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
1228 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 1212 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1229 async () => await _mainForm.SaveConfiguration(), _cancellationToken); 1213 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1230   1214  
1231 var directory = commonOpenFileDialog.FileName; 1215 var directory = commonOpenFileDialog.FileName;
1232   1216  
1233 var rows = GetSelectedDataGridViewRows(dataGridView1); 1217 var rows = GetSelectedDataGridViewRows(dataGridView1);
1234   1218  
1235 var count = rows.Count; 1219 var count = rows.Count;
1236   1220  
1237 toolStripProgressBar1.Minimum = 0; 1221 toolStripProgressBar1.Minimum = 0;
1238 toolStripProgressBar1.Maximum = count; 1222 toolStripProgressBar1.Maximum = count;
1239   1223  
1240 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1224 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1241 { 1225 {
1242 var fileInfo = 1226 var fileInfo =
1243 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value); 1227 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value);
1244 var file = fileInfo.Name; 1228 var file = fileInfo.Name;
1245 var path = Path.Combine(directory, file); 1229 var path = Path.Combine(directory, file);
1246   1230  
1247 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1231 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1248 { 1232 {
1249 Log.Error(rowProgressFailure.Exception, "Could not save snapshot."); 1233 Log.Error(rowProgressFailure.Exception, "Could not save snapshot.");
1250   1234  
1251 toolStripStatusLabel1.Text = 1235 toolStripStatusLabel1.Text =
1252 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1236 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1253 toolStripProgressBar1.Value = rowProgress.Index + 1; 1237 toolStripProgressBar1.Value = rowProgress.Index + 1;
1254   1238  
1255 statusStrip1.Update(); 1239 statusStrip1.Update();
1256   1240  
1257 return; 1241 return;
1258 } 1242 }
1259   1243  
1260 toolStripStatusLabel1.Text = 1244 toolStripStatusLabel1.Text =
1261 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1245 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1262 toolStripProgressBar1.Value = rowProgress.Index + 1; 1246 toolStripProgressBar1.Value = rowProgress.Index + 1;
1263   1247  
1264 statusStrip1.Update(); 1248 statusStrip1.Update();
1265 }); 1249 });
1266   1250  
1267 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken); 1251 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken);
1268   1252  
1269 if (_cancellationToken.IsCancellationRequested) 1253 if (_cancellationToken.IsCancellationRequested)
1270 { 1254 {
1271 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1255 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1272 toolStripStatusLabel1.Text = "Done."; 1256 toolStripStatusLabel1.Text = "Done.";
1273 } 1257 }
1274 } 1258 }
1275 } 1259 }
1276   1260  
1277 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e) 1261 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e)
1278 { 1262 {
1279 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 1263 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1280   1264  
1281 if (select == null) 1265 if (select == null)
1282 { 1266 {
1283 return; 1267 return;
1284 } 1268 }
1285   1269  
1286 // C:\aa\bbb\dd.txt 1270 // C:\aa\bbb\dd.txt
1287 var path = (string)select.Cells["PathColumn"].Value; 1271 var path = (string)select.Cells["PathColumn"].Value;
1288   1272  
1289 // C:\aa\bbb\ 1273 // C:\aa\bbb\
1290 var basePath = Path.GetDirectoryName(path); 1274 var basePath = Path.GetDirectoryName(path);
1291   1275  
1292 var dialog = new CommonOpenFileDialog { IsFolderPicker = true }; 1276 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
1293 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 1277 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
1294 { 1278 {
1295 //Log.Information(dialog.FileName); 1279 //Log.Information(dialog.FileName);
1296 var rows = GetAllDataGridViewRows(dataGridView1); 1280 var rows = GetAllDataGridViewRows(dataGridView1);
1297 var count = rows.Count; 1281 var count = rows.Count;
1298   1282  
1299 toolStripProgressBar1.Minimum = 0; 1283 toolStripProgressBar1.Minimum = 0;
1300 toolStripProgressBar1.Maximum = count; 1284 toolStripProgressBar1.Maximum = count;
1301 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1285 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1302 { 1286 {
1303 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1287 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1304 { 1288 {
1305 Log.Error(rowProgressFailure.Exception, "Could not save file."); 1289 Log.Error(rowProgressFailure.Exception, "Could not save file.");
1306   1290  
1307 toolStripStatusLabel1.Text = 1291 toolStripStatusLabel1.Text =
1308 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}..."; 1292 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1309 toolStripProgressBar1.Value = rowProgress.Index + 1; 1293 toolStripProgressBar1.Value = rowProgress.Index + 1;
1310   1294  
1311 statusStrip1.Update(); 1295 statusStrip1.Update();
1312   1296  
1313 return; 1297 return;
1314 } 1298 }
1315   1299  
1316 toolStripStatusLabel1.Text = 1300 toolStripStatusLabel1.Text =
1317 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}..."; 1301 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1318 toolStripProgressBar1.Value = rowProgress.Index + 1; 1302 toolStripProgressBar1.Value = rowProgress.Index + 1;
1319   1303  
1320 statusStrip1.Update(); 1304 statusStrip1.Update();
1321 }); 1305 });
1322   1306  
1323 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken), 1307 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken),
1324 _cancellationToken); 1308 _cancellationToken);
1325   1309  
1326 if (_cancellationToken.IsCancellationRequested) 1310 if (_cancellationToken.IsCancellationRequested)
1327 { 1311 {
1328 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1312 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1329 toolStripStatusLabel1.Text = "Done."; 1313 toolStripStatusLabel1.Text = "Done.";
1330 } 1314 }
1331 } 1315 }
1332 } 1316 }
1333   1317  
1334 private void TextBox1_TextChanged(object sender, EventArgs e) 1318 private void TextBox1_TextChanged(object sender, EventArgs e)
1335 { 1319 {
1336 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () => 1320 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
1337 { 1321 {
1338 textBox1.InvokeIfRequired(textBox => 1322 textBox1.InvokeIfRequired(textBox =>
1339 { 1323 {
1340 var search = textBox.Text; 1324 var search = textBox.Text;
1341   1325  
1342 dataGridView1.InvokeIfRequired(dataGridView => 1326 dataGridView1.InvokeIfRequired(dataGridView =>
1343 { 1327 {
1344 foreach (var row in GetAllDataGridViewRows(dataGridView)) 1328 foreach (var row in GetAllDataGridViewRows(dataGridView))
1345 { 1329 {
1346 if(row.Cells["PathColumn"].Value == null) 1330 if(row.Cells["PathColumn"].Value == null)
1347 { 1331 {
1348 continue; 1332 continue;
1349 } 1333 }
1350   1334  
1351 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search, 1335 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search,
1352 StringComparison.OrdinalIgnoreCase)) 1336 StringComparison.OrdinalIgnoreCase))
1353 { 1337 {
1354 case -1: 1338 case -1:
1355 row.Visible = false; 1339 row.Visible = false;
1356 break; 1340 break;
1357 default: 1341 default:
1358 row.Visible = true; 1342 row.Visible = true;
1359 break; 1343 break;
1360 } 1344 }
1361 } 1345 }
1362 }); 1346 });
1363 }); 1347 });
1364 }, _cancellationToken); 1348 }, _cancellationToken);
1365 } 1349 }
1366   1350  
1367 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e) 1351 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e)
1368 { 1352 {
1369 var rows = GetSelectedDataGridViewRows(dataGridView1); 1353 var rows = GetSelectedDataGridViewRows(dataGridView1);
1370   1354  
1371 var count = rows.Count; 1355 var count = rows.Count;
1372   1356  
1373 toolStripProgressBar1.Minimum = 0; 1357 toolStripProgressBar1.Minimum = 0;
1374 toolStripProgressBar1.Maximum = count; 1358 toolStripProgressBar1.Maximum = count;
1375   1359  
1376 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1360 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1377 { 1361 {
1378 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1362 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1379 { 1363 {
1380 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot."); 1364 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot.");
1381   1365  
1382 toolStripStatusLabel1.Text = 1366 toolStripStatusLabel1.Text =
1383 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1367 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1384 toolStripProgressBar1.Value = rowProgress.Index + 1; 1368 toolStripProgressBar1.Value = rowProgress.Index + 1;
1385   1369  
1386 statusStrip1.Update(); 1370 statusStrip1.Update();
1387   1371  
1388 return; 1372 return;
1389 } 1373 }
1390   1374  
1391 toolStripStatusLabel1.Text = 1375 toolStripStatusLabel1.Text =
1392 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1376 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1393 toolStripProgressBar1.Value = rowProgress.Index + 1; 1377 toolStripProgressBar1.Value = rowProgress.Index + 1;
1394   1378  
1395 statusStrip1.Update(); 1379 statusStrip1.Update();
1396 }); 1380 });
1397   1381  
1398 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken); 1382 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken);
1399   1383  
1400 if (_cancellationToken.IsCancellationRequested) 1384 if (_cancellationToken.IsCancellationRequested)
1401 { 1385 {
1402 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1386 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1403 toolStripStatusLabel1.Text = "Done."; 1387 toolStripStatusLabel1.Text = "Done.";
1404 } 1388 }
1405 } 1389 }
1406   1390  
1407 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e) 1391 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e)
1408 { 1392 {
1409 var rows = GetSelectedDataGridViewRows(dataGridView1); 1393 var rows = GetSelectedDataGridViewRows(dataGridView1);
1410   1394  
1411 var count = rows.Count; 1395 var count = rows.Count;
1412   1396  
1413 toolStripProgressBar1.Minimum = 0; 1397 toolStripProgressBar1.Minimum = 0;
1414 toolStripProgressBar1.Maximum = count; 1398 toolStripProgressBar1.Maximum = count;
1415   1399  
1416 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1400 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1417 { 1401 {
1418 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1402 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1419 { 1403 {
1420 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot."); 1404 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot.");
1421   1405  
1422 toolStripStatusLabel1.Text = 1406 toolStripStatusLabel1.Text =
1423 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1407 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1424 toolStripProgressBar1.Value = rowProgress.Index + 1; 1408 toolStripProgressBar1.Value = rowProgress.Index + 1;
1425   1409  
1426 statusStrip1.Update(); 1410 statusStrip1.Update();
1427   1411  
1428 return; 1412 return;
1429 } 1413 }
1430   1414  
1431 toolStripStatusLabel1.Text = 1415 toolStripStatusLabel1.Text =
1432 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1416 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1433 toolStripProgressBar1.Value = rowProgress.Index + 1; 1417 toolStripProgressBar1.Value = rowProgress.Index + 1;
1434   1418  
1435 statusStrip1.Update(); 1419 statusStrip1.Update();
1436 }); 1420 });
1437   1421  
1438 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken); 1422 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken);
1439   1423  
1440 if (_cancellationToken.IsCancellationRequested) 1424 if (_cancellationToken.IsCancellationRequested)
1441 { 1425 {
1442 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1426 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1443 toolStripStatusLabel1.Text = "Done."; 1427 toolStripStatusLabel1.Text = "Done.";
1444 } 1428 }
1445 } 1429 }
1446   1430  
1447 #endregion 1431 #endregion
1448   1432  
1449 #region Private Methods 1433 #region Private Methods
1450 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) 1434 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
1451 { 1435 {
1452 DataGridView dataGridView = sender as DataGridView; 1436 DataGridView dataGridView = sender as DataGridView;
1453 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells) 1437 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells)
1454 { 1438 {
1455 if (cell.Selected == false) { continue; } 1439 if (cell.Selected == false) { continue; }
1456 var bgColorCell = Color.White; 1440 var bgColorCell = Color.White;
1457 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; } 1441 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; }
1458 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; } 1442 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; }
1459 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4); 1443 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4);
1460 } 1444 }
1461 } 1445 }
1462   1446  
1463 //Mix two colors 1447 //Mix two colors
1464 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1 1448 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1
1465 /// <summary> 1449 /// <summary>
1466 /// Mix two colors. 1450 /// Mix two colors.
1467 /// </summary> 1451 /// </summary>
1468 /// <param name="Color1"></param> 1452 /// <param name="Color1"></param>
1469 /// <param name="Color2"></param> 1453 /// <param name="Color2"></param>
1470 /// <param name="Steps"></param> 1454 /// <param name="Steps"></param>
1471 /// <param name="Position"></param> 1455 /// <param name="Position"></param>
1472 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example> 1456 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example>
1473 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks> 1457 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks>
1474 /// <returns></returns> 1458 /// <returns></returns>
1475 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position) 1459 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position)
1476 { 1460 {
1477 if (Position <= 0 || Steps <= 1) { return Color1; } 1461 if (Position <= 0 || Steps <= 1) { return Color1; }
1478 if (Position >= Steps) { return Color2; } 1462 if (Position >= Steps) { return Color2; }
1479 return Color.FromArgb( 1463 return Color.FromArgb(
1480 Color1.R + ((Color2.R - Color1.R) / Steps * Position), 1464 Color1.R + ((Color2.R - Color1.R) / Steps * Position),
1481 Color1.G + ((Color2.G - Color1.G) / Steps * Position), 1465 Color1.G + ((Color2.G - Color1.G) / Steps * Position),
1482 Color1.B + ((Color2.B - Color1.B) / Steps * Position) 1466 Color1.B + ((Color2.B - Color1.B) / Steps * Position)
1483 ); 1467 );
1484 } 1468 }
1485   1469  
1486 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, 1470 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1487 CancellationToken cancellationToken) 1471 CancellationToken cancellationToken)
1488 { 1472 {
1489 var count = rows.Count; 1473 var count = rows.Count;
1490   1474  
1491 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index) 1475 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index)
1492 { 1476 {
1493 try 1477 try
1494 { 1478 {
1495 await _snapshotDatabase.RemoveFileAsync((string)rows[index].Cells["HashColumn"].Value, 1479 var hash = (string)rows[index].Cells["HashColumn"].Value;
-   1480  
1496 cancellationToken); 1481 await _snapshotDatabase.RemoveFileAsync(hash, cancellationToken);
1497   1482  
1498 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index)); 1483 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index));
1499 } 1484 }
1500 catch (Exception exception) 1485 catch (Exception exception)
1501 { 1486 {
1502 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception)); 1487 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception));
1503 } 1488 }
1504 } 1489 }
1505 } 1490 }
1506   1491  
1507 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken) 1492 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken)
1508 { 1493 {
1509 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value); 1494 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value);
-   1495  
-   1496 try
1510   1497 {
-   1498 await _snapshotDatabase.RemoveFileFastAsync(hashes, cancellationToken);
-   1499 }
-   1500 catch (Exception exception)
-   1501 {
-   1502 Log.Error(exception, "Failed to remove files.");
1511 await _snapshotDatabase.RemoveFileFastAsync(hashes, cancellationToken); 1503 }
1512 } 1504 }
1513   1505  
1514 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note, 1506 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note,
1515 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1507 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1516 { 1508 {
1517 var count = rows.Count; 1509 var count = rows.Count;
1518   1510  
1519 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1511 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1520 { 1512 {
1521 try 1513 try
1522 { 1514 {
1523 await _snapshotDatabase.UpdateNoteAsync((string)rows[i].Cells["HashColumn"].Value, note, 1515 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1516  
1524 cancellationToken); 1517 await _snapshotDatabase.UpdateNoteAsync(hash, note,cancellationToken);
1525   1518  
1526 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1519 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1527 } 1520 }
1528 catch (Exception exception) 1521 catch (Exception exception)
1529 { 1522 {
1530 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1523 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1531 } 1524 }
1532 } 1525 }
1533 } 1526 }
1534   1527  
1535 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView) 1528 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView)
1536 { 1529 {
1537 return dataGridView.SelectedRows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList(); 1530 return dataGridView.SelectedRows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList();
1538 } 1531 }
1539   1532  
1540 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView) 1533 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView)
1541 { 1534 {
1542 return dataGridView.Rows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList(); 1535 return dataGridView.Rows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList();
1543 } 1536 }
1544   1537  
1545 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows, 1538 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows,
1546 IProgress<DataGridViewRowProgress> progress, 1539 IProgress<DataGridViewRowProgress> progress,
1547 CancellationToken cancellationToken) 1540 CancellationToken cancellationToken)
1548 { 1541 {
1549 var count = rows.Count; 1542 var count = rows.Count;
1550   1543  
1551 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1544 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1552 { 1545 {
1553 try 1546 try
1554 { 1547 {
1555 await _snapshotDatabase.RemoveColorAsync((string)rows[i].Cells["HashColumn"].Value, 1548 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1549  
1556 cancellationToken); 1550 await _snapshotDatabase.RemoveColorAsync(hash, cancellationToken);
1557   1551  
1558 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1552 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1559 } 1553 }
1560 catch (Exception exception) 1554 catch (Exception exception)
1561 { 1555 {
1562 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1556 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1563 } 1557 }
1564 } 1558 }
1565 } 1559 }
1566   1560  
1567 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color, 1561 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color,
1568 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1562 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1569 { 1563 {
1570 var count = rows.Count; 1564 var count = rows.Count;
1571   1565  
1572 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1566 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1573 { 1567 {
1574 try 1568 try
1575 { 1569 {
1576 await _snapshotDatabase.UpdateColorAsync((string)rows[i].Cells["HashColumn"].Value, color, 1570 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1571  
1577 cancellationToken); 1572 await _snapshotDatabase.UpdateColorAsync(hash, color, cancellationToken);
1578   1573  
1579 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1574 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1580 } 1575 }
1581 catch (Exception exception) 1576 catch (Exception exception)
1582 { 1577 {
1583 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1578 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1584 } 1579 }
1585 } 1580 }
1586 } 1581 }
1587   1582  
1588 private async Task TransferFiles(IReadOnlyList<DataGridViewRow> rows, Service service, 1583 private async Task TransferFiles(IReadOnlyList<DataGridViewRow> rows, Service service,
1589 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1584 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1590 { 1585 {
1591 1586
1592 var client = new WatsonTcpClient($"{service.HostEntry.AddressList[0]}", service.UPort); 1587 var client = new WatsonTcpClient($"{service.HostEntry.AddressList[0]}", service.UPort);
1593 client.Events.MessageReceived += Events_MessageReceived; 1588 client.Events.MessageReceived += Events_MessageReceived;
1594 client.Events.ServerConnected += Events_ServerConnected; 1589 client.Events.ServerConnected += Events_ServerConnected;
1595 client.Events.ServerDisconnected += Events_ServerDisconnected; 1590 client.Events.ServerDisconnected += Events_ServerDisconnected;
1596 client.Events.ExceptionEncountered += Events_ExceptionEncountered; 1591 client.Events.ExceptionEncountered += Events_ExceptionEncountered;
1597   1592  
1598 try 1593 try
1599 { 1594 {
1600 client.Connect(); 1595 client.Connect();
1601   1596  
1602 var count = rows.Count; 1597 var count = rows.Count;
1603   1598  
1604 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1599 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1605 { 1600 {
1606 try 1601 try
1607 { 1602 {
-   1603 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1604  
1608 var completeSnapshot = 1605 var completeSnapshot =
1609 await _snapshotDatabase.GenerateTransferSnapshotAsync( 1606 await _snapshotDatabase.GenerateTransferSnapshotAsync(hash, cancellationToken);
1610 (string)rows[i].Cells["HashColumn"].Value, cancellationToken); -  
1611   1607  
1612 var jsonSnapshot = JsonConvert.SerializeObject(completeSnapshot); 1608 var jsonSnapshot = JsonConvert.SerializeObject(completeSnapshot);
1613   1609  
1614 await client.SendAsync(jsonSnapshot, null, cancellationToken); 1610 await client.SendAsync(jsonSnapshot, null, cancellationToken);
1615   1611  
1616 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1612 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1617 } 1613 }
1618 catch (Exception exception) 1614 catch (Exception exception)
1619 { 1615 {
1620 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1616 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1621 } 1617 }
1622 } 1618 }
1623 } 1619 }
1624 catch (Exception exception) when (exception is TimeoutException || exception is SocketException) 1620 catch (Exception exception) when (exception is TimeoutException || exception is SocketException)
1625 { 1621 {
1626 Log.Error(exception, "Client threw exception."); 1622 Log.Error(exception, "Client threw exception.");
1627 } 1623 }
1628 finally 1624 finally
1629 { 1625 {
1630 client.Events.MessageReceived -= Events_MessageReceived; 1626 client.Events.MessageReceived -= Events_MessageReceived;
1631 client.Events.ServerConnected -= Events_ServerConnected; 1627 client.Events.ServerConnected -= Events_ServerConnected;
1632 client.Events.ServerDisconnected -= Events_ServerDisconnected; 1628 client.Events.ServerDisconnected -= Events_ServerDisconnected;
1633 client.Events.ExceptionEncountered -= Events_ExceptionEncountered; 1629 client.Events.ExceptionEncountered -= Events_ExceptionEncountered;
1634   1630  
1635 client.Dispose(); 1631 client.Dispose();
1636 } 1632 }
1637   1633  
1638 } 1634 }
1639   1635  
1640 private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e) 1636 private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e)
1641 { 1637 {
1642 Log.Error(e.Exception, $"Client threw exception."); 1638 Log.Error(e.Exception, $"Client threw exception.");
1643 } 1639 }
1644   1640  
1645 private void Events_ServerDisconnected(object sender, DisconnectionEventArgs e) 1641 private void Events_ServerDisconnected(object sender, DisconnectionEventArgs e)
1646 { 1642 {
1647 Log.Information($"{e.Client?.IpPort} connected to server due to: {e.Reason}."); 1643 Log.Information($"{e.Client?.IpPort} connected to server due to: {e.Reason}.");
1648 } 1644 }
1649   1645  
1650 private void Events_MessageReceived(object sender, MessageReceivedEventArgs e) 1646 private void Events_MessageReceived(object sender, MessageReceivedEventArgs e)
1651 { 1647 {
1652 Log.Information($"{e.Data?.Length} byte long message received from {e.Client?.IpPort}"); 1648 Log.Information($"{e.Data?.Length} byte long message received from {e.Client?.IpPort}");
1653 } 1649 }
1654   1650  
1655 private void Events_ServerConnected(object sender, WatsonTcp.ConnectionEventArgs e) 1651 private void Events_ServerConnected(object sender, WatsonTcp.ConnectionEventArgs e)
1656 { 1652 {
1657 Log.Information($"{e.Client?.IpPort} connected to server."); 1653 Log.Information($"{e.Client?.IpPort} connected to server.");
1658 } 1654 }
1659   1655  
1660 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, 1656 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1661 CancellationToken cancellationToken) 1657 CancellationToken cancellationToken)
1662 { 1658 {
1663 var count = rows.Count; 1659 var count = rows.Count;
1664   1660  
1665 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1661 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1666 { 1662 {
1667 try 1663 try
1668 { 1664 {
1669 await _snapshotDatabase.RevertFileAsync((string)rows[i].Cells["NameColumn"].Value, 1665 var path = (string)rows[i].Cells["NameColumn"].Value;
1670 (string)rows[i].Cells["HashColumn"].Value, 1666 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1667  
1671 cancellationToken, _mainForm.Configuration.AtomicOperations); 1668 await _snapshotDatabase.RevertFileAsync(path, hash, cancellationToken, _mainForm.Configuration.AtomicOperations);
1672   1669  
1673 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1670 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1674 } 1671 }
1675 catch (Exception exception) 1672 catch (Exception exception)
1676 { 1673 {
1677 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1674 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1678 } 1675 }
1679 } 1676 }
1680 } 1677 }
1681   1678  
1682 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory, 1679 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory,
1683 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1680 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1684 { 1681 {
1685 var count = rows.Count; 1682 var count = rows.Count;
1686   1683  
1687 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1684 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1688 { 1685 {
1689 try 1686 try
1690 { 1687 {
-   1688 var hash = (string)rows[i].Cells["HashColumn"].Value;
1691 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value); 1689 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value);
1692 var file = fileInfo.Name; 1690 var file = fileInfo.Name;
1693 var path = Path.Combine(directory, file); 1691 var path = Path.Combine(directory, file);
1694   1692  
1695 await _snapshotDatabase.SaveFileAsync(path, (string)rows[i].Cells["HashColumn"].Value, -  
1696 cancellationToken); 1693 await _snapshotDatabase.SaveFileAsync(path, hash, cancellationToken);
1697   1694  
1698 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1695 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1699 } 1696 }
1700 catch (Exception exception) 1697 catch (Exception exception)
1701 { 1698 {
1702 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1699 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1703 } 1700 }
1704 } 1701 }
1705 } 1702 }
1706   1703  
1707 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory, 1704 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory,
1708 IProgress<DataGridViewRowProgress> progress, 1705 IProgress<DataGridViewRowProgress> progress,
1709 CancellationToken cancellationToken) 1706 CancellationToken cancellationToken)
1710 { 1707 {
1711 var count = rows.Count; 1708 var count = rows.Count;
1712   1709  
1713 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1710 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1714 { 1711 {
1715 try 1712 try
1716 { 1713 {
-   1714 var hash = (string)rows[i].Cells["HashColumn"].Value;
1717 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value); 1715 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value);
1718   1716  
1719 await _snapshotDatabase.RelocateFileAsync((string)rows[i].Cells["HashColumn"].Value, path, -  
1720 cancellationToken); 1717 await _snapshotDatabase.RelocateFileAsync(hash, path, cancellationToken);
1721   1718  
1722 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1719 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1723 } 1720 }
1724 catch (Exception exception) 1721 catch (Exception exception)
1725 { 1722 {
1726 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1723 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1727 } 1724 }
1728 } 1725 }
1729 } 1726 }
1730   1727  
1731 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows, 1728 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows,
1732 IProgress<DataGridViewRowProgress> progress, 1729 IProgress<DataGridViewRowProgress> progress,
1733 CancellationToken cancellationToken) 1730 CancellationToken cancellationToken)
1734 { 1731 {
1735 var count = rows.Count; 1732 var count = rows.Count;
1736   1733  
1737 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1734 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1738 { 1735 {
1739 try 1736 try
1740 { 1737 {
1741 using (var memoryStream = 1738 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1739  
1742 await _snapshotDatabase.RetrieveFileStreamAsync((string)rows[i].Cells["HashColumn"].Value, 1740 using var memoryStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, cancellationToken);
1743 cancellationToken)) 1741 if (memoryStream == null)
1744 { 1742 {
1745 if (memoryStream == null) -  
1746 { -  
1747 continue; 1743 continue;
1748 } 1744 }
1749   1745  
1750 using (var md5 = MD5.Create()) -  
1751 { -  
1752 var recomputedHash = md5.ComputeHash(memoryStream); -  
1753 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "") -  
-   1746 using var md5 = MD5.Create();
-   1747  
-   1748 var recomputedHash = md5.ComputeHash(memoryStream);
-   1749 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "")
1754 .ToLowerInvariant(); 1750 .ToLowerInvariant();
1755   1751
1756 await _snapshotDatabase.UpdateHashAsync((string)rows[i].Cells["HashColumn"].Value, hashHex, 1752 await _snapshotDatabase.UpdateHashAsync(hash, hashHex,
1757 cancellationToken); 1753 cancellationToken);
1758   -  
1759 rows[i].Cells["HashColumn"].Value = hashHex; -  
1760   1754  
1761 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1755 rows[i].Cells["HashColumn"].Value = hashHex;
1762 } 1756  
1763 } 1757 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1764 } 1758 }
1765 catch (Exception exception) 1759 catch (Exception exception)
1766 { 1760 {
1767 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1761 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1768 } 1762 }
1769 } 1763 }
1770 } 1764 }
1771   1765  
1772 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath, 1766 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath,
1773 IProgress<DataGridViewRowProgress> progress, 1767 IProgress<DataGridViewRowProgress> progress,
1774 CancellationToken cancellationToken) 1768 CancellationToken cancellationToken)
1775 { 1769 {
1776 var store = new HashSet<string>(); 1770 var store = new HashSet<string>();
1777   1771  
1778 var count = rows.Count; 1772 var count = rows.Count;
1779   1773  
1780 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1774 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1781 { 1775 {
1782 try 1776 try
1783 { 1777 {
1784 // C:\aa\bbb\fff\gg.txt 1778 // C:\aa\bbb\fff\gg.txt
1785 var rowPath = (string)rows[i].Cells["PathColumn"].Value; 1779 var rowPath = (string)rows[i].Cells["PathColumn"].Value;
1786 if (store.Contains(rowPath)) 1780 if (store.Contains(rowPath))
1787 { 1781 {
1788 continue; 1782 continue;
1789 } 1783 }
1790   1784  
1791 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\ 1785 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\
1792 if (!rowPath.IsPathEqual(basePath) && 1786 if (!rowPath.IsPathEqual(basePath) &&
1793 !rowPath.IsSubPathOf(basePath)) 1787 !rowPath.IsSubPathOf(basePath))
1794 { 1788 {
1795 continue; 1789 continue;
1796 } 1790 }
1797   1791  
1798 var rootPath = new DirectoryInfo(basePath).Name; 1792 var rootPath = new DirectoryInfo(basePath).Name;
1799 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\'); 1793 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\');
1800 var newPath = Path.Combine(targetPath, rootPath, relPath); 1794 var newPath = Path.Combine(targetPath, rootPath, relPath);
1801   1795  
1802 var hash = (string)rows[i].Cells["HashColumn"].Value; 1796 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1797  
1803 await _snapshotDatabase.SaveFileAsync(newPath, hash, cancellationToken); 1798 await _snapshotDatabase.SaveFileAsync(newPath, hash, cancellationToken);
1804   1799  
1805 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1800 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1806   1801  
1807 if (!store.Contains(rowPath)) 1802 if (!store.Contains(rowPath))
1808 { 1803 {
1809 store.Add(rowPath); 1804 store.Add(rowPath);
1810 } 1805 }
1811 } 1806 }
1812 catch (Exception exception) 1807 catch (Exception exception)
1813 { 1808 {
1814 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1809 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1815 } 1810 }
1816 } 1811 }
1817 } 1812 }
1818   1813  
1819 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows, 1814 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows,
1820 IProgress<DataGridViewRowProgress> progress, 1815 IProgress<DataGridViewRowProgress> progress,
1821 CancellationToken cancellationToken) 1816 CancellationToken cancellationToken)
1822 { 1817 {
1823 var count = rows.Count; 1818 var count = rows.Count;
1824   1819  
1825 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1820 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1826 { 1821 {
1827 try 1822 try
1828 { 1823 {
1829 await _snapshotDatabase.NormalizeTimeAsync((string)rows[i].Cells["HashColumn"].Value, 1824 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1825  
1830 cancellationToken); 1826 await _snapshotDatabase.NormalizeTimeAsync(hash, cancellationToken);
1831   1827  
1832 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1828 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1833 } 1829 }
1834 catch (Exception exception) 1830 catch (Exception exception)
1835 { 1831 {
1836 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1832 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1837 } 1833 }
1838 } 1834 }
1839 } 1835 }
1840   1836  
1841 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e) 1837 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
1842 { 1838 {
1843 var toolStripMenuItem = (ToolStripMenuItem)sender; 1839 var toolStripMenuItem = (ToolStripMenuItem)sender;
1844   1840  
1845 var rows = GetSelectedDataGridViewRows(dataGridView1); 1841 var rows = GetSelectedDataGridViewRows(dataGridView1);
1846   1842  
1847 var count = rows.Count; 1843 var count = rows.Count;
1848   1844  
1849 toolStripProgressBar1.Minimum = 0; 1845 toolStripProgressBar1.Minimum = 0;
1850 toolStripProgressBar1.Maximum = count; 1846 toolStripProgressBar1.Maximum = count;
1851   1847  
1852 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1848 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1853 { 1849 {
1854 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1850 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1855 { 1851 {
1856 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot."); 1852 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot.");
1857   1853  
1858 toolStripStatusLabel1.Text = 1854 toolStripStatusLabel1.Text =
1859 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1855 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}...";
1860 toolStripProgressBar1.Value = rowProgress.Index + 1; 1856 toolStripProgressBar1.Value = rowProgress.Index + 1;
1861   1857  
1862 statusStrip1.Update(); 1858 statusStrip1.Update();
1863   1859  
1864 return; 1860 return;
1865 } 1861 }
1866   1862  
1867 toolStripStatusLabel1.Text = 1863 toolStripStatusLabel1.Text =
1868 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}..."; 1864 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
1869 toolStripProgressBar1.Value = rowProgress.Index + 1; 1865 toolStripProgressBar1.Value = rowProgress.Index + 1;
1870   1866  
1871 statusStrip1.Update(); 1867 statusStrip1.Update();
1872 }); 1868 });
1873   1869  
1874 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken); 1870 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken);
1875   1871  
1876 if (_cancellationToken.IsCancellationRequested) 1872 if (_cancellationToken.IsCancellationRequested)
1877 { 1873 {
1878 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1874 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1879 toolStripStatusLabel1.Text = "Done."; 1875 toolStripStatusLabel1.Text = "Done.";
1880 } 1876 }
1881 } 1877 }
1882   1878  
1883 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows, 1879 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows,
1884 IProgress<DataGridViewRowProgress> progress, 1880 IProgress<DataGridViewRowProgress> progress,
1885 CancellationToken cancellationToken) 1881 CancellationToken cancellationToken)
1886 { 1882 {
1887 var count = rows.Count; 1883 var count = rows.Count;
1888   1884  
1889 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1885 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1890 { 1886 {
1891 try 1887 try
1892 { 1888 {
1893 await _snapshotDatabase.DeleteScreenshotAsync((string)rows[i].Cells["HashColumn"].Value, 1889 var hash = (string)rows[i].Cells["HashColumn"].Value;
-   1890  
1894 cancellationToken); 1891 await _snapshotDatabase.DeleteScreenshotAsync(hash, cancellationToken);
1895   1892  
1896 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1893 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1897 } 1894 }
1898 catch (Exception exception) 1895 catch (Exception exception)
1899 { 1896 {
1900 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1897 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1901 } 1898 }
1902 } 1899 }
1903 } 1900 }
1904   1901  
1905 #endregion 1902 #endregion
1906   1903  
1907 private void copyHashToolStripMenuItem_Click(object sender, EventArgs e) 1904 private void copyHashToolStripMenuItem_Click(object sender, EventArgs e)
1908 { 1905 {
1909 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 1906 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1910 if (row == null) 1907 if (row == null)
1911 { 1908 {
1912 return; 1909 return;
1913 } 1910 }
1914   1911  
1915 Clipboard.SetText($"{row.Cells["HashColumn"].Value}"); 1912 Clipboard.SetText($"{row.Cells["HashColumn"].Value}");
1916 1913
1917 } 1914 }
1918 } 1915 }
1919 } 1916 }
1920   1917  
1921
Generated by GNU Enscript 1.6.5.90.
1918
Generated by GNU Enscript 1.6.5.90.
1922   1919  
1923   1920  
1924   1921