diff --git a/.gitmodules b/.gitmodules index 573c48b..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +0,0 @@ -[submodule "Common.Wpf"] - path = Common.Wpf - url = https://github.com/ckaczor/Common.Wpf.git -[submodule "Common"] - path = Common - url = https://github.com/ckaczor/Common.git diff --git a/Application/App.xaml b/Application/App.xaml index d1ce5b2..40e1a21 100644 --- a/Application/App.xaml +++ b/Application/App.xaml @@ -4,7 +4,7 @@ - + diff --git a/Application/App.xaml.cs b/Application/App.xaml.cs index c71e977..08f0b36 100644 --- a/Application/App.xaml.cs +++ b/Application/App.xaml.cs @@ -1,13 +1,12 @@ -using Common.Debug; -using Common.Helpers; -using Common.IO; -using Common.Settings; -using Common.Wpf.Extensions; +using CKaczor.GenericSettingsProvider; +using CKaczor.Wpf.Application; using FeedCenter.Data; using FeedCenter.Properties; +using Serilog; using System; -using System.Diagnostics; -using System.Globalization; +using System.IO; +using System.Linq; +using System.Windows.Threading; namespace FeedCenter { @@ -34,32 +33,23 @@ namespace FeedCenter var app = new App(); app.InitializeComponent(); - // Create an isolation handle to see if we are already running - var isolationHandle = ApplicationIsolation.GetIsolationHandle(FeedCenter.Properties.Resources.ApplicationName); + // Create an single instance handle to see if we are already running + var isolationHandle = SingleInstance.GetSingleInstanceHandleAsync(Name).Result; // If there is another copy then pass it the command line and exit if (isolationHandle == null) - { - InterprocessMessageSender.SendMessage(FeedCenter.Properties.Resources.ApplicationName, Environment.CommandLine); return; - } // Use the handle over the lifetime of the application using (isolationHandle) { - // Set the data directory based on debug or not - AppDomain.CurrentDomain.SetData("DataDirectory", SystemConfiguration.DataDirectory); - - // Get the data directory - var path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - // Set the path - Database.DatabasePath = path; - Database.DatabaseFile = System.IO.Path.Combine(path, Settings.Default.DatabaseFile); + Database.DatabasePath = SystemConfiguration.DataDirectory; + Database.DatabaseFile = Path.Combine(SystemConfiguration.DataDirectory, Settings.Default.DatabaseFile); Database.Load(); // Get the generic provider - var genericProvider = (GenericSettingsProvider) Settings.Default.Providers[typeof(GenericSettingsProvider).Name]; + var genericProvider = (GenericSettingsProvider) Settings.Default.Providers[nameof(GenericSettingsProvider)]; if (genericProvider == null) return; @@ -73,8 +63,22 @@ namespace FeedCenter genericProvider.GetVersionList = SettingsStore.GetVersionList; genericProvider.DeleteOldVersionsOnUpgrade = !IsDebugBuild; - // Initialize the tracer with the current process ID - Tracer.Initialize(SystemConfiguration.UserSettingsPath, FeedCenter.Properties.Resources.ApplicationName, Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture), false); + Log.Logger = new LoggerConfiguration() + .Enrich.WithThreadId() + .WriteTo.Console() + .WriteTo.File( + Path.Join(SystemConfiguration.UserSettingsPath, $"{FeedCenter.Properties.Resources.ApplicationName}_.txt"), + rollingInterval: RollingInterval.Day, retainedFileCountLimit: 5, + outputTemplate: "[{Timestamp:u} - {ThreadId} - {Level:u3}] {Message:lj}{NewLine}{Exception}") + .CreateLogger(); + + Log.Logger.Information("---"); + Log.Logger.Information("Application started"); + + Log.Logger.Information("Command line arguments:"); + + foreach (var arg in Environment.GetCommandLineArgs().Select((value, index) => (Value: value, Index: index))) + Log.Logger.Information("\tArg {0}: {1}", arg.Index, arg.Value); Current.DispatcherUnhandledException += HandleCurrentDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += HandleCurrentDomainUnhandledException; @@ -103,22 +107,19 @@ namespace FeedCenter // Run the app app.Run(mainWindow); - - // Terminate the tracer - Tracer.Dispose(); } } private static void HandleCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) { - Tracer.WriteException((Exception) e.ExceptionObject); - Tracer.Flush(); + Log.Logger.Error((Exception) e.ExceptionObject, "Exception"); } - private static void HandleCurrentDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + private static void HandleCurrentDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { - Tracer.WriteException(e.Exception); - Tracer.Flush(); + Log.Logger.Error(e.Exception, "Exception"); } + + public static string Name => FeedCenter.Properties.Resources.ApplicationName; } -} +} \ No newline at end of file diff --git a/Application/BrowserCommon.cs b/Application/BrowserCommon.cs deleted file mode 100644 index 9ead038..0000000 --- a/Application/BrowserCommon.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Common.Debug; -using Common.Internet; -using FeedCenter.Properties; -using System; -using System.Diagnostics; - -namespace FeedCenter -{ - public static class BrowserCommon - { - public static Browser FindBrowser(string browserKey) - { - Browser browser = null; - - // Get the list of installed browsers - var browsers = Browser.DetectInstalledBrowsers(); - - // Make sure the desired browser exists - if (browsers.ContainsKey(browserKey)) - { - // Get the browser - browser = browsers[browserKey]; - } - - return browser; - } - - public static bool OpenLink(string url) - { - // Get the browser - var browser = FindBrowser(Settings.Default.Browser); - - // Start the browser - return OpenLink(browser, url); - } - - public static bool OpenLink(Browser browser, string url) - { - try - { - // Don't bother with empty links - if (String.IsNullOrEmpty(url)) - return true; - - // Add quotes around the URL for safety - url = $"\"{url}\""; - - // Start the browser - if (browser == null) - { - var ps = new ProcessStartInfo(url) - { - UseShellExecute = true, - Verb = "open" - }; - - Process.Start(ps); - } - else - Process.Start(browser.Command, url); - - return true; - } - catch (Exception exception) - { - // Just log the exception - Tracer.WriteException(exception); - - return false; - } - } - } -} diff --git a/Application/Data/Database.cs b/Application/Data/Database.cs index c7c16bf..6c70743 100644 --- a/Application/Data/Database.cs +++ b/Application/Data/Database.cs @@ -1,5 +1,5 @@ -using Common.Debug; -using FeedCenter.Properties; +using FeedCenter.Properties; +using Serilog; using System; using System.Collections.Generic; using System.Data.SqlServerCe; @@ -38,9 +38,9 @@ namespace FeedCenter.Data private static SqlServerCeFileVersion GetFileVersion(string databasePath) { // Create a mapping of version numbers to the version enumeration - var versionMapping = new Dictionary - { - { 0x73616261, SqlServerCeFileVersion.Version20 }, + var versionMapping = new Dictionary + { + { 0x73616261, SqlServerCeFileVersion.Version20 }, { 0x002dd714, SqlServerCeFileVersion.Version30 }, { 0x00357b9d, SqlServerCeFileVersion.Version35 }, { 0x003d0900, SqlServerCeFileVersion.Version40 } @@ -63,7 +63,7 @@ namespace FeedCenter.Data } catch (Exception exception) { - Tracer.WriteException(exception); + Log.Logger.Error(exception, "Exception"); throw; } @@ -78,16 +78,16 @@ namespace FeedCenter.Data public static void CreateDatabase() { - Tracer.WriteLine("Creating database engine"); + Log.Logger.Information("Creating database engine"); // Create the database engine using var engine = new SqlCeEngine($"Data Source={DatabaseFile}"); - Tracer.WriteLine("Creating database"); + Log.Logger.Information("Creating database"); // Create the database itself engine.CreateDatabase(); - Tracer.WriteLine("Running database script"); + Log.Logger.Information("Running database script"); // Run the creation script ExecuteScript(Resources.CreateDatabase); @@ -113,34 +113,34 @@ namespace FeedCenter.Data if (string.IsNullOrEmpty(versionString)) versionString = "0"; - Tracer.WriteLine("Database version: {0}", versionString); + Log.Logger.Information("Database version: {0}", versionString); return int.Parse(versionString); } public static void UpdateDatabase() { - Tracer.WriteLine("Getting database file version"); + Log.Logger.Information("Getting database file version"); // Get the database file version var fileVersion = GetFileVersion(DatabaseFile); - Tracer.WriteLine("Database file version: {0}", fileVersion); + Log.Logger.Information("Database file version: {0}", fileVersion); // See if we need to upgrade the database file version if (fileVersion != SqlServerCeFileVersion.Version40) { - Tracer.WriteLine("Creating database engine"); + Log.Logger.Information("Creating database engine"); // Create the database engine using var engine = new SqlCeEngine($"Data Source={DatabaseFile}"); - Tracer.WriteLine("Upgrading database"); + Log.Logger.Information("Upgrading database"); // Upgrade the database (if needed) engine.Upgrade(); } - Tracer.WriteLine("Getting database version"); + Log.Logger.Information("Getting database version"); // Create a database connection using var connection = new SqlCeConnection($"Data Source={DatabaseFile}"); @@ -183,11 +183,11 @@ namespace FeedCenter.Data public static void MaintainDatabase() { - Tracer.WriteLine("Creating database engine"); + Log.Logger.Information("Creating database engine"); // Create the database engine using var engine = new SqlCeEngine($"Data Source={DatabaseFile}"); - Tracer.WriteLine("Shrinking database"); + Log.Logger.Information("Shrinking database"); // Compact the database engine.Shrink(); diff --git a/Application/FeedCenter.csproj b/Application/FeedCenter.csproj index 7e65192..85e7b59 100644 --- a/Application/FeedCenter.csproj +++ b/Application/FeedCenter.csproj @@ -125,22 +125,50 @@ - - - + + + + + + + + + + + + - + + NU1701 + + + + + + + + True + True + Settings.settings + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + if not exist "$(TargetDir)x86" md "$(TargetDir)x86" diff --git a/Application/FeedChooserWindow.xaml b/Application/FeedChooserWindow.xaml index a3d7e45..5f6d992 100644 --- a/Application/FeedChooserWindow.xaml +++ b/Application/FeedChooserWindow.xaml @@ -3,8 +3,8 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:windows="clr-namespace:Common.Wpf.Windows;assembly=Common.Wpf" xmlns:my="clr-namespace:FeedCenter.Properties" + xmlns:controlBox="clr-namespace:CKaczor.Wpf.Windows;assembly=Wpf.Windows.ControlBox" mc:Ignorable="d" Title="{x:Static my:Resources.FeedChooserWindow}" Height="247.297" @@ -12,8 +12,8 @@ WindowStartupLocation="CenterOwner" Icon="/FeedCenter;component/Resources/Application.ico" FocusManager.FocusedElement="{Binding ElementName=FeedDataGrid}" - windows:ControlBox.HasMaximizeButton="False" - windows:ControlBox.HasMinimizeButton="False"> + controlBox:ControlBox.HasMaximizeButton="False" + controlBox:ControlBox.HasMinimizeButton="False"> diff --git a/Application/FeedErrorWindow.xaml b/Application/FeedErrorWindow.xaml index 455e565..b777968 100644 --- a/Application/FeedErrorWindow.xaml +++ b/Application/FeedErrorWindow.xaml @@ -1,16 +1,16 @@  + controlBox:ControlBox.HasMaximizeButton="False" + controlBox:ControlBox.HasMinimizeButton="False"> @@ -53,31 +53,31 @@ BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"> - - - - - + + + + + - + \ No newline at end of file diff --git a/Application/FeedErrorWindow.xaml.cs b/Application/FeedErrorWindow.xaml.cs index bc72b7c..f595897 100644 --- a/Application/FeedErrorWindow.xaml.cs +++ b/Application/FeedErrorWindow.xaml.cs @@ -1,9 +1,11 @@ -using FeedCenter.Options; +using CKaczor.InstalledBrowsers; +using FeedCenter.Data; +using FeedCenter.Options; +using FeedCenter.Properties; using System.ComponentModel; using System.Windows; using System.Windows.Data; using System.Windows.Input; -using FeedCenter.Data; namespace FeedCenter { @@ -87,13 +89,13 @@ namespace FeedCenter private void HandleOpenPageButtonClick(object sender, RoutedEventArgs e) { var feed = (Feed) FeedDataGrid.SelectedItem; - BrowserCommon.OpenLink(feed.Link); + InstalledBrowser.OpenLink(Settings.Default.Browser, feed.Link); } private void HandleOpenFeedButtonClick(object sender, RoutedEventArgs e) { var feed = (Feed) FeedDataGrid.SelectedItem; - BrowserCommon.OpenLink(feed.Source); + InstalledBrowser.OpenLink(Settings.Default.Browser, feed.Source); } private void HandleOkayButtonClick(object sender, RoutedEventArgs e) @@ -129,4 +131,4 @@ namespace FeedCenter IsEnabled = true; } } -} +} \ No newline at end of file diff --git a/Application/FeedParsers/AtomParser.cs b/Application/FeedParsers/AtomParser.cs index 6bba127..ddaa7e3 100644 --- a/Application/FeedParsers/AtomParser.cs +++ b/Application/FeedParsers/AtomParser.cs @@ -1,5 +1,5 @@ -using System; -using Common.Debug; +using Serilog; +using System; using System.Xml; namespace FeedCenter.FeedParsers @@ -68,7 +68,7 @@ namespace FeedCenter.FeedParsers } catch (XmlException xmlException) { - Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText); + Log.Logger.Error(xmlException, "Exception: {0}", feedText); return FeedReadResult.InvalidXml; } diff --git a/Application/FeedParsers/FeedParserBase.cs b/Application/FeedParsers/FeedParserBase.cs index 201c168..4bfc8fd 100644 --- a/Application/FeedParsers/FeedParserBase.cs +++ b/Application/FeedParsers/FeedParserBase.cs @@ -1,4 +1,4 @@ -using Common.Debug; +using Serilog; using System; using System.Linq; using System.Xml; @@ -55,7 +55,7 @@ namespace FeedCenter.FeedParsers // Check to see if we already have this feed item if (existingFeedItem == null) { - Tracer.WriteLine("New link: " + newFeedItem.Link); + Log.Logger.Information("New link: " + newFeedItem.Link); // Associate the new item with the right feed newFeedItem.Feed = Feed; @@ -71,7 +71,7 @@ namespace FeedCenter.FeedParsers } else { - Tracer.WriteLine("Existing link: " + newFeedItem.Link); + Log.Logger.Information("Existing link: " + newFeedItem.Link); // Update the fields in the existing item existingFeedItem.Link = newFeedItem.Link; @@ -150,8 +150,8 @@ namespace FeedCenter.FeedParsers } catch (Exception exception) { - Tracer.WriteException(exception); - + Log.Logger.Error(exception, "Exception: {0}", feedText); + return FeedType.Unknown; } } diff --git a/Application/FeedParsers/RdfParser.cs b/Application/FeedParsers/RdfParser.cs index 8289f53..1a1f155 100644 --- a/Application/FeedParsers/RdfParser.cs +++ b/Application/FeedParsers/RdfParser.cs @@ -1,6 +1,7 @@ -using Common.Debug; -using Common.Xml; +using Serilog; +using System; using System.Xml; +using FeedCenter.Xml; namespace FeedCenter.FeedParsers { @@ -73,7 +74,7 @@ namespace FeedCenter.FeedParsers } catch (XmlException xmlException) { - Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText); + Log.Logger.Error(xmlException, "Exception: {0}", feedText); return FeedReadResult.InvalidXml; } diff --git a/Application/FeedParsers/RssParser.cs b/Application/FeedParsers/RssParser.cs index fe414c9..4f34174 100644 --- a/Application/FeedParsers/RssParser.cs +++ b/Application/FeedParsers/RssParser.cs @@ -1,7 +1,7 @@ -using Common.Debug; -using Common.Xml; +using Serilog; using System; using System.Xml; +using FeedCenter.Xml; namespace FeedCenter.FeedParsers { @@ -67,7 +67,7 @@ namespace FeedCenter.FeedParsers } catch (XmlException xmlException) { - Tracer.WriteLine("XML error: " + xmlException.Message + "\n" + feedText); + Log.Logger.Error(xmlException, "Exception: {0}", feedText); return FeedReadResult.InvalidXml; } diff --git a/Application/Feeds/Feed.cs b/Application/Feeds/Feed.cs index 96e7e75..8c7642d 100644 --- a/Application/Feeds/Feed.cs +++ b/Application/Feeds/Feed.cs @@ -1,10 +1,9 @@ -using Common.Debug; -using Common.Update; -using Common.Xml; +using CKaczor.ApplicationUpdate; using FeedCenter.Data; using FeedCenter.FeedParsers; using FeedCenter.Properties; using Realms; +using Serilog; using System; using System.Collections.Generic; using System.Diagnostics; @@ -16,6 +15,8 @@ using System.Net.Http.Headers; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using FeedCenter.Xml; +using Resources = FeedCenter.Properties.Resources; namespace FeedCenter { @@ -64,6 +65,7 @@ namespace FeedCenter [PrimaryKey] [MapTo("ID")] public Guid Id { get; set; } + public string Name { get; set; } public string Title { get; set; } public string Source { get; set; } @@ -140,8 +142,7 @@ namespace FeedCenter public FeedReadResult Read(FeedCenterEntities database, bool forceRead = false) { - Tracer.WriteLine("Reading feed: {0}", Source); - Tracer.IncrementIndentLevel(); + Log.Logger.Information("Reading feed: {0}", Source); var result = ReadFeed(database, forceRead); @@ -166,8 +167,7 @@ namespace FeedCenter if (result == FeedReadResult.Success && LastUpdated == Extensions.SqlDateTimeZero.Value) LastUpdated = DateTimeOffset.Now; - Tracer.DecrementIndentLevel(); - Tracer.WriteLine("Done reading feed: {0}", result); + Log.Logger.Information("Done reading feed: {0}", result); return result; } @@ -236,7 +236,7 @@ namespace FeedCenter } catch (IOException ioException) { - Tracer.WriteLine(ioException.Message); + Log.Logger.Error(ioException, "Exception"); return Tuple.Create(FeedReadResult.ConnectionFailed, string.Empty); } @@ -281,7 +281,7 @@ namespace FeedCenter break; } - Tracer.WriteException(webException); + Log.Logger.Error(webException, "Exception"); if (result == FeedReadResult.UnknownError) Debug.Print("Unknown error"); @@ -290,7 +290,7 @@ namespace FeedCenter } catch (Exception exception) { - Tracer.WriteLine(exception.Message); + Log.Logger.Error(exception, "Exception"); return Tuple.Create(FeedReadResult.UnknownError, string.Empty); } @@ -357,13 +357,13 @@ namespace FeedCenter } catch (InvalidFeedFormatException exception) { - Tracer.WriteException(exception.InnerException); + Log.Logger.Error(exception, "Exception"); return FeedReadResult.InvalidXml; } catch (Exception exception) { - Tracer.WriteLine(exception.Message); + Log.Logger.Error(exception, "Exception"); return FeedReadResult.UnknownError; } diff --git a/Application/MainWindow/CategoryList.cs b/Application/MainWindow/CategoryList.cs index 96ec954..1509726 100644 --- a/Application/MainWindow/CategoryList.cs +++ b/Application/MainWindow/CategoryList.cs @@ -1,12 +1,12 @@ -using System; +using FeedCenter.Properties; +using System; using System.Linq; using System.Windows; using System.Windows.Controls; -using FeedCenter.Properties; namespace FeedCenter { - public partial class MainWindow : IDisposable + public partial class MainWindow { private void DisplayCategory() { @@ -96,12 +96,5 @@ namespace FeedCenter Settings.Default.LastCategoryID = _currentCategory?.Id.ToString() ?? string.Empty; } - - public void Dispose() - { - _mainTimer?.Dispose(); - _feedReadWorker?.Dispose(); - _commandLineListener?.Dispose(); - } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/CommandLine.cs b/Application/MainWindow/CommandLine.cs index 6756e1b..c4e28ee 100644 --- a/Application/MainWindow/CommandLine.cs +++ b/Application/MainWindow/CommandLine.cs @@ -1,42 +1,38 @@ -using Common.IO; -using System; +using System; namespace FeedCenter { public partial class MainWindow { - private InterprocessMessageListener _commandLineListener; - - private void HandleCommandLine(object sender, InterprocessMessageListener.InterprocessMessageEventArgs e) + private void HandleCommandLine(string commandLine) { // If the command line is blank then ignore it - if (e.Message.Length == 0) + if (commandLine.Length == 0) return; // Pad the command line with a trailing space just to be lazy in parsing - var commandLine = e.Message + " "; + commandLine += " "; // Look for the feed URL in the command line var startPosition = commandLine.IndexOf("feed://", StringComparison.Ordinal); - // If we found one then we should extract and process it - if (startPosition > 0) - { - // Advance past the protocol - startPosition += 7; + // If nothing was found then exit + if (startPosition <= 0) return; - // Starting at the URL position look for the next space - var endPosition = commandLine.IndexOf(" ", startPosition, StringComparison.Ordinal); + // Advance past the protocol + startPosition += 7; - // Extract the feed URL - var feedUrl = commandLine.Substring(startPosition, endPosition - startPosition); + // Starting at the URL position look for the next space + var endPosition = commandLine.IndexOf(" ", startPosition, StringComparison.Ordinal); - // Add the HTTP protocol by default - feedUrl = "http://" + feedUrl; + // Extract the feed URL + var feedUrl = commandLine.Substring(startPosition, endPosition - startPosition); - // Create a new feed using the URL - HandleNewFeed(feedUrl); - } + // Add the HTTP protocol by default + feedUrl = "http://" + feedUrl; + + // Create a new feed using the URL + HandleNewFeed(feedUrl); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/FeedCreation.cs b/Application/MainWindow/FeedCreation.cs index 75e973f..0f70142 100644 --- a/Application/MainWindow/FeedCreation.cs +++ b/Application/MainWindow/FeedCreation.cs @@ -1,20 +1,29 @@ -using System; +using FeedCenter.Options; +using System; using System.Linq; using System.Net; -using Common.Internet; -using FeedCenter.Options; namespace FeedCenter { public partial class MainWindow { private delegate void NewFeedDelegate(string feedUrl); + + private static string GetAbsoluteUrlString(string baseUrl, string url) + { + var uri = new Uri(url, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) + uri = new Uri(new Uri(baseUrl), uri); + return uri.ToString(); + } + private void HandleNewFeed(string feedUrl) { // Create and configure the new feed var feed = Feed.Create(_database); feed.Source = feedUrl; feed.Category = _database.DefaultCategory; + feed.Enabled = true; // Try to detect the feed type var feedTypeResult = feed.DetectFeedType(); @@ -32,7 +41,7 @@ namespace FeedCenter // Look for all RSS or atom links in the document var rssLinks = htmlDocument.DocumentNode.Descendants("link") .Where(n => n.Attributes["type"] != null && (n.Attributes["type"].Value == "application/rss+xml" || n.Attributes["type"].Value == "application/atom+xml")) - .Select(n => new Tuple(UrlHelper.GetAbsoluteUrlString(feed.Source, n.Attributes["href"].Value), WebUtility.HtmlDecode(n.Attributes["title"]?.Value ?? feedUrl))) + .Select(n => new Tuple(GetAbsoluteUrlString(feed.Source, n.Attributes["href"].Value), WebUtility.HtmlDecode(n.Attributes["title"]?.Value ?? feedUrl))) .Distinct() .ToList(); @@ -91,4 +100,4 @@ namespace FeedCenter } } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/FeedList.cs b/Application/MainWindow/FeedList.cs index 5f3ce9f..d668a4f 100644 --- a/Application/MainWindow/FeedList.cs +++ b/Application/MainWindow/FeedList.cs @@ -1,4 +1,6 @@ -using System; +using CKaczor.InstalledBrowsers; +using FeedCenter.Properties; +using System; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -49,19 +51,19 @@ namespace FeedCenter // Get the feed item var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext; - // Open the item link - if (BrowserCommon.OpenLink(feedItem.Link)) - { - // The feed item has been read and is no longer new - _database.SaveChanges(() => - { - feedItem.BeenRead = true; - feedItem.New = false; - }); + // Try to open the item link + if (!InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link)) + return; - // Remove the item from the list - LinkTextList.Items.Remove(feedItem); - } + // The feed item has been read and is no longer new + _database.SaveChanges(() => + { + feedItem.BeenRead = true; + feedItem.New = false; + }); + + // Remove the item from the list + LinkTextList.Items.Remove(feedItem); } private void HandleFeedButtonClick(object sender, RoutedEventArgs e) @@ -130,4 +132,4 @@ namespace FeedCenter DisplayFeed(); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/FeedReading.cs b/Application/MainWindow/FeedReading.cs index 53ba953..e8dd651 100644 --- a/Application/MainWindow/FeedReading.cs +++ b/Application/MainWindow/FeedReading.cs @@ -1,4 +1,4 @@ -using Common.Update; +using CKaczor.ApplicationUpdate; using FeedCenter.Properties; using System; using System.Collections.Generic; @@ -113,8 +113,8 @@ namespace FeedCenter // Set the text to match the number of errors FeedErrorsLink.Text = feedErrorCount == 1 - ? Properties.Resources.FeedErrorLink - : string.Format(Properties.Resources.FeedErrorsLink, feedErrorCount); + ? Properties.Resources.FeedErrorLink + : string.Format(Properties.Resources.FeedErrorsLink, feedErrorCount); } private static void HandleFeedReadWorkerStart(object sender, DoWorkEventArgs e) @@ -123,10 +123,10 @@ namespace FeedCenter var database = new FeedCenterEntities(); // Get the worker - var worker = (BackgroundWorker)sender; + var worker = (BackgroundWorker) sender; // Get the input information - var workerInput = (FeedReadWorkerInput)e.Argument ?? new FeedReadWorkerInput { Feed = null, ForceRead = false }; + var workerInput = (FeedReadWorkerInput) e.Argument ?? new FeedReadWorkerInput { Feed = null, ForceRead = false }; // Setup for progress var currentProgress = 0; @@ -179,4 +179,4 @@ namespace FeedCenter Thread.Sleep(Settings.Default.ProgressSleepInterval * 3); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/Header.cs b/Application/MainWindow/Header.cs index 9ea5af5..4787955 100644 --- a/Application/MainWindow/Header.cs +++ b/Application/MainWindow/Header.cs @@ -1,4 +1,5 @@ -using FeedCenter.Properties; +using CKaczor.InstalledBrowsers; +using FeedCenter.Properties; using System.Windows; using System.Windows.Input; @@ -26,7 +27,7 @@ namespace FeedCenter { // Open the link for the current feed on a left double click if (e.ClickCount == 2 && e.ChangedButton == MouseButton.Left) - BrowserCommon.OpenLink(_currentFeed.Link); + InstalledBrowser.OpenLink(Settings.Default.Browser, _currentFeed.Link); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/MainWindow.xaml b/Application/MainWindow/MainWindow.xaml index b091efb..f712822 100644 --- a/Application/MainWindow/MainWindow.xaml +++ b/Application/MainWindow/MainWindow.xaml @@ -2,13 +2,13 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:properties="clr-namespace:FeedCenter.Properties" - xmlns:windows="clr-namespace:Common.Wpf.Windows;assembly=Common.Wpf" - xmlns:toolbar="clr-namespace:Common.Wpf.Toolbar;assembly=Common.Wpf" - xmlns:splitButton="clr-namespace:Common.Wpf.Toolbar.SplitButton;assembly=Common.Wpf" - xmlns:linkControl="clr-namespace:Common.Wpf.LinkControl;assembly=Common.Wpf" - xmlns:htmlTextBlock="clr-namespace:Common.Wpf.HtmlTextBlock;assembly=Common.Wpf" + xmlns:windows="clr-namespace:CKaczor.Wpf.Windows;assembly=Wpf.Windows.SnappingWindow" + xmlns:toolbar="clr-namespace:CKaczor.Wpf.Controls.Toolbar;assembly=Wpf.Controls.Toolbar" + xmlns:splitButton="clr-namespace:CKaczor.Wpf.Controls.Toolbar;assembly=Wpf.Controls.Toolbar" + xmlns:htmlTextBlock="clr-namespace:CKaczor.Wpf.Controls;assembly=Wpf.Controls.HtmlTextBlock" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:nameBasedGrid="clr-namespace:NameBasedGrid;assembly=NameBasedGrid" + xmlns:controls="clr-namespace:CKaczor.Wpf.Controls;assembly=Wpf.Controls.Link" Title="MainWindow" Height="360" Width="252" @@ -80,18 +80,19 @@ FontFamily="Marlett" Content="r" FontSize="8" - Grid.Column="1"> + Grid.Column="1"> + - - + + - + - + - - + + - + \ No newline at end of file diff --git a/Application/MainWindow/MainWindow.xaml.cs b/Application/MainWindow/MainWindow.xaml.cs index 803213d..2c403c9 100644 --- a/Application/MainWindow/MainWindow.xaml.cs +++ b/Application/MainWindow/MainWindow.xaml.cs @@ -1,9 +1,8 @@ -using Common.Debug; -using Common.Helpers; -using Common.IO; -using Common.Update; +using CKaczor.ApplicationUpdate; +using CKaczor.Wpf.Application; using FeedCenter.Data; using FeedCenter.Properties; +using Serilog; using System; using System.Collections.Generic; using System.ComponentModel; @@ -14,7 +13,7 @@ using System.Windows.Media; namespace FeedCenter { - public partial class MainWindow + public partial class MainWindow : IDisposable { private FeedCenterEntities _database; private int _feedIndex; @@ -28,6 +27,21 @@ namespace FeedCenter InitializeComponent(); } + protected override async void OnClosed(EventArgs e) + { + base.OnClosed(e); + + await SingleInstance.Stop(); + } + + public void Dispose() + { + _mainTimer?.Dispose(); + _feedReadWorker?.Dispose(); + + GC.SuppressFinalize(this); + } + public async void Initialize() { // Setup the update handler @@ -52,12 +66,12 @@ namespace FeedCenter // Setup the database _database = Database.Entities; - // Initialize the command line listener - _commandLineListener = new InterprocessMessageListener(Properties.Resources.ApplicationName); - _commandLineListener.MessageReceived += HandleCommandLine; + // Initialize the single instance listener + SingleInstance.MessageReceived += SingleInstance_MessageReceived; + await SingleInstance.StartAsync(App.Name); // Handle any command line we were started with - HandleCommandLine(null, new InterprocessMessageListener.InterprocessMessageEventArgs(Environment.CommandLine)); + HandleCommandLine(Environment.CommandLine); // Create a timer to keep track of things we need to do InitializeTimer(); @@ -73,7 +87,12 @@ namespace FeedCenter if (UpdateCheck.UpdateAvailable) NewVersionLink.Visibility = Visibility.Visible; - Tracer.WriteLine("MainForm creation finished"); + Log.Logger.Information("MainForm creation finished"); + } + + private void SingleInstance_MessageReceived(object sender, string commandLine) + { + HandleCommandLine(commandLine); } #region Setting events @@ -87,30 +106,36 @@ namespace FeedCenter return; } - if (e.PropertyName == Reflection.GetPropertyName(() => Settings.Default.MultipleLineDisplay)) + switch (e.PropertyName) { - // Update the current feed - DisplayFeed(); - } - else if (e.PropertyName == Reflection.GetPropertyName(() => Settings.Default.WindowLocked)) - { - // Update the window for the new window lock value - HandleWindowLockState(); - } - else if (e.PropertyName == Reflection.GetPropertyName(() => Settings.Default.ToolbarLocation)) - { - // Update the window for the toolbar location - switch (Settings.Default.ToolbarLocation) - { - case Dock.Top: - NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "TopToolbarRow"); + case nameof(Settings.Default.MultipleLineDisplay): + // Update the current feed + DisplayFeed(); + break; + case nameof(Settings.Default.WindowLocked): + // Update the window for the new window lock value + HandleWindowLockState(); + break; + case nameof(Settings.Default.ToolbarLocation): + // Update the window for the toolbar location + switch (Settings.Default.ToolbarLocation) + { + case Dock.Top: + NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "TopToolbarRow"); - break; - case Dock.Bottom: - NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "BottomToolbarRow"); + break; + case Dock.Bottom: + NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "BottomToolbarRow"); - break; - } + break; + case Dock.Left: + case Dock.Right: + throw new NotSupportedException(); + default: + throw new ArgumentOutOfRangeException(nameof(Settings.Default.ToolbarLocation)); + } + + break; } } @@ -124,15 +149,15 @@ namespace FeedCenter var feedCount = _feedList?.Count() ?? 0; // Set button states - PreviousToolbarButton.IsEnabled = (feedCount > 1); - NextToolbarButton.IsEnabled = (feedCount > 1); - RefreshToolbarButton.IsEnabled = (feedCount > 0); - FeedButton.IsEnabled = (feedCount > 0); - OpenAllToolbarButton.IsEnabled = (feedCount > 0); - MarkReadToolbarButton.IsEnabled = (feedCount > 0); - FeedLabel.Visibility = (feedCount == 0 ? Visibility.Hidden : Visibility.Visible); - FeedButton.Visibility = (feedCount > 1 ? Visibility.Hidden : Visibility.Visible); - CategoryGrid.Visibility = (_database.Categories.Count() > 1 ? Visibility.Visible : Visibility.Collapsed); + PreviousToolbarButton.IsEnabled = feedCount > 1; + NextToolbarButton.IsEnabled = feedCount > 1; + RefreshToolbarButton.IsEnabled = feedCount > 0; + FeedButton.IsEnabled = feedCount > 0; + OpenAllToolbarButton.IsEnabled = feedCount > 0; + MarkReadToolbarButton.IsEnabled = feedCount > 0; + FeedLabel.Visibility = feedCount == 0 ? Visibility.Hidden : Visibility.Visible; + FeedButton.Visibility = feedCount > 1 ? Visibility.Hidden : Visibility.Visible; + CategoryGrid.Visibility = _database.Categories.Count > 1 ? Visibility.Visible : Visibility.Collapsed; } private void InitializeDisplay() @@ -196,7 +221,7 @@ namespace FeedCenter _currentFeed = _feedList.OrderBy(feed => feed.Name).AsEnumerable().ElementAt(_feedIndex); // If the current feed has unread items then we can display it - if (_currentFeed.Items.Count(item => !item.BeenRead) > 0) + if (_currentFeed.Items.Any(item => !item.BeenRead)) { found = true; break; @@ -204,8 +229,7 @@ namespace FeedCenter // Increment the index and adjust if we've gone around the end _feedIndex = (_feedIndex + 1) % feedCount; - } - while (_feedIndex != startIndex); + } while (_feedIndex != startIndex); // If nothing was found then clear the current feed if (!found) @@ -263,7 +287,7 @@ namespace FeedCenter _currentFeed = _feedList.OrderBy(feed => feed.Name).AsEnumerable().ElementAt(_feedIndex); // If the current feed has unread items then we can display it - if (_currentFeed.Items.Count(item => !item.BeenRead) > 0) + if (_currentFeed.Items.Any(item => !item.BeenRead)) { found = true; break; @@ -275,8 +299,7 @@ namespace FeedCenter // If we've gone below the start of the list then reset to the end if (_feedIndex < 0) _feedIndex = feedCount - 1; - } - while (_feedIndex != startIndex); + } while (_feedIndex != startIndex); // If nothing was found then clear the current feed if (!found) @@ -395,4 +418,4 @@ namespace FeedCenter #endregion } -} +} \ No newline at end of file diff --git a/Application/MainWindow/Toolbar.cs b/Application/MainWindow/Toolbar.cs index 46c6ddf..7d45ff9 100644 --- a/Application/MainWindow/Toolbar.cs +++ b/Application/MainWindow/Toolbar.cs @@ -1,4 +1,5 @@ -using FeedCenter.Options; +using CKaczor.InstalledBrowsers; +using FeedCenter.Options; using FeedCenter.Properties; using System.IO; using System.Linq; @@ -26,9 +27,6 @@ namespace FeedCenter // Create a new list of feed items var feedItems = (from FeedItem feedItem in LinkTextList.Items select feedItem).ToList(); - // Get the browser - var browser = BrowserCommon.FindBrowser(Settings.Default.Browser); - // Cache the settings object var settings = Settings.Default; @@ -39,7 +37,7 @@ namespace FeedCenter foreach (var feedItem in feedItems) { // Try to open the link - if (BrowserCommon.OpenLink(browser, feedItem.Link)) + if (InstalledBrowser.OpenLink(Settings.Default.Browser, feedItem.Link)) { // Mark the feed as read _database.SaveChanges(() => feedItem.BeenRead = true); @@ -236,9 +234,9 @@ namespace FeedCenter textWriter.Flush(); textWriter.Close(); - BrowserCommon.OpenLink(fileName); + InstalledBrowser.OpenLink(Settings.Default.Browser, fileName); MarkAllItemsAsRead(); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/UpdateHandler.cs b/Application/MainWindow/UpdateHandler.cs index 82cc3d5..920658d 100644 --- a/Application/MainWindow/UpdateHandler.cs +++ b/Application/MainWindow/UpdateHandler.cs @@ -1,4 +1,4 @@ -using Common.Update; +using CKaczor.ApplicationUpdate; using FeedCenter.Properties; using System.Windows; @@ -8,12 +8,13 @@ namespace FeedCenter { private static void InitializeUpdate() { - UpdateCheck.ApplicationName = Properties.Resources.ApplicationDisplayName; - UpdateCheck.UpdateServerType = ServerType.GitHub; - UpdateCheck.UpdateServer = Settings.Default.VersionLocation; - UpdateCheck.ApplicationShutdown = ApplicationShutdown; - UpdateCheck.ApplicationCurrentMessage = ApplicationCurrentMessage; - UpdateCheck.ApplicationUpdateMessage = ApplicationUpdateMessage; + UpdateCheck.Initialize(ServerType.GitHub, + Settings.Default.VersionLocation, + string.Empty, + Properties.Resources.ApplicationDisplayName, + ApplicationShutdown, + ApplicationCurrentMessage, + ApplicationUpdateMessage); } private static bool ApplicationUpdateMessage(string title, string message) @@ -33,8 +34,7 @@ namespace FeedCenter private void HandleNewVersionLinkClick(object sender, RoutedEventArgs e) { - // Display update information UpdateCheck.DisplayUpdateInformation(true); } } -} +} \ No newline at end of file diff --git a/Application/MainWindow/WindowHandler.cs b/Application/MainWindow/WindowHandler.cs index 6e17bb7..80ea56f 100644 --- a/Application/MainWindow/WindowHandler.cs +++ b/Application/MainWindow/WindowHandler.cs @@ -1,11 +1,11 @@ -using FeedCenter.Properties; +using DebounceThrottle; +using FeedCenter.Properties; using System; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media; -using Common.Helpers; namespace FeedCenter { @@ -90,25 +90,22 @@ namespace FeedCenter SaveWindowSettings(); // Save settings - _database.SaveChanges(() => Settings.Default.Save()); + _database.SaveChanges(Settings.Default.Save); // Get rid of the notification icon NotificationIcon.Dispose(); } - private DelayedMethod _windowStateDelay; + private readonly DebounceDispatcher _updateWindowSettingsDispatcher = new(500); + private void HandleWindowSizeChanged(object sender, SizeChangedEventArgs e) { - _windowStateDelay ??= new DelayedMethod(500, UpdateWindowSettings); - - _windowStateDelay.Reset(); + _updateWindowSettingsDispatcher.Debounce(() => Dispatcher.Invoke(UpdateWindowSettings)); } private void HandleWindowLocationChanged(object sender, EventArgs e) { - _windowStateDelay ??= new DelayedMethod(500, UpdateWindowSettings); - - _windowStateDelay.Reset(); + _updateWindowSettingsDispatcher.Debounce(() => Dispatcher.Invoke(UpdateWindowSettings)); } private void UpdateBorder() @@ -152,6 +149,7 @@ namespace FeedCenter } private bool _activated; + protected override void OnActivated(EventArgs e) { base.OnActivated(e); @@ -172,4 +170,4 @@ namespace FeedCenter Settings.Default.PropertyChanged += HandlePropertyChanged; } } -} +} \ No newline at end of file diff --git a/Application/NotificationIcon.cs b/Application/NotificationIcon.cs index d0e558b..cffce32 100644 --- a/Application/NotificationIcon.cs +++ b/Application/NotificationIcon.cs @@ -20,10 +20,7 @@ namespace FeedCenter // Setup the menu var contextMenuStrip = new ContextMenuStrip(); - var toolStripMenuItem = new ToolStripMenuItem(Resources.NotificationIconContextMenuLocked, null, HandleLockWindowClicked) - { - Checked = Settings.Default.WindowLocked - }; + var toolStripMenuItem = new ToolStripMenuItem(Resources.NotificationIconContextMenuLocked, null, HandleLockWindowClicked) { Checked = Settings.Default.WindowLocked }; contextMenuStrip.Items.Add(toolStripMenuItem); contextMenuStrip.Items.Add(new ToolStripSeparator()); diff --git a/Application/Options/AboutOptionsPanel.xaml.cs b/Application/Options/AboutOptionsPanel.xaml.cs index 3336a7c..ee7cfa3 100644 --- a/Application/Options/AboutOptionsPanel.xaml.cs +++ b/Application/Options/AboutOptionsPanel.xaml.cs @@ -1,4 +1,4 @@ -using Common.Update; +using CKaczor.ApplicationUpdate; using System.Reflection; namespace FeedCenter.Options @@ -33,4 +33,4 @@ namespace FeedCenter.Options public override string CategoryName => Properties.Resources.optionCategoryAbout; } -} +} \ No newline at end of file diff --git a/Application/Options/BulkFeedWindow.xaml b/Application/Options/BulkFeedWindow.xaml index 0210c17..4d040c3 100644 --- a/Application/Options/BulkFeedWindow.xaml +++ b/Application/Options/BulkFeedWindow.xaml @@ -5,8 +5,8 @@ Height="300" Width="500" xmlns:my="clr-namespace:FeedCenter.Properties" - xmlns:linkControl="clr-namespace:Common.Wpf.LinkControl;assembly=Common.Wpf" xmlns:feedCenter="clr-namespace:FeedCenter" + xmlns:controls="clr-namespace:CKaczor.Wpf.Controls;assembly=Wpf.Controls.Link" WindowStartupLocation="CenterOwner" Icon="/FeedCenter;component/Resources/Application.ico" FocusManager.FocusedElement="{Binding ElementName=FeedLinkFilterText}"> @@ -61,18 +61,18 @@ - - - - - - + + + + + + @@ -124,4 +124,4 @@ Width="75" Grid.Row="3" /> - + \ No newline at end of file diff --git a/Application/Options/BulkFeedWindow.xaml.cs b/Application/Options/BulkFeedWindow.xaml.cs index 141cc1e..14f301a 100644 --- a/Application/Options/BulkFeedWindow.xaml.cs +++ b/Application/Options/BulkFeedWindow.xaml.cs @@ -1,5 +1,4 @@ -using Common.Wpf; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Windows; @@ -36,7 +35,7 @@ namespace FeedCenter.Options ShowDialog(); } - void HandleCollectionViewSourceFilter(object sender, FilterEventArgs e) + private void HandleCollectionViewSourceFilter(object sender, FilterEventArgs e) { var checkedListBoxItem = (CheckedListItem) e.Item; diff --git a/Application/Options/CategoryWindow.xaml b/Application/Options/CategoryWindow.xaml index 2b9545e..59b9890 100644 --- a/Application/Options/CategoryWindow.xaml +++ b/Application/Options/CategoryWindow.xaml @@ -2,44 +2,71 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:properties="clr-namespace:FeedCenter.Properties" + xmlns:feedCenter="clr-namespace:FeedCenter" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:validation="clr-namespace:CKaczor.Wpf.Validation;assembly=Wpf.Validation" + xmlns:controls="clr-namespace:CKaczor.Wpf.Windows;assembly=Wpf.Windows.ControlBox" + d:DataContext="{d:DesignInstance Type=feedCenter:Category}" Title="CategoryWindow" - Height="119" - Width="339" + Width="300" + ResizeMode="NoResize" + SizeToContent="Height" WindowStartupLocation="CenterOwner" Icon="/FeedCenter;component/Resources/Application.ico" - FocusManager.FocusedElement="{Binding ElementName=NameTextBox}"> - + FocusManager.FocusedElement="{Binding ElementName=NameTextBox}" + controls:ControlBox.HasMinimizeButton="False" + controls:ControlBox.HasMaximizeButton="False"> + + + + + + - +