Horizon – Diff between revs 9 and 11

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