mirror of
https://github.com/ckaczor/FeedCenter.git
synced 2026-01-13 17:22:48 -05:00
- Add support for filtering by category
- Break up main window into partials instead of using regions
This commit is contained in:
97
Application/MainWindow/CategoryList.cs
Normal file
97
Application/MainWindow/CategoryList.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void DisplayCategory()
|
||||
{
|
||||
CategoryLabel.Text = string.Format(Properties.Resources.CategoryFilterHeader, _currentCategory == null ? Properties.Resources.AllCategory : _currentCategory.Name);
|
||||
}
|
||||
|
||||
private void HandleCategoryButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create a new context menu
|
||||
var contextMenu = new ContextMenu();
|
||||
|
||||
// Create the "all" menu item
|
||||
var menuItem = new MenuItem
|
||||
{
|
||||
Header = Properties.Resources.AllCategory,
|
||||
Tag = null,
|
||||
|
||||
// Set the current item to bold
|
||||
FontWeight = _currentCategory == null ? FontWeights.Bold : FontWeights.Normal
|
||||
};
|
||||
|
||||
// Handle the click
|
||||
menuItem.Click += HandleCategoryMenuItemClick;
|
||||
|
||||
// Add the item to the list
|
||||
contextMenu.Items.Add(menuItem);
|
||||
|
||||
// Loop over each feed
|
||||
foreach (var category in _database.Categories.OrderBy(category => category.Name))
|
||||
{
|
||||
// Create a menu item
|
||||
menuItem = new MenuItem
|
||||
{
|
||||
Header = category.Name,
|
||||
Tag = category,
|
||||
|
||||
// Set the current item to bold
|
||||
FontWeight = category.ID == _currentCategory?.ID ? FontWeights.Bold : FontWeights.Normal
|
||||
};
|
||||
|
||||
// Handle the click
|
||||
menuItem.Click += HandleCategoryMenuItemClick;
|
||||
|
||||
// Add the item to the list
|
||||
contextMenu.Items.Add(menuItem);
|
||||
}
|
||||
|
||||
// Set the context menu placement to this button
|
||||
contextMenu.PlacementTarget = this;
|
||||
|
||||
// Open the context menu
|
||||
contextMenu.IsOpen = true;
|
||||
}
|
||||
|
||||
private void HandleCategoryMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Get the menu item clicked
|
||||
var menuItem = (MenuItem) sender;
|
||||
|
||||
// Get the category from the menu item tab
|
||||
var category = (Category) menuItem.Tag;
|
||||
|
||||
// If the category changed then reset the current feed to the first in the category
|
||||
if (_currentCategory?.ID != category?.ID)
|
||||
{
|
||||
_currentFeed = category == null ? _database.Feeds.FirstOrDefault() : category.Feeds.FirstOrDefault();
|
||||
}
|
||||
|
||||
// Set the current category
|
||||
_currentCategory = category;
|
||||
|
||||
// Get the current feed list to match the category
|
||||
_feedList = _currentCategory?.Feeds.ToList() ?? _database.Feeds.ToList();
|
||||
|
||||
// Reset the feed index
|
||||
_feedIndex = -1;
|
||||
|
||||
// Get the first feed
|
||||
NextFeed();
|
||||
|
||||
// Update the feed timestamp
|
||||
_lastFeedDisplay = DateTime.Now;
|
||||
|
||||
// Update the display
|
||||
DisplayCategory();
|
||||
DisplayFeed();
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Application/MainWindow/CommandLine.cs
Normal file
42
Application/MainWindow/CommandLine.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Common.IO;
|
||||
using System;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private InterprocessMessageListener _commandLineListener;
|
||||
|
||||
private void HandleCommandLine(object sender, InterprocessMessageListener.InterprocessMessageEventArgs e)
|
||||
{
|
||||
// If the command line is blank then ignore it
|
||||
if (e.Message.Length == 0)
|
||||
return;
|
||||
|
||||
// Pad the command line with a trailing space just to be lazy in parsing
|
||||
var commandLine = e.Message + " ";
|
||||
|
||||
// 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;
|
||||
|
||||
// Starting at the URL position look for the next space
|
||||
var endPosition = commandLine.IndexOf(" ", startPosition, StringComparison.Ordinal);
|
||||
|
||||
// Extract the feed URL
|
||||
var feedUrl = commandLine.Substring(startPosition, endPosition - startPosition);
|
||||
|
||||
// Add the HTTP protocol by default
|
||||
feedUrl = "http://" + feedUrl;
|
||||
|
||||
// Create a new feed using the URL
|
||||
HandleNewFeed(feedUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
Application/MainWindow/DragDrop.cs
Normal file
52
Application/MainWindow/DragDrop.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Windows;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private readonly string[] _chromeExtensions = { "chrome-extension://ehojfdcmnajoklleckniaifaijfnkpbi/subscribe.html?", "chrome-extension://nlbjncdgjeocebhnmkbbbdekmmmcbfjd/subscribe.html?" };
|
||||
|
||||
private void HandleDragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
// Default to not allowed
|
||||
e.Effects = DragDropEffects.None;
|
||||
e.Handled = true;
|
||||
|
||||
// If there isn't any text in the data then it is not allowed
|
||||
if (!e.Data.GetDataPresent(DataFormats.Text))
|
||||
return;
|
||||
|
||||
// Get the data as a string
|
||||
var data = (string) e.Data.GetData(DataFormats.Text);
|
||||
|
||||
// If the data doesn't look like a URI then it is not allowed
|
||||
if (!Uri.IsWellFormedUriString(data, UriKind.Absolute))
|
||||
return;
|
||||
|
||||
// Allowed
|
||||
e.Effects = DragDropEffects.Copy;
|
||||
}
|
||||
|
||||
private void HandleDragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
// Get the data as a string
|
||||
var data = (string) e.Data.GetData(DataFormats.Text);
|
||||
|
||||
// Check to see if the data starts with any known Chrome extension
|
||||
var chromeExtension = _chromeExtensions.FirstOrDefault(c => data.StartsWith(c));
|
||||
|
||||
// Remove the Chrome extension URL and decode the URL
|
||||
if (chromeExtension != null)
|
||||
{
|
||||
data = data.Substring(chromeExtension.Length);
|
||||
data = WebUtility.UrlDecode(data);
|
||||
}
|
||||
|
||||
// Handle the new feed but allow the drag/drop to complete
|
||||
Dispatcher.BeginInvoke(new NewFeedDelegate(HandleNewFeed), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Application/MainWindow/FeedCreation.cs
Normal file
99
Application/MainWindow/FeedCreation.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
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 void HandleNewFeed(string feedUrl)
|
||||
{
|
||||
// Create and configure the new feed
|
||||
var feed = Feed.Create(_database);
|
||||
feed.Source = feedUrl;
|
||||
feed.Category = _database.DefaultCategory;
|
||||
|
||||
// Try to detect the feed type
|
||||
var feedTypeResult = feed.DetectFeedType();
|
||||
|
||||
// If we can't figure it out it could be an HTML page
|
||||
if (feedTypeResult.Item1 == FeedType.Unknown)
|
||||
{
|
||||
// Only check if the feed was able to be read - otherwise fall through and show the dialog
|
||||
if (feedTypeResult.Item2.Length > 0)
|
||||
{
|
||||
// Create and load an HTML document with the text
|
||||
var htmlDocument = new HtmlAgilityPack.HtmlDocument();
|
||||
htmlDocument.LoadHtml(feedTypeResult.Item2);
|
||||
|
||||
// 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<string, string>(UrlHelper.GetAbsoluteUrlString(feed.Source, n.Attributes["href"].Value), WebUtility.HtmlDecode(n.Attributes["title"]?.Value ?? string.Empty)))
|
||||
.ToList();
|
||||
|
||||
// If there was only one link found then switch to feed to it
|
||||
if (rssLinks.Count == 1)
|
||||
{
|
||||
feed.Source = rssLinks[0].Item1;
|
||||
}
|
||||
else
|
||||
{
|
||||
var feedChooserWindow = new FeedChooserWindow();
|
||||
var feedLink = feedChooserWindow.Display(this, rssLinks);
|
||||
|
||||
if (string.IsNullOrEmpty(feedLink))
|
||||
return;
|
||||
|
||||
feed.Source = feedLink;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the feed for the first time
|
||||
var feedReadResult = feed.Read(_database);
|
||||
|
||||
// See if we read the feed okay
|
||||
if (feedReadResult == FeedReadResult.Success)
|
||||
{
|
||||
// Update the feed name to be the title
|
||||
feed.Name = feed.Title;
|
||||
|
||||
// Add the feed to the feed table
|
||||
_database.Feeds.Add(feed);
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
|
||||
// Show a tip
|
||||
NotificationIcon.ShowBalloonTip(string.Format(Properties.Resources.FeedAddedNotification, feed.Name), System.Windows.Forms.ToolTipIcon.Info);
|
||||
|
||||
// Re-initialize the feed display
|
||||
DisplayFeed();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Feed read failed - ceate a new feed window
|
||||
var feedForm = new FeedWindow();
|
||||
|
||||
var dialogResult = feedForm.Display(_database, feed, this);
|
||||
|
||||
// Display the new feed form
|
||||
if (dialogResult.HasValue && dialogResult.Value)
|
||||
{
|
||||
// Add the feed to the feed table
|
||||
_database.Feeds.Add(feed);
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
|
||||
// Re-initialize the feed display
|
||||
DisplayFeed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Application/MainWindow/FeedList.cs
Normal file
135
Application/MainWindow/FeedList.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void HandleLinkTextListMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
switch (e.ChangedButton)
|
||||
{
|
||||
case MouseButton.XButton1:
|
||||
|
||||
PreviousFeed();
|
||||
break;
|
||||
|
||||
case MouseButton.XButton2:
|
||||
|
||||
NextFeed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleItemMouseUp(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// Only handle the middle button
|
||||
if (e.ChangedButton != MouseButton.Middle)
|
||||
return;
|
||||
|
||||
// Get the feed item
|
||||
var feedItem = (FeedItem) ((ListBoxItem) sender).DataContext;
|
||||
|
||||
// The feed item has been read and is no longer new
|
||||
feedItem.BeenRead = true;
|
||||
feedItem.New = false;
|
||||
|
||||
// Remove the item from the list
|
||||
LinkTextList.Items.Remove(feedItem);
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
|
||||
}
|
||||
|
||||
private void HandleItemMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 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
|
||||
feedItem.BeenRead = true;
|
||||
feedItem.New = false;
|
||||
|
||||
// Remove the item from the list
|
||||
LinkTextList.Items.Remove(feedItem);
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFeedButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create a new context menu
|
||||
var contextMenu = new ContextMenu();
|
||||
|
||||
// Loop over each feed
|
||||
foreach (var feed in _feedList.OrderBy(feed => feed.Name))
|
||||
{
|
||||
// Build a string to display the feed name and the unread count
|
||||
var display = $"{feed.Name} ({feed.Items.Count(item => !item.BeenRead):d})";
|
||||
|
||||
// Create a menu item
|
||||
var menuItem = new MenuItem
|
||||
{
|
||||
Header = display,
|
||||
Tag = feed,
|
||||
|
||||
// Set the current item to bold
|
||||
FontWeight = feed == _currentFeed ? FontWeights.Bold : FontWeights.Normal
|
||||
};
|
||||
|
||||
|
||||
// Handle the click
|
||||
menuItem.Click += HandleFeedMenuItemClick;
|
||||
|
||||
// Add the item to the list
|
||||
contextMenu.Items.Add(menuItem);
|
||||
}
|
||||
|
||||
// Set the context menu placement to this button
|
||||
contextMenu.PlacementTarget = this;
|
||||
|
||||
// Open the context menu
|
||||
contextMenu.IsOpen = true;
|
||||
}
|
||||
|
||||
private void HandleFeedMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Get the menu item clicked
|
||||
var menuItem = (MenuItem) sender;
|
||||
|
||||
// Get the feed from the menu item tab
|
||||
var feed = (Feed) menuItem.Tag;
|
||||
|
||||
// Loop over all feeds and look for the index of the new one
|
||||
var feedIndex = 0;
|
||||
foreach (var loopFeed in _feedList.OrderBy(loopFeed => loopFeed.Name))
|
||||
{
|
||||
if (loopFeed == feed)
|
||||
{
|
||||
_feedIndex = feedIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
feedIndex++;
|
||||
}
|
||||
|
||||
// Set the current feed
|
||||
_currentFeed = feed;
|
||||
|
||||
// Update the feed timestamp
|
||||
_lastFeedDisplay = DateTime.Now;
|
||||
|
||||
// Update the display
|
||||
DisplayFeed();
|
||||
}
|
||||
}
|
||||
}
|
||||
185
Application/MainWindow/FeedReading.cs
Normal file
185
Application/MainWindow/FeedReading.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using Common.Update;
|
||||
using FeedCenter.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private BackgroundWorker _feedReadWorker;
|
||||
|
||||
private class FeedReadWorkerInput
|
||||
{
|
||||
public bool ForceRead;
|
||||
public Feed Feed;
|
||||
}
|
||||
|
||||
private void SetProgressMode(bool value, int feedCount)
|
||||
{
|
||||
// Reset the progress bar if we need it
|
||||
if (value)
|
||||
{
|
||||
FeedReadProgress.Value = 0;
|
||||
FeedReadProgress.Maximum = feedCount + 2;
|
||||
FeedReadProgress.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
FeedReadProgress.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCurrentFeed(bool forceRead = false)
|
||||
{
|
||||
// Don't read if we're already working
|
||||
if (_feedReadWorker.IsBusy)
|
||||
return;
|
||||
|
||||
// Don't read if there is nothing to read
|
||||
if (!_database.Feeds.Any())
|
||||
return;
|
||||
|
||||
// Switch to progress mode
|
||||
SetProgressMode(true, 1);
|
||||
|
||||
// Create the input class
|
||||
var workerInput = new FeedReadWorkerInput { ForceRead = forceRead, Feed = _currentFeed };
|
||||
|
||||
// Start the worker
|
||||
_feedReadWorker.RunWorkerAsync(workerInput);
|
||||
}
|
||||
|
||||
private void ReadFeeds(bool forceRead = false)
|
||||
{
|
||||
// Don't read if we're already working
|
||||
if (_feedReadWorker.IsBusy)
|
||||
return;
|
||||
|
||||
// Don't read if there is nothing to read
|
||||
if (!_database.Feeds.Any())
|
||||
return;
|
||||
|
||||
// Switch to progress mode
|
||||
SetProgressMode(true, _database.Feeds.Count());
|
||||
|
||||
// Create the input class
|
||||
var workerInput = new FeedReadWorkerInput { ForceRead = forceRead, Feed = null };
|
||||
|
||||
// Start the worker
|
||||
_feedReadWorker.RunWorkerAsync(workerInput);
|
||||
}
|
||||
|
||||
private void HandleFeedReadWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
// Set progress
|
||||
FeedReadProgress.Value = e.ProgressPercentage;
|
||||
}
|
||||
|
||||
private void HandleFeedReadWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
// Reset the database to current settings
|
||||
ResetDatabase();
|
||||
|
||||
// Save settings
|
||||
Settings.Default.Save();
|
||||
|
||||
// Set the read timestamp
|
||||
_lastFeedRead = DateTime.Now;
|
||||
|
||||
// Update the current feed
|
||||
DisplayFeed();
|
||||
|
||||
// Switch to normal mode
|
||||
SetProgressMode(false, 0);
|
||||
|
||||
// Check for update
|
||||
if (UpdateCheck.UpdateAvailable)
|
||||
NewVersionLink.Visibility = Visibility.Visible;
|
||||
|
||||
UpdateErrorLink();
|
||||
}
|
||||
|
||||
private void UpdateErrorLink()
|
||||
{
|
||||
var feedErrorCount = _database.Feeds.Count(f => f.LastReadResult != FeedReadResult.Success);
|
||||
|
||||
// Set the visibility of the error link
|
||||
FeedErrorsLink.Visibility = feedErrorCount == 0 ? Visibility.Collapsed : Visibility.Visible;
|
||||
|
||||
// Set the text to match the number of errors
|
||||
FeedErrorsLink.Text = feedErrorCount == 1
|
||||
? Properties.Resources.FeedErrorLink
|
||||
: string.Format(Properties.Resources.FeedErrorsLink, feedErrorCount);
|
||||
}
|
||||
|
||||
private static void HandleFeedReadWorkerStart(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
// Create a new database instance for just this thread
|
||||
var database = new FeedCenterEntities();
|
||||
|
||||
// Get the worker
|
||||
var worker = (BackgroundWorker) sender;
|
||||
|
||||
// Get the input information
|
||||
var workerInput = (FeedReadWorkerInput) e.Argument;
|
||||
|
||||
// Setup for progress
|
||||
var currentProgress = 0;
|
||||
|
||||
// Create the list of feeds to read
|
||||
var feedsToRead = new List<Feed>();
|
||||
|
||||
// If we have a single feed then add it to the list - otherwise add them all
|
||||
if (workerInput.Feed != null)
|
||||
feedsToRead.Add(database.Feeds.First(feed => feed.ID == workerInput.Feed.ID));
|
||||
else
|
||||
feedsToRead.AddRange(database.Feeds);
|
||||
|
||||
// Loop over each feed and read it
|
||||
foreach (var feed in feedsToRead)
|
||||
{
|
||||
// Read the feed
|
||||
feed.Read(database, workerInput.ForceRead);
|
||||
|
||||
// Increment progress
|
||||
currentProgress += 1;
|
||||
|
||||
// Report progress
|
||||
worker.ReportProgress(currentProgress);
|
||||
}
|
||||
|
||||
// Save the changes
|
||||
database.SaveChanges();
|
||||
|
||||
// Increment progress
|
||||
currentProgress += 1;
|
||||
|
||||
// Report progress
|
||||
worker.ReportProgress(currentProgress);
|
||||
|
||||
// See if we're due for a version check
|
||||
if (DateTime.Now - Settings.Default.LastVersionCheck >= Settings.Default.VersionCheckInterval)
|
||||
{
|
||||
// Get the update information
|
||||
UpdateCheck.CheckForUpdate();
|
||||
|
||||
// Update the last check time
|
||||
Settings.Default.LastVersionCheck = DateTime.Now;
|
||||
}
|
||||
|
||||
// Increment progress
|
||||
currentProgress += 1;
|
||||
|
||||
// Report progress
|
||||
worker.ReportProgress(currentProgress);
|
||||
|
||||
// Sleep for a little bit so the user can see the update
|
||||
Thread.Sleep(Settings.Default.ProgressSleepInterval * 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Application/MainWindow/Header.cs
Normal file
32
Application/MainWindow/Header.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using FeedCenter.Properties;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void HandleHeaderLabelMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// Ignore if the window is locked
|
||||
if (Settings.Default.WindowLocked)
|
||||
return;
|
||||
|
||||
// Start dragging
|
||||
DragMove();
|
||||
}
|
||||
|
||||
private void HandleCloseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Close the window
|
||||
Close();
|
||||
}
|
||||
|
||||
private void HandleFeedLabelMouseDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// Open the link for the current feed on a left double click
|
||||
if (e.ClickCount == 2 && e.ChangedButton == MouseButton.Left)
|
||||
BrowserCommon.OpenLink(_currentFeed.Link);
|
||||
}
|
||||
}
|
||||
}
|
||||
311
Application/MainWindow/MainWindow.xaml
Normal file
311
Application/MainWindow/MainWindow.xaml
Normal file
@@ -0,0 +1,311 @@
|
||||
<windows:SnappingWindow x:Class="FeedCenter.MainWindow"
|
||||
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:system="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:nameBasedGrid="clr-namespace:NameBasedGrid;assembly=NameBasedGrid"
|
||||
Title="MainWindow"
|
||||
Height="360"
|
||||
Width="252"
|
||||
WindowStyle="None"
|
||||
ResizeMode="NoResize"
|
||||
Background="{x:Static SystemColors.DesktopBrush}"
|
||||
AllowDrop="True"
|
||||
DragOver="HandleDragOver"
|
||||
Drop="HandleDragDrop"
|
||||
ShowInTaskbar="False">
|
||||
<Window.Resources>
|
||||
<Style TargetType="{x:Type ToolBar}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ToolBar}">
|
||||
<ToolBarPanel IsItemsHost="True"
|
||||
Margin="0,1,2,2"
|
||||
SnapsToDevicePixels="True" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
<Border BorderBrush="{x:Static SystemColors.ActiveBorderBrush}"
|
||||
BorderThickness="1"
|
||||
Name="WindowBorder"
|
||||
Padding="0"
|
||||
Background="{x:Static SystemColors.DesktopBrush}">
|
||||
<nameBasedGrid:NameBasedGrid Name="MainGrid">
|
||||
<nameBasedGrid:NameBasedGrid.RowDefinitions>
|
||||
<!-- ReSharper disable UnusedMember.Global -->
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="HeaderRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="NewVersionRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="CategoryRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="FeedRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="TopToolbarRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="*"
|
||||
Name="FeedListRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="ProgressRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="BottomToolbarRow" />
|
||||
<nameBasedGrid:ColumnOrRow Size="Auto"
|
||||
Name="FeedErrorsRow" />
|
||||
<!-- ReSharper restore UnusedMember.Global -->
|
||||
</nameBasedGrid:NameBasedGrid.RowDefinitions>
|
||||
<Grid Height="21"
|
||||
nameBasedGrid:NameBasedGrid.Row="HeaderRow">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="21" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Label Content="{x:Static properties:Resources.ApplicationDisplayName}"
|
||||
Name="HeaderLabel"
|
||||
Padding="3,0"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
MouseLeftButtonDown="HandleHeaderLabelMouseLeftButtonDown"
|
||||
VerticalContentAlignment="Center"
|
||||
Grid.Column="0" />
|
||||
<Button Width="13"
|
||||
Height="13"
|
||||
Click="HandleCloseButtonClick"
|
||||
FontFamily="Marlett"
|
||||
Content="r"
|
||||
FontSize="8"
|
||||
Grid.Column="1"></Button>
|
||||
</Grid>
|
||||
<linkControl:LinkControl Name="NewVersionLink"
|
||||
Height="21"
|
||||
nameBasedGrid:NameBasedGrid.Row="NewVersionRow"
|
||||
Text="{x:Static properties:Resources.NewVersionLink}"
|
||||
Background="AntiqueWhite"
|
||||
VerticalContentAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
Visibility="Collapsed"
|
||||
Click="HandleNewVersionLinkClick">
|
||||
</linkControl:LinkControl>
|
||||
<Grid Name="CategoryGrid"
|
||||
Height="21"
|
||||
nameBasedGrid:NameBasedGrid.Row="CategoryRow"
|
||||
Visibility="Visible">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="21" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="*Category Name"
|
||||
Name="CategoryLabel"
|
||||
Padding="3,0"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
Width="Auto"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
Grid.Column="0" />
|
||||
<Button Width="13"
|
||||
Height="13"
|
||||
Click="HandleCategoryButtonClick"
|
||||
FontFamily="Marlett"
|
||||
Content="u"
|
||||
FontSize="8"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
<Grid Height="21"
|
||||
nameBasedGrid:NameBasedGrid.Row="FeedRow">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="21" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="*Feed Name"
|
||||
Name="FeedLabel"
|
||||
Padding="3,0"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Left"
|
||||
Width="Auto"
|
||||
Cursor="Hand"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
MouseDown="HandleFeedLabelMouseDown"
|
||||
Grid.Column="0" />
|
||||
<Button Name="FeedButton"
|
||||
Width="13"
|
||||
Height="13"
|
||||
Click="HandleFeedButtonClick"
|
||||
FontFamily="Marlett"
|
||||
Content="u"
|
||||
FontSize="8"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
<ListBox Name="LinkTextList"
|
||||
BorderThickness="0"
|
||||
Background="{x:Static SystemColors.DesktopBrush}"
|
||||
MouseUp="HandleLinkTextListMouseUp"
|
||||
Foreground="White"
|
||||
nameBasedGrid:NameBasedGrid.Row="FeedListRow"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||
<ListBoxItem Content="Test item" />
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<htmlTextBlock:HtmlTextBlock Html="{Binding}"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,1" />
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
<ListBox.ItemContainerStyle>
|
||||
<Style TargetType="ListBoxItem">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListBoxItem">
|
||||
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
|
||||
Padding="{TemplateBinding Control.Padding}"
|
||||
BorderBrush="{TemplateBinding Border.BorderBrush}"
|
||||
Background="{TemplateBinding Panel.Background}"
|
||||
Name="Bd"
|
||||
SnapsToDevicePixels="True">
|
||||
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
|
||||
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
|
||||
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
|
||||
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
|
||||
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="Selector.IsMouseOver">
|
||||
<Trigger.Value>
|
||||
<system:Boolean>True</system:Boolean>
|
||||
</Trigger.Value>
|
||||
<Setter Property="Panel.Background"
|
||||
TargetName="Bd">
|
||||
<Setter.Value>
|
||||
<DynamicResource ResourceKey="{x:Static SystemColors.HighlightBrushKey}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="TextElement.Foreground">
|
||||
<Setter.Value>
|
||||
<DynamicResource ResourceKey="{x:Static SystemColors.HighlightTextBrushKey}" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="Panel.Cursor"
|
||||
TargetName="Bd"
|
||||
Value="Hand">
|
||||
</Setter>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<EventSetter Event="MouseDoubleClick"
|
||||
Handler="HandleItemMouseDoubleClick" />
|
||||
<EventSetter Event="MouseUp"
|
||||
Handler="HandleItemMouseUp" />
|
||||
</Style>
|
||||
</ListBox.ItemContainerStyle>
|
||||
</ListBox>
|
||||
<ProgressBar Name="FeedReadProgress"
|
||||
Height="15"
|
||||
Visibility="Collapsed"
|
||||
nameBasedGrid:NameBasedGrid.Row="ProgressRow" />
|
||||
<ToolBarTray Name="NavigationToolbarTray"
|
||||
Background="Transparent"
|
||||
Orientation="Horizontal"
|
||||
nameBasedGrid:NameBasedGrid.Row="TopToolbarRow">
|
||||
<ToolBar ToolBarTray.IsLocked="True"
|
||||
Background="Transparent"
|
||||
ToolBar.OverflowMode="Never">
|
||||
<toolbar:ImageButton Height="20"
|
||||
Width="20"
|
||||
Name="PreviousToolbarButton"
|
||||
Click="HandlePreviousToolbarButtonClick"
|
||||
ToolTip="{x:Static properties:Resources.previousToolbarButton}"
|
||||
ImageSource="../Resources/Left.ico" />
|
||||
<toolbar:ImageButton Height="20"
|
||||
Width="20"
|
||||
Name="NextToolbarButton"
|
||||
Click="HandleNextToolbarButtonClick"
|
||||
ToolTip="{x:Static properties:Resources.nextToolbarButton}"
|
||||
ImageSource="../Resources/Right.ico" />
|
||||
<splitButton:SplitButton Name="RefreshToolbarButton"
|
||||
Image="../Resources/Rss-Download.ico"
|
||||
ToolTip="{x:Static properties:Resources.refreshAllToolbarButton}"
|
||||
Height="20"
|
||||
MinWidth="35"
|
||||
Margin="5,0,0,0"
|
||||
Click="HandleRefreshToolbarButtonClick">
|
||||
<splitButton:SplitButton.DropDownContextMenu>
|
||||
<ContextMenu MenuItem.Click="HandleRefreshMenuItemClick">
|
||||
<MenuItem Name="MenuRefreshAll"
|
||||
Header="{x:Static properties:Resources.refreshAllToolbarButton}" />
|
||||
<MenuItem Name="MenuRefresh"
|
||||
Header="{x:Static properties:Resources.refreshToolbarButton}" />
|
||||
</ContextMenu>
|
||||
</splitButton:SplitButton.DropDownContextMenu>
|
||||
</splitButton:SplitButton>
|
||||
<splitButton:SplitButton Name="OpenAllToolbarButton"
|
||||
Image="../Resources/News.ico"
|
||||
ToolTip="{x:Static properties:Resources.openAllMultipleToolbarButton}"
|
||||
Height="20"
|
||||
MinWidth="35"
|
||||
Margin="5,0,0,0"
|
||||
Click="HandleOpenAllToolbarButtonClick">
|
||||
<splitButton:SplitButton.DropDownContextMenu>
|
||||
<ContextMenu MenuItem.Click="HandleOpenAllMenuItemClick">
|
||||
<MenuItem Name="MenuOpenAllMultiplePages"
|
||||
Header="{x:Static properties:Resources.openAllMultipleToolbarButton}" />
|
||||
<MenuItem Name="MenuOpenAllSinglePage"
|
||||
Header="{x:Static properties:Resources.openAllSingleToolbarButton}" />
|
||||
</ContextMenu>
|
||||
</splitButton:SplitButton.DropDownContextMenu>
|
||||
</splitButton:SplitButton>
|
||||
<toolbar:ImageButton Height="20"
|
||||
Width="20"
|
||||
Margin="5,0,0,0"
|
||||
Name="MarkReadToolbarButton"
|
||||
Click="HandleMarkReadToolbarButtonClick"
|
||||
ToolTip="{x:Static properties:Resources.markReadToolbarButton}"
|
||||
ImageSource="../Resources/Comments-edit.ico" />
|
||||
<splitButton:SplitButton Height="20"
|
||||
MinWidth="35"
|
||||
Margin="5,0,0,0"
|
||||
Click="HandleOptionsToolbarButtonClick"
|
||||
ToolTip="{x:Static properties:Resources.optionsToolbarButton}"
|
||||
Image="../Resources/Compile.ico">
|
||||
<splitButton:SplitButton.DropDownContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="{x:Static properties:Resources.lockWindowCheckBox}"
|
||||
IsCheckable="True"
|
||||
IsChecked="{Binding Source={x:Static properties:Settings.Default}, Path=WindowLocked}" />
|
||||
<Separator />
|
||||
<MenuItem Header="{x:Static properties:Resources.CurrentFeed}">
|
||||
<MenuItem Header="{x:Static properties:Resources.EditMenu}"
|
||||
Click="HandleEditCurrentFeedMenuItemClick" />
|
||||
<MenuItem Header="{x:Static properties:Resources.DeleteMenu}"
|
||||
Click="HandleDeleteCurrentFeedMenuItemClick" />
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</splitButton:SplitButton.DropDownContextMenu>
|
||||
</splitButton:SplitButton>
|
||||
</ToolBar>
|
||||
</ToolBarTray>
|
||||
<linkControl:LinkControl Name="FeedErrorsLink"
|
||||
Height="21"
|
||||
nameBasedGrid:NameBasedGrid.Row="FeedErrorsRow"
|
||||
Text="{x:Static properties:Resources.FeedErrorsLink}"
|
||||
ToolTip="{x:Static properties:Resources.showErrorsToolbarButton}"
|
||||
Background="AntiqueWhite"
|
||||
VerticalContentAlignment="Center"
|
||||
HorizontalContentAlignment="Center"
|
||||
Visibility="Collapsed"
|
||||
Click="HandleShowErrorsButtonClick">
|
||||
</linkControl:LinkControl>
|
||||
</nameBasedGrid:NameBasedGrid>
|
||||
</Border>
|
||||
</windows:SnappingWindow>
|
||||
395
Application/MainWindow/MainWindow.xaml.cs
Normal file
395
Application/MainWindow/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
using Common.Debug;
|
||||
using Common.Helpers;
|
||||
using Common.IO;
|
||||
using Common.Update;
|
||||
using FeedCenter.Properties;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private FeedCenterEntities _database;
|
||||
private int _feedIndex;
|
||||
|
||||
private Category _currentCategory;
|
||||
private ICollection<Feed> _feedList;
|
||||
private Feed _currentFeed;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
// Setup the update handler
|
||||
InitializeUpdate();
|
||||
|
||||
// Show the notification icon
|
||||
NotificationIcon.Initialize(this);
|
||||
|
||||
// Load window settings
|
||||
LoadWindowSettings();
|
||||
|
||||
// Set the foreground color to something that can be seen
|
||||
LinkTextList.Foreground = (System.Drawing.SystemColors.Desktop.GetBrightness() < 0.5) ? Brushes.White : Brushes.Black;
|
||||
HeaderLabel.Foreground = LinkTextList.Foreground;
|
||||
|
||||
// Create the background worker that does the actual reading
|
||||
_feedReadWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
|
||||
_feedReadWorker.DoWork += HandleFeedReadWorkerStart;
|
||||
_feedReadWorker.ProgressChanged += HandleFeedReadWorkerProgressChanged;
|
||||
_feedReadWorker.RunWorkerCompleted += HandleFeedReadWorkerCompleted;
|
||||
|
||||
// Setup the database
|
||||
_database = new FeedCenterEntities();
|
||||
|
||||
// Initialize the command line listener
|
||||
_commandLineListener = new InterprocessMessageListener(Properties.Resources.ApplicationName);
|
||||
_commandLineListener.MessageReceived += HandleCommandLine;
|
||||
|
||||
// Handle any command line we were started with
|
||||
HandleCommandLine(null, new InterprocessMessageListener.InterprocessMessageEventArgs(Environment.CommandLine));
|
||||
|
||||
// Create a timer to keep track of things we need to do
|
||||
InitializeTimer();
|
||||
|
||||
// Initialize the feed display
|
||||
InitializeDisplay();
|
||||
|
||||
// Check for update
|
||||
if (Settings.Default.CheckVersionAtStartup)
|
||||
UpdateCheck.CheckForUpdate();
|
||||
|
||||
// Show the link if updates are available
|
||||
if (UpdateCheck.UpdateAvailable)
|
||||
NewVersionLink.Visibility = Visibility.Visible;
|
||||
|
||||
Tracer.WriteLine("MainForm creation finished");
|
||||
}
|
||||
|
||||
#region Setting events
|
||||
|
||||
private void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
// Make sure we're on the right thread
|
||||
if (!Dispatcher.CheckAccess())
|
||||
{
|
||||
Dispatcher.Invoke(new EventHandler<PropertyChangedEventArgs>(HandlePropertyChanged), sender, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.PropertyName == Reflection.GetPropertyName(() => Settings.Default.MultipleLineDisplay))
|
||||
{
|
||||
// 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");
|
||||
|
||||
break;
|
||||
case Dock.Bottom:
|
||||
NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "BottomToolbarRow");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Feed display
|
||||
|
||||
private void UpdateToolbarButtonState()
|
||||
{
|
||||
// Cache the feed count to save (a little) time
|
||||
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);
|
||||
}
|
||||
|
||||
private void InitializeDisplay()
|
||||
{
|
||||
UpdateToolbarButtonState();
|
||||
|
||||
// Get the last category (defaulting to none)
|
||||
_currentCategory = _database.Categories.FirstOrDefault(category => category.ID.ToString() == Settings.Default.LastCategoryID);
|
||||
DisplayCategory();
|
||||
|
||||
// Get the current feed list to match the category
|
||||
_feedList = _currentCategory?.Feeds.ToList() ?? _database.Feeds.ToList();
|
||||
|
||||
// Clear the link list
|
||||
LinkTextList.Items.Clear();
|
||||
|
||||
// Reset the feed index
|
||||
_feedIndex = -1;
|
||||
|
||||
// Start the timer
|
||||
StartTimer();
|
||||
|
||||
// Don't go further if we have no feeds
|
||||
if (_feedList.Count == 0)
|
||||
return;
|
||||
|
||||
// Get the first feed
|
||||
NextFeed();
|
||||
}
|
||||
|
||||
private void NextFeed()
|
||||
{
|
||||
var feedCount = _feedList.Count();
|
||||
|
||||
if (feedCount == 0)
|
||||
return;
|
||||
|
||||
if (Settings.Default.DisplayEmptyFeeds)
|
||||
{
|
||||
// Increment the index and adjust if we've gone around the end
|
||||
_feedIndex = (_feedIndex + 1) % feedCount;
|
||||
|
||||
// Get the feed
|
||||
_currentFeed = _feedList.OrderBy(feed => feed.Name).AsEnumerable().ElementAt(_feedIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep track if we found something
|
||||
var found = false;
|
||||
|
||||
// Remember our starting position
|
||||
var startIndex = (_feedIndex == -1 ? 0 : _feedIndex);
|
||||
|
||||
// Increment the index and adjust if we've gone around the end
|
||||
_feedIndex = (_feedIndex + 1) % feedCount;
|
||||
|
||||
// Loop until we come back to the start index
|
||||
do
|
||||
{
|
||||
// Get the feed
|
||||
_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)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Increment the index and adjust if we've gone around the end
|
||||
_feedIndex = (_feedIndex + 1) % feedCount;
|
||||
}
|
||||
while (_feedIndex != startIndex);
|
||||
|
||||
// If nothing was found then clear the current feed
|
||||
if (!found)
|
||||
{
|
||||
_feedIndex = -1;
|
||||
_currentFeed = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the feed timestamp
|
||||
_lastFeedDisplay = DateTime.Now;
|
||||
|
||||
// Update the display
|
||||
DisplayFeed();
|
||||
}
|
||||
|
||||
private void PreviousFeed()
|
||||
{
|
||||
var feedCount = _feedList.Count();
|
||||
|
||||
if (feedCount == 0)
|
||||
return;
|
||||
|
||||
if (Settings.Default.DisplayEmptyFeeds)
|
||||
{
|
||||
// Decrement the feed index
|
||||
_feedIndex--;
|
||||
|
||||
// If we've gone below the start of the list then reset to the end
|
||||
if (_feedIndex < 0)
|
||||
_feedIndex = feedCount - 1;
|
||||
|
||||
// Get the feed
|
||||
_currentFeed = _feedList.OrderBy(feed => feed.Name).AsEnumerable().ElementAt(_feedIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep track if we found something
|
||||
var found = false;
|
||||
|
||||
// Remember our starting position
|
||||
var startIndex = (_feedIndex == -1 ? 0 : _feedIndex);
|
||||
|
||||
// Decrement the feed index
|
||||
_feedIndex--;
|
||||
|
||||
// If we've gone below the start of the list then reset to the end
|
||||
if (_feedIndex < 0)
|
||||
_feedIndex = feedCount - 1;
|
||||
|
||||
// Loop until we come back to the start index
|
||||
do
|
||||
{
|
||||
// Get the feed
|
||||
_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)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Decrement the feed index
|
||||
_feedIndex--;
|
||||
|
||||
// If we've gone below the start of the list then reset to the end
|
||||
if (_feedIndex < 0)
|
||||
_feedIndex = feedCount - 1;
|
||||
}
|
||||
while (_feedIndex != startIndex);
|
||||
|
||||
// If nothing was found then clear the current feed
|
||||
if (!found)
|
||||
{
|
||||
_feedIndex = -1;
|
||||
_currentFeed = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the feed timestamp
|
||||
_lastFeedDisplay = DateTime.Now;
|
||||
|
||||
// Update the display
|
||||
DisplayFeed();
|
||||
}
|
||||
|
||||
private void UpdateOpenAllButton()
|
||||
{
|
||||
var multipleOpenAction = _currentFeed.MultipleOpenAction;
|
||||
|
||||
switch (multipleOpenAction)
|
||||
{
|
||||
case MultipleOpenAction.IndividualPages:
|
||||
OpenAllToolbarButton.ToolTip = Properties.Resources.openAllMultipleToolbarButton;
|
||||
break;
|
||||
case MultipleOpenAction.SinglePage:
|
||||
OpenAllToolbarButton.ToolTip = Properties.Resources.openAllSingleToolbarButton;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayFeed()
|
||||
{
|
||||
// Just clear the display if we have no feed
|
||||
if (_currentFeed == null)
|
||||
{
|
||||
FeedLabel.Text = string.Empty;
|
||||
FeedButton.Visibility = Visibility.Hidden;
|
||||
LinkTextList.Items.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the header to the feed title
|
||||
FeedLabel.Text = (_currentFeed.Name.Length > 0 ? _currentFeed.Name : _currentFeed.Title);
|
||||
FeedButton.Visibility = _feedList.Count() > 1 ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
// Clear the current list
|
||||
LinkTextList.Items.Clear();
|
||||
|
||||
// Sort the items by sequence
|
||||
var sortedItems = _currentFeed.Items.Where(item => !item.BeenRead).OrderBy(item => item.Sequence);
|
||||
|
||||
// Loop over all items in the current feed
|
||||
foreach (var feedItem in sortedItems)
|
||||
{
|
||||
// Add the list item
|
||||
LinkTextList.Items.Add(feedItem);
|
||||
}
|
||||
|
||||
UpdateOpenAllButton();
|
||||
}
|
||||
|
||||
private void MarkAllItemsAsRead()
|
||||
{
|
||||
// Loop over all items and mark them as read
|
||||
foreach (FeedItem feedItem in LinkTextList.Items)
|
||||
feedItem.BeenRead = true;
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
|
||||
// Clear the list
|
||||
LinkTextList.Items.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Database helpers
|
||||
|
||||
private void ResetDatabase()
|
||||
{
|
||||
// Get the ID of the current feed
|
||||
var currentId = _currentFeed?.ID ?? Guid.Empty;
|
||||
|
||||
// Create a new database object
|
||||
_database = new FeedCenterEntities();
|
||||
|
||||
UpdateToolbarButtonState();
|
||||
|
||||
// Get a list of feeds ordered by name
|
||||
var feedList = _feedList.OrderBy(f => f.Name).ToList();
|
||||
|
||||
// First try to find the current feed by ID to see if it is still there
|
||||
var newIndex = feedList.FindIndex(f => f.ID == currentId);
|
||||
|
||||
if (newIndex == -1)
|
||||
{
|
||||
// The current feed isn't there anymore so see if we can find a feed at the old index
|
||||
if (feedList.ElementAtOrDefault(_feedIndex) != null)
|
||||
newIndex = _feedIndex;
|
||||
|
||||
// If there is no feed at the old location then give up and go back to the start
|
||||
if (newIndex == -1 && feedList.Count > 0)
|
||||
newIndex = 0;
|
||||
}
|
||||
|
||||
// Set the current index to the new index
|
||||
_feedIndex = newIndex;
|
||||
|
||||
// Re-get the current feed
|
||||
_currentFeed = (_feedIndex == -1 ? null : _feedList.OrderBy(feed => feed.Name).AsEnumerable().ElementAt(_feedIndex));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
59
Application/MainWindow/Timer.cs
Normal file
59
Application/MainWindow/Timer.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using FeedCenter.Properties;
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private Timer _mainTimer;
|
||||
private DateTime _lastFeedRead;
|
||||
private DateTime _lastFeedDisplay;
|
||||
|
||||
private void InitializeTimer()
|
||||
{
|
||||
_mainTimer = new Timer { Interval = 1000 };
|
||||
_mainTimer.Tick += HandleMainTimerTick;
|
||||
}
|
||||
|
||||
private void TerminateTimer()
|
||||
{
|
||||
StopTimer();
|
||||
|
||||
_mainTimer.Dispose();
|
||||
}
|
||||
|
||||
private void StartTimer()
|
||||
{
|
||||
_mainTimer.Start();
|
||||
}
|
||||
|
||||
private void StopTimer()
|
||||
{
|
||||
_mainTimer.Stop();
|
||||
}
|
||||
|
||||
private void HandleMainTimerTick(object sender, EventArgs e)
|
||||
{
|
||||
// If the background worker is busy then don't do anything
|
||||
if (_feedReadWorker.IsBusy)
|
||||
return;
|
||||
|
||||
// Stop the timer for now
|
||||
StopTimer();
|
||||
|
||||
// Move to the next feed if the scroll interval has expired and the mouse isn't hovering
|
||||
if (LinkTextList.IsMouseOver)
|
||||
_lastFeedDisplay = DateTime.Now;
|
||||
else if (DateTime.Now - _lastFeedDisplay >= Settings.Default.FeedScrollInterval)
|
||||
NextFeed();
|
||||
|
||||
// Check to see if we should try to read the feeds
|
||||
if (DateTime.Now - _lastFeedRead >= Settings.Default.FeedCheckInterval)
|
||||
ReadFeeds();
|
||||
|
||||
// Get the timer going again
|
||||
StartTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
254
Application/MainWindow/Toolbar.cs
Normal file
254
Application/MainWindow/Toolbar.cs
Normal file
@@ -0,0 +1,254 @@
|
||||
using FeedCenter.Options;
|
||||
using FeedCenter.Properties;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web.UI;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void HandlePreviousToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
PreviousFeed();
|
||||
}
|
||||
|
||||
private void HandleNextToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NextFeed();
|
||||
}
|
||||
|
||||
private void OpenAllFeedItemsIndividually()
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Start with a longer sleep interval to give time for the browser to come up
|
||||
var sleepInterval = settings.OpenAllSleepIntervalFirst;
|
||||
|
||||
// Loop over all items
|
||||
foreach (var feedItem in feedItems)
|
||||
{
|
||||
// Try to open the link
|
||||
if (BrowserCommon.OpenLink(browser, feedItem.Link))
|
||||
{
|
||||
// Mark the feed as read
|
||||
feedItem.BeenRead = true;
|
||||
|
||||
// Remove the item
|
||||
LinkTextList.Items.Remove(feedItem);
|
||||
}
|
||||
|
||||
// Wait a little bit
|
||||
Thread.Sleep(sleepInterval);
|
||||
|
||||
// Switch to the normal sleep interval
|
||||
sleepInterval = settings.OpenAllSleepInterval;
|
||||
}
|
||||
|
||||
// Save the changes
|
||||
_database.SaveChanges();
|
||||
}
|
||||
|
||||
private void HandleOptionsToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create the options form
|
||||
var optionsWindow = new OptionsWindow { Owner = this };
|
||||
|
||||
// Show the options form and get the result
|
||||
var result = optionsWindow.ShowDialog();
|
||||
|
||||
// If okay was selected
|
||||
if (result.HasValue && result.Value)
|
||||
{
|
||||
// Reset the database to current settings
|
||||
ResetDatabase();
|
||||
|
||||
// Re-initialize the feed display
|
||||
DisplayFeed();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMarkReadToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
MarkAllItemsAsRead();
|
||||
}
|
||||
|
||||
private void HandleShowErrorsButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create the feed error window
|
||||
var feedErrorWindow = new FeedErrorWindow();
|
||||
|
||||
// Display the window
|
||||
var result = feedErrorWindow.Display(this);
|
||||
|
||||
// If okay was selected
|
||||
if (result.GetValueOrDefault())
|
||||
{
|
||||
// Reset the database to current settings
|
||||
ResetDatabase();
|
||||
|
||||
// Re-initialize the feed display
|
||||
DisplayFeed();
|
||||
|
||||
UpdateErrorLink();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleRefreshMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var menuItem = (MenuItem) e.Source;
|
||||
|
||||
if (Equals(menuItem, MenuRefresh))
|
||||
ReadCurrentFeed(true);
|
||||
else if (Equals(menuItem, MenuRefreshAll))
|
||||
ReadFeeds(true);
|
||||
}
|
||||
|
||||
private void HandleRefreshToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ReadFeeds(true);
|
||||
}
|
||||
|
||||
private void HandleOpenAllMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var menuItem = (MenuItem) e.Source;
|
||||
|
||||
if (Equals(menuItem, MenuOpenAllSinglePage))
|
||||
OpenAllFeedItemsOnSinglePage();
|
||||
else if (Equals(menuItem, MenuOpenAllMultiplePages))
|
||||
OpenAllFeedItemsIndividually();
|
||||
}
|
||||
|
||||
private void HandleOpenAllToolbarButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var multipleOpenAction = _currentFeed.MultipleOpenAction;
|
||||
|
||||
switch (multipleOpenAction)
|
||||
{
|
||||
case MultipleOpenAction.IndividualPages:
|
||||
OpenAllFeedItemsIndividually();
|
||||
break;
|
||||
case MultipleOpenAction.SinglePage:
|
||||
OpenAllFeedItemsOnSinglePage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleEditCurrentFeedMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Create a new feed window
|
||||
var feedWindow = new FeedWindow();
|
||||
|
||||
// Display the feed window and get the result
|
||||
var result = feedWindow.Display(_database, _currentFeed, this);
|
||||
|
||||
// If OK was clicked...
|
||||
if (result.HasValue && result.Value)
|
||||
{
|
||||
// Save
|
||||
_database.SaveChanges();
|
||||
|
||||
// Update feed
|
||||
DisplayFeed();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDeleteCurrentFeedMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Confirm this delete since it is for real
|
||||
if (MessageBox.Show(this, Properties.Resources.ConfirmDelete, string.Empty, MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
|
||||
return;
|
||||
|
||||
// Get the current feed
|
||||
var feedToDelete = _currentFeed;
|
||||
|
||||
// Move to the next feed
|
||||
NextFeed();
|
||||
|
||||
// Delete all items
|
||||
foreach (var item in feedToDelete.Items.ToList())
|
||||
_database.FeedItems.Remove(item);
|
||||
|
||||
// Delete the feed
|
||||
_database.Feeds.Remove(feedToDelete);
|
||||
|
||||
// Save
|
||||
_database.SaveChanges();
|
||||
}
|
||||
|
||||
private void OpenAllFeedItemsOnSinglePage()
|
||||
{
|
||||
var fileName = Path.GetTempFileName() + ".html";
|
||||
TextWriter textWriter = new StreamWriter(fileName);
|
||||
|
||||
using (var htmlTextWriter = new HtmlTextWriter(textWriter))
|
||||
{
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Html);
|
||||
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Head);
|
||||
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Title);
|
||||
htmlTextWriter.Write(_currentFeed.Title);
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
htmlTextWriter.AddAttribute("http-equiv", "Content-Type");
|
||||
htmlTextWriter.AddAttribute("content", "text/html; charset=utf-8");
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Meta);
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Body);
|
||||
|
||||
var sortedItems = from item in _currentFeed.Items where !item.BeenRead orderby item.Sequence ascending select item;
|
||||
|
||||
var firstItem = true;
|
||||
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
if (!firstItem)
|
||||
{
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Hr);
|
||||
htmlTextWriter.RenderEndTag();
|
||||
}
|
||||
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Div);
|
||||
|
||||
htmlTextWriter.AddAttribute(HtmlTextWriterAttribute.Href, item.Link);
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.A);
|
||||
htmlTextWriter.Write(item.Title.Length == 0 ? item.Link : item.Title);
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
htmlTextWriter.RenderBeginTag(HtmlTextWriterTag.Br);
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
htmlTextWriter.Write(item.Description);
|
||||
|
||||
htmlTextWriter.RenderEndTag();
|
||||
|
||||
firstItem = false;
|
||||
}
|
||||
|
||||
htmlTextWriter.RenderEndTag();
|
||||
htmlTextWriter.RenderEndTag();
|
||||
}
|
||||
|
||||
textWriter.Flush();
|
||||
textWriter.Close();
|
||||
|
||||
BrowserCommon.OpenLink(fileName);
|
||||
|
||||
MarkAllItemsAsRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Application/MainWindow/UpdateHandler.cs
Normal file
40
Application/MainWindow/UpdateHandler.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Common.Update;
|
||||
using FeedCenter.Properties;
|
||||
using System.Windows;
|
||||
|
||||
namespace FeedCenter
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private static void InitializeUpdate()
|
||||
{
|
||||
UpdateCheck.ApplicationName = Properties.Resources.ApplicationDisplayName;
|
||||
UpdateCheck.UpdateServer = Settings.Default.VersionLocation;
|
||||
UpdateCheck.UpdateFile = Settings.Default.VersionFile;
|
||||
UpdateCheck.ApplicationShutdown = ApplicationShutdown;
|
||||
UpdateCheck.ApplicationCurrentMessage = ApplicationCurrentMessage;
|
||||
UpdateCheck.ApplicationUpdateMessage = ApplicationUpdateMessage;
|
||||
}
|
||||
|
||||
private static bool ApplicationUpdateMessage(string title, string message)
|
||||
{
|
||||
return MessageBox.Show(message, title, MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes;
|
||||
}
|
||||
|
||||
private static void ApplicationCurrentMessage(string title, string message)
|
||||
{
|
||||
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
private static void ApplicationShutdown()
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private void HandleNewVersionLinkClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Display update information
|
||||
UpdateCheck.DisplayUpdateInformation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
180
Application/MainWindow/WindowHandler.cs
Normal file
180
Application/MainWindow/WindowHandler.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
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
|
||||
{
|
||||
public partial class MainWindow
|
||||
{
|
||||
private void LoadWindowSettings()
|
||||
{
|
||||
// Get the last window location
|
||||
var windowLocation = Settings.Default.WindowLocation;
|
||||
|
||||
// Set the window into position
|
||||
Left = windowLocation.X;
|
||||
Top = windowLocation.Y;
|
||||
|
||||
// Get the last window size
|
||||
var windowSize = Settings.Default.WindowSize;
|
||||
|
||||
// Set the window into the previous size if it is valid
|
||||
if (!windowSize.Width.Equals(0) && !windowSize.Height.Equals(0))
|
||||
{
|
||||
Width = windowSize.Width;
|
||||
Height = windowSize.Height;
|
||||
}
|
||||
|
||||
// Set the location of the navigation tray
|
||||
switch (Settings.Default.ToolbarLocation)
|
||||
{
|
||||
case Dock.Top:
|
||||
NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "TopToolbarRow");
|
||||
break;
|
||||
case Dock.Bottom:
|
||||
NameBasedGrid.NameBasedGrid.SetRow(NavigationToolbarTray, "BottomToolbarRow");
|
||||
break;
|
||||
}
|
||||
|
||||
// Load the lock state
|
||||
HandleWindowLockState();
|
||||
}
|
||||
|
||||
private void SaveWindowSettings()
|
||||
{
|
||||
// Set the last window location
|
||||
Settings.Default.WindowLocation = new Point(Left, Top);
|
||||
|
||||
// Set the last window size
|
||||
Settings.Default.WindowSize = new Size(Width, Height);
|
||||
|
||||
// Save the dock on the navigation tray
|
||||
Settings.Default.ToolbarLocation = NameBasedGrid.NameBasedGrid.GetRow(NavigationToolbarTray) == "TopToolbarRow" ? Dock.Top : Dock.Bottom;
|
||||
|
||||
// Save settings
|
||||
Settings.Default.Save();
|
||||
}
|
||||
|
||||
private void HandleWindowLockState()
|
||||
{
|
||||
// Set the resize mode for the window
|
||||
ResizeMode = Settings.Default.WindowLocked ? ResizeMode.NoResize : ResizeMode.CanResize;
|
||||
|
||||
// Show or hide the border
|
||||
WindowBorder.BorderBrush = Settings.Default.WindowLocked ? SystemColors.ActiveBorderBrush : Brushes.Transparent;
|
||||
|
||||
// Update the borders
|
||||
UpdateBorder();
|
||||
}
|
||||
|
||||
protected override void OnClosing(CancelEventArgs e)
|
||||
{
|
||||
base.OnClosing(e);
|
||||
|
||||
// Ditch the worker
|
||||
if (_feedReadWorker != null)
|
||||
{
|
||||
_feedReadWorker.CancelAsync();
|
||||
_feedReadWorker.Dispose();
|
||||
}
|
||||
|
||||
// Get rid of the timer
|
||||
TerminateTimer();
|
||||
|
||||
// Save current window settings
|
||||
SaveWindowSettings();
|
||||
|
||||
// Save settings
|
||||
Settings.Default.Save();
|
||||
|
||||
// Save options
|
||||
_database.SaveChanges();
|
||||
|
||||
// Get rid of the notification icon
|
||||
NotificationIcon.Dispose();
|
||||
}
|
||||
|
||||
private DelayedMethod _windowStateDelay;
|
||||
private void HandleWindowSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (_windowStateDelay == null)
|
||||
_windowStateDelay = new DelayedMethod(500, UpdateWindowSettings);
|
||||
|
||||
_windowStateDelay.Reset();
|
||||
}
|
||||
|
||||
private void HandleWindowLocationChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_windowStateDelay == null)
|
||||
_windowStateDelay = new DelayedMethod(500, UpdateWindowSettings);
|
||||
|
||||
_windowStateDelay.Reset();
|
||||
}
|
||||
|
||||
private void UpdateBorder()
|
||||
{
|
||||
var windowInteropHelper = new WindowInteropHelper(this);
|
||||
|
||||
var screen = System.Windows.Forms.Screen.FromHandle(windowInteropHelper.Handle);
|
||||
|
||||
var rectangle = new System.Drawing.Rectangle
|
||||
{
|
||||
X = (int) Left,
|
||||
Y = (int) Top,
|
||||
Width = (int) Width,
|
||||
Height = (int) Height
|
||||
};
|
||||
|
||||
var borderThickness = new Thickness();
|
||||
|
||||
if (rectangle.Right != screen.WorkingArea.Right)
|
||||
borderThickness.Right = 1;
|
||||
|
||||
if (rectangle.Left != screen.WorkingArea.Left)
|
||||
borderThickness.Left = 1;
|
||||
|
||||
if (rectangle.Top != screen.WorkingArea.Top)
|
||||
borderThickness.Top = 1;
|
||||
|
||||
if (rectangle.Bottom != screen.WorkingArea.Bottom)
|
||||
borderThickness.Bottom = 1;
|
||||
|
||||
WindowBorder.BorderThickness = borderThickness;
|
||||
}
|
||||
|
||||
private void UpdateWindowSettings()
|
||||
{
|
||||
// Save current window settings
|
||||
SaveWindowSettings();
|
||||
|
||||
// Update the border
|
||||
UpdateBorder();
|
||||
}
|
||||
|
||||
private bool _activated;
|
||||
protected override void OnActivated(EventArgs e)
|
||||
{
|
||||
base.OnActivated(e);
|
||||
|
||||
if (_activated)
|
||||
return;
|
||||
|
||||
_activated = true;
|
||||
|
||||
// Load the lock state
|
||||
HandleWindowLockState();
|
||||
|
||||
// Watch for size and location changes
|
||||
SizeChanged += HandleWindowSizeChanged;
|
||||
LocationChanged += HandleWindowLocationChanged;
|
||||
|
||||
// Watch for setting changes
|
||||
Settings.Default.PropertyChanged += HandlePropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user