- Add support for filtering by category

- Break up main window into partials instead of using regions
This commit is contained in:
2016-05-31 09:59:37 -04:00
parent 2de33134ce
commit fbc6500229
23 changed files with 1699 additions and 1461 deletions

View File

@@ -85,7 +85,8 @@ namespace FeedCenter
var splashWindow = new SplashWindow(); var splashWindow = new SplashWindow();
splashWindow.ShowDialog(); splashWindow.ShowDialog();
// Set whether we should auto-start // Set whether we should auto-start (if not debugging)
if (!IsDebugBuild)
Current.SetStartWithWindows(Settings.Default.StartWithWindows); Current.SetStartWithWindows(Settings.Default.StartWithWindows);
// Initialize the window // Initialize the window

View File

@@ -144,9 +144,6 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Common.Wpf.MarkupExtensions">
<HintPath>..\..\Common.Wpf.MarkupExtensions\bin\Release\Common.Wpf.MarkupExtensions.dll</HintPath>
</Reference>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"> <Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath> <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath>
@@ -161,6 +158,10 @@
<HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath> <HintPath>..\packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="NameBasedGrid, Version=0.10.1.0, Culture=neutral, PublicKeyToken=a434c4ad23d0fd33, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Public\namebasedgrid\bin\Debug\NameBasedGrid.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.configuration" /> <Reference Include="System.configuration" />
@@ -190,7 +191,18 @@
<Compile Include="FeedChooserWindow.xaml.cs"> <Compile Include="FeedChooserWindow.xaml.cs">
<DependentUpon>FeedChooserWindow.xaml</DependentUpon> <DependentUpon>FeedChooserWindow.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="MainWindow\CategoryList.cs" />
<Compile Include="MainWindow\CommandLine.cs" />
<Compile Include="MainWindow\DragDrop.cs" />
<Compile Include="MainWindow\FeedCreation.cs" />
<Compile Include="MainWindow\FeedList.cs" />
<Compile Include="MainWindow\FeedReading.cs" />
<Compile Include="MainWindow\Header.cs" />
<Compile Include="MainWindow\Timer.cs" />
<Compile Include="MainWindow\Toolbar.cs" />
<Compile Include="MainWindow\WindowHandler.cs" />
<Compile Include="SystemConfiguration.cs" /> <Compile Include="SystemConfiguration.cs" />
<Compile Include="MainWindow\UpdateHandler.cs" />
<Page Include="App.xaml"> <Page Include="App.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@@ -277,7 +289,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="MainWindow.xaml"> <Page Include="MainWindow\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
@@ -293,7 +305,7 @@
<Compile Include="Feeds\Feed.cs" /> <Compile Include="Feeds\Feed.cs" />
<Compile Include="Feeds\FeedAction.cs" /> <Compile Include="Feeds\FeedAction.cs" />
<Compile Include="Feeds\FeedItem.cs" /> <Compile Include="Feeds\FeedItem.cs" />
<Compile Include="MainWindow.xaml.cs"> <Compile Include="MainWindow\MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon> <DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>

View File

@@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Feeds/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=Feeds/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=mainwindow/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

File diff suppressed because it is too large Load Diff

View 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();
}
}
}

View 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);
}
}
}
}

View 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);
}
}
}

