Horizon – Diff between revs 8 and 11

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