/Horizon/Snapshots/SnapshotManagerForm.cs |
@@ -6,11 +6,8 @@ |
using System.Diagnostics; |
using System.Drawing; |
using System.IO; |
using System.IO.Ports; |
using System.Linq; |
using System.Net.Sockets; |
using System.Security.Cryptography; |
using System.Text; |
using System.Threading; |
using System.Threading.Tasks; |
using System.Windows.Forms; |
@@ -17,13 +14,7 @@ |
using Horizon.Database; |
using Horizon.Utilities; |
using Microsoft.WindowsAPICodePack.Dialogs; |
using Mono.Zeroconf; |
using Mono.Zeroconf.Providers.Bonjour; |
using Newtonsoft.Json; |
using Org.BouncyCastle.Crypto; |
using Org.BouncyCastle.Utilities.Net; |
using Serilog; |
using WatsonTcp; |
|
namespace Horizon.Snapshots |
{ |
@@ -35,6 +26,12 @@ |
|
#endregion |
|
#region Public Events & Delegates |
|
public event EventHandler<PreviewRetrievedEventArgs> PreviewRetrieved; |
|
#endregion |
|
#region Private Delegates, Events, Enums, Properties, Indexers and Fields |
|
private readonly MainForm _mainForm; |
@@ -43,7 +40,7 @@ |
|
private HexViewForm _hexViewForm; |
|
private SnapshotNoteForm _snapshotNoteForm; |
private SnapshotNoteForm _snapshotNote; |
|
private SnapshotPreviewForm _snapshotPreviewForm; |
|
@@ -57,19 +54,14 @@ |
|
private readonly CancellationToken _localCancellationToken; |
|
private readonly Mono.Zeroconf.ServiceBrowser _horizonServiceBrowser; |
|
private IResolvableService _resolvedService; |
|
private readonly ConcurrentDictionary<string, Service> _discoveredHorizonNetworkShares; |
|
#endregion |
|
#region Constructors, Destructors and Finalizers |
|
private SnapshotManagerForm() |
public SnapshotManagerForm() |
{ |
InitializeComponent(); |
Utilities.WindowState.FormTracker.Track(this); |
|
dataGridView1.Columns["TimeColumn"].ValueType = typeof(DateTime); |
|
@@ -77,10 +69,6 @@ |
|
_localCancellationTokenSource = new CancellationTokenSource(); |
_localCancellationToken = _localCancellationTokenSource.Token; |
|
_discoveredHorizonNetworkShares = new ConcurrentDictionary<string, Service>(); |
_horizonServiceBrowser = new Mono.Zeroconf.ServiceBrowser(); |
_horizonServiceBrowser.ServiceAdded += OnHorizonServiceBrowserOnServiceAdded; |
} |
|
public SnapshotManagerForm(MainForm mainForm, SnapshotDatabase snapshotDatabase, |
@@ -89,7 +77,6 @@ |
_mainForm = mainForm; |
_snapshotDatabase = snapshotDatabase; |
_snapshotDatabase.SnapshotCreate += SnapshotManager_SnapshotCreate; |
_snapshotDatabase.SnapshotTransferReceived += _snapshotDatabase_SnapshotTransferReceived; |
|
_cancellationTokenSource = |
CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, cancellationToken); |
@@ -108,10 +95,7 @@ |
} |
|
_snapshotDatabase.SnapshotCreate -= SnapshotManager_SnapshotCreate; |
_snapshotDatabase.SnapshotTransferReceived -= _snapshotDatabase_SnapshotTransferReceived; |
|
_horizonServiceBrowser.ServiceAdded -= OnHorizonServiceBrowserOnServiceAdded; |
_horizonServiceBrowser.Dispose(); |
_localCancellationTokenSource.Cancel(); |
|
base.Dispose(disposing); |
@@ -120,14 +104,6 @@ |
#endregion |
|
#region Event Handlers |
private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) |
{ |
} |
|
private void contextMenuStrip1_Opened(object sender, EventArgs e) |
{ |
} |
|
private void DataGridView1_MouseDown(object sender, MouseEventArgs e) |
{ |
var dataGridView = (DataGridView)sender; |
@@ -234,9 +210,9 @@ |
} |
}); |
|
await Task.Run(() => RetrieveFileStream(rows, progress, _cancellationToken), _cancellationToken); |
await Task.Run(() => RetriveFileStream(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -255,7 +231,7 @@ |
} |
} |
|
private async Task RetrieveFileStream(IReadOnlyList<DataGridViewRow> rows, |
private async Task RetriveFileStream(IReadOnlyList<DataGridViewRow> rows, |
IProgress<DataGridViewRowProgress> progress, |
CancellationToken cancellationToken) |
{ |
@@ -265,10 +241,10 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
var fileStream = await _snapshotDatabase.RetrieveFileStream( |
(string)rows[i].Cells["HashColumn"].Value, |
cancellationToken); |
|
var fileStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccessRetrieveFileStream(rows[i], i, fileStream)); |
} |
catch (Exception exception) |
@@ -280,7 +256,7 @@ |
|
private void SnapshotManagerForm_Resize(object sender, EventArgs e) |
{ |
if (_snapshotPreviewForm is { Visible: true }) |
if (_snapshotPreviewForm != null) |
{ |
_snapshotPreviewForm.WindowState = WindowState; |
} |
@@ -297,10 +273,18 @@ |
Process.Start("explorer.exe", $"/select, \"{(string)row.Cells["PathColumn"].Value}\""); |
} |
|
private void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) |
private async void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) |
{ |
var dataGridView = (DataGridView)sender; |
|
if (_snapshotPreviewForm == null) |
{ |
_snapshotPreviewForm = new SnapshotPreviewForm(this, _snapshotDatabase); |
_snapshotPreviewForm.Owner = this; |
_snapshotPreviewForm.Closing += SnapshotPreviewForm_Closing; |
_snapshotPreviewForm.Show(); |
} |
|
var row = GetSelectedDataGridViewRows(dataGridView).FirstOrDefault(); |
if (row == null) |
{ |
@@ -309,26 +293,27 @@ |
|
var hash = (string)row.Cells["HashColumn"].Value; |
|
if (_snapshotPreviewForm is { Visible: true }) |
var snapshotPreview = |
await Task.Run(async () => await _snapshotDatabase.RetrievePreview(hash, _cancellationToken), |
_cancellationToken); |
|
if (snapshotPreview == null) |
{ |
_snapshotPreviewForm.Close(); |
return; |
} |
|
_snapshotPreviewForm = new SnapshotPreviewForm(this, hash, _snapshotDatabase, _cancellationToken); |
_snapshotPreviewForm.Owner = this; |
_snapshotPreviewForm.Closing += SnapshotPreviewForm_Closing; |
_snapshotPreviewForm.Show(); |
PreviewRetrieved?.Invoke(this, new PreviewRetrievedEventArgs(snapshotPreview)); |
} |
|
private void SnapshotPreviewForm_Closing(object sender, CancelEventArgs e) |
{ |
if (_snapshotPreviewForm is { Visible: false }) |
if (_snapshotPreviewForm == null) |
{ |
return; |
} |
|
_snapshotPreviewForm.Closing -= SnapshotPreviewForm_Closing; |
_snapshotPreviewForm.Dispose(); |
_snapshotPreviewForm = null; |
} |
|
private async void NoneToolStripMenuItem_Click(object sender, EventArgs e) |
@@ -366,7 +351,7 @@ |
|
await Task.Run(() => RemoveColorFiles(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -411,7 +396,7 @@ |
|
await Task.Run(() => ColorFiles(rows, color, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -458,7 +443,7 @@ |
|
await Task.Run(() => DeleteFiles(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -500,7 +485,6 @@ |
dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash; |
dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color; |
|
dataGridView.Rows[index].Selected = true; |
dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending); |
}); |
break; |
@@ -510,33 +494,6 @@ |
} |
} |
|
|
private void _snapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e) |
{ |
switch (e) |
{ |
case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs: |
dataGridView1.InvokeIfRequired(dataGridView => |
{ |
var index = dataGridView.Rows.Add(); |
|
dataGridView.Rows[index].Cells["TimeColumn"].Value = |
DateTime.Parse(snapshotCreateSuccessEventArgs.Time); |
dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name; |
dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path; |
dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash; |
dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color; |
|
dataGridView.Rows[index].Selected = true; |
dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending); |
}); |
break; |
case SnapshotCreateFailureEventArgs snapshotCreateFailure: |
Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot."); |
break; |
} |
} |
|
private void RevertToThisToolStripMenuItem_Click(object sender, EventArgs e) |
{ |
_mainForm.InvokeIfRequired(async form => |
@@ -602,7 +559,7 @@ |
|
await Task.Run(() => RevertFile(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -631,45 +588,25 @@ |
|
private async void SnapshotManagerForm_Load(object sender, EventArgs e) |
{ |
// Start browsing for network services. |
_horizonServiceBrowser.Browse("_horizon._tcp", "local"); |
|
int count; |
try |
{ |
count = (int)await _snapshotDatabase.CountSnapshotsAsync(_cancellationToken); |
} |
catch (Exception exception) |
{ |
Log.Error(exception, "Could not count snapshots."); |
return; |
} |
|
if (count == 0) |
{ |
return; |
} |
|
// Load snapshots. |
toolStripProgressBar1.Minimum = 0; |
toolStripProgressBar1.Maximum = count; |
toolStripProgressBar1.Maximum = (int)await _snapshotDatabase.CountSnapshots(_cancellationToken); |
|
var snapshotQueue = new ConcurrentQueue<Snapshot>(); |
var snapshotsQueuedTaskCompletionSource = new TaskCompletionSource<object>(); |
|
void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs) |
async void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs) |
{ |
await snapshotsQueuedTaskCompletionSource.Task; |
|
try |
{ |
if (snapshotQueue.IsEmpty) |
if (!snapshotQueue.TryDequeue(out var snapshot)) |
{ |
Application.Idle -= IdleHandler; |
|
dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending); |
toolStripStatusLabel1.Text = "Done."; |
} |
|
if (!snapshotQueue.TryDequeue(out var snapshot)) |
{ |
return; |
} |
|
@@ -692,15 +629,16 @@ |
Log.Error(exception, "Could not update data grid view."); |
} |
} |
|
|
Application.Idle += IdleHandler; |
try |
{ |
await foreach (var snapshot in _snapshotDatabase.LoadSnapshotsAsync(_cancellationToken).WithCancellation(_cancellationToken)) |
foreach (var snapshot in await _snapshotDatabase.LoadSnapshots(_cancellationToken)) |
{ |
snapshotQueue.Enqueue(snapshot); |
} |
|
Application.Idle += IdleHandler; |
snapshotsQueuedTaskCompletionSource.TrySetResult(new { }); |
} |
catch (Exception exception) |
{ |
@@ -710,96 +648,20 @@ |
} |
} |
|
private void OnHorizonServiceBrowserOnServiceAdded(object o, Mono.Zeroconf.ServiceBrowseEventArgs args) |
{ |
_resolvedService = args.Service; |
|
Log.Information("Found Service: {0}", _resolvedService.Name); |
|
_resolvedService.Resolved += OnServiceOnResolved; |
|
_resolvedService.Resolve(); |
|
_resolvedService.Resolved -= OnServiceOnResolved; |
} |
|
private void OnServiceOnResolved(object o, ServiceResolvedEventArgs args) |
{ |
var service = (Service) args.Service; |
|
Log.Information("Resolved Service: {0} - {1}:{2} ({3} TXT record entries)", service.FullName, service.HostEntry.AddressList[0], service.UPort, service.TxtRecord.Count); |
|
// Do not add discovered services more than once. |
if (!_discoveredHorizonNetworkShares.TryAdd(service.Name, service)) |
{ |
return; |
} |
|
var discoveredServiceMenuItem = new ToolStripMenuItem(service.Name, null, OnShareWithNodeClicked); |
discoveredServiceMenuItem.Tag = service; |
|
this.InvokeIfRequired(form => |
{ |
form.shareToToolStripMenuItem.DropDownItems.Add(discoveredServiceMenuItem); |
}); |
} |
|
private async void OnShareWithNodeClicked(object sender, EventArgs e) |
{ |
var toolStripMenuItem = (ToolStripMenuItem)sender; |
|
var service = (Service)toolStripMenuItem.Tag; |
|
var rows = GetSelectedDataGridViewRows(dataGridView1); |
|
var count = rows.Count; |
|
toolStripProgressBar1.Minimum = 0; |
toolStripProgressBar1.Maximum = count; |
|
var progress = new Progress<DataGridViewRowProgress>(rowProgress => |
{ |
if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure) |
{ |
Log.Error(rowProgressFailure.Exception, "Unable to transfer data."); |
|
toolStripStatusLabel1.Text = |
$"Could not transfer {rowProgress.Row.Cells["NameColumn"].Value}..."; |
toolStripProgressBar1.Value = rowProgress.Index + 1; |
|
statusStrip1.Update(); |
|
return; |
} |
|
toolStripStatusLabel1.Text = |
$"Transferred {rowProgress.Row.Cells["NameColumn"].Value}..."; |
toolStripProgressBar1.Value = rowProgress.Index + 1; |
|
statusStrip1.Update(); |
}); |
|
await Task.Run(() => TransferFiles(rows, service, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
} |
} |
|
private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e) |
{ |
_cancellationTokenSource.Cancel(); |
|
if (_snapshotPreviewForm is { Visible: true }) |
if (_snapshotPreviewForm != null) |
{ |
_snapshotPreviewForm.Close(); |
_snapshotPreviewForm = null; |
} |
|
if (_hexViewForm is { Visible: true }) |
if (_hexViewForm != null) |
{ |
_hexViewForm.Close(); |
_hexViewForm = null; |
} |
} |
|
@@ -814,20 +676,31 @@ |
e.Effect = DragDropEffects.None; |
} |
|
private void CreateSnapshots(IReadOnlyList<string> files, Bitmap screenCapture, TrackedFolders.TrackedFolders trackedFolders, IProgress<CreateSnapshotProgress> progress, CancellationToken cancellationToken) |
private async void DataGridView1_DragDrop(object sender, DragEventArgs e) |
{ |
Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 512 }, async file => |
if (!e.Data.GetDataPresent(DataFormats.FileDrop)) |
{ |
return; |
} |
|
var files = (string[])e.Data.GetData(DataFormats.FileDrop); |
|
toolStripProgressBar1.Minimum = 0; |
toolStripProgressBar1.Maximum = files.Length; |
toolStripStatusLabel1.Text = "Snapshotting files..."; |
|
var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); |
for (var i = 0; i < files.Length; ++i) |
{ |
var color = Color.Empty; |
if (_mainForm.TrackedFolders.TryGet(file, out var folder)) |
if (_mainForm.TrackedFolders.TryGet(files[i], out var folder)) |
{ |
color = folder.Color; |
} |
|
var fileInfo = File.GetAttributes(file); |
if (fileInfo.HasFlag(FileAttributes.Directory)) |
if (Directory.Exists(files[i])) |
{ |
foreach (var directoryFile in Directory.GetFiles(file, "*.*", SearchOption.AllDirectories)) |
foreach (var directoryFile in Directory.GetFiles(files[i], "*.*", SearchOption.AllDirectories)) |
{ |
var name = Path.GetFileName(directoryFile); |
var path = Path.Combine(Path.GetDirectoryName(directoryFile), name); |
@@ -834,78 +707,61 @@ |
|
try |
{ |
await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken); |
await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color, |
_cancellationToken); |
|
progress.Report(new CreateSnapshotProgressSuccess(file)); |
toolStripProgressBar1.Value = i + 1; |
toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}."; |
statusStrip1.Update(); |
} |
catch (SQLiteException exception) |
{ |
if (exception.ResultCode == SQLiteErrorCode.Constraint) |
{ |
Log.Information(exception, "Snapshot already exists."); |
|
toolStripProgressBar1.Value = i + 1; |
toolStripStatusLabel1.Text = "Snapshot already exists."; |
statusStrip1.Update(); |
} |
} |
catch (Exception exception) |
{ |
progress.Report(new CreateSnapshotProgressFailure(file, exception)); |
Log.Warning(exception, "Could not create snapshot."); |
} |
} |
|
return; |
continue; |
} |
|
var fileName = Path.GetFileName(file); |
var fileName = Path.GetFileName(files[i]); |
var pathName = Path.Combine(Path.GetDirectoryName(files[i]), fileName); |
|
var pathName = Path.Combine(Path.GetDirectoryName(file), fileName); |
|
try |
{ |
await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color, |
await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color, |
_cancellationToken); |
|
progress.Report(new CreateSnapshotProgressSuccess(file)); |
toolStripProgressBar1.Value = i + 1; |
toolStripStatusLabel1.Text = $"Snapshot taken of {files[i]}."; |
statusStrip1.Update(); |
} |
catch (SQLiteException exception) |
{ |
if (exception.ResultCode == SQLiteErrorCode.Constraint) |
{ |
Log.Information(exception, "Snapshot already exists."); |
|
toolStripProgressBar1.Value = i + 1; |
toolStripStatusLabel1.Text = "Snapshot already exists."; |
statusStrip1.Update(); |
} |
} |
catch (Exception exception) |
{ |
progress.Report(new CreateSnapshotProgressFailure(file, exception)); |
Log.Warning(exception, "Could not create snapshot"); |
} |
}); |
} |
private async void DataGridView1_DragDrop(object sender, DragEventArgs e) |
{ |
if (!e.Data.GetDataPresent(DataFormats.FileDrop)) |
{ |
return; |
} |
|
var files = (string[])e.Data.GetData(DataFormats.FileDrop); |
|
toolStripProgressBar1.Minimum = 0; |
toolStripProgressBar1.Maximum = files.Length; |
toolStripStatusLabel1.Text = "Snapshotting files..."; |
|
var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode); |
|
var progress = new Progress<CreateSnapshotProgress>(createSnapshotProgress => |
{ |
switch (createSnapshotProgress) |
{ |
case CreateSnapshotProgressSuccess createSnapshotProgressSuccess: |
toolStripStatusLabel1.Text = $"Snapshot taken of {createSnapshotProgressSuccess.File}."; |
break; |
case CreateSnapshotProgressFailure createSnapshotProgressFailure: |
if (createSnapshotProgressFailure.Exception is SQLiteException { ResultCode: SQLiteErrorCode.Constraint }) |
{ |
toolStripStatusLabel1.Text = $"Snapshot of file {createSnapshotProgressFailure.File} already exists."; |
break; |
} |
|
toolStripStatusLabel1.Text = $"Could not snapshot file {createSnapshotProgressFailure.File}"; |
Log.Warning(createSnapshotProgressFailure.Exception, $"Could not snapshot file {createSnapshotProgressFailure.File}"); |
break; |
} |
|
toolStripProgressBar1.Increment(1); |
statusStrip1.Update(); |
}); |
|
await Task.Factory.StartNew( () => |
{ |
CreateSnapshots(files, screenCapture, _mainForm.TrackedFolders, progress, _cancellationToken); |
}, _cancellationToken); |
} |
|
private async void FileToolStripMenuItem_Click(object sender, EventArgs e) |
@@ -927,7 +783,7 @@ |
|
try |
{ |
await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color, |
await _snapshotDatabase.CreateSnapshot(fileName, pathName, screenCapture, color, |
_cancellationToken); |
} |
catch (SQLiteException exception) |
@@ -964,7 +820,7 @@ |
|
try |
{ |
await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken); |
await _snapshotDatabase.CreateSnapshot(name, path, screenCapture, color, _cancellationToken); |
} |
catch (SQLiteException exception) |
{ |
@@ -1036,7 +892,7 @@ |
|
await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1043,8 +899,13 @@ |
} |
} |
|
private void NoteToolStripMenuItem_Click(object sender, EventArgs e) |
private async void NoteToolStripMenuItem_Click(object sender, EventArgs e) |
{ |
if (_snapshotNote != null) |
{ |
return; |
} |
|
var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); |
if (row == null) |
{ |
@@ -1051,21 +912,29 @@ |
return; |
} |
|
var hash = (string)row.Cells["HashColumn"].Value; |
try |
{ |
var snapshotPreview = await _snapshotDatabase.RetrievePreview( |
(string)row.Cells["HashColumn"].Value, _cancellationToken); |
|
if (_snapshotNoteForm is { Visible: true }) |
if (snapshotPreview == null) |
{ |
return; |
} |
|
_snapshotNote = new SnapshotNoteForm(this, snapshotPreview); |
_snapshotNote.Owner = this; |
_snapshotNote.SaveNote += SnapshotNote_SaveNote; |
_snapshotNote.Closing += SnapshotNote_Closing; |
_snapshotNote.Show(); |
} |
catch (Exception exception) |
{ |
_snapshotNoteForm.Close(); |
Log.Error(exception, "Could not open notes form."); |
} |
|
_snapshotNoteForm = new SnapshotNoteForm(hash, _snapshotDatabase, _cancellationToken); |
_snapshotNoteForm.Owner = this; |
_snapshotNoteForm.SaveNote += SnapshotNoteFormSaveNoteForm; |
_snapshotNoteForm.Closing += SnapshotNoteForm_Closing; |
_snapshotNoteForm.Show(); |
} |
|
private async void SnapshotNoteFormSaveNoteForm(object sender, SaveNoteEventArgs e) |
private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e) |
{ |
var rows = GetSelectedDataGridViewRows(dataGridView1); |
|
@@ -1098,7 +967,7 @@ |
|
await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1105,19 +974,19 @@ |
} |
} |
|
private void SnapshotNoteForm_Closing(object sender, CancelEventArgs e) |
private void SnapshotNote_Closing(object sender, CancelEventArgs e) |
{ |
if (_snapshotNoteForm is { Visible: false }) |
if (_snapshotNote == null) |
{ |
return; |
} |
|
_snapshotNoteForm.SaveNote -= SnapshotNoteFormSaveNoteForm; |
_snapshotNoteForm.Closing -= SnapshotNoteForm_Closing; |
_snapshotNoteForm.Dispose(); |
_snapshotNote.Closing -= SnapshotNote_Closing; |
_snapshotNote.Close(); |
_snapshotNote = null; |
} |
|
private void ViewHexToolStripMenuItem_Click(object sender, EventArgs e) |
private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e) |
{ |
var rows = GetSelectedDataGridViewRows(dataGridView1); |
var row = rows.FirstOrDefault(); |
@@ -1128,32 +997,33 @@ |
|
var hash = (string)row.Cells["HashColumn"].Value; |
|
if (_hexViewForm is { Visible: true }) |
using (var memoryStream = await _snapshotDatabase.RetrieveFileStream(hash, _cancellationToken)) |
{ |
_hexViewForm.Close(); |
if (memoryStream == null) |
{ |
return; |
} |
|
if (_hexViewForm != null) |
{ |
_hexViewForm.UpdateData(memoryStream.ToArray()); |
_hexViewForm.Activate(); |
return; |
} |
|
_hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray()); |
_hexViewForm.Owner = this; |
_hexViewForm.Closing += HexViewForm_Closing; |
_hexViewForm.SaveData += HexViewForm_SaveData; |
|
_hexViewForm.Show(); |
} |
|
_hexViewForm = new HexViewForm(hash, _snapshotDatabase, _cancellationToken); |
_hexViewForm.Owner = this; |
_hexViewForm.Closing += HexViewForm_Closing; |
_hexViewForm.SaveData += HexViewForm_SaveData; |
_hexViewForm.Show(); |
} |
|
private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e) |
{ |
string hash; |
var hash = await _snapshotDatabase.UpdateFile(e.Hash, e.Data, _cancellationToken); |
|
try |
{ |
hash = await _snapshotDatabase.UpdateFileAsync(e.Hash, e.Data, _cancellationToken); |
} |
catch (Exception exception) |
{ |
Log.Error(exception, "Could not update snapshot data."); |
return; |
} |
|
if (string.IsNullOrEmpty(hash)) |
{ |
return; |
@@ -1188,7 +1058,7 @@ |
|
private void HexViewForm_Closing(object sender, CancelEventArgs e) |
{ |
if (_hexViewForm is { Visible: false }) |
if (_hexViewForm == null) |
{ |
return; |
} |
@@ -1195,7 +1065,8 @@ |
|
_hexViewForm.SaveData -= HexViewForm_SaveData; |
_hexViewForm.Closing -= HexViewForm_Closing; |
_hexViewForm.Dispose(); |
_hexViewForm.Close(); |
_hexViewForm = null; |
} |
|
private async void FileToolStripMenuItem2_Click(object sender, EventArgs e) |
@@ -1250,7 +1121,7 @@ |
|
await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1307,7 +1178,7 @@ |
await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken), |
_cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1381,7 +1252,7 @@ |
|
await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1421,7 +1292,7 @@ |
|
await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1431,42 +1302,7 @@ |
#endregion |
|
#region Private Methods |
private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) |
{ |
DataGridView dataGridView = sender as DataGridView; |
foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells) |
{ |
if (cell.Selected == false) { continue; } |
var bgColorCell = Color.White; |
if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; } |
else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; } |
cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4); |
} |
} |
|
//Mix two colors |
//Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1 |
/// <summary> |
/// Mix two colors. |
/// </summary> |
/// <param name="Color1"></param> |
/// <param name="Color2"></param> |
/// <param name="Steps"></param> |
/// <param name="Position"></param> |
/// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example> |
/// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks> |
/// <returns></returns> |
public static Color MixColor(Color Color1, Color Color2, int Steps, int Position) |
{ |
if (Position <= 0 || Steps <= 1) { return Color1; } |
if (Position >= Steps) { return Color2; } |
return Color.FromArgb( |
Color1.R + ((Color2.R - Color1.R) / Steps * Position), |
Color1.G + ((Color2.G - Color1.G) / Steps * Position), |
Color1.B + ((Color2.B - Color1.B) / Steps * Position) |
); |
} |
|
private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, |
CancellationToken cancellationToken) |
{ |
@@ -1476,10 +1312,9 @@ |
{ |
try |
{ |
var hash = (string)rows[index].Cells["HashColumn"].Value; |
await _snapshotDatabase.RemoveFile((string)rows[index].Cells["HashColumn"].Value, |
cancellationToken); |
|
await _snapshotDatabase.RemoveFileAsync(hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[index], index)); |
} |
catch (Exception exception) |
@@ -1493,14 +1328,7 @@ |
{ |
var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value); |
|
try |
{ |
await _snapshotDatabase.RemoveFileFastAsync(hashes, cancellationToken); |
} |
catch (Exception exception) |
{ |
Log.Error(exception, "Failed to remove files."); |
} |
await _snapshotDatabase.RemoveFileFast(hashes, cancellationToken); |
} |
|
private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note, |
@@ -1512,10 +1340,9 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.UpdateNote((string)rows[i].Cells["HashColumn"].Value, note, |
cancellationToken); |
|
await _snapshotDatabase.UpdateNoteAsync(hash, note,cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1527,12 +1354,12 @@ |
|
private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView) |
{ |
return dataGridView.SelectedRows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList(); |
return dataGridView.SelectedRows.OfType<DataGridViewRow>().ToList(); |
} |
|
private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView) |
{ |
return dataGridView.Rows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList(); |
return dataGridView.Rows.OfType<DataGridViewRow>().ToList(); |
} |
|
private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows, |
@@ -1545,10 +1372,9 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.RemoveColor((string)rows[i].Cells["HashColumn"].Value, |
cancellationToken); |
|
await _snapshotDatabase.RemoveColorAsync(hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1567,10 +1393,9 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.UpdateColor((string)rows[i].Cells["HashColumn"].Value, color, |
cancellationToken); |
|
await _snapshotDatabase.UpdateColorAsync(hash, color, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1580,79 +1405,6 @@ |
} |
} |
|
private async Task TransferFiles(IReadOnlyList<DataGridViewRow> rows, Service service, |
IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken) |
{ |
|
var client = new WatsonTcpClient($"{service.HostEntry.AddressList[0]}", service.UPort); |
client.Events.MessageReceived += Events_MessageReceived; |
client.Events.ServerConnected += Events_ServerConnected; |
client.Events.ServerDisconnected += Events_ServerDisconnected; |
client.Events.ExceptionEncountered += Events_ExceptionEncountered; |
|
try |
{ |
client.Connect(); |
|
var count = rows.Count; |
|
for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i) |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
|
var completeSnapshot = |
await _snapshotDatabase.GenerateTransferSnapshotAsync(hash, cancellationToken); |
|
var jsonSnapshot = JsonConvert.SerializeObject(completeSnapshot); |
|
await client.SendAsync(jsonSnapshot, null, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
{ |
progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception)); |
} |
} |
} |
catch (Exception exception) when (exception is TimeoutException || exception is SocketException) |
{ |
Log.Error(exception, "Client threw exception."); |
} |
finally |
{ |
client.Events.MessageReceived -= Events_MessageReceived; |
client.Events.ServerConnected -= Events_ServerConnected; |
client.Events.ServerDisconnected -= Events_ServerDisconnected; |
client.Events.ExceptionEncountered -= Events_ExceptionEncountered; |
|
client.Dispose(); |
} |
|
} |
|
private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e) |
{ |
Log.Error(e.Exception, $"Client threw exception."); |
} |
|
private void Events_ServerDisconnected(object sender, DisconnectionEventArgs e) |
{ |
Log.Information($"{e.Client?.IpPort} connected to server due to: {e.Reason}."); |
} |
|
private void Events_MessageReceived(object sender, MessageReceivedEventArgs e) |
{ |
Log.Information($"{e.Data?.Length} byte long message received from {e.Client?.IpPort}"); |
} |
|
private void Events_ServerConnected(object sender, WatsonTcp.ConnectionEventArgs e) |
{ |
Log.Information($"{e.Client?.IpPort} connected to server."); |
} |
|
private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress, |
CancellationToken cancellationToken) |
{ |
@@ -1662,11 +1414,10 @@ |
{ |
try |
{ |
var path = (string)rows[i].Cells["NameColumn"].Value; |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.RevertFile((string)rows[i].Cells["NameColumn"].Value, |
(string)rows[i].Cells["HashColumn"].Value, |
cancellationToken, _mainForm.Configuration.AtomicOperations); |
|
await _snapshotDatabase.RevertFileAsync(path, hash, cancellationToken, _mainForm.Configuration.AtomicOperations); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1685,12 +1436,12 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value); |
var file = fileInfo.Name; |
var path = Path.Combine(directory, file); |
|
await _snapshotDatabase.SaveFileAsync(path, hash, cancellationToken); |
await _snapshotDatabase.SaveFile(path, (string)rows[i].Cells["HashColumn"].Value, |
cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
@@ -1711,10 +1462,10 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value); |
|
await _snapshotDatabase.RelocateFileAsync(hash, path, cancellationToken); |
await _snapshotDatabase.RelocateFile((string)rows[i].Cells["HashColumn"].Value, path, |
cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
@@ -1735,26 +1486,29 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
|
using var memoryStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, cancellationToken); |
if (memoryStream == null) |
using (var memoryStream = |
await _snapshotDatabase.RetrieveFileStream((string)rows[i].Cells["HashColumn"].Value, |
cancellationToken)) |
{ |
continue; |
} |
if (memoryStream == null) |
{ |
continue; |
} |
|
using var md5 = MD5.Create(); |
using (var md5 = MD5.Create()) |
{ |
var recomputedHash = md5.ComputeHash(memoryStream); |
var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "") |
.ToLowerInvariant(); |
|
var recomputedHash = md5.ComputeHash(memoryStream); |
var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "") |
.ToLowerInvariant(); |
|
await _snapshotDatabase.UpdateHashAsync(hash, hashHex, |
cancellationToken); |
await _snapshotDatabase.UpdateHash((string)rows[i].Cells["HashColumn"].Value, hashHex, |
cancellationToken); |
|
rows[i].Cells["HashColumn"].Value = hashHex; |
rows[i].Cells["HashColumn"].Value = hashHex; |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
} |
} |
catch (Exception exception) |
{ |
@@ -1794,9 +1548,8 @@ |
var newPath = Path.Combine(targetPath, rootPath, relPath); |
|
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.SaveFile(newPath, hash, cancellationToken); |
|
await _snapshotDatabase.SaveFileAsync(newPath, hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
|
if (!store.Contains(rowPath)) |
@@ -1821,10 +1574,9 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.NormalizeTime((string)rows[i].Cells["HashColumn"].Value, |
cancellationToken); |
|
await _snapshotDatabase.NormalizeTimeAsync(hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1869,7 +1621,7 @@ |
|
await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken); |
|
if (_cancellationToken.IsCancellationRequested) |
if (!_cancellationToken.IsCancellationRequested) |
{ |
toolStripProgressBar1.Value = toolStripProgressBar1.Maximum; |
toolStripStatusLabel1.Text = "Done."; |
@@ -1886,10 +1638,9 @@ |
{ |
try |
{ |
var hash = (string)rows[i].Cells["HashColumn"].Value; |
await _snapshotDatabase.DeleteScreenshot((string)rows[i].Cells["HashColumn"].Value, |
cancellationToken); |
|
await _snapshotDatabase.DeleteScreenshotAsync(hash, cancellationToken); |
|
progress.Report(new DataGridViewRowProgressSuccess(rows[i], i)); |
} |
catch (Exception exception) |
@@ -1901,16 +1652,40 @@ |
|
#endregion |
|
private void copyHashToolStripMenuItem_Click(object sender, EventArgs e) |
private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e) |
{ |
var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault(); |
if (row == null) |
DataGridView dataGridView = sender as DataGridView; |
foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells) |
{ |
return; |
if (cell.Selected == false) { continue; } |
var bgColorCell = Color.White; |
if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; } |
else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; } |
cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4); |
} |
} |
|
Clipboard.SetText($"{row.Cells["HashColumn"].Value}"); |
|
//Mix two colors |
//Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1 |
/// <summary> |
/// Mix two colors. |
/// </summary> |
/// <param name="Color1"></param> |
/// <param name="Color2"></param> |
/// <param name="Steps"></param> |
/// <param name="Position"></param> |
/// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example> |
/// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks> |
/// <returns></returns> |
public static Color MixColor(Color Color1, Color Color2, int Steps, int Position) |
{ |
if (Position <= 0 || Steps <= 1) { return Color1; } |
if (Position >= Steps) { return Color2; } |
return Color.FromArgb( |
Color1.R + ((Color2.R - Color1.R) / Steps * Position), |
Color1.G + ((Color2.G - Color1.G) / Steps * Position), |
Color1.B + ((Color2.B - Color1.B) / Steps * Position) |
); |
} |
} |
} |