View 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();
}
}
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -5,10 +5,10 @@
xmlns:windows="clr-namespace:Common.Wpf.Windows;assembly=Common.Wpf" xmlns:windows="clr-namespace:Common.Wpf.Windows;assembly=Common.Wpf"
xmlns:toolbar="clr-namespace:Common.Wpf.Toolbar;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:splitButton="clr-namespace:Common.Wpf.Toolbar.SplitButton;assembly=Common.Wpf"
xmlns:markup="clr-namespace:Common.Wpf.MarkupExtensions;assembly=Common.Wpf.MarkupExtensions"
xmlns:linkControl="clr-namespace:Common.Wpf.LinkControl;assembly=Common.Wpf" xmlns:linkControl="clr-namespace:Common.Wpf.LinkControl;assembly=Common.Wpf"
xmlns:htmlTextBlock="clr-namespace:Common.Wpf.HtmlTextBlock;assembly=Common.Wpf" xmlns:htmlTextBlock="clr-namespace:Common.Wpf.HtmlTextBlock;assembly=Common.Wpf"
xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:nameBasedGrid="clr-namespace:NameBasedGrid;assembly=NameBasedGrid"
Title="MainWindow" Title="MainWindow"
Height="360" Height="360"
Width="252" Width="252"
@@ -37,29 +37,31 @@
Name="WindowBorder" Name="WindowBorder"
Padding="0" Padding="0"
Background="{x:Static SystemColors.DesktopBrush}"> Background="{x:Static SystemColors.DesktopBrush}">
<Grid Name="MainGrid"> <nameBasedGrid:NameBasedGrid Name="MainGrid">
<Grid.RowDefinitions> <nameBasedGrid:NameBasedGrid.RowDefinitions>
<!-- ReSharper disable UnusedMember.Global --> <!-- ReSharper disable UnusedMember.Global -->
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="HeaderRow" /> Name="HeaderRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="NewVersionRow" /> Name="NewVersionRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="CategoryRow" />
<nameBasedGrid:ColumnOrRow Size="Auto"
Name="FeedRow" /> Name="FeedRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="TopToolbarRow" /> Name="TopToolbarRow" />
<RowDefinition Height="*" <nameBasedGrid:ColumnOrRow Size="*"
Name="FeedListRow" /> Name="FeedListRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="ProgressRow" /> Name="ProgressRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="BottomToolbarRow" /> Name="BottomToolbarRow" />
<RowDefinition Height="Auto" <nameBasedGrid:ColumnOrRow Size="Auto"
Name="FeedErrorsRow" /> Name="FeedErrorsRow" />
<!-- ReSharper restore UnusedMember.Global --> <!-- ReSharper restore UnusedMember.Global -->
</Grid.RowDefinitions> </nameBasedGrid:NameBasedGrid.RowDefinitions>
<Grid Height="21" <Grid Height="21"
Grid.Row="{markup:GridRow RowName=HeaderRow}"> nameBasedGrid:NameBasedGrid.Row="HeaderRow">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition Width="21" /> <ColumnDefinition Width="21" />
@@ -82,7 +84,7 @@
</Grid> </Grid>
<linkControl:LinkControl Name="NewVersionLink" <linkControl:LinkControl Name="NewVersionLink"
Height="21" Height="21"
Grid.Row="{markup:GridRow NewVersionRow}" nameBasedGrid:NameBasedGrid.Row="NewVersionRow"
Text="{x:Static properties:Resources.NewVersionLink}" Text="{x:Static properties:Resources.NewVersionLink}"
Background="AntiqueWhite" Background="AntiqueWhite"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
@@ -90,8 +92,34 @@
Visibility="Collapsed" Visibility="Collapsed"
Click="HandleNewVersionLinkClick"> Click="HandleNewVersionLinkClick">
</linkControl:LinkControl> </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" <Grid Height="21"
Grid.Row="{markup:GridRow RowName=FeedRow}"> nameBasedGrid:NameBasedGrid.Row="FeedRow">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition /> <ColumnDefinition />
<ColumnDefinition Width="21" /> <ColumnDefinition Width="21" />
@@ -122,7 +150,7 @@
Background="{x:Static SystemColors.DesktopBrush}" Background="{x:Static SystemColors.DesktopBrush}"
MouseUp="HandleLinkTextListMouseUp" MouseUp="HandleLinkTextListMouseUp"
Foreground="White" Foreground="White"
Grid.Row="{markup:GridRow RowName=FeedListRow}" nameBasedGrid:NameBasedGrid.Row="FeedListRow"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"> ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBoxItem Content="Test item" /> <ListBoxItem Content="Test item" />
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
@@ -185,11 +213,11 @@
<ProgressBar Name="FeedReadProgress" <ProgressBar Name="FeedReadProgress"
Height="15" Height="15"
Visibility="Collapsed" Visibility="Collapsed"
Grid.Row="{markup:GridRow RowName=ProgressRow}" /> nameBasedGrid:NameBasedGrid.Row="ProgressRow" />
<ToolBarTray Name="NavigationToolbarTray" <ToolBarTray Name="NavigationToolbarTray"
Background="Transparent" Background="Transparent"
Orientation="Horizontal" Orientation="Horizontal"
Grid.Row="{markup:GridRow RowName=BottomToolbarRow}"> nameBasedGrid:NameBasedGrid.Row="TopToolbarRow">
<ToolBar ToolBarTray.IsLocked="True" <ToolBar ToolBarTray.IsLocked="True"
Background="Transparent" Background="Transparent"
ToolBar.OverflowMode="Never"> ToolBar.OverflowMode="Never">
@@ -198,15 +226,15 @@
Name="PreviousToolbarButton" Name="PreviousToolbarButton"
Click="HandlePreviousToolbarButtonClick" Click="HandlePreviousToolbarButtonClick"
ToolTip="{x:Static properties:Resources.previousToolbarButton}" ToolTip="{x:Static properties:Resources.previousToolbarButton}"
ImageSource="Resources/Left.ico" /> ImageSource="../Resources/Left.ico" />
<toolbar:ImageButton Height="20" <toolbar:ImageButton Height="20"
Width="20" Width="20"
Name="NextToolbarButton" Name="NextToolbarButton"
Click="HandleNextToolbarButtonClick" Click="HandleNextToolbarButtonClick"
ToolTip="{x:Static properties:Resources.nextToolbarButton}" ToolTip="{x:Static properties:Resources.nextToolbarButton}"
ImageSource="Resources/Right.ico" /> ImageSource="../Resources/Right.ico" />
<splitButton:SplitButton Name="RefreshToolbarButton" <splitButton:SplitButton Name="RefreshToolbarButton"
Image="Resources/Rss-Download.ico" Image="../Resources/Rss-Download.ico"
ToolTip="{x:Static properties:Resources.refreshAllToolbarButton}" ToolTip="{x:Static properties:Resources.refreshAllToolbarButton}"
Height="20" Height="20"
MinWidth="35" MinWidth="35"
@@ -222,7 +250,7 @@
</splitButton:SplitButton.DropDownContextMenu> </splitButton:SplitButton.DropDownContextMenu>
</splitButton:SplitButton> </splitButton:SplitButton>
<splitButton:SplitButton Name="OpenAllToolbarButton" <splitButton:SplitButton Name="OpenAllToolbarButton"
Image="Resources/News.ico" Image="../Resources/News.ico"
ToolTip="{x:Static properties:Resources.openAllMultipleToolbarButton}" ToolTip="{x:Static properties:Resources.openAllMultipleToolbarButton}"
Height="20" Height="20"
MinWidth="35" MinWidth="35"
@@ -243,13 +271,13 @@
Name="MarkReadToolbarButton" Name="MarkReadToolbarButton"
Click="HandleMarkReadToolbarButtonClick" Click="HandleMarkReadToolbarButtonClick"
ToolTip="{x:Static properties:Resources.markReadToolbarButton}" ToolTip="{x:Static properties:Resources.markReadToolbarButton}"
ImageSource="Resources/Comments-edit.ico" /> ImageSource="../Resources/Comments-edit.ico" />
<splitButton:SplitButton Height="20" <splitButton:SplitButton Height="20"
MinWidth="35" MinWidth="35"
Margin="5,0,0,0" Margin="5,0,0,0"
Click="HandleOptionsToolbarButtonClick" Click="HandleOptionsToolbarButtonClick"
ToolTip="{x:Static properties:Resources.optionsToolbarButton}" ToolTip="{x:Static properties:Resources.optionsToolbarButton}"
Image="Resources/Compile.ico"> Image="../Resources/Compile.ico">
<splitButton:SplitButton.DropDownContextMenu> <splitButton:SplitButton.DropDownContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem Header="{x:Static properties:Resources.lockWindowCheckBox}" <MenuItem Header="{x:Static properties:Resources.lockWindowCheckBox}"
@@ -269,7 +297,7 @@
</ToolBarTray> </ToolBarTray>
<linkControl:LinkControl Name="FeedErrorsLink" <linkControl:LinkControl Name="FeedErrorsLink"
Height="21" Height="21"
Grid.Row="{markup:GridRow FeedErrorsRow}" nameBasedGrid:NameBasedGrid.Row="FeedErrorsRow"
Text="{x:Static properties:Resources.FeedErrorsLink}" Text="{x:Static properties:Resources.FeedErrorsLink}"
ToolTip="{x:Static properties:Resources.showErrorsToolbarButton}" ToolTip="{x:Static properties:Resources.showErrorsToolbarButton}"
Background="AntiqueWhite" Background="AntiqueWhite"
@@ -278,6 +306,6 @@
Visibility="Collapsed" Visibility="Collapsed"
Click="HandleShowErrorsButtonClick"> Click="HandleShowErrorsButtonClick">
</linkControl:LinkControl> </linkControl:LinkControl>
</Grid> </nameBasedGrid:NameBasedGrid>
</Border> </Border>
</windows:SnappingWindow> </windows:SnappingWindow>

