Horizon – Blame information for rev 27

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