Horizon – Diff between revs 1 and 3

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 1 Rev 3
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 Serilog; 17 using Serilog;
18   18  
19 namespace Horizon.Snapshots 19 namespace Horizon.Snapshots
20 { 20 {
21 public partial class SnapshotManagerForm : Form 21 public partial class SnapshotManagerForm : Form
22 { 22 {
23 #region Static Fields and Constants 23 #region Static Fields and Constants
24   24  
25 private static ScheduledContinuation _searchTextBoxChangedContinuation; 25 private static ScheduledContinuation _searchTextBoxChangedContinuation;
26   26  
27 #endregion 27 #endregion
28   28  
29 #region Public Events & Delegates 29 #region Public Events & Delegates
30   30  
31 public event EventHandler<PreviewRetrievedEventArgs> PreviewRetrieved; 31 public event EventHandler<PreviewRetrievedEventArgs> PreviewRetrieved;
32   32  
33 #endregion 33 #endregion
34   34  
35 #region Private Delegates, Events, Enums, Properties, Indexers and Fields 35 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
36   36  
37 private readonly MainForm _mainForm; 37 private readonly MainForm _mainForm;
38   38  
39 private readonly SnapshotDatabase _snapshotDatabase; 39 private readonly SnapshotDatabase _snapshotDatabase;
40   40  
41 private HexViewForm _hexViewForm; 41 private HexViewForm _hexViewForm;
42   42  
43 private SnapshotNoteForm _snapshotNote; 43 private SnapshotNoteForm _snapshotNote;
44   44  
45 private SnapshotPreviewForm _snapshotPreviewForm; 45 private SnapshotPreviewForm _snapshotPreviewForm;
46   46  
47 private readonly object _mouseMoveLock = new object(); 47 private readonly object _mouseMoveLock = new object();
48   48  
49 private readonly CancellationTokenSource _cancellationTokenSource; 49 private readonly CancellationTokenSource _cancellationTokenSource;
50   50  
51 private readonly CancellationToken _cancellationToken; 51 private readonly CancellationToken _cancellationToken;
52   52  
53 private readonly CancellationTokenSource _localCancellationTokenSource; 53 private readonly CancellationTokenSource _localCancellationTokenSource;
54   54  
55 private readonly CancellationToken _localCancellationToken; 55 private readonly CancellationToken _localCancellationToken;
56   56  
57 #endregion 57 #endregion
58   58  
59 #region Constructors, Destructors and Finalizers 59 #region Constructors, Destructors and Finalizers
60   60  
61 public SnapshotManagerForm() 61 public SnapshotManagerForm()
62 { 62 {
63 InitializeComponent(); 63 InitializeComponent();
64 Utilities.WindowState.FormTracker.Track(this); 64 Utilities.WindowState.FormTracker.Track(this);
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(() => RetriveFileStream(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 RetriveFileStream(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.RetrieveFileStream(
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.RetrievePreview(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.CountSnapshots(_cancellationToken);
593   593  
594 var snapshotQueue = new ConcurrentQueue<Snapshot>(); 594 var snapshotQueue = new ConcurrentQueue<Snapshot>();
595 var snapshotsQueuedTaskCompletionSource = new TaskCompletionSource<object>(); 595 var snapshotsQueuedTaskCompletionSource = new TaskCompletionSource<object>();
596   596  
597 async void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs) 597 async void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
598 { 598 {
599 await snapshotsQueuedTaskCompletionSource.Task; 599 await snapshotsQueuedTaskCompletionSource.Task;
600   600  
601 try 601 try
602 { 602 {
603 if (!snapshotQueue.TryDequeue(out var snapshot)) 603 if (!snapshotQueue.TryDequeue(out var snapshot))
604 { 604 {
605 Application.Idle -= IdleHandler; 605 Application.Idle -= IdleHandler;
606   606  
607 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending); 607 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending);
608 toolStripStatusLabel1.Text = "Done."; 608 toolStripStatusLabel1.Text = "Done.";
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 Application.Idle += IdleHandler; 633 Application.Idle += IdleHandler;
634 try 634 try
635 { 635 {
636 foreach (var snapshot in await _snapshotDatabase.LoadSnapshots(_cancellationToken)) 636 foreach (var snapshot in await _snapshotDatabase.LoadSnapshots(_cancellationToken))
637 { 637 {
638 snapshotQueue.Enqueue(snapshot); 638 snapshotQueue.Enqueue(snapshot);
639 } 639 }
640   640  
641 snapshotsQueuedTaskCompletionSource.TrySetResult(new { }); 641 snapshotsQueuedTaskCompletionSource.TrySetResult(new { });
642 } 642 }
643 catch (Exception exception) 643 catch (Exception exception)
644 { 644 {
645 Application.Idle -= IdleHandler; 645 Application.Idle -= IdleHandler;
646   646  
647 Log.Error(exception, "Unable to load snapshots."); 647 Log.Error(exception, "Unable to load snapshots.");
648 } 648 }
649 } 649 }
650   650  
651 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e) 651 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e)
652 { 652 {
653 _cancellationTokenSource.Cancel(); 653 _cancellationTokenSource.Cancel();
654   654  
655 if (_snapshotPreviewForm != null) 655 if (_snapshotPreviewForm != null)
656 { 656 {
657 _snapshotPreviewForm.Close(); 657 _snapshotPreviewForm.Close();
658 _snapshotPreviewForm = null; 658 _snapshotPreviewForm = null;
659 } 659 }
660   660  
661 if (_hexViewForm != null) 661 if (_hexViewForm != null)
662 { 662 {
663 _hexViewForm.Close(); 663 _hexViewForm.Close();
664 _hexViewForm = null; 664 _hexViewForm = null;
665 } 665 }
666 } 666 }
667   667  
668 private void DataGridView1_DragEnter(object sender, DragEventArgs e) 668 private void DataGridView1_DragEnter(object sender, DragEventArgs e)
669 { 669 {
670 if (e.Data.GetDataPresent(DataFormats.FileDrop)) 670 if (e.Data.GetDataPresent(DataFormats.FileDrop))
671 { 671 {
672 e.Effect = DragDropEffects.Copy; 672 e.Effect = DragDropEffects.Copy;
673 return; 673 return;
674 } 674 }
675   675  
676 e.Effect = DragDropEffects.None; 676 e.Effect = DragDropEffects.None;
677 } 677 }
678   678  
679 private async void DataGridView1_DragDrop(object sender, DragEventArgs e) 679 private async void DataGridView1_DragDrop(object sender, DragEventArgs e)
680 { 680 {
681 if (!e.Data.GetDataPresent(DataFormats.FileDrop)) 681 if (!e.Data.GetDataPresent(DataFormats.FileDrop))
682 { 682 {
683 return; 683 return;
684 } 684 }
685   685  
686 var files = (string[])e.Data.GetData(DataFormats.FileDrop); 686 var files = (string[])e.Data.GetData(DataFormats.FileDrop);
687   687  
688 toolStripProgressBar1.Minimum = 0; 688 toolStripProgressBar1.Minimum = 0;
689 toolStripProgressBar1.Maximum = files.Length; 689 toolStripProgressBar1.Maximum = files.Length;
690 toolStripStatusLabel1.Text = "Snapshotting files..."; 690 toolStripStatusLabel1.Text = "Snapshotting files...";
691   691  
692 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 692 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
693 for (var i = 0; i < files.Length; ++i) 693 for (var i = 0; i < files.Length; ++i)
694 { 694 {
695 var color = Color.Empty; 695 var color = Color.Empty;
696 if (_mainForm.TrackedFolders.TryGet(files[i], out var folder)) 696 if (_mainForm.TrackedFolders.TryGet(files[i], out var folder))
697 { 697 {
698 color = folder.Color; 698 color = folder.Color;
699 } 699 }
700   700  
701 if (Directory.Exists(files[i])) 701 if (Directory.Exists(files[i]))
702 { 702 {
703 foreach (var directoryFile in Directory.GetFiles(files[i], "*.*", SearchOption.AllDirectories)) 703 foreach (var directoryFile in Directory.GetFiles(files[i], "*.*", SearchOption.AllDirectories))
704 { 704 {
705 var name = Path.GetFileName(directoryFile); 705 var name = Path.GetFileName(directoryFile);
706 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name); 706 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name);
707   707  
708 try 708 try
709 { 709 {
710 await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color, 710 await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color,
711 _cancellationToken); 711 _cancellationToken);
712   712  
713 toolStripProgressBar1.Value = i + 1; 713 toolStripProgressBar1.Value = i + 1;
714 toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}."; 714 toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}.";
715 statusStrip1.Update(); 715 statusStrip1.Update();
716 } 716 }
717 catch (SQLiteException exception) 717 catch (SQLiteException exception)
718 { 718 {
719 if (exception.ResultCode == SQLiteErrorCode.Constraint) 719 if (exception.ResultCode == SQLiteErrorCode.Constraint)
720 { 720 {
721 Log.Information(exception, "Snapshot already exists."); 721 Log.Information(exception, "Snapshot already exists.");
722   722  
723 toolStripProgressBar1.Value = i + 1; 723 toolStripProgressBar1.Value = i + 1;
724 toolStripStatusLabel1.Text = "Snapshot already exists."; 724 toolStripStatusLabel1.Text = "Snapshot already exists.";
725 statusStrip1.Update(); 725 statusStrip1.Update();
726 } 726 }
727 } 727 }
728 catch (Exception exception) 728 catch (Exception exception)
729 { 729 {
730 Log.Warning(exception, "Could not create snapshot."); 730 Log.Warning(exception, "Could not create snapshot.");
731 } 731 }
732 } 732 }
733   733  
734 continue; 734 continue;
735 } 735 }
736   736  
737 var fileName = Path.GetFileName(files[i]); 737 var fileName = Path.GetFileName(files[i]);
738 var pathName = Path.Combine(Path.GetDirectoryName(files[i]), fileName); 738 var pathName = Path.Combine(Path.GetDirectoryName(files[i]), fileName);
739   739  
740 try 740 try
741 { 741 {
742 await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color, 742 await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color,
743 _cancellationToken); 743 _cancellationToken);
744   744  
745 toolStripProgressBar1.Value = i + 1; 745 toolStripProgressBar1.Value = i + 1;
746 toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}."; 746 toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}.";
747 statusStrip1.Update(); 747 statusStrip1.Update();
748 } 748 }
749 catch (SQLiteException exception) 749 catch (SQLiteException exception)
750 { 750 {
751 if (exception.ResultCode == SQLiteErrorCode.Constraint) 751 if (exception.ResultCode == SQLiteErrorCode.Constraint)
752 { 752 {
753 Log.Information(exception, "Snapshot already exists."); 753 Log.Information(exception, "Snapshot already exists.");
754   754  
755 toolStripProgressBar1.Value = i + 1; 755 toolStripProgressBar1.Value = i + 1;
756 toolStripStatusLabel1.Text = "Snapshot already exists."; 756 toolStripStatusLabel1.Text = "Snapshot already exists.";
757 statusStrip1.Update(); 757 statusStrip1.Update();
758 } 758 }
759 } 759 }
760 catch (Exception exception) 760 catch (Exception exception)
761 { 761 {
762 Log.Warning(exception, "Could not create snapshot"); 762 Log.Warning(exception, "Could not create snapshot");
763 } 763 }
764 } 764 }
765 } 765 }
766   766  
767 private async void FileToolStripMenuItem_Click(object sender, EventArgs e) 767 private async void FileToolStripMenuItem_Click(object sender, EventArgs e)
768 { 768 {
769 var dialog = new CommonOpenFileDialog(); 769 var dialog = new CommonOpenFileDialog();
770 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 770 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
771 { 771 {
772 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 772 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
773   773  
774 var fileName = Path.GetFileName(dialog.FileName); 774 var fileName = Path.GetFileName(dialog.FileName);
775 var directory = Path.GetDirectoryName(dialog.FileName); 775 var directory = Path.GetDirectoryName(dialog.FileName);
776 var pathName = Path.Combine(directory, fileName); 776 var pathName = Path.Combine(directory, fileName);
777   777  
778 var color = Color.Empty; 778 var color = Color.Empty;
779 if (_mainForm.TrackedFolders.TryGet(directory, out var folder)) 779 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
780 { 780 {
781 color = folder.Color; 781 color = folder.Color;
782 } 782 }
783   783  
784 try 784 try
785 { 785 {
786 await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color, 786 await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color,
787 _cancellationToken); 787 _cancellationToken);
788 } 788 }
789 catch (SQLiteException exception) 789 catch (SQLiteException exception)
790 { 790 {
791 if (exception.ResultCode == SQLiteErrorCode.Constraint) 791 if (exception.ResultCode == SQLiteErrorCode.Constraint)
792 { 792 {
793 Log.Information(exception, "Snapshot already exists."); 793 Log.Information(exception, "Snapshot already exists.");
794 } 794 }
795 } 795 }
796 catch (Exception exception) 796 catch (Exception exception)
797 { 797 {
798 Log.Warning(exception, "Could not create snapshot."); 798 Log.Warning(exception, "Could not create snapshot.");
799 } 799 }
800 } 800 }
801 } 801 }
802   802  
803 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e) 803 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e)
804 { 804 {
805 var dialog = new CommonOpenFileDialog { IsFolderPicker = true }; 805 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
806 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 806 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
807 { 807 {
808 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); 808 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
809 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories)) 809 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories))
810 { 810 {
811 var name = Path.GetFileName(directoryFile); 811 var name = Path.GetFileName(directoryFile);
812 var directory = Path.GetDirectoryName(directoryFile); 812 var directory = Path.GetDirectoryName(directoryFile);
813 var path = Path.Combine(directory, name); 813 var path = Path.Combine(directory, name);
814   814  
815 var color = Color.Empty; 815 var color = Color.Empty;
816 if (_mainForm.TrackedFolders.TryGet(directory, out var folder)) 816 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
817 { 817 {
818 color = folder.Color; 818 color = folder.Color;
819 } 819 }
820   820  
821 try 821 try
822 { 822 {
823 await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color, _cancellationToken); 823 await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color, _cancellationToken);
824 } 824 }
825 catch (SQLiteException exception) 825 catch (SQLiteException exception)
826 { 826 {
827 if (exception.ResultCode == SQLiteErrorCode.Constraint) 827 if (exception.ResultCode == SQLiteErrorCode.Constraint)
828 { 828 {
829 Log.Information(exception, "Snapshot already exists."); 829 Log.Information(exception, "Snapshot already exists.");
830 } 830 }
831 } 831 }
832 catch (Exception exception) 832 catch (Exception exception)
833 { 833 {
834 Log.Warning(exception, "Could not create snapshot."); 834 Log.Warning(exception, "Could not create snapshot.");
835 } 835 }
836 } 836 }
837 } 837 }
838 } 838 }
839   839  
840 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e) 840 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e)
841 { 841 {
842 var commonOpenFileDialog = new CommonOpenFileDialog 842 var commonOpenFileDialog = new CommonOpenFileDialog
843 { 843 {
844 InitialDirectory = _mainForm.Configuration.LastFolder, 844 InitialDirectory = _mainForm.Configuration.LastFolder,
845 IsFolderPicker = true 845 IsFolderPicker = true
846 }; 846 };
847   847  
848 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok) 848 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok)
849 { 849 {
850 return; 850 return;
851 } 851 }
852   852  
853 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName; 853 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
854 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 854 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
855 async () => await _mainForm.SaveConfiguration(), _cancellationToken); 855 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
856   856  
857 var directory = commonOpenFileDialog.FileName; 857 var directory = commonOpenFileDialog.FileName;
858   858  
859 var rows = GetSelectedDataGridViewRows(dataGridView1); 859 var rows = GetSelectedDataGridViewRows(dataGridView1);
860   860  
861 var count = rows.Count; 861 var count = rows.Count;
862   862  
863 toolStripProgressBar1.Minimum = 0; 863 toolStripProgressBar1.Minimum = 0;
864 toolStripProgressBar1.Maximum = count; 864 toolStripProgressBar1.Maximum = count;
865   865  
866 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 866 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
867 { 867 {
868 var path = Path.Combine(directory, 868 var path = Path.Combine(directory,
869 (string)rowProgress.Row.Cells["NameColumn"].Value); 869 (string)rowProgress.Row.Cells["NameColumn"].Value);
870   870  
871 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 871 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
872 { 872 {
873 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot."); 873 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot.");
874   874  
875 toolStripStatusLabel1.Text = 875 toolStripStatusLabel1.Text =
876 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 876 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
877 toolStripProgressBar1.Value = rowProgress.Index + 1; 877 toolStripProgressBar1.Value = rowProgress.Index + 1;
878   878  
879 statusStrip1.Update(); 879 statusStrip1.Update();
880   880  
881 return; 881 return;
882 } 882 }
883   883  
884 rowProgress.Row.Cells["PathColumn"].Value = path; 884 rowProgress.Row.Cells["PathColumn"].Value = path;
885   885  
886 toolStripStatusLabel1.Text = 886 toolStripStatusLabel1.Text =
887 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 887 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
888 toolStripProgressBar1.Value = rowProgress.Index + 1; 888 toolStripProgressBar1.Value = rowProgress.Index + 1;
889   889  
890 statusStrip1.Update(); 890 statusStrip1.Update();
891 }); 891 });
892   892  
893 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken); 893 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken);
894   894  
895 if (!_cancellationToken.IsCancellationRequested) 895 if (_cancellationToken.IsCancellationRequested)
896 { 896 {
897 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 897 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
898 toolStripStatusLabel1.Text = "Done."; 898 toolStripStatusLabel1.Text = "Done.";
899 } 899 }
900 } 900 }
901   901  
902 private async void NoteToolStripMenuItem_Click(object sender, EventArgs e) 902 private async void NoteToolStripMenuItem_Click(object sender, EventArgs e)
903 { 903 {
904 if (_snapshotNote != null) 904 if (_snapshotNote != null)
905 { 905 {
906 return; 906 return;
907 } 907 }
908   908  
909 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 909 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
910 if (row == null) 910 if (row == null)
911 { 911 {
912 return; 912 return;
913 } 913 }
914   914  
915 try 915 try
916 { 916 {
917 var snapshotPreview = await _snapshotDatabase.RetrievePreview( 917 var snapshotPreview = await _snapshotDatabase.RetrievePreview(
918 (string)row.Cells["HashColumn"].Value, _cancellationToken); 918 (string)row.Cells["HashColumn"].Value, _cancellationToken);
919   919  
920 if (snapshotPreview == null) 920 if (snapshotPreview == null)
921 { 921 {
922 return; 922 return;
923 } 923 }
924   924  
925 _snapshotNote = new SnapshotNoteForm(this, snapshotPreview); 925 _snapshotNote = new SnapshotNoteForm(this, snapshotPreview);
926 _snapshotNote.Owner = this; 926 _snapshotNote.Owner = this;
927 _snapshotNote.SaveNote += SnapshotNote_SaveNote; 927 _snapshotNote.SaveNote += SnapshotNote_SaveNote;
928 _snapshotNote.Closing += SnapshotNote_Closing; 928 _snapshotNote.Closing += SnapshotNote_Closing;
929 _snapshotNote.Show(); 929 _snapshotNote.Show();
930 } 930 }
931 catch (Exception exception) 931 catch (Exception exception)
932 { 932 {
933 Log.Error(exception, "Could not open notes form."); 933 Log.Error(exception, "Could not open notes form.");
934 } 934 }
935 } 935 }
936   936  
937 private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e) 937 private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e)
938 { 938 {
939 var rows = GetSelectedDataGridViewRows(dataGridView1); 939 var rows = GetSelectedDataGridViewRows(dataGridView1);
940   940  
941 var count = rows.Count; 941 var count = rows.Count;
942   942  
943 toolStripProgressBar1.Minimum = 0; 943 toolStripProgressBar1.Minimum = 0;
944 toolStripProgressBar1.Maximum = count; 944 toolStripProgressBar1.Maximum = count;
945   945  
946 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 946 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
947 { 947 {
948 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 948 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
949 { 949 {
950 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot."); 950 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot.");
951   951  
952 toolStripStatusLabel1.Text = 952 toolStripStatusLabel1.Text =
953 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}..."; 953 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}...";
954 toolStripProgressBar1.Value = rowProgress.Index + 1; 954 toolStripProgressBar1.Value = rowProgress.Index + 1;
955   955  
956 statusStrip1.Update(); 956 statusStrip1.Update();
957   957  
958 return; 958 return;
959 } 959 }
960   960  
961 toolStripStatusLabel1.Text = 961 toolStripStatusLabel1.Text =
962 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}..."; 962 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}...";
963 toolStripProgressBar1.Value = rowProgress.Index + 1; 963 toolStripProgressBar1.Value = rowProgress.Index + 1;
964   964  
965 statusStrip1.Update(); 965 statusStrip1.Update();
966 }); 966 });
967   967  
968 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken); 968 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken);
969   969  
970 if (!_cancellationToken.IsCancellationRequested) 970 if (_cancellationToken.IsCancellationRequested)
971 { 971 {
972 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 972 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
973 toolStripStatusLabel1.Text = "Done."; 973 toolStripStatusLabel1.Text = "Done.";
974 } 974 }
975 } 975 }
976   976  
977 private void SnapshotNote_Closing(object sender, CancelEventArgs e) 977 private void SnapshotNote_Closing(object sender, CancelEventArgs e)
978 { 978 {
979 if (_snapshotNote == null) 979 if (_snapshotNote == null)
980 { 980 {
981 return; 981 return;
982 } 982 }
983   983  
984 _snapshotNote.Closing -= SnapshotNote_Closing; 984 _snapshotNote.Closing -= SnapshotNote_Closing;
985 _snapshotNote.Close(); 985 _snapshotNote.Close();
986 _snapshotNote = null; 986 _snapshotNote = null;
987 } 987 }
988   988  
989 private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e) 989 private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e)
990 { 990 {
991 var rows = GetSelectedDataGridViewRows(dataGridView1); 991 var rows = GetSelectedDataGridViewRows(dataGridView1);
992 var row = rows.FirstOrDefault(); 992 var row = rows.FirstOrDefault();
993 if (row == null) 993 if (row == null)
994 { 994 {
995 return; 995 return;
996 } 996 }
997   997  
998 var hash = (string)row.Cells["HashColumn"].Value; 998 var hash = (string)row.Cells["HashColumn"].Value;
999   999  
1000 using (var memoryStream = await _snapshotDatabase.RetrieveFileStream(hash, _cancellationToken)) 1000 using (var memoryStream = await _snapshotDatabase.RetrieveFileStream(hash, _cancellationToken))
1001 { 1001 {
1002 if (memoryStream == null) 1002 if (memoryStream == null)
1003 { 1003 {
1004 return; 1004 return;
1005 } 1005 }
1006   1006  
1007 if (_hexViewForm != null) 1007 if (_hexViewForm != null)
1008 { 1008 {
1009 _hexViewForm.UpdateData(memoryStream.ToArray()); 1009 _hexViewForm.UpdateData(memoryStream.ToArray());
1010 _hexViewForm.Activate(); 1010 _hexViewForm.Activate();
1011 return; 1011 return;
1012 } 1012 }
1013   1013  
1014 _hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray()); 1014 _hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray());
1015 _hexViewForm.Owner = this; 1015 _hexViewForm.Owner = this;
1016 _hexViewForm.Closing += HexViewForm_Closing; 1016 _hexViewForm.Closing += HexViewForm_Closing;
1017 _hexViewForm.SaveData += HexViewForm_SaveData; 1017 _hexViewForm.SaveData += HexViewForm_SaveData;
1018   1018  
1019 _hexViewForm.Show(); 1019 _hexViewForm.Show();
1020 } 1020 }
1021 } 1021 }
1022   1022  
1023 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e) 1023 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e)
1024 { 1024 {
1025 var hash = await _snapshotDatabase.UpdateFile(e.Hash, e.Data, _cancellationToken); 1025 var hash = await _snapshotDatabase.UpdateFile(e.Hash, e.Data, _cancellationToken);
1026   1026  
1027 if (string.IsNullOrEmpty(hash)) 1027 if (string.IsNullOrEmpty(hash))
1028 { 1028 {
1029 return; 1029 return;
1030 } 1030 }
1031   1031  
1032 dataGridView1.InvokeIfRequired(dataGridView => 1032 dataGridView1.InvokeIfRequired(dataGridView =>
1033 { 1033 {
1034 // Update the hash in the datagridview. 1034 // Update the hash in the datagridview.
1035 var removeRows = new List<DataGridViewRow>(); 1035 var removeRows = new List<DataGridViewRow>();
1036 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>()) 1036 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>())
1037 { 1037 {
1038 if ((string)row.Cells["HashColumn"].Value == hash) 1038 if ((string)row.Cells["HashColumn"].Value == hash)
1039 { 1039 {
1040 removeRows.Add(row); 1040 removeRows.Add(row);
1041 } 1041 }
1042   1042  
1043 if ((string)row.Cells["HashColumn"].Value != e.Hash) 1043 if ((string)row.Cells["HashColumn"].Value != e.Hash)
1044 { 1044 {
1045 continue; 1045 continue;
1046 } 1046 }
1047   1047  
1048 row.Cells["HashColumn"].Value = hash; 1048 row.Cells["HashColumn"].Value = hash;
1049 } 1049 }
1050   1050  
1051 // Remove rows that might have the same hash. 1051 // Remove rows that might have the same hash.
1052 foreach (var row in removeRows) 1052 foreach (var row in removeRows)
1053 { 1053 {
1054 dataGridView.Rows.Remove(row); 1054 dataGridView.Rows.Remove(row);
1055 } 1055 }
1056 }); 1056 });
1057 } 1057 }
1058   1058  
1059 private void HexViewForm_Closing(object sender, CancelEventArgs e) 1059 private void HexViewForm_Closing(object sender, CancelEventArgs e)
1060 { 1060 {
1061 if (_hexViewForm == null) 1061 if (_hexViewForm == null)
1062 { 1062 {
1063 return; 1063 return;
1064 } 1064 }
1065   1065  
1066 _hexViewForm.SaveData -= HexViewForm_SaveData; 1066 _hexViewForm.SaveData -= HexViewForm_SaveData;
1067 _hexViewForm.Closing -= HexViewForm_Closing; 1067 _hexViewForm.Closing -= HexViewForm_Closing;
1068 _hexViewForm.Close(); 1068 _hexViewForm.Close();
1069 _hexViewForm = null; 1069 _hexViewForm = null;
1070 } 1070 }
1071   1071  
1072 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e) 1072 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e)
1073 { 1073 {
1074 var commonOpenFileDialog = new CommonOpenFileDialog 1074 var commonOpenFileDialog = new CommonOpenFileDialog
1075 { 1075 {
1076 InitialDirectory = _mainForm.Configuration.LastFolder, 1076 InitialDirectory = _mainForm.Configuration.LastFolder,
1077 IsFolderPicker = true 1077 IsFolderPicker = true
1078 }; 1078 };
1079   1079  
1080 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok) 1080 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok)
1081 { 1081 {
1082 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName; 1082 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
1083 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 1083 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1084 async () => await _mainForm.SaveConfiguration(), _cancellationToken); 1084 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1085   1085  
1086 var directory = commonOpenFileDialog.FileName; 1086 var directory = commonOpenFileDialog.FileName;
1087   1087  
1088 var rows = GetSelectedDataGridViewRows(dataGridView1); 1088 var rows = GetSelectedDataGridViewRows(dataGridView1);
1089   1089  
1090 var count = rows.Count; 1090 var count = rows.Count;
1091   1091  
1092 toolStripProgressBar1.Minimum = 0; 1092 toolStripProgressBar1.Minimum = 0;
1093 toolStripProgressBar1.Maximum = count; 1093 toolStripProgressBar1.Maximum = count;
1094   1094  
1095 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1095 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1096 { 1096 {
1097 var fileInfo = 1097 var fileInfo =
1098 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value); 1098 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value);
1099 var file = fileInfo.Name; 1099 var file = fileInfo.Name;
1100 var path = Path.Combine(directory, file); 1100 var path = Path.Combine(directory, file);
1101   1101  
1102 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1102 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1103 { 1103 {
1104 Log.Error(rowProgressFailure.Exception, "Could not save snapshot."); 1104 Log.Error(rowProgressFailure.Exception, "Could not save snapshot.");
1105   1105  
1106 toolStripStatusLabel1.Text = 1106 toolStripStatusLabel1.Text =
1107 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1107 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1108 toolStripProgressBar1.Value = rowProgress.Index + 1; 1108 toolStripProgressBar1.Value = rowProgress.Index + 1;
1109   1109  
1110 statusStrip1.Update(); 1110 statusStrip1.Update();
1111   1111  
1112 return; 1112 return;
1113 } 1113 }
1114   1114  
1115 toolStripStatusLabel1.Text = 1115 toolStripStatusLabel1.Text =
1116 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}..."; 1116 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1117 toolStripProgressBar1.Value = rowProgress.Index + 1; 1117 toolStripProgressBar1.Value = rowProgress.Index + 1;
1118   1118  
1119 statusStrip1.Update(); 1119 statusStrip1.Update();
1120 }); 1120 });
1121   1121  
1122 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken); 1122 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken);
1123   1123  
1124 if (!_cancellationToken.IsCancellationRequested) 1124 if (_cancellationToken.IsCancellationRequested)
1125 { 1125 {
1126 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1126 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1127 toolStripStatusLabel1.Text = "Done."; 1127 toolStripStatusLabel1.Text = "Done.";
1128 } 1128 }
1129 } 1129 }
1130 } 1130 }
1131   1131  
1132 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e) 1132 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e)
1133 { 1133 {
1134 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); 1134 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1135   1135  
1136 if (select == null) 1136 if (select == null)
1137 { 1137 {
1138 return; 1138 return;
1139 } 1139 }
1140   1140  
1141 // C:\aa\bbb\dd.txt 1141 // C:\aa\bbb\dd.txt
1142 var path = (string)select.Cells["PathColumn"].Value; 1142 var path = (string)select.Cells["PathColumn"].Value;
1143   1143  
1144 // C:\aa\bbb\ 1144 // C:\aa\bbb\
1145 var basePath = Path.GetDirectoryName(path); 1145 var basePath = Path.GetDirectoryName(path);
1146   1146  
1147 var dialog = new CommonOpenFileDialog { IsFolderPicker = true }; 1147 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
1148 if (dialog.ShowDialog() == CommonFileDialogResult.Ok) 1148 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
1149 { 1149 {
1150 //Log.Information(dialog.FileName); 1150 //Log.Information(dialog.FileName);
1151 var rows = GetAllDataGridViewRows(dataGridView1); 1151 var rows = GetAllDataGridViewRows(dataGridView1);
1152 var count = rows.Count; 1152 var count = rows.Count;
1153   1153  
1154 toolStripProgressBar1.Minimum = 0; 1154 toolStripProgressBar1.Minimum = 0;
1155 toolStripProgressBar1.Maximum = count; 1155 toolStripProgressBar1.Maximum = count;
1156 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1156 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1157 { 1157 {
1158 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1158 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1159 { 1159 {
1160 Log.Error(rowProgressFailure.Exception, "Could not save file."); 1160 Log.Error(rowProgressFailure.Exception, "Could not save file.");
1161   1161  
1162 toolStripStatusLabel1.Text = 1162 toolStripStatusLabel1.Text =
1163 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}..."; 1163 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1164 toolStripProgressBar1.Value = rowProgress.Index + 1; 1164 toolStripProgressBar1.Value = rowProgress.Index + 1;
1165   1165  
1166 statusStrip1.Update(); 1166 statusStrip1.Update();
1167   1167  
1168 return; 1168 return;
1169 } 1169 }
1170   1170  
1171 toolStripStatusLabel1.Text = 1171 toolStripStatusLabel1.Text =
1172 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}..."; 1172 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1173 toolStripProgressBar1.Value = rowProgress.Index + 1; 1173 toolStripProgressBar1.Value = rowProgress.Index + 1;
1174   1174  
1175 statusStrip1.Update(); 1175 statusStrip1.Update();
1176 }); 1176 });
1177   1177  
1178 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken), 1178 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken),
1179 _cancellationToken); 1179 _cancellationToken);
1180   1180  
1181 if (!_cancellationToken.IsCancellationRequested) 1181 if (_cancellationToken.IsCancellationRequested)
1182 { 1182 {
1183 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1183 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1184 toolStripStatusLabel1.Text = "Done."; 1184 toolStripStatusLabel1.Text = "Done.";
1185 } 1185 }
1186 } 1186 }
1187 } 1187 }
1188   1188  
1189 private void TextBox1_TextChanged(object sender, EventArgs e) 1189 private void TextBox1_TextChanged(object sender, EventArgs e)
1190 { 1190 {
1191 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () => 1191 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
1192 { 1192 {
1193 textBox1.InvokeIfRequired(textBox => 1193 textBox1.InvokeIfRequired(textBox =>
1194 { 1194 {
1195 var search = textBox.Text; 1195 var search = textBox.Text;
1196   1196  
1197 dataGridView1.InvokeIfRequired(dataGridView => 1197 dataGridView1.InvokeIfRequired(dataGridView =>
1198 { 1198 {
1199 foreach (var row in GetAllDataGridViewRows(dataGridView)) 1199 foreach (var row in GetAllDataGridViewRows(dataGridView))
1200 { 1200 {
1201 if(row.Cells["PathColumn"].Value == null) 1201 if(row.Cells["PathColumn"].Value == null)
1202 { 1202 {
1203 continue; 1203 continue;
1204 } 1204 }
1205   1205  
1206 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search, 1206 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search,
1207 StringComparison.OrdinalIgnoreCase)) 1207 StringComparison.OrdinalIgnoreCase))
1208 { 1208 {
1209 case -1: 1209 case -1:
1210 row.Visible = false; 1210 row.Visible = false;
1211 break; 1211 break;
1212 default: 1212 default:
1213 row.Visible = true; 1213 row.Visible = true;
1214 break; 1214 break;
1215 } 1215 }
1216 } 1216 }
1217 }); 1217 });
1218 }); 1218 });
1219 }, _cancellationToken); 1219 }, _cancellationToken);
1220 } 1220 }
1221   1221  
1222 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e) 1222 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e)
1223 { 1223 {
1224 var rows = GetSelectedDataGridViewRows(dataGridView1); 1224 var rows = GetSelectedDataGridViewRows(dataGridView1);
1225   1225  
1226 var count = rows.Count; 1226 var count = rows.Count;
1227   1227  
1228 toolStripProgressBar1.Minimum = 0; 1228 toolStripProgressBar1.Minimum = 0;
1229 toolStripProgressBar1.Maximum = count; 1229 toolStripProgressBar1.Maximum = count;
1230   1230  
1231 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1231 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1232 { 1232 {
1233 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1233 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1234 { 1234 {
1235 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot."); 1235 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot.");
1236   1236  
1237 toolStripStatusLabel1.Text = 1237 toolStripStatusLabel1.Text =
1238 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1238 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1239 toolStripProgressBar1.Value = rowProgress.Index + 1; 1239 toolStripProgressBar1.Value = rowProgress.Index + 1;
1240   1240  
1241 statusStrip1.Update(); 1241 statusStrip1.Update();
1242   1242  
1243 return; 1243 return;
1244 } 1244 }
1245   1245  
1246 toolStripStatusLabel1.Text = 1246 toolStripStatusLabel1.Text =
1247 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1247 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1248 toolStripProgressBar1.Value = rowProgress.Index + 1; 1248 toolStripProgressBar1.Value = rowProgress.Index + 1;
1249   1249  
1250 statusStrip1.Update(); 1250 statusStrip1.Update();
1251 }); 1251 });
1252   1252  
1253 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken); 1253 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken);
1254   1254  
1255 if (!_cancellationToken.IsCancellationRequested) 1255 if (_cancellationToken.IsCancellationRequested)
1256 { 1256 {
1257 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1257 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1258 toolStripStatusLabel1.Text = "Done."; 1258 toolStripStatusLabel1.Text = "Done.";
1259 } 1259 }
1260 } 1260 }
1261   1261  
1262 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e) 1262 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e)
1263 { 1263 {
1264 var rows = GetSelectedDataGridViewRows(dataGridView1); 1264 var rows = GetSelectedDataGridViewRows(dataGridView1);
1265   1265  
1266 var count = rows.Count; 1266 var count = rows.Count;
1267   1267  
1268 toolStripProgressBar1.Minimum = 0; 1268 toolStripProgressBar1.Minimum = 0;
1269 toolStripProgressBar1.Maximum = count; 1269 toolStripProgressBar1.Maximum = count;
1270   1270  
1271 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1271 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1272 { 1272 {
1273 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1273 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1274 { 1274 {
1275 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot."); 1275 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot.");
1276   1276  
1277 toolStripStatusLabel1.Text = 1277 toolStripStatusLabel1.Text =
1278 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1278 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1279 toolStripProgressBar1.Value = rowProgress.Index + 1; 1279 toolStripProgressBar1.Value = rowProgress.Index + 1;
1280   1280  
1281 statusStrip1.Update(); 1281 statusStrip1.Update();
1282   1282  
1283 return; 1283 return;
1284 } 1284 }
1285   1285  
1286 toolStripStatusLabel1.Text = 1286 toolStripStatusLabel1.Text =
1287 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1287 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1288 toolStripProgressBar1.Value = rowProgress.Index + 1; 1288 toolStripProgressBar1.Value = rowProgress.Index + 1;
1289   1289  
1290 statusStrip1.Update(); 1290 statusStrip1.Update();
1291 }); 1291 });
1292   1292  
1293 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken); 1293 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken);
1294   1294  
1295 if (!_cancellationToken.IsCancellationRequested) 1295 if (_cancellationToken.IsCancellationRequested)
1296 { 1296 {
1297 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1297 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1298 toolStripStatusLabel1.Text = "Done."; 1298 toolStripStatusLabel1.Text = "Done.";
1299 } 1299 }
1300 } 1300 }
1301   1301  
1302 #endregion 1302 #endregion
1303   1303  
1304 #region Private Methods 1304 #region Private Methods
1305   1305  
1306 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, 1306 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1307 CancellationToken cancellationToken) 1307 CancellationToken cancellationToken)
1308 { 1308 {
1309 var count = rows.Count; 1309 var count = rows.Count;
1310   1310  
1311 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index) 1311 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index)
1312 { 1312 {
1313 try 1313 try
1314 { 1314 {
1315 await _snapshotDatabase.RemoveFile((string)rows[index].Cells["HashColumn"].Value, 1315 await _snapshotDatabase.RemoveFile((string)rows[index].Cells["HashColumn"].Value,
1316 cancellationToken); 1316 cancellationToken);
1317   1317  
1318 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index)); 1318 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index));
1319 } 1319 }
1320 catch (Exception exception) 1320 catch (Exception exception)
1321 { 1321 {
1322 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception)); 1322 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception));
1323 } 1323 }
1324 } 1324 }
1325 } 1325 }
1326   1326  
1327 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken) 1327 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken)
1328 { 1328 {
1329 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value); 1329 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value);
1330   1330  
1331 await _snapshotDatabase.RemoveFileFast(hashes, cancellationToken); 1331 await _snapshotDatabase.RemoveFileFast(hashes, cancellationToken);
1332 } 1332 }
1333   1333  
1334 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note, 1334 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note,
1335 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1335 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1336 { 1336 {
1337 var count = rows.Count; 1337 var count = rows.Count;
1338   1338  
1339 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1339 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1340 { 1340 {
1341 try 1341 try
1342 { 1342 {
1343 await _snapshotDatabase.UpdateNote((string)rows[i].Cells["HashColumn"].Value, note, 1343 await _snapshotDatabase.UpdateNote((string)rows[i].Cells["HashColumn"].Value, note,
1344 cancellationToken); 1344 cancellationToken);
1345   1345  
1346 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1346 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1347 } 1347 }
1348 catch (Exception exception) 1348 catch (Exception exception)
1349 { 1349 {
1350 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1350 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1351 } 1351 }
1352 } 1352 }
1353 } 1353 }
1354   1354  
1355 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView) 1355 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView)
1356 { 1356 {
1357 return dataGridView.SelectedRows.OfType<DataGridViewRow>().ToList(); 1357 return dataGridView.SelectedRows.OfType<DataGridViewRow>().ToList();
1358 } 1358 }
1359   1359  
1360 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView) 1360 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView)
1361 { 1361 {
1362 return dataGridView.Rows.OfType<DataGridViewRow>().ToList(); 1362 return dataGridView.Rows.OfType<DataGridViewRow>().ToList();
1363 } 1363 }
1364   1364  
1365 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows, 1365 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows,
1366 IProgress<DataGridViewRowProgress> progress, 1366 IProgress<DataGridViewRowProgress> progress,
1367 CancellationToken cancellationToken) 1367 CancellationToken cancellationToken)
1368 { 1368 {
1369 var count = rows.Count; 1369 var count = rows.Count;
1370   1370  
1371 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1371 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1372 { 1372 {
1373 try 1373 try
1374 { 1374 {
1375 await _snapshotDatabase.RemoveColor((string)rows[i].Cells["HashColumn"].Value, 1375 await _snapshotDatabase.RemoveColor((string)rows[i].Cells["HashColumn"].Value,
1376 cancellationToken); 1376 cancellationToken);
1377   1377  
1378 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1378 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1379 } 1379 }
1380 catch (Exception exception) 1380 catch (Exception exception)
1381 { 1381 {
1382 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1382 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1383 } 1383 }
1384 } 1384 }
1385 } 1385 }
1386   1386  
1387 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color, 1387 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color,
1388 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1388 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1389 { 1389 {
1390 var count = rows.Count; 1390 var count = rows.Count;
1391   1391  
1392 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1392 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1393 { 1393 {
1394 try 1394 try
1395 { 1395 {
1396 await _snapshotDatabase.UpdateColor((string)rows[i].Cells["HashColumn"].Value, color, 1396 await _snapshotDatabase.UpdateColor((string)rows[i].Cells["HashColumn"].Value, color,
1397 cancellationToken); 1397 cancellationToken);
1398   1398  
1399 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1399 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1400 } 1400 }
1401 catch (Exception exception) 1401 catch (Exception exception)
1402 { 1402 {
1403 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1403 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1404 } 1404 }
1405 } 1405 }
1406 } 1406 }
1407   1407  
1408 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, 1408 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1409 CancellationToken cancellationToken) 1409 CancellationToken cancellationToken)
1410 { 1410 {
1411 var count = rows.Count; 1411 var count = rows.Count;
1412   1412  
1413 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1413 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1414 { 1414 {
1415 try 1415 try
1416 { 1416 {
1417 await _snapshotDatabase.RevertFile((string)rows[i].Cells["NameColumn"].Value, 1417 await _snapshotDatabase.RevertFile((string)rows[i].Cells["NameColumn"].Value,
1418 (string)rows[i].Cells["HashColumn"].Value, 1418 (string)rows[i].Cells["HashColumn"].Value,
1419 cancellationToken, _mainForm.Configuration.AtomicOperations); 1419 cancellationToken, _mainForm.Configuration.AtomicOperations);
1420   1420  
1421 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1421 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1422 } 1422 }
1423 catch (Exception exception) 1423 catch (Exception exception)
1424 { 1424 {
1425 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1425 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1426 } 1426 }
1427 } 1427 }
1428 } 1428 }
1429   1429  
1430 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory, 1430 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory,
1431 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) 1431 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1432 { 1432 {
1433 var count = rows.Count; 1433 var count = rows.Count;
1434   1434  
1435 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1435 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1436 { 1436 {
1437 try 1437 try
1438 { 1438 {
1439 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value); 1439 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value);
1440 var file = fileInfo.Name; 1440 var file = fileInfo.Name;
1441 var path = Path.Combine(directory, file); 1441 var path = Path.Combine(directory, file);
1442   1442  
1443 await _snapshotDatabase.SaveFile(path, (string)rows[i].Cells["HashColumn"].Value, 1443 await _snapshotDatabase.SaveFile(path, (string)rows[i].Cells["HashColumn"].Value,
1444 cancellationToken); 1444 cancellationToken);
1445   1445  
1446 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1446 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1447 } 1447 }
1448 catch (Exception exception) 1448 catch (Exception exception)
1449 { 1449 {
1450 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1450 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1451 } 1451 }
1452 } 1452 }
1453 } 1453 }
1454   1454  
1455 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory, 1455 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory,
1456 IProgress<DataGridViewRowProgress> progress, 1456 IProgress<DataGridViewRowProgress> progress,
1457 CancellationToken cancellationToken) 1457 CancellationToken cancellationToken)
1458 { 1458 {
1459 var count = rows.Count; 1459 var count = rows.Count;
1460   1460  
1461 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1461 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1462 { 1462 {
1463 try 1463 try
1464 { 1464 {
1465 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value); 1465 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value);
1466   1466  
1467 await _snapshotDatabase.RelocateFile((string)rows[i].Cells["HashColumn"].Value, path, 1467 await _snapshotDatabase.RelocateFile((string)rows[i].Cells["HashColumn"].Value, path,
1468 cancellationToken); 1468 cancellationToken);
1469   1469  
1470 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1470 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1471 } 1471 }
1472 catch (Exception exception) 1472 catch (Exception exception)
1473 { 1473 {
1474 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1474 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1475 } 1475 }
1476 } 1476 }
1477 } 1477 }
1478   1478  
1479 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows, 1479 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows,
1480 IProgress<DataGridViewRowProgress> progress, 1480 IProgress<DataGridViewRowProgress> progress,
1481 CancellationToken cancellationToken) 1481 CancellationToken cancellationToken)
1482 { 1482 {
1483 var count = rows.Count; 1483 var count = rows.Count;
1484   1484  
1485 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1485 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1486 { 1486 {
1487 try 1487 try
1488 { 1488 {
1489 using (var memoryStream = 1489 using (var memoryStream =
1490 await _snapshotDatabase.RetrieveFileStream((string)rows[i].Cells["HashColumn"].Value, 1490 await _snapshotDatabase.RetrieveFileStream((string)rows[i].Cells["HashColumn"].Value,
1491 cancellationToken)) 1491 cancellationToken))
1492 { 1492 {
1493 if (memoryStream == null) 1493 if (memoryStream == null)
1494 { 1494 {
1495 continue; 1495 continue;
1496 } 1496 }
1497   1497  
1498 using (var md5 = MD5.Create()) 1498 using (var md5 = MD5.Create())
1499 { 1499 {
1500 var recomputedHash = md5.ComputeHash(memoryStream); 1500 var recomputedHash = md5.ComputeHash(memoryStream);
1501 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "") 1501 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "")
1502 .ToLowerInvariant(); 1502 .ToLowerInvariant();
1503   1503  
1504 await _snapshotDatabase.UpdateHash((string)rows[i].Cells["HashColumn"].Value, hashHex, 1504 await _snapshotDatabase.UpdateHash((string)rows[i].Cells["HashColumn"].Value, hashHex,
1505 cancellationToken); 1505 cancellationToken);
1506   1506  
1507 rows[i].Cells["HashColumn"].Value = hashHex; 1507 rows[i].Cells["HashColumn"].Value = hashHex;
1508   1508  
1509 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1509 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1510 } 1510 }
1511 } 1511 }
1512 } 1512 }
1513 catch (Exception exception) 1513 catch (Exception exception)
1514 { 1514 {
1515 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1515 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1516 } 1516 }
1517 } 1517 }
1518 } 1518 }
1519   1519  
1520 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath, 1520 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath,
1521 IProgress<DataGridViewRowProgress> progress, 1521 IProgress<DataGridViewRowProgress> progress,
1522 CancellationToken cancellationToken) 1522 CancellationToken cancellationToken)
1523 { 1523 {
1524 var store = new HashSet<string>(); 1524 var store = new HashSet<string>();
1525   1525  
1526 var count = rows.Count; 1526 var count = rows.Count;
1527   1527  
1528 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1528 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1529 { 1529 {
1530 try 1530 try
1531 { 1531 {
1532 // C:\aa\bbb\fff\gg.txt 1532 // C:\aa\bbb\fff\gg.txt
1533 var rowPath = (string)rows[i].Cells["PathColumn"].Value; 1533 var rowPath = (string)rows[i].Cells["PathColumn"].Value;
1534 if (store.Contains(rowPath)) 1534 if (store.Contains(rowPath))
1535 { 1535 {
1536 continue; 1536 continue;
1537 } 1537 }
1538   1538  
1539 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\ 1539 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\
1540 if (!rowPath.IsPathEqual(basePath) && 1540 if (!rowPath.IsPathEqual(basePath) &&
1541 !rowPath.IsSubPathOf(basePath)) 1541 !rowPath.IsSubPathOf(basePath))
1542 { 1542 {
1543 continue; 1543 continue;
1544 } 1544 }
1545   1545  
1546 var rootPath = new DirectoryInfo(basePath).Name; 1546 var rootPath = new DirectoryInfo(basePath).Name;
1547 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\'); 1547 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\');
1548 var newPath = Path.Combine(targetPath, rootPath, relPath); 1548 var newPath = Path.Combine(targetPath, rootPath, relPath);
1549   1549  
1550 var hash = (string)rows[i].Cells["HashColumn"].Value; 1550 var hash = (string)rows[i].Cells["HashColumn"].Value;
1551 await _snapshotDatabase.SaveFile(newPath, hash, cancellationToken); 1551 await _snapshotDatabase.SaveFile(newPath, hash, cancellationToken);
1552   1552  
1553 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1553 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1554   1554  
1555 if (!store.Contains(rowPath)) 1555 if (!store.Contains(rowPath))
1556 { 1556 {
1557 store.Add(rowPath); 1557 store.Add(rowPath);
1558 } 1558 }
1559 } 1559 }
1560 catch (Exception exception) 1560 catch (Exception exception)
1561 { 1561 {
1562 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1562 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1563 } 1563 }
1564 } 1564 }
1565 } 1565 }
1566   1566  
1567 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows, 1567 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows,
1568 IProgress<DataGridViewRowProgress> progress, 1568 IProgress<DataGridViewRowProgress> progress,
1569 CancellationToken cancellationToken) 1569 CancellationToken cancellationToken)
1570 { 1570 {
1571 var count = rows.Count; 1571 var count = rows.Count;
1572   1572  
1573 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1573 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1574 { 1574 {
1575 try 1575 try
1576 { 1576 {
1577 await _snapshotDatabase.NormalizeTime((string)rows[i].Cells["HashColumn"].Value, 1577 await _snapshotDatabase.NormalizeTime((string)rows[i].Cells["HashColumn"].Value,
1578 cancellationToken); 1578 cancellationToken);
1579   1579  
1580 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1580 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1581 } 1581 }
1582 catch (Exception exception) 1582 catch (Exception exception)
1583 { 1583 {
1584 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1584 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1585 } 1585 }
1586 } 1586 }
1587 } 1587 }
1588   1588  
1589 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e) 1589 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
1590 { 1590 {
1591 var toolStripMenuItem = (ToolStripMenuItem)sender; 1591 var toolStripMenuItem = (ToolStripMenuItem)sender;
1592   1592  
1593 var rows = GetSelectedDataGridViewRows(dataGridView1); 1593 var rows = GetSelectedDataGridViewRows(dataGridView1);
1594   1594  
1595 var count = rows.Count; 1595 var count = rows.Count;
1596   1596  
1597 toolStripProgressBar1.Minimum = 0; 1597 toolStripProgressBar1.Minimum = 0;
1598 toolStripProgressBar1.Maximum = count; 1598 toolStripProgressBar1.Maximum = count;
1599   1599  
1600 var progress = new Progress<DataGridViewRowProgress>(rowProgress => 1600 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1601 { 1601 {
1602 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) 1602 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1603 { 1603 {
1604 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot."); 1604 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot.");
1605   1605  
1606 toolStripStatusLabel1.Text = 1606 toolStripStatusLabel1.Text =
1607 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}..."; 1607 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}...";
1608 toolStripProgressBar1.Value = rowProgress.Index + 1; 1608 toolStripProgressBar1.Value = rowProgress.Index + 1;
1609   1609  
1610 statusStrip1.Update(); 1610 statusStrip1.Update();
1611   1611  
1612 return; 1612 return;
1613 } 1613 }
1614   1614  
1615 toolStripStatusLabel1.Text = 1615 toolStripStatusLabel1.Text =
1616 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}..."; 1616 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
1617 toolStripProgressBar1.Value = rowProgress.Index + 1; 1617 toolStripProgressBar1.Value = rowProgress.Index + 1;
1618   1618  
1619 statusStrip1.Update(); 1619 statusStrip1.Update();
1620 }); 1620 });
1621   1621  
1622 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken); 1622 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken);
1623   1623  
1624 if (!_cancellationToken.IsCancellationRequested) 1624 if (_cancellationToken.IsCancellationRequested)
1625 { 1625 {
1626 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; 1626 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1627 toolStripStatusLabel1.Text = "Done."; 1627 toolStripStatusLabel1.Text = "Done.";
1628 } 1628 }
1629 } 1629 }
1630   1630  
1631 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows, 1631 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows,
1632 IProgress<DataGridViewRowProgress> progress, 1632 IProgress<DataGridViewRowProgress> progress,
1633 CancellationToken cancellationToken) 1633 CancellationToken cancellationToken)
1634 { 1634 {
1635 var count = rows.Count; 1635 var count = rows.Count;
1636   1636  
1637 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) 1637 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1638 { 1638 {
1639 try 1639 try
1640 { 1640 {
1641 await _snapshotDatabase.DeleteScreenshot((string)rows[i].Cells["HashColumn"].Value, 1641 await _snapshotDatabase.DeleteScreenshot((string)rows[i].Cells["HashColumn"].Value,
1642 cancellationToken); 1642 cancellationToken);
1643   1643  
1644 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); 1644 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1645 } 1645 }
1646 catch (Exception exception) 1646 catch (Exception exception)
1647 { 1647 {
1648 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); 1648 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1649 } 1649 }
1650 } 1650 }
1651 } 1651 }
1652   1652  
1653 #endregion 1653 #endregion
1654   1654  
1655 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) 1655 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
1656 { 1656 {
1657 DataGridView dataGridView = sender as DataGridView; 1657 DataGridView dataGridView = sender as DataGridView;
1658 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells) 1658 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells)
1659 { 1659 {
1660 if (cell.Selected == false) { continue; } 1660 if (cell.Selected == false) { continue; }
1661 var bgColorCell = Color.White; 1661 var bgColorCell = Color.White;
1662 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; } 1662 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; }
1663 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; } 1663 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; }
1664 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4); 1664 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4);
1665 } 1665 }
1666 } 1666 }
1667   1667  
1668 //Mix two colors 1668 //Mix two colors
1669 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1 1669 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1
1670 /// <summary> 1670 /// <summary>
1671 /// Mix two colors. 1671 /// Mix two colors.
1672 /// </summary> 1672 /// </summary>
1673 /// <param name="Color1"></param> 1673 /// <param name="Color1"></param>
1674 /// <param name="Color2"></param> 1674 /// <param name="Color2"></param>
1675 /// <param name="Steps"></param> 1675 /// <param name="Steps"></param>
1676 /// <param name="Position"></param> 1676 /// <param name="Position"></param>
1677 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example> 1677 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example>
1678 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks> 1678 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks>
1679 /// <returns></returns> 1679 /// <returns></returns>
1680 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position) 1680 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position)
1681 { 1681 {
1682 if (Position <= 0 || Steps <= 1) { return Color1; } 1682 if (Position <= 0 || Steps <= 1) { return Color1; }
1683 if (Position >= Steps) { return Color2; } 1683 if (Position >= Steps) { return Color2; }
1684 return Color.FromArgb( 1684 return Color.FromArgb(
1685 Color1.R + ((Color2.R - Color1.R) / Steps * Position), 1685 Color1.R + ((Color2.R - Color1.R) / Steps * Position),
1686 Color1.G + ((Color2.G - Color1.G) / Steps * Position), 1686 Color1.G + ((Color2.G - Color1.G) / Steps * Position),
1687 Color1.B + ((Color2.B - Color1.B) / Steps * Position) 1687 Color1.B + ((Color2.B - Color1.B) / Steps * Position)
1688 ); 1688 );
1689 } 1689 }
1690 } 1690 }
1691 } 1691 }
1692   1692  
1693
Generated by GNU Enscript 1.6.5.90.
1693
Generated by GNU Enscript 1.6.5.90.
1694   1694  
1695   1695  
1696   1696