Horizon – Diff between revs 23 and 28

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 23 Rev 28
1 using System; 1 using System;
2 using System.Collections.Generic; 2 using System.Collections.Generic;
3 using System.Collections.Specialized; 3 using System.Collections.Specialized;
4 using System.ComponentModel; 4 using System.ComponentModel;
5 using System.Data.SQLite; 5 using System.Data.SQLite;
6 using System.Diagnostics; 6 using System.Diagnostics;
7 using System.Drawing; 7 using System.Drawing;
8 using System.IO; 8 using System.IO;
9 using System.Linq; 9 using System.Linq;
10 using System.Net; 10 using System.Net;
11 using System.Reflection; 11 using System.Reflection;
12 using System.Text; 12 using System.Text;
13 using System.Threading; 13 using System.Threading;
14 using System.Threading.Tasks; 14 using System.Threading.Tasks;
15 using System.Threading.Tasks.Dataflow; 15 using System.Threading.Tasks.Dataflow;
16 using System.Windows.Forms; 16 using System.Windows.Forms;
17 using Horizon.Database; 17 using Horizon.Database;
18 using Horizon.Snapshots; 18 using Horizon.Snapshots;
19 using Horizon.Utilities; 19 using Horizon.Utilities;
20 using Horizon.Utilities.Serialization; 20 using Horizon.Utilities.Serialization;
21 using Mono.Zeroconf; 21 using Mono.Zeroconf;
22 using NetSparkleUpdater; 22 using NetSparkleUpdater;
23 using NetSparkleUpdater.Enums; 23 using NetSparkleUpdater.Enums;
24 using NetSparkleUpdater.SignatureVerifiers; 24 using NetSparkleUpdater.SignatureVerifiers;
25 using NetSparkleUpdater.UI.WinForms; 25 using NetSparkleUpdater.UI.WinForms;
26 using Newtonsoft.Json; 26 using Newtonsoft.Json;
27 using Serilog; 27 using Serilog;
28 using TrackedFolders; 28 using TrackedFolders;
29 using WatsonTcp; 29 using WatsonTcp;
30 using static Horizon.Utilities.Networking.Miscellaneous; 30 using static Horizon.Utilities.Networking.Miscellaneous;
31 using CaptureMode = Configuration.CaptureMode; 31 using CaptureMode = Configuration.CaptureMode;
32 using Path = System.IO.Path; 32 using Path = System.IO.Path;
33   33  
34 namespace Horizon 34 namespace Horizon
35 { 35 {
36 public partial class MainForm : Form 36 public partial class MainForm : Form
37 { 37 {
38 #region Public Enums, Properties and Fields 38 #region Public Enums, Properties and Fields
39   39  
40 public List<FileSystemWatcher> FileSystemWatchers { get; } 40 public List<FileSystemWatcher> FileSystemWatchers { get; }
41   41  
42 public TrackedFolders.TrackedFolders TrackedFolders { get; set; } 42 public TrackedFolders.TrackedFolders TrackedFolders { get; set; }
43   43  
44 public Configuration.Configuration Configuration { get; set; } 44 public Configuration.Configuration Configuration { get; set; }
45   45  
46 public ScheduledContinuation ChangedConfigurationContinuation { get; set; } 46 public ScheduledContinuation ChangedConfigurationContinuation { get; set; }
47   47  
48 #endregion 48 #endregion
49   49  
50 #region Static Fields and Constants 50 #region Static Fields and Constants
51   51  
52 private static SemaphoreSlim _changedFilesLock; 52 private static SemaphoreSlim _changedFilesLock;
53   53  
54 private static HashSet<string> _changedFiles; 54 private static HashSet<string> _changedFiles;
55   55  
56 private static ScheduledContinuation _changedFilesContinuation; 56 private static ScheduledContinuation _changedFilesContinuation;
57   57  
58 #endregion 58 #endregion
59   59  
60 #region Private Delegates, Events, Enums, Properties, Indexers and Fields 60 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
61   61  
62 private readonly CancellationToken _cancellationToken; 62 private readonly CancellationToken _cancellationToken;
63   63  
64 private readonly CancellationTokenSource _cancellationTokenSource; 64 private readonly CancellationTokenSource _cancellationTokenSource;
65   65  
66 private AboutForm _aboutForm; 66 private AboutForm _aboutForm;
67   67  
68 private ManageFoldersForm _manageFoldersForm; 68 private ManageFoldersForm _manageFoldersForm;
69   69  
70 private readonly SnapshotDatabase _snapshotDatabase; 70 private readonly SnapshotDatabase _snapshotDatabase;
71   71  
72 private SnapshotManagerForm _snapshotManagerForm; 72 private SnapshotManagerForm _snapshotManagerForm;
73   73  
74 private readonly SparkleUpdater _sparkle; 74 private readonly SparkleUpdater _sparkle;
75   75  
76 private LogViewForm _logViewForm; 76 private LogViewForm _logViewForm;
77   77  
78 private readonly LogMemorySink _memorySink; 78 private readonly LogMemorySink _memorySink;
79   79  
80 private RegisterService _horizonDiscoveryService; 80 private RegisterService _horizonDiscoveryService;
81   81  
82 private WatsonTcpServer _horizonNetworkShare; 82 private WatsonTcpServer _horizonNetworkShare;
-   83  
-   84 private NotifyFilters _fileSystemWatchersNotifyFilters = NotifyFilters.LastWrite | NotifyFilters.Attributes;
83   85  
84 public bool MemorySinkEnabled { get; set; } 86 public bool MemorySinkEnabled { get; set; }
85   87  
86 #endregion 88 #endregion
87   89  
88 #region Constructors, Destructors and Finalizers 90 #region Constructors, Destructors and Finalizers
89   91  
90 public MainForm(Mutex mutex) : this() 92 public MainForm(Mutex mutex) : this()
91 { 93 {
92 _memorySink = new LogMemorySink(); 94 _memorySink = new LogMemorySink();
93   95  
94 Log.Logger = new LoggerConfiguration() 96 Log.Logger = new LoggerConfiguration()
95 .MinimumLevel.Debug() 97 .MinimumLevel.Debug()
96 .WriteTo.Conditional(condition => MemorySinkEnabled, configureSink => configureSink.Sink(_memorySink)) 98 .WriteTo.Conditional(condition => MemorySinkEnabled, configureSink => configureSink.Sink(_memorySink))
97 .WriteTo.File(Path.Combine(Constants.UserApplicationDirectory, "Logs", $"{Constants.AssemblyName}.log"), 99 .WriteTo.File(Path.Combine(Constants.UserApplicationDirectory, "Logs", $"{Constants.AssemblyName}.log"),
98 rollingInterval: RollingInterval.Day) 100 rollingInterval: RollingInterval.Day)
99 .CreateLogger(); 101 .CreateLogger();
100   102  
101 _snapshotDatabase = new SnapshotDatabase(_cancellationToken); 103 _snapshotDatabase = new SnapshotDatabase(_cancellationToken);
102 _snapshotDatabase.SnapshotRevert += SnapshotDatabase_SnapshotRevert; 104 _snapshotDatabase.SnapshotRevert += SnapshotDatabase_SnapshotRevert;
103 _snapshotDatabase.SnapshotCreate += SnapshotDatabase_SnapshotCreate; 105 _snapshotDatabase.SnapshotCreate += SnapshotDatabase_SnapshotCreate;
104 _snapshotDatabase.SnapshotTransferReceived += SnapshotDatabase_SnapshotTransferReceived; 106 _snapshotDatabase.SnapshotTransferReceived += SnapshotDatabase_SnapshotTransferReceived;
105   107  
106 TrackedFolders = new TrackedFolders.TrackedFolders(); 108 TrackedFolders = new TrackedFolders.TrackedFolders();
107 TrackedFolders.Folder.CollectionChanged += Folder_CollectionChanged; 109 TrackedFolders.Folder.CollectionChanged += Folder_CollectionChanged;
108   110  
109 // Start application update. 111 // Start application update.
110 var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName; 112 var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName;
111 var icon = Icon.ExtractAssociatedIcon(manifestModuleName); 113 var icon = Icon.ExtractAssociatedIcon(manifestModuleName);
112   114  
113 _sparkle = new SparkleUpdater("https://horizon.grimore.org/update/appcast.xml", 115 _sparkle = new SparkleUpdater("https://horizon.grimore.org/update/appcast.xml",
114 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY=")) 116 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
115 { 117 {
116 UIFactory = new UIFactory(icon), 118 UIFactory = new UIFactory(icon),
117 RelaunchAfterUpdate = true, 119 RelaunchAfterUpdate = true,
118 SecurityProtocolType = SecurityProtocolType.Tls12 120 SecurityProtocolType = SecurityProtocolType.Tls12
119 }; 121 };
120 _sparkle.StartLoop(true, true); 122 _sparkle.StartLoop(true, true);
121 } 123 }
122   124  
123 public MainForm() 125 public MainForm()
124 { 126 {
125 InitializeComponent(); 127 InitializeComponent();
126   128  
127 _cancellationTokenSource = new CancellationTokenSource(); 129 _cancellationTokenSource = new CancellationTokenSource();
128 _cancellationToken = _cancellationTokenSource.Token; 130 _cancellationToken = _cancellationTokenSource.Token;
129   131  
130 _changedFilesLock = new SemaphoreSlim(1, 1); 132 _changedFilesLock = new SemaphoreSlim(1, 1);
131 FileSystemWatchers = new List<FileSystemWatcher>(); 133 FileSystemWatchers = new List<FileSystemWatcher>();
132   134  
133 _changedFiles = new HashSet<string>(); 135 _changedFiles = new HashSet<string>();
134 _changedFilesContinuation = new ScheduledContinuation(); 136 _changedFilesContinuation = new ScheduledContinuation();
135   137  
136 ChangedConfigurationContinuation = new ScheduledContinuation(); 138 ChangedConfigurationContinuation = new ScheduledContinuation();
137 } 139 }
138   140  
139 /// <summary> 141 /// <summary>
140 /// Clean up any resources being used. 142 /// Clean up any resources being used.
141 /// </summary> 143 /// </summary>
142 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 144 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
143 protected override void Dispose(bool disposing) 145 protected override void Dispose(bool disposing)
144 { 146 {
145 if (disposing && components != null) 147 if (disposing && components != null)
146 { 148 {
147 components.Dispose(); 149 components.Dispose();
148 } 150 }
149   151  
150 _cancellationTokenSource.Cancel(); 152 _cancellationTokenSource.Cancel();
151   153  
152 _snapshotDatabase.SnapshotRevert -= SnapshotDatabase_SnapshotRevert; 154 _snapshotDatabase.SnapshotRevert -= SnapshotDatabase_SnapshotRevert;
153 _snapshotDatabase.SnapshotCreate -= SnapshotDatabase_SnapshotCreate; 155 _snapshotDatabase.SnapshotCreate -= SnapshotDatabase_SnapshotCreate;
154 _snapshotDatabase.SnapshotTransferReceived -= SnapshotDatabase_SnapshotTransferReceived; 156 _snapshotDatabase.SnapshotTransferReceived -= SnapshotDatabase_SnapshotTransferReceived;
155   157  
156 _snapshotDatabase.Dispose(); 158 _snapshotDatabase.Dispose();
157   159  
158 base.Dispose(disposing); 160 base.Dispose(disposing);
159 } 161 }
160   162  
161 #endregion 163 #endregion
162   164  
163 #region Event Handlers 165 #region Event Handlers
-   166 private void attributesToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
-   167 {
-   168 var toolStripMenuItem = (ToolStripMenuItem)sender;
-   169  
-   170 var text = toolStripMenuItem.Text;
-   171 var state = toolStripMenuItem.CheckState;
-   172  
-   173 foreach (var flag in Enum.GetNames(typeof(NotifyFilters)))
-   174 {
-   175 if (string.Equals(flag, text, StringComparison.OrdinalIgnoreCase))
-   176 {
-   177 if (Enum.TryParse<NotifyFilters>(flag, true, out var setting))
-   178 {
-   179 switch (state)
-   180 {
-   181 case CheckState.Checked:
-   182 _fileSystemWatchersNotifyFilters = _fileSystemWatchersNotifyFilters | setting;
-   183 break;
-   184 case CheckState.Unchecked:
-   185 _fileSystemWatchersNotifyFilters = _fileSystemWatchersNotifyFilters & ~setting;
-   186 break;
-   187 }
-   188
-   189 }
-   190 }
-   191 }
-   192  
-   193 Configuration.NotifyFilters = _fileSystemWatchersNotifyFilters;
-   194  
-   195 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
-   196 async () => { await SaveConfiguration(); }, _cancellationToken);
-   197 }
164   198  
165 private void networkSharingToolStripMenuItem_CheckStateChanged(object sender, EventArgs e) 199 private void networkSharingToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
166 { 200 {
167 var toolStripMenuItem = (ToolStripMenuItem)sender; 201 var toolStripMenuItem = (ToolStripMenuItem)sender;
168   202  
169 switch (toolStripMenuItem.CheckState) 203 switch (toolStripMenuItem.CheckState)
170 { 204 {
171 case CheckState.Checked: 205 case CheckState.Checked:
172 var freePort = GetAvailableTcpPort(); 206 var freePort = GetAvailableTcpPort();
173   207  
174 _horizonNetworkShare = new WatsonTcpServer("0.0.0.0", freePort); 208 _horizonNetworkShare = new WatsonTcpServer("0.0.0.0", freePort);
175 _horizonNetworkShare.Events.ClientConnected += Events_ClientConnected; 209 _horizonNetworkShare.Events.ClientConnected += Events_ClientConnected;
176 _horizonNetworkShare.Events.ClientDisconnected += Events_ClientDisconnected; 210 _horizonNetworkShare.Events.ClientDisconnected += Events_ClientDisconnected;
177 _horizonNetworkShare.Events.MessageReceived += Events_MessageReceived; 211 _horizonNetworkShare.Events.MessageReceived += Events_MessageReceived;
178 _horizonNetworkShare.Events.ExceptionEncountered += Events_ExceptionEncountered; 212 _horizonNetworkShare.Events.ExceptionEncountered += Events_ExceptionEncountered;
179 #pragma warning disable CS4014 213 #pragma warning disable CS4014
180 _horizonNetworkShare.Start(); 214 _horizonNetworkShare.Start();
181 #pragma warning restore CS4014 215 #pragma warning restore CS4014
182   216  
183 try 217 try
184 { 218 {
185 _horizonDiscoveryService = new RegisterService(); 219 _horizonDiscoveryService = new RegisterService();
186 _horizonDiscoveryService.Name = $"Horizon ({Environment.MachineName})"; 220 _horizonDiscoveryService.Name = $"Horizon ({Environment.MachineName})";
187 _horizonDiscoveryService.RegType = "_horizon._tcp"; 221 _horizonDiscoveryService.RegType = "_horizon._tcp";
188 _horizonDiscoveryService.ReplyDomain = "local."; 222 _horizonDiscoveryService.ReplyDomain = "local.";
189 _horizonDiscoveryService.UPort = freePort; 223 _horizonDiscoveryService.UPort = freePort;
190 _horizonDiscoveryService.Register(); 224 _horizonDiscoveryService.Register();
191 } 225 }
192 catch (Exception exception) 226 catch (Exception exception)
193 { 227 {
194 Log.Error(exception, "Service discovery protocol could not be stared."); 228 Log.Error(exception, "Service discovery protocol could not be stared.");
195 } 229 }
196   230  
197 Configuration.NetworkSharing = true; 231 Configuration.NetworkSharing = true;
198 break; 232 break;
199 case CheckState.Unchecked: 233 case CheckState.Unchecked:
200 if (_horizonNetworkShare != null) 234 if (_horizonNetworkShare != null)
201 { 235 {
202 _horizonNetworkShare.Events.ClientConnected -= Events_ClientConnected; 236 _horizonNetworkShare.Events.ClientConnected -= Events_ClientConnected;
203 _horizonNetworkShare.Events.ClientDisconnected -= Events_ClientDisconnected; 237 _horizonNetworkShare.Events.ClientDisconnected -= Events_ClientDisconnected;
204 _horizonNetworkShare.Events.MessageReceived -= Events_MessageReceived; 238 _horizonNetworkShare.Events.MessageReceived -= Events_MessageReceived;
205 _horizonNetworkShare.Events.ExceptionEncountered -= Events_ExceptionEncountered; 239 _horizonNetworkShare.Events.ExceptionEncountered -= Events_ExceptionEncountered;
206   240  
207 _horizonNetworkShare.Dispose(); 241 _horizonNetworkShare.Dispose();
208 _horizonNetworkShare = null; 242 _horizonNetworkShare = null;
209 } 243 }
210   244  
211 if (_horizonDiscoveryService != null) 245 if (_horizonDiscoveryService != null)
212 { 246 {
213 _horizonDiscoveryService.Dispose(); 247 _horizonDiscoveryService.Dispose();
214 _horizonDiscoveryService = null; 248 _horizonDiscoveryService = null;
215   249  
216 } 250 }
217   251  
218 Configuration.NetworkSharing = false; 252 Configuration.NetworkSharing = false;
219 break; 253 break;
220 } 254 }
221   255  
222 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 256 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
223 async () => { await SaveConfiguration(); }, _cancellationToken); 257 async () => { await SaveConfiguration(); }, _cancellationToken);
224 } 258 }
225   259  
226 private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e) 260 private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e)
227 { 261 {
228 Log.Error(e.Exception,$"Client threw exception."); 262 Log.Error(e.Exception,$"Client threw exception.");
229 } 263 }
230   264  
231 private async void Events_MessageReceived(object sender, MessageReceivedEventArgs e) 265 private async void Events_MessageReceived(object sender, MessageReceivedEventArgs e)
232 { 266 {
233 Log.Information($"Client {e.Client?.IpPort} sent {e.Data?.Length} bytes via network sharing."); 267 Log.Information($"Client {e.Client?.IpPort} sent {e.Data?.Length} bytes via network sharing.");
234   268  
235 if (e.Data?.Length == 0) 269 if (e.Data?.Length == 0)
236 { 270 {
237 return; 271 return;
238 } 272 }
239   273  
240 try 274 try
241 { 275 {
242 var payload = Encoding.UTF8.GetString(e.Data); 276 var payload = Encoding.UTF8.GetString(e.Data);
243   277  
244 var completeSnapshot = JsonConvert.DeserializeObject<TransferSnapshot>(payload); 278 var completeSnapshot = JsonConvert.DeserializeObject<TransferSnapshot>(payload);
245   279  
246 await _snapshotDatabase.ApplyTransferSnapshotAsync(completeSnapshot, _cancellationToken); 280 await _snapshotDatabase.ApplyTransferSnapshotAsync(completeSnapshot, _cancellationToken);
247   281  
248 Log.Information($"Stored {completeSnapshot.Name} from {e.Client?.IpPort}"); 282 Log.Information($"Stored {completeSnapshot.Name} from {e.Client?.IpPort}");
249 } 283 }
250 catch (Exception exception) 284 catch (Exception exception)
251 { 285 {
252 Log.Error(exception, $"Failed to process network share from {e.Client?.IpPort}."); 286 Log.Error(exception, $"Failed to process network share from {e.Client?.IpPort}.");
253 } 287 }
254 } 288 }
255   289  
256 private void Events_ClientDisconnected(object sender, WatsonTcp.DisconnectionEventArgs e) 290 private void Events_ClientDisconnected(object sender, WatsonTcp.DisconnectionEventArgs e)
257 { 291 {
258 Log.Information($"Client {e.Client?.IpPort} disconnected from network sharing."); 292 Log.Information($"Client {e.Client?.IpPort} disconnected from network sharing.");
259 } 293 }
260   294  
261 private void Events_ClientConnected(object sender, WatsonTcp.ConnectionEventArgs e) 295 private void Events_ClientConnected(object sender, WatsonTcp.ConnectionEventArgs e)
262 { 296 {
263 Log.Information($"Client {e.Client?.IpPort} connected to network sharing."); 297 Log.Information($"Client {e.Client?.IpPort} connected to network sharing.");
264 } 298 }
265   299  
266 private void WindowToolStripMenuItem_Click(object sender, EventArgs e) 300 private void WindowToolStripMenuItem_Click(object sender, EventArgs e)
267 { 301 {
268 windowToolStripMenuItem.Checked = true; 302 windowToolStripMenuItem.Checked = true;
269 screenToolStripMenuItem.Checked = false; 303 screenToolStripMenuItem.Checked = false;
270   304  
271 Configuration.CaptureMode = CaptureMode.Window; 305 Configuration.CaptureMode = CaptureMode.Window;
272   306  
273 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 307 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
274 async () => { await SaveConfiguration(); }, _cancellationToken); 308 async () => { await SaveConfiguration(); }, _cancellationToken);
275 } 309 }
276   310  
277 private void ScreenToolStripMenuItem_Click(object sender, EventArgs e) 311 private void ScreenToolStripMenuItem_Click(object sender, EventArgs e)
278 { 312 {
279 screenToolStripMenuItem.Checked = true; 313 screenToolStripMenuItem.Checked = true;
280 windowToolStripMenuItem.Checked = false; 314 windowToolStripMenuItem.Checked = false;
281   315  
282 Configuration.CaptureMode = CaptureMode.Screen; 316 Configuration.CaptureMode = CaptureMode.Screen;
283   317  
284 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 318 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
285 async () => { await SaveConfiguration(); }, _cancellationToken); 319 async () => { await SaveConfiguration(); }, _cancellationToken);
286 } 320 }
287   321  
288 private void LogViewToolStripMenuItem_Click(object sender, EventArgs e) 322 private void LogViewToolStripMenuItem_Click(object sender, EventArgs e)
289 { 323 {
290 if (_logViewForm != null) 324 if (_logViewForm != null)
291 { 325 {
292 return; 326 return;
293 } 327 }
294   328  
295 _logViewForm = new LogViewForm(this, _memorySink, _cancellationToken); 329 _logViewForm = new LogViewForm(this, _memorySink, _cancellationToken);
296 _logViewForm.Closing += LogViewFormClosing; 330 _logViewForm.Closing += LogViewFormClosing;
297 _logViewForm.Show(); 331 _logViewForm.Show();
298 } 332 }
299   333  
300 private void LogViewFormClosing(object sender, CancelEventArgs e) 334 private void LogViewFormClosing(object sender, CancelEventArgs e)
301 { 335 {
302 if (_logViewForm == null) 336 if (_logViewForm == null)
303 { 337 {
304 return; 338 return;
305 } 339 }
306   340  
307 _logViewForm.Closing -= LogViewFormClosing; 341 _logViewForm.Closing -= LogViewFormClosing;
308 _logViewForm.Close(); 342 _logViewForm.Close();
309 _logViewForm = null; 343 _logViewForm = null;
310 } 344 }
311   345  
312 private void SnapshotDatabase_SnapshotCreate(object sender, SnapshotCreateEventArgs e) 346 private void SnapshotDatabase_SnapshotCreate(object sender, SnapshotCreateEventArgs e)
313 { 347 {
314 switch (e) 348 switch (e)
315 { 349 {
316 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs: 350 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
317 if (Configuration.ShowBalloonTooltips) 351 if (Configuration.ShowBalloonTooltips)
318 { 352 {
319 ShowBalloon("Snapshot Succeeded", $"Took a snapshot of {snapshotCreateSuccessEventArgs.Path}.", 353 ShowBalloon("Snapshot Succeeded", $"Took a snapshot of {snapshotCreateSuccessEventArgs.Path}.",
320 5000); 354 5000);
321 } 355 }
322   356  
323 Log.Information($"Took a snapshot of {snapshotCreateSuccessEventArgs.Path}."); 357 Log.Information($"Took a snapshot of {snapshotCreateSuccessEventArgs.Path}.");
324   358  
325 break; 359 break;
326 case SnapshotCreateFailureEventArgs snapshotCreateFailureEventArgs: 360 case SnapshotCreateFailureEventArgs snapshotCreateFailureEventArgs:
327 if (Configuration.ShowBalloonTooltips) 361 if (Configuration.ShowBalloonTooltips)
328 { 362 {
329 ShowBalloon("Snapshot Failed", 363 ShowBalloon("Snapshot Failed",
330 $"Failed to take a snapshot of {snapshotCreateFailureEventArgs.Path}.", 5000); 364 $"Failed to take a snapshot of {snapshotCreateFailureEventArgs.Path}.", 5000);
331 } 365 }
332   366  
333 Log.Information($"Failed to take a snapshot of {snapshotCreateFailureEventArgs.Path}."); 367 Log.Information($"Failed to take a snapshot of {snapshotCreateFailureEventArgs.Path}.");
334   368  
335 break; 369 break;
336 } 370 }
337 } 371 }
338   372  
339 private void SnapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e) 373 private void SnapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e)
340 { 374 {
341 switch (e) 375 switch (e)
342 { 376 {
343 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs: 377 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
344 if (Configuration.ShowBalloonTooltips) 378 if (Configuration.ShowBalloonTooltips)
345 { 379 {
346 ShowBalloon("Snapshot Transfer Success", $"A snapshot has been transferred {snapshotCreateSuccessEventArgs.Path}.", 380 ShowBalloon("Snapshot Transfer Success", $"A snapshot has been transferred {snapshotCreateSuccessEventArgs.Path}.",
347 5000); 381 5000);
348 } 382 }
349   383  
350 Log.Information($"A snapshot transfer succeeded {snapshotCreateSuccessEventArgs.Path}."); 384 Log.Information($"A snapshot transfer succeeded {snapshotCreateSuccessEventArgs.Path}.");
351   385  
352 break; 386 break;
353 case SnapshotCreateFailureEventArgs snapshotCreateFailureEventArgs: 387 case SnapshotCreateFailureEventArgs snapshotCreateFailureEventArgs:
354 if (Configuration.ShowBalloonTooltips) 388 if (Configuration.ShowBalloonTooltips)
355 { 389 {
356 ShowBalloon("Snapshot Transfer Failure", 390 ShowBalloon("Snapshot Transfer Failure",
357 $"A snapshot failed to transfer {snapshotCreateFailureEventArgs.Path}.", 5000); 391 $"A snapshot failed to transfer {snapshotCreateFailureEventArgs.Path}.", 5000);
358 } 392 }
359   393  
360 Log.Information($"A snapshot failed to transfer {snapshotCreateFailureEventArgs.Path}."); 394 Log.Information($"A snapshot failed to transfer {snapshotCreateFailureEventArgs.Path}.");
361   395  
362 break; 396 break;
363 } 397 }
364 } 398 }
365   399  
366 private void SnapshotDatabase_SnapshotRevert(object sender, SnapshotRevertEventArgs e) 400 private void SnapshotDatabase_SnapshotRevert(object sender, SnapshotRevertEventArgs e)
367 { 401 {
368 switch (e) 402 switch (e)
369 { 403 {
370 case SnapshotRevertSuccessEventArgs snapshotRevertSuccessEventArgs: 404 case SnapshotRevertSuccessEventArgs snapshotRevertSuccessEventArgs:
371 if (Configuration.ShowBalloonTooltips) 405 if (Configuration.ShowBalloonTooltips)
372 { 406 {
373 ShowBalloon("Revert Succeeded", $"File {snapshotRevertSuccessEventArgs.Name} reverted.", 5000); 407 ShowBalloon("Revert Succeeded", $"File {snapshotRevertSuccessEventArgs.Name} reverted.", 5000);
374 } 408 }
375   409  
376 Log.Information($"File {snapshotRevertSuccessEventArgs.Name} reverted."); 410 Log.Information($"File {snapshotRevertSuccessEventArgs.Name} reverted.");
377   411  
378 break; 412 break;
379 case SnapshotRevertFailureEventArgs snapshotRevertFailureEventArgs: 413 case SnapshotRevertFailureEventArgs snapshotRevertFailureEventArgs:
380 if (Configuration.ShowBalloonTooltips) 414 if (Configuration.ShowBalloonTooltips)
381 { 415 {
382 ShowBalloon("Revert Failed", $"Reverting file {snapshotRevertFailureEventArgs.Name} failed.", 416 ShowBalloon("Revert Failed", $"Reverting file {snapshotRevertFailureEventArgs.Name} failed.",
383 5000); 417 5000);
384 } 418 }
385   419  
386 Log.Information($"Reverting file {snapshotRevertFailureEventArgs.Name} failed."); 420 Log.Information($"Reverting file {snapshotRevertFailureEventArgs.Name} failed.");
387   421  
388 break; 422 break;
389 } 423 }
390 } 424 }
391   425  
392 private async void Folder_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 426 private async void Folder_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
393 { 427 {
394 if (e.OldItems != null) 428 if (e.OldItems != null)
395 { 429 {
396 foreach (var item in e.OldItems.OfType<Folder>()) 430 foreach (var item in e.OldItems.OfType<Folder>())
397 { 431 {
398 RemoveWatcher(item.Path); 432 RemoveWatcher(item.Path);
399 } 433 }
400 } 434 }
401   435  
402 if (e.NewItems != null) 436 if (e.NewItems != null)
403 { 437 {
404 foreach (var item in e.NewItems.OfType<Folder>()) 438 foreach (var item in e.NewItems.OfType<Folder>())
405 { 439 {
406 // If the folder is not enabled then do not add watchers for the path. 440 // If the folder is not enabled then do not add watchers for the path.
407 if (!item.Enable) 441 if (!item.Enable)
408 { 442 {
409 continue; 443 continue;
410 } 444 }
411   445  
412 if (Directory.Exists(item.Path)) 446 if (Directory.Exists(item.Path))
413 { 447 {
414 AddWatcher(item.Path, item.Recursive); 448 AddWatcher(item.Path, item.Recursive);
415 } 449 }
416 } 450 }
417 } 451 }
418   452  
419 await SaveFolders(); 453 await SaveFolders();
420 } 454 }
421   455  
422 private async void MainForm_Load(object sender, EventArgs e) 456 private async void MainForm_Load(object sender, EventArgs e)
423 { 457 {
424 // Load configuration. 458 // Load configuration.
425 Configuration = await LoadConfiguration(); 459 Configuration = await LoadConfiguration();
426 launchOnBootToolStripMenuItem.Checked = Configuration.LaunchOnBoot; 460 launchOnBootToolStripMenuItem.Checked = Configuration.LaunchOnBoot;
427 atomicOperationsToolStripMenuItem.Checked = Configuration.AtomicOperations; 461 atomicOperationsToolStripMenuItem.Checked = Configuration.AtomicOperations;
428 enableToolStripMenuItem.Checked = Configuration.Enabled; 462 enableToolStripMenuItem.Checked = Configuration.Enabled;
429 showBalloonTooltipsToolStripMenuItem.Checked = Configuration.Enabled; 463 showBalloonTooltipsToolStripMenuItem.Checked = Configuration.Enabled;
430 windowToolStripMenuItem.Checked = Configuration.CaptureMode == CaptureMode.Window; 464 windowToolStripMenuItem.Checked = Configuration.CaptureMode == CaptureMode.Window;
431 screenToolStripMenuItem.Checked = Configuration.CaptureMode == CaptureMode.Screen; 465 screenToolStripMenuItem.Checked = Configuration.CaptureMode == CaptureMode.Screen;
432 networkSharingToolStripMenuItem.Checked = Configuration.NetworkSharing; 466 networkSharingToolStripMenuItem.Checked = Configuration.NetworkSharing;
-   467 foreach (var item in attributesToolStripMenuItem.DropDownItems.OfType<ToolStripMenuItem>())
-   468 {
-   469 var text = item.Text;
-   470  
-   471 if (Enum.TryParse<NotifyFilters>(text, out var notifyFilter))
-   472 {
-   473 item.Checked = Configuration.NotifyFilters.HasFlag(notifyFilter);
-   474 }
-   475 }
433   476  
434 // Load all tracked folders. 477 // Load all tracked folders.
435 var folders = await LoadFolders(); 478 var folders = await LoadFolders();
436   479  
437 foreach (var folder in folders.Folder) 480 foreach (var folder in folders.Folder)
438 { 481 {
439 TrackedFolders.Folder.Add(folder); 482 TrackedFolders.Folder.Add(folder);
440 } 483 }
441   484  
442 ToggleWatchers(); 485 ToggleWatchers();
443 } 486 }
444   487  
445 private void ShowBalloonTooltipsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 488 private void ShowBalloonTooltipsToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
446 { 489 {
447 Configuration.ShowBalloonTooltips = ((ToolStripMenuItem)sender).Checked; 490 Configuration.ShowBalloonTooltips = ((ToolStripMenuItem)sender).Checked;
448   491  
449 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 492 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
450 async () => { await SaveConfiguration(); }, _cancellationToken); 493 async () => { await SaveConfiguration(); }, _cancellationToken);
451 } 494 }
452   495  
453 private void LaunchOnBootToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 496 private void LaunchOnBootToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
454 { 497 {
455 Configuration.LaunchOnBoot = ((ToolStripMenuItem)sender).Checked; 498 Configuration.LaunchOnBoot = ((ToolStripMenuItem)sender).Checked;
456   499  
457 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 500 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
458 async () => { await SaveConfiguration(); }, _cancellationToken); 501 async () => { await SaveConfiguration(); }, _cancellationToken);
459   502  
460 Miscellaneous.LaunchOnBootSet(Configuration.LaunchOnBoot); 503 Miscellaneous.LaunchOnBootSet(Configuration.LaunchOnBoot);
461 } 504 }
462   505  
463 private void AtomicOperationsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 506 private void AtomicOperationsToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
464 { 507 {
465 Configuration.AtomicOperations = ((ToolStripMenuItem)sender).Checked; 508 Configuration.AtomicOperations = ((ToolStripMenuItem)sender).Checked;
466 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 509 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
467 async () => { await SaveConfiguration(); }, _cancellationToken); 510 async () => { await SaveConfiguration(); }, _cancellationToken);
468 } 511 }
469   512  
470 private void TrashDatabaseToolStripMenuItem_Click(object sender, EventArgs e) 513 private void TrashDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
471 { 514 {
472 try 515 try
473 { 516 {
474 File.Delete(Constants.DatabaseFilePath); 517 File.Delete(Constants.DatabaseFilePath);
475   518  
476 if (Configuration.ShowBalloonTooltips) 519 if (Configuration.ShowBalloonTooltips)
477 { 520 {
478 ShowBalloon("Database Deleted", $"Database file {Constants.DatabaseFilePath} has been deleted.", 521 ShowBalloon("Database Deleted", $"Database file {Constants.DatabaseFilePath} has been deleted.",
479 5000); 522 5000);
480 } 523 }
481   524  
482 Log.Information($"Database file {Constants.DatabaseFilePath} has been deleted."); 525 Log.Information($"Database file {Constants.DatabaseFilePath} has been deleted.");
483 } 526 }
484 catch (Exception exception) 527 catch (Exception exception)
485 { 528 {
486 if (Configuration.ShowBalloonTooltips) 529 if (Configuration.ShowBalloonTooltips)
487 { 530 {
488 ShowBalloon("Could not Delete Database", 531 ShowBalloon("Could not Delete Database",
489 $"Database file {Constants.DatabaseFilePath} delete failed with error: {exception.Message}", 532 $"Database file {Constants.DatabaseFilePath} delete failed with error: {exception.Message}",
490 5000); 533 5000);
491 } 534 }
492   535  
493 Log.Information( 536 Log.Information(
494 $"Database file {Constants.DatabaseFilePath} delete failed with error: {exception.Message}"); 537 $"Database file {Constants.DatabaseFilePath} delete failed with error: {exception.Message}");
495 } 538 }
496 } 539 }
497   540  
498 private void EnableToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 541 private void EnableToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
499 { 542 {
500 Configuration.Enabled = enableToolStripMenuItem.Checked; 543 Configuration.Enabled = enableToolStripMenuItem.Checked;
501   544  
502 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1), 545 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
503 async () => { await SaveConfiguration(); }, _cancellationToken); 546 async () => { await SaveConfiguration(); }, _cancellationToken);
504   547  
505 ToggleWatchers(); 548 ToggleWatchers();
506 } 549 }
507   550  
508 private void SnapshotsToolStripMenuItem_Click(object sender, EventArgs e) 551 private void SnapshotsToolStripMenuItem_Click(object sender, EventArgs e)
509 { 552 {
510 if (_snapshotManagerForm != null) 553 if (_snapshotManagerForm != null)
511 { 554 {
512 return; 555 return;
513 } 556 }
514   557  
515 _snapshotManagerForm = new SnapshotManagerForm(this, _snapshotDatabase, _cancellationToken); 558 _snapshotManagerForm = new SnapshotManagerForm(this, _snapshotDatabase, _cancellationToken);
516 _snapshotManagerForm.Closing += SnapshotManagerFormClosing; 559 _snapshotManagerForm.Closing += SnapshotManagerFormClosing;
517 _snapshotManagerForm.Show(); 560 _snapshotManagerForm.Show();
518 } 561 }
519   562  
520 private void SnapshotManagerFormClosing(object sender, CancelEventArgs e) 563 private void SnapshotManagerFormClosing(object sender, CancelEventArgs e)
521 { 564 {
522 if (_snapshotManagerForm == null) 565 if (_snapshotManagerForm == null)
523 { 566 {
524 return; 567 return;
525 } 568 }
526   569  
527 _snapshotManagerForm.Closing -= SnapshotManagerFormClosing; 570 _snapshotManagerForm.Closing -= SnapshotManagerFormClosing;
528 _snapshotManagerForm.Close(); 571 _snapshotManagerForm.Close();
529 _snapshotManagerForm = null; 572 _snapshotManagerForm = null;
530 } 573 }
531   574  
532 private async void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e) 575 private async void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
533 { 576 {
534 // Ignore directories. 577 // Ignore directories.
535 if (Directory.Exists(e.FullPath)) 578 if (Directory.Exists(e.FullPath))
536 { 579 {
537 return; 580 return;
538 } 581 }
539   -  
540 await _changedFilesLock.WaitAsync(_cancellationToken); 582 #pragma warning disable CS4014
541 try 583 Task.Run(async () =>
-   584 #pragma warning restore CS4014
542 { 585 {
-   586 await _changedFilesLock.WaitAsync(_cancellationToken);
-   587 try
-   588 {
543 var delay = global::TrackedFolders.Constants.Delay; 589 var delay = global::TrackedFolders.Constants.Delay;
544 var color = Color.Empty; 590 var color = Color.Empty;
-   591  
-   592 if (TrackedFolders.TryGet(e.FullPath, out var folder))
-   593 {
-   594 delay = folder.Delay;
-   595 color = folder.Color;
-   596 }
-   597  
-   598 if (_changedFiles.Contains(e.FullPath))
-   599 {
-   600 _changedFilesContinuation.Schedule(delay,
-   601 async () => await TakeSnapshots(color, _cancellationToken), _cancellationToken);
-   602 return;
-   603 }
-   604  
-   605 _changedFiles.Add(e.FullPath);
545   606  
-   607 _changedFilesContinuation.Schedule(delay,
-   608 async () => await TakeSnapshots(color, _cancellationToken), _cancellationToken);
-   609 }
546 if (TrackedFolders.TryGet(e.FullPath, out var folder)) 610 catch (Exception exception)
547 { -  
548 delay = folder.Delay; 611 {
549 color = folder.Color; 612 Log.Error(exception, "Could not process changed files.");
550 } -  
551   613 }
552 if (_changedFiles.Contains(e.FullPath)) 614 finally
553 { -  
554 _changedFilesContinuation.Schedule(delay, async () => await TakeSnapshots(color, _cancellationToken), _cancellationToken); 615 {
555 return; 616 _changedFilesLock.Release();
556 } -  
557   -  
558 _changedFiles.Add(e.FullPath); -  
559   -  
560 _changedFilesContinuation.Schedule(delay, async () => await TakeSnapshots(color, _cancellationToken), _cancellationToken); -  
561 } 617 }
562 catch (Exception exception) -  
563 { -  
564 Log.Error(exception, "Could not process changed files."); -  
565 } -  
566 finally -  
567 { -  
568 _changedFilesLock.Release(); -  
569 } 618 }, CancellationToken.None);
570 } 619 }
571   620  
572 private void AboutToolStripMenuItem_Click(object sender, EventArgs e) 621 private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
573 { 622 {
574 if (_aboutForm != null) 623 if (_aboutForm != null)
575 { 624 {
576 return; 625 return;
577 } 626 }
578   627  
579 _aboutForm = new AboutForm(_cancellationToken); 628 _aboutForm = new AboutForm(_cancellationToken);
580 _aboutForm.Closing += AboutForm_Closing; 629 _aboutForm.Closing += AboutForm_Closing;
581 _aboutForm.Show(); 630 _aboutForm.Show();
582 } 631 }
583   632  
584 private void AboutForm_Closing(object sender, CancelEventArgs e) 633 private void AboutForm_Closing(object sender, CancelEventArgs e)
585 { 634 {
586 if (_aboutForm == null) 635 if (_aboutForm == null)
587 { 636 {
588 return; 637 return;
589 } 638 }
590   639  
591 _aboutForm.Dispose(); 640 _aboutForm.Dispose();
592 _aboutForm = null; 641 _aboutForm = null;
593 } 642 }
594   643  
595 private void QuitToolStripMenuItem_Click(object sender, EventArgs e) 644 private void QuitToolStripMenuItem_Click(object sender, EventArgs e)
596 { 645 {
597 Close(); 646 Close();
598 } 647 }
599   648  
600 private async void UpdateToolStripMenuItem_Click(object sender, EventArgs e) 649 private async void UpdateToolStripMenuItem_Click(object sender, EventArgs e)
601 { 650 {
602 // Manually check for updates, this will not show a ui 651 // Manually check for updates, this will not show a ui
603 var result = await _sparkle.CheckForUpdatesQuietly(); 652 var result = await _sparkle.CheckForUpdatesQuietly();
604 if (result.Status == UpdateStatus.UpdateAvailable) 653 if (result.Status == UpdateStatus.UpdateAvailable)
605 { 654 {
606 // if update(s) are found, then we have to trigger the UI to show it gracefully 655 // if update(s) are found, then we have to trigger the UI to show it gracefully
607 _sparkle.ShowUpdateNeededUI(); 656 _sparkle.ShowUpdateNeededUI();
608 return; 657 return;
609 } 658 }
610   659  
611 MessageBox.Show("No updates available at this time.", "Horizon", MessageBoxButtons.OK, 660 MessageBox.Show("No updates available at this time.", "Horizon", MessageBoxButtons.OK,
612 MessageBoxIcon.Asterisk, 661 MessageBoxIcon.Asterisk,
613 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false); 662 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
614 } 663 }
615   664  
616 private void NotifyIcon1_Click(object sender, EventArgs e) 665 private void NotifyIcon1_Click(object sender, EventArgs e)
617 { 666 {
618 if (e is MouseEventArgs mouseEventArgs && mouseEventArgs.Button == MouseButtons.Left) 667 if (e is MouseEventArgs mouseEventArgs && mouseEventArgs.Button == MouseButtons.Left)
619 { 668 {
620 } 669 }
621 } 670 }
622   671  
623 private void ManageFoldersToolStripMenuItem_Click(object sender, EventArgs e) 672 private void ManageFoldersToolStripMenuItem_Click(object sender, EventArgs e)
624 { 673 {
625 if (_manageFoldersForm != null) 674 if (_manageFoldersForm != null)
626 { 675 {
627 return; 676 return;
628 } 677 }
629   678  
630 _manageFoldersForm = new ManageFoldersForm(this, _cancellationToken); 679 _manageFoldersForm = new ManageFoldersForm(this, _cancellationToken);
631 _manageFoldersForm.Closing += ManageFoldersForm_Closing; 680 _manageFoldersForm.Closing += ManageFoldersForm_Closing;
632 _manageFoldersForm.Show(); 681 _manageFoldersForm.Show();
633 } 682 }
634   683  
635 private void ManageFoldersForm_Closing(object sender, CancelEventArgs e) 684 private void ManageFoldersForm_Closing(object sender, CancelEventArgs e)
636 { 685 {
637 if (_manageFoldersForm == null) 686 if (_manageFoldersForm == null)
638 { 687 {
639 return; 688 return;
640 } 689 }
641   690  
642 _manageFoldersForm.Closing -= ManageFoldersForm_Closing; 691 _manageFoldersForm.Closing -= ManageFoldersForm_Closing;
643 _manageFoldersForm.Close(); 692 _manageFoldersForm.Close();
644 _manageFoldersForm = null; 693 _manageFoldersForm = null;
645 } 694 }
646   695  
647 #endregion 696 #endregion
648   697  
649 #region Public Methods 698 #region Public Methods
650   699  
651 public void ShowBalloon(string title, string text, int time) 700 public void ShowBalloon(string title, string text, int time)
652 { 701 {
653 notifyIcon1.BalloonTipTitle = title; 702 notifyIcon1.BalloonTipTitle = title;
654 notifyIcon1.BalloonTipText = text; 703 notifyIcon1.BalloonTipText = text;
655 notifyIcon1.ShowBalloonTip(time); 704 notifyIcon1.ShowBalloonTip(time);
656 } 705 }
657   706  
658 public async Task TakeSnapshot(string path) 707 public async Task TakeSnapshot(string path)
659 { 708 {
660 foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.TopDirectoryOnly)) 709 foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.TopDirectoryOnly))
661 { 710 {
662 try 711 try
663 { 712 {
664 var fileName = Path.GetFileName(file); 713 var fileName = Path.GetFileName(file);
665 var directory = Path.GetDirectoryName(fileName); 714 var directory = Path.GetDirectoryName(fileName);
666 var color = Color.Empty; 715 var color = Color.Empty;
667 if (TrackedFolders.TryGet(directory, out var folder)) 716 if (TrackedFolders.TryGet(directory, out var folder))
668 { 717 {
669 color = folder.Color; 718 color = folder.Color;
670 } 719 }
671   720  
672 await _snapshotDatabase.CreateSnapshotAsync(fileName, file, color, _cancellationToken); 721 await _snapshotDatabase.CreateSnapshotAsync(fileName, file, color, _cancellationToken);
673 } 722 }
674 catch (SQLiteException exception) 723 catch (SQLiteException exception)
675 { 724 {
676 if (exception.ResultCode == SQLiteErrorCode.Constraint) 725 if (exception.ResultCode == SQLiteErrorCode.Constraint)
677 { 726 {
678 Log.Information(exception, "Snapshot already exists."); 727 Log.Information(exception, "Snapshot already exists.");
679 } 728 }
680 } 729 }
681 catch (Exception exception) 730 catch (Exception exception)
682 { 731 {
683 Log.Error(exception, "Could not take snapshot.", file); 732 Log.Error(exception, "Could not take snapshot.", file);
684 } 733 }
685 } 734 }
686 } 735 }
687   736  
688 public async Task TakeSnapshotRecursive(string path) 737 public async Task TakeSnapshotRecursive(string path)
689 { 738 {
690 foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories)) 739 foreach (var file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
691 { 740 {
692 try 741 try
693 { 742 {
694 var fileName = Path.GetFileName(file); 743 var fileName = Path.GetFileName(file);
695 var directory = Path.GetDirectoryName(fileName); 744 var directory = Path.GetDirectoryName(fileName);
696 var color = Color.Empty; 745 var color = Color.Empty;
697 if (TrackedFolders.TryGet(directory, out var folder)) 746 if (TrackedFolders.TryGet(directory, out var folder))
698 { 747 {
699 color = folder.Color; 748 color = folder.Color;
700 } 749 }
701   750  
702 await _snapshotDatabase.CreateSnapshotAsync(fileName, file, color, _cancellationToken); 751 await _snapshotDatabase.CreateSnapshotAsync(fileName, file, color, _cancellationToken);
703 } 752 }
704 catch (SQLiteException exception) 753 catch (SQLiteException exception)
705 { 754 {
706 if (exception.ResultCode == SQLiteErrorCode.Constraint) 755 if (exception.ResultCode == SQLiteErrorCode.Constraint)
707 { 756 {
708 Log.Information(exception, "Snapshot already exists."); 757 Log.Information(exception, "Snapshot already exists.");
709 } 758 }
710 } 759 }
711 catch (Exception exception) 760 catch (Exception exception)
712 { 761 {
713 Log.Error(exception, "Could not take snapshot.", file); 762 Log.Error(exception, "Could not take snapshot.", file);
714 } 763 }
715 } 764 }
716 } 765 }
717   766  
718 public async Task SaveConfiguration() 767 public async Task SaveConfiguration()
719 { 768 {
720 if (!Directory.Exists(Constants.UserApplicationDirectory)) 769 if (!Directory.Exists(Constants.UserApplicationDirectory))
721 { 770 {
722 Directory.CreateDirectory(Constants.UserApplicationDirectory); 771 Directory.CreateDirectory(Constants.UserApplicationDirectory);
723 } 772 }
724   773  
725 switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration", 774 switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration",
726 "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>", 775 "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
727 CancellationToken.None)) 776 CancellationToken.None))
728 { 777 {
729 case SerializationSuccess<Configuration.Configuration> _: 778 case SerializationSuccess<Configuration.Configuration> _:
730 Log.Information("Serialized configuration."); 779 Log.Information("Serialized configuration.");
731 break; 780 break;
732 case SerializationFailure serializationFailure: 781 case SerializationFailure serializationFailure:
733 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration."); 782 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration.");
734 break; 783 break;
735 } 784 }
736 } 785 }
737   786  
738 public static async Task<Configuration.Configuration> LoadConfiguration() 787 public static async Task<Configuration.Configuration> LoadConfiguration()
739 { 788 {
740 if (!Directory.Exists(Constants.UserApplicationDirectory)) 789 if (!Directory.Exists(Constants.UserApplicationDirectory))
741 { 790 {
742 Directory.CreateDirectory(Constants.UserApplicationDirectory); 791 Directory.CreateDirectory(Constants.UserApplicationDirectory);
743 } 792 }
744   793  
745 var deserializationResult = 794 var deserializationResult =
746 await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile, 795 await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile,
747 Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None); 796 Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None);
748   797  
749 switch (deserializationResult) 798 switch (deserializationResult)
750 { 799 {
751 case SerializationSuccess<Configuration.Configuration> serializationSuccess: 800 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
752 return serializationSuccess.Result; 801 return serializationSuccess.Result;
753 case SerializationFailure serializationFailure: 802 case SerializationFailure serializationFailure:
754 Log.Warning(serializationFailure.Exception, "Failed to load configuration."); 803 Log.Warning(serializationFailure.Exception, "Failed to load configuration.");
755 return new Configuration.Configuration(); 804 return new Configuration.Configuration();
756 default: 805 default:
757 return new Configuration.Configuration(); 806 return new Configuration.Configuration();
758 } 807 }
759 } 808 }
760   809  
761 public static async Task<TrackedFolders.TrackedFolders> LoadFolders() 810 public static async Task<TrackedFolders.TrackedFolders> LoadFolders()
762 { 811 {
763 if (!Directory.Exists(Constants.UserApplicationDirectory)) 812 if (!Directory.Exists(Constants.UserApplicationDirectory))
764 { 813 {
765 Directory.CreateDirectory(Constants.UserApplicationDirectory); 814 Directory.CreateDirectory(Constants.UserApplicationDirectory);
766 } 815 }
767   816  
768 var deserializationResult = 817 var deserializationResult =
769 await Serialization.Deserialize<TrackedFolders.TrackedFolders>(Constants.FoldersFile, 818 await Serialization.Deserialize<TrackedFolders.TrackedFolders>(Constants.FoldersFile,
770 Constants.TrackedFoldersNamespace, Constants.TrackedFoldersXsd, CancellationToken.None); 819 Constants.TrackedFoldersNamespace, Constants.TrackedFoldersXsd, CancellationToken.None);
771   820  
772 switch (deserializationResult) 821 switch (deserializationResult)
773 { 822 {
774 case SerializationSuccess<TrackedFolders.TrackedFolders> serializationSuccess: 823 case SerializationSuccess<TrackedFolders.TrackedFolders> serializationSuccess:
775 return serializationSuccess.Result; 824 return serializationSuccess.Result;
776 case SerializationFailure serializationFailure: 825 case SerializationFailure serializationFailure:
777 Log.Warning(serializationFailure.Exception, "Failed to load tracked folders"); 826 Log.Warning(serializationFailure.Exception, "Failed to load tracked folders");
778 return new TrackedFolders.TrackedFolders(); 827 return new TrackedFolders.TrackedFolders();
779 default: 828 default:
780 return new TrackedFolders.TrackedFolders(); 829 return new TrackedFolders.TrackedFolders();
781 } 830 }
782 } 831 }
783   832  
784 public async Task SaveFolders() 833 public async Task SaveFolders()
785 { 834 {
786 if (!Directory.Exists(Constants.UserApplicationDirectory)) 835 if (!Directory.Exists(Constants.UserApplicationDirectory))
787 { 836 {
788 Directory.CreateDirectory(Constants.UserApplicationDirectory); 837 Directory.CreateDirectory(Constants.UserApplicationDirectory);
789 } 838 }
790   839  
791 switch (await Serialization.Serialize(TrackedFolders, Constants.FoldersFile, "TrackedFolders", 840 switch (await Serialization.Serialize(TrackedFolders, Constants.FoldersFile, "TrackedFolders",
792 "<!ATTLIST TrackedFolders xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>", 841 "<!ATTLIST TrackedFolders xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
793 CancellationToken.None)) 842 CancellationToken.None))
794 { 843 {
795 case SerializationSuccess<TrackedFolders.TrackedFolders> _: 844 case SerializationSuccess<TrackedFolders.TrackedFolders> _:
796 Log.Information("Serialized tracked folders."); 845 Log.Information("Serialized tracked folders.");
797 break; 846 break;
798 case SerializationFailure serializationFailure: 847 case SerializationFailure serializationFailure:
799 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize tracked folders."); 848 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize tracked folders.");
800 break; 849 break;
801 } 850 }
802 } 851 }
803   852  
804 #endregion 853 #endregion
805   854  
806 #region Private Methods 855 #region Private Methods
807   856  
808 private void RemoveWatcher(string folder) 857 private void RemoveWatcher(string folder)
809 { 858 {
810 var removeList = new List<FileSystemWatcher>(); 859 var removeList = new List<FileSystemWatcher>();
811 foreach (var fileSystemWatcher in FileSystemWatchers) 860 foreach (var fileSystemWatcher in FileSystemWatchers)
812 { 861 {
813 if (fileSystemWatcher.Path.IsPathEqual(folder) || 862 if (fileSystemWatcher.Path.IsPathEqual(folder) ||
814 fileSystemWatcher.Path.IsSubPathOf(folder)) 863 fileSystemWatcher.Path.IsSubPathOf(folder))
815 { 864 {
816 removeList.Add(fileSystemWatcher); 865 removeList.Add(fileSystemWatcher);
817 } 866 }
818 } 867 }
819   868  
820 foreach (var fileSystemWatcher in removeList) 869 foreach (var fileSystemWatcher in removeList)
821 { 870 {
822 FileSystemWatchers.Remove(fileSystemWatcher); 871 FileSystemWatchers.Remove(fileSystemWatcher);
823 fileSystemWatcher.Changed -= FileSystemWatcher_Changed; 872 fileSystemWatcher.Changed -= FileSystemWatcher_Changed;
824 fileSystemWatcher.Dispose(); 873 fileSystemWatcher.Dispose();
825 } 874 }
826 } 875 }
827   876  
828 private void AddWatcher(string folder, bool recursive) 877 private void AddWatcher(string folder, bool recursive)
829 { 878 {
830 var fileSystemWatcher = new FileSystemWatcher 879 var fileSystemWatcher = new FileSystemWatcher
831 { 880 {
832 IncludeSubdirectories = recursive, 881 IncludeSubdirectories = recursive,
833 NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Attributes, 882 NotifyFilter = _fileSystemWatchersNotifyFilters,
834 Path = folder, 883 Path = folder,
835 EnableRaisingEvents = true 884 EnableRaisingEvents = true
836 }; 885 };
837   886  
838 fileSystemWatcher.Changed += FileSystemWatcher_Changed; 887 fileSystemWatcher.Changed += FileSystemWatcher_Changed;
839   888  
840 FileSystemWatchers.Add(fileSystemWatcher); 889 FileSystemWatchers.Add(fileSystemWatcher);
841 } 890 }
842   891  
843 private void ToggleWatchers() 892 private void ToggleWatchers()
844 { 893 {
845 switch (Configuration.Enabled) 894 switch (Configuration.Enabled)
846 { 895 {
847 case true: 896 case true:
848 foreach (var watcher in FileSystemWatchers) 897 foreach (var watcher in FileSystemWatchers)
849 { 898 {
850 watcher.EnableRaisingEvents = true; 899 watcher.EnableRaisingEvents = true;
851 } 900 }
852   901  
853 if (Configuration.ShowBalloonTooltips) 902 if (Configuration.ShowBalloonTooltips)
854 { 903 {
855 ShowBalloon("Watching", "Watching folders...", 5000); 904 ShowBalloon("Watching", "Watching folders...", 5000);
856 } 905 }
857   906  
858 Log.Information("Watching folders."); 907 Log.Information("Watching folders.");
859   908  
860 break; 909 break;
861 default: 910 default:
862 foreach (var watcher in FileSystemWatchers) 911 foreach (var watcher in FileSystemWatchers)
863 { 912 {
864 watcher.EnableRaisingEvents = false; 913 watcher.EnableRaisingEvents = false;
865 } 914 }
866   915  
867 if (Configuration.ShowBalloonTooltips) 916 if (Configuration.ShowBalloonTooltips)
868 { 917 {
869 ShowBalloon("Not Watching", "Folders are not being watched.", 5000); 918 ShowBalloon("Not Watching", "Folders are not being watched.", 5000);
870 } 919 }
871   920  
872 Log.Information("Folders are not being watched."); 921 Log.Information("Folders are not being watched.");
873   922  
874 break; 923 break;
875 } 924 }
876 } 925 }
877   926  
878 private async Task TakeSnapshots(Color color, CancellationToken cancellationToken) 927 private async Task TakeSnapshots(Color color, CancellationToken cancellationToken)
879 { 928 {
880 var bufferBlock = new BufferBlock<string>(new DataflowBlockOptions() {CancellationToken = cancellationToken}); 929 var bufferBlock = new BufferBlock<string>(new DataflowBlockOptions() {CancellationToken = cancellationToken});
881 var actionBlock = new ActionBlock<string>(async path => 930 var actionBlock = new ActionBlock<string>(async path =>
882 { 931 {
883 // In case files have vanished strictly due to the time specified by the tracked folders delay. 932 // In case files have vanished strictly due to the time specified by the tracked folders delay.
884 if (!File.Exists(path)) 933 if (!File.Exists(path))
885 { 934 {
886 Log.Warning("File vanished after tracked folder delay.", path); 935 Log.Warning("File vanished after tracked folder delay.", path);
887   936  
888 return; 937 return;
889 } 938 }
890   939  
891 try 940 try
892 { 941 {
893 var fileName = System.IO.Path.GetFileName(path); 942 var fileName = System.IO.Path.GetFileName(path);
894 var screenCapture = ScreenCapture.Capture((Utilities.CaptureMode)Configuration.CaptureMode); 943 var screenCapture = ScreenCapture.Capture((Utilities.CaptureMode)Configuration.CaptureMode);
895   944  
896 await _snapshotDatabase.CreateSnapshotAsync(fileName, path, screenCapture, color, 945 await _snapshotDatabase.CreateSnapshotAsync(fileName, path, screenCapture, color,
897 _cancellationToken); 946 _cancellationToken);
898 } 947 }
899 catch (SQLiteException exception) 948 catch (SQLiteException exception)
900 { 949 {
901 if (exception.ResultCode == SQLiteErrorCode.Constraint) 950 if (exception.ResultCode == SQLiteErrorCode.Constraint)
902 { 951 {
903 Log.Information(exception, "Snapshot already exists."); 952 Log.Information(exception, "Snapshot already exists.");
904 } 953 }
905 } 954 }
906 catch (Exception exception) 955 catch (Exception exception)
907 { 956 {
908 Log.Error(exception, "Could not take snapshot.", path); 957 Log.Error(exception, "Could not take snapshot.", path);
909 } 958 }
910 }); 959 });
911   960  
912 using (var snapshotLink = 961 using (var snapshotLink =
913 bufferBlock.LinkTo(actionBlock, new DataflowLinkOptions() { PropagateCompletion = true })) 962 bufferBlock.LinkTo(actionBlock, new DataflowLinkOptions() { PropagateCompletion = true }))
914 { 963 {
915 await _changedFilesLock.WaitAsync(_cancellationToken); 964 await _changedFilesLock.WaitAsync(_cancellationToken);
916 try 965 try
917 { 966 {
918 foreach (var path in _changedFiles) 967 foreach (var path in _changedFiles)
919 { 968 {
920 await bufferBlock.SendAsync(path, cancellationToken); 969 await bufferBlock.SendAsync(path, cancellationToken);
921 } 970 }
922 bufferBlock.Complete(); 971 bufferBlock.Complete();
923 await bufferBlock.Completion; 972 await bufferBlock.Completion;
924 } 973 }
925 catch (Exception exception) 974 catch (Exception exception)
926 { 975 {
927 Log.Error(exception, "Could not take snapshots."); 976 Log.Error(exception, "Could not take snapshots.");
928 } 977 }
929 finally 978 finally
930 { 979 {
931 _changedFiles.Clear(); 980 _changedFiles.Clear();
932 _changedFilesLock.Release(); 981 _changedFilesLock.Release();
933 } 982 }
934 } 983 }
935 } 984 }
936   985  
937 #endregion 986 #endregion
938 } 987 }
939 } 988 }
940   989  
941
Generated by GNU Enscript 1.6.5.90.
990
Generated by GNU Enscript 1.6.5.90.
942   991  
943   992  
944   993