View 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
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@@ -394,7 +394,9 @@ namespace FeedCenter.Options
_collectionViewSource.View.Refresh(); _collectionViewSource.View.Refresh();
//textBlock.TextDecorations = null; var dataGridRow = (DataGridRow) sender;
dataGridRow.FontWeight = FontWeights.Normal;
} }
private void HandleListBoxItemPreviewMouseMove(object sender, MouseEventArgs e) private void HandleListBoxItemPreviewMouseMove(object sender, MouseEventArgs e)

View File

@@ -96,6 +96,15 @@ namespace FeedCenter.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to &lt; all &gt;.
/// </summary>
public static string AllCategory {
get {
return ResourceManager.GetString("AllCategory", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary> /// </summary>
@@ -196,6 +205,15 @@ namespace FeedCenter.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Category: {0}.
/// </summary>
public static string CategoryFilterHeader {
get {
return ResourceManager.GetString("CategoryFilterHeader", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Category. /// Looks up a localized string similar to Category.
/// </summary> /// </summary>

View File

@@ -517,4 +517,10 @@
<data name="FeedChooserWindow" xml:space="preserve"> <data name="FeedChooserWindow" xml:space="preserve">
<value>Choose Feed to Add</value> <value>Choose Feed to Add</value>
</data> </data>
<data name="AllCategory" xml:space="preserve">
<value>&lt; all &gt;</value>
</data>
<data name="CategoryFilterHeader" xml:space="preserve">
<value>Category: {0}</value>
</data>
</root> </root>

View File

@@ -277,5 +277,17 @@ namespace FeedCenter.Properties {
return ((string)(this["VersionLocation"])); return ((string)(this["VersionLocation"]));
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string LastCategoryID {
get {
return ((string)(this["LastCategoryID"]));
}
set {
this["LastCategoryID"] = value;
}
}
} }
} }

View File

@@ -68,5 +68,8 @@
<Setting Name="VersionLocation" Type="System.String" Scope="Application"> <Setting Name="VersionLocation" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://server/FeedCenter/</Value> <Value Profile="(Default)">http://server/FeedCenter/</Value>
</Setting> </Setting>
<Setting Name="LastCategoryID" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@@ -57,6 +57,9 @@
<setting name="MultipleLineDisplay" serializeAs="String"> <setting name="MultipleLineDisplay" serializeAs="String">
<value>Normal</value> <value>Normal</value>
</setting> </setting>
<setting name="LastCategoryID" serializeAs="String">
<value />
</setting>
</FeedCenter.Properties.Settings> </FeedCenter.Properties.Settings>
</userSettings> </userSettings>
<applicationSettings> <applicationSettings>