Horizon – Diff between revs 11 and 12

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