Horizon – Diff between revs 11 and 12

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