Refactoring and addressing comments

This commit is contained in:
Michael Bucari-Tovo 2022-05-23 21:20:26 -06:00
parent 4a82541ffd
commit d71cdecd35
13 changed files with 218 additions and 205 deletions

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Version>7.7.0.14</Version>
<Version>7.7.1.1</Version>
</PropertyGroup>
<ItemGroup>

View File

@ -98,7 +98,7 @@
// filterBtn
//
this.filterBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.filterBtn.Location = new System.Drawing.Point(748, 3);
this.filterBtn.Location = new System.Drawing.Point(916, 3);
this.filterBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.filterBtn.Name = "filterBtn";
this.filterBtn.Size = new System.Drawing.Size(88, 27);
@ -114,7 +114,7 @@
this.filterSearchTb.Location = new System.Drawing.Point(196, 7);
this.filterSearchTb.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.filterSearchTb.Name = "filterSearchTb";
this.filterSearchTb.Size = new System.Drawing.Size(544, 23);
this.filterSearchTb.Size = new System.Drawing.Size(712, 23);
this.filterSearchTb.TabIndex = 1;
this.filterSearchTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.filterSearchTb_KeyPress);
//
@ -132,7 +132,7 @@
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2);
this.menuStrip1.Size = new System.Drawing.Size(893, 24);
this.menuStrip1.Size = new System.Drawing.Size(1061, 24);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
@ -396,7 +396,7 @@
this.statusStrip1.Location = new System.Drawing.Point(0, 618);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 16, 0);
this.statusStrip1.Size = new System.Drawing.Size(893, 22);
this.statusStrip1.Size = new System.Drawing.Size(1061, 22);
this.statusStrip1.TabIndex = 6;
this.statusStrip1.Text = "statusStrip1";
//
@ -410,7 +410,7 @@
// springLbl
//
this.springLbl.Name = "springLbl";
this.springLbl.Size = new System.Drawing.Size(379, 17);
this.springLbl.Size = new System.Drawing.Size(547, 17);
this.springLbl.Spring = true;
//
// backupsCountsLbl
@ -452,8 +452,8 @@
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.processBookQueue1);
this.splitContainer1.Size = new System.Drawing.Size(1231, 640);
this.splitContainer1.SplitterDistance = 893;
this.splitContainer1.Size = new System.Drawing.Size(1463, 640);
this.splitContainer1.SplitterDistance = 1061;
this.splitContainer1.SplitterWidth = 8;
this.splitContainer1.TabIndex = 7;
//
@ -470,7 +470,7 @@
this.panel1.Location = new System.Drawing.Point(0, 24);
this.panel1.Margin = new System.Windows.Forms.Padding(0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(893, 594);
this.panel1.Size = new System.Drawing.Size(1061, 594);
this.panel1.TabIndex = 7;
//
// productsDisplay
@ -482,16 +482,16 @@
this.productsDisplay.Location = new System.Drawing.Point(15, 36);
this.productsDisplay.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.productsDisplay.Name = "productsDisplay";
this.productsDisplay.Size = new System.Drawing.Size(863, 555);
this.productsDisplay.Size = new System.Drawing.Size(1031, 555);
this.productsDisplay.TabIndex = 9;
this.productsDisplay.LiberateClicked += new System.EventHandler<DataLayer.LibraryBook>(this.ProductsDisplay_LiberateClicked);
this.productsDisplay.VisibleCountChanged += new System.EventHandler<int>(this.productsDisplay_VisibleCountChanged);
this.productsDisplay.LiberateClicked += new System.EventHandler<DataLayer.LibraryBook>(this.ProductsDisplay_LiberateClicked);
this.productsDisplay.InitialLoaded += new System.EventHandler(this.productsDisplay_InitialLoaded);
//
// toggleQueueHideBtn
//
this.toggleQueueHideBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.toggleQueueHideBtn.Location = new System.Drawing.Point(845, 3);
this.toggleQueueHideBtn.Location = new System.Drawing.Point(1013, 3);
this.toggleQueueHideBtn.Margin = new System.Windows.Forms.Padding(4, 3, 15, 3);
this.toggleQueueHideBtn.Name = "toggleQueueHideBtn";
this.toggleQueueHideBtn.Size = new System.Drawing.Size(33, 27);
@ -507,14 +507,14 @@
this.processBookQueue1.Location = new System.Drawing.Point(0, 0);
this.processBookQueue1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.processBookQueue1.Name = "processBookQueue1";
this.processBookQueue1.Size = new System.Drawing.Size(330, 640);
this.processBookQueue1.Size = new System.Drawing.Size(394, 640);
this.processBookQueue1.TabIndex = 0;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1231, 640);
this.ClientSize = new System.Drawing.Size(1463, 640);
this.Controls.Add(this.splitContainer1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;

View File

@ -57,12 +57,48 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="filterHelpBtn.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="filterBtn.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="filterSearchTb.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="menuStrip1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>132, 17</value>
</metadata>
<metadata name="statusStrip1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="addQuickFilterBtn.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="splitContainer1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="panel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="productsDisplay.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="toggleQueueHideBtn.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="processBookQueue1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>

View File

@ -46,24 +46,31 @@ namespace LibationWinForms.ProcessQueue
public ProcessQueueControl()
{
InitializeComponent();
Logger = LogMe.RegisterForm(this);
runningTimeLbl.Text = string.Empty;
popoutBtn.DisplayStyle = ToolStripItemDisplayStyle.Text;
popoutBtn.Name = "popoutBtn";
popoutBtn.Text = "Pop Out";
popoutBtn.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
popoutBtn.Alignment = ToolStripItemAlignment.Right;
popoutBtn.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
statusStrip1.Items.Add(popoutBtn);
Logger = LogMe.RegisterForm(this);
virtualFlowControl2.RequestData += VirtualFlowControl1_RequestData;
virtualFlowControl2.ButtonClicked += VirtualFlowControl2_ButtonClicked;
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
Load += ProcessQueueControl_Load;
}
private void ProcessQueueControl_Load(object sender, EventArgs e)
{
if (DesignMode) return;
runningTimeLbl.Text = string.Empty;
QueuedCount = 0;
ErrorCount = 0;
CompletedCount = 0;

View File

@ -114,9 +114,6 @@ namespace LibationWinForms.ProcessQueue
BookControls.Add(control);
panel1.Controls.Add(control);
if (DesignMode)
return;
for (int i = 1; i < NUM_ACTUAL_CONTROLS; i++)
{
control = InitControl(VirtualControlHeight * i);

View File

@ -5,37 +5,12 @@ using LibationFileManager;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;using System.Drawing;
using System.Drawing;
using System.Linq;
namespace LibationWinForms
{
public interface IHierarchical<T> where T : class
{
T Parent { get; }
}
public class LiberateButtonStatus : IComparable
{
public LiberatedStatus BookStatus;
public LiberatedStatus? PdfStatus;
public bool IsSeries;
public bool Expanded;
public int CompareTo(object obj)
{
if (obj is not LiberateButtonStatus second) return -1;
if (IsSeries && !second.IsSeries) return -1;
else if (!IsSeries && second.IsSeries) return 1;
else if (IsSeries && second.IsSeries) return 0;
else if (BookStatus == LiberatedStatus.Liberated && second.BookStatus != LiberatedStatus.Liberated) return -1;
else if (BookStatus != LiberatedStatus.Liberated && second.BookStatus == LiberatedStatus.Liberated) return 1;
else return BookStatus.CompareTo(second.BookStatus);
}
}
public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable, IHierarchical<GridEntry>
public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable
{
protected abstract Book Book { get; }
@ -50,12 +25,7 @@ namespace LibationWinForms
NotifyPropertyChanged();
}
}
[Browsable(false)]
public new bool InvokeRequired => base.InvokeRequired;
[Browsable(false)]
public GridEntry Parent { get; set; }
[Browsable(false)]
public abstract DateTime DateAdded { get; }
public abstract string ProductRating { get; protected set; }
public abstract string PurchaseDate { get; protected set; }
@ -116,9 +86,16 @@ namespace LibationWinForms
internal static class GridEntryExtensions
{
#nullable enable
public static IEnumerable<SeriesEntry> Series(this IEnumerable<GridEntry> gridEntries)
=> gridEntries.Where(i => i is SeriesEntry).Cast<SeriesEntry>();
public static IEnumerable<LibraryBookEntry> LibraryBooks(this IEnumerable<GridEntry> gridEntries)
=> gridEntries.Where(i => i is LibraryBookEntry).Cast<LibraryBookEntry>();
public static LibraryBookEntry? FindBookByAsin(this IEnumerable<LibraryBookEntry> gridEntries, string audibleProductID)
=> gridEntries.FirstOrDefault(i => i.AudibleProductId == audibleProductID);
public static SeriesEntry? FindBookSeriesEntry(this IEnumerable<GridEntry> gridEntries, IEnumerable<SeriesBook> matchSeries)
=> gridEntries.Series().FirstOrDefault(i => matchSeries.Any(s => s.Series.Name == i.Series));
public static IEnumerable<SeriesEntry> EmptySeries(this IEnumerable<GridEntry> gridEntries)
=> gridEntries.Series().Where(i => i.Children.Count == 0);
}
}

View File

@ -8,7 +8,7 @@ using System.Linq;
namespace LibationWinForms
{
/*
* Allows filtering of the underlying SortableBindingList<GridEntry>
* Allows filtering and sorting of the underlying BindingList<GridEntry>
* by implementing IBindingListView and using SearchEngineCommands
*
* When filtering is applied, the filtered-out items are removed
@ -19,19 +19,34 @@ namespace LibationWinForms
* Remove is overridden to ensure that removed items are removed from
* the base list (visible items) as well as the FilterRemoved list.
*/
internal class FilterableSortableBindingList : SortableBindingList1<GridEntry>, IBindingListView
internal class GridEntryBindingList : BindingList<GridEntry>, IBindingListView
{
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
public GridEntryBindingList() : base(new List<GridEntry>()) { }
public GridEntryBindingList(IEnumerable<GridEntry> enumeration) : base(new List<GridEntry>(enumeration)) { }
/// <returns>All items in the list, including those filtered out.</returns>
public List<GridEntry> AllItems() => Items.Concat(FilterRemoved).ToList();
protected MemberComparer<GridEntry> Comparer { get; } = new();
protected override bool SupportsSortingCore => true;
protected override bool SupportsSearchingCore => true;
protected override bool IsSortedCore => isSorted;
protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
protected override ListSortDirection SortDirectionCore => listSortDirection;
public bool SupportsFiltering => true;
public string Filter { get => FilterString; set => ApplyFilter(value); }
/// <summary>
/// Items that were removed from the base list due to filtering
/// </summary>
private readonly List<GridEntry> FilterRemoved = new();
private string FilterString;
private LibationSearchEngine.SearchResultSet SearchResults;
public FilterableSortableBindingList(IEnumerable<GridEntry> enumeration) : base(enumeration) { }
public FilterableSortableBindingList() : base(new List<GridEntry>()) { }
public bool SupportsFiltering => true;
public string Filter { get => FilterString; set => ApplyFilter(value); }
#region Unused - Advanced Filtering
public bool SupportsAdvancedSorting => false;
@ -49,9 +64,6 @@ namespace LibationWinForms
base.Remove(entry);
}
/// <returns>All items in the list, including those filtered out.</returns>
public List<GridEntry> AllItems() => Items.Concat(FilterRemoved).ToList();
private void ApplyFilter(string filterString)
{
if (filterString != FilterString)
@ -88,7 +100,7 @@ namespace LibationWinForms
public void CollapseItem(SeriesEntry sEntry)
{
foreach (var episode in Items.Where(b => b.Parent == sEntry).Cast<LibraryBookEntry>().ToList())
foreach (var episode in Items.LibraryBooks().Where(b => b.Parent == sEntry).ToList())
{
FilterRemoved.Add(episode);
base.Remove(episode);
@ -101,7 +113,7 @@ namespace LibationWinForms
{
var sindex = Items.IndexOf(sEntry);
foreach (var episode in FilterRemoved.Where(b => b.Parent == sEntry).Cast<LibraryBookEntry>().ToList())
foreach (var episode in FilterRemoved.LibraryBooks().Where(b => b.Parent == sEntry).ToList())
{
if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId))
{
@ -120,23 +132,19 @@ namespace LibationWinForms
int visibleCount = Items.Count;
SuspendSorting = true;
foreach (var item in FilterRemoved.ToList())
{
if (item.Parent is null || item.Parent.Liberate.Expanded)
if (item is SeriesEntry || (item is LibraryBookEntry lbe && (lbe.Parent is null || lbe.Parent.Liberate.Expanded)))
{
FilterRemoved.Remove(item);
base.InsertItem(visibleCount++, item);
}
}
SuspendSorting = false;
if (IsSortedCore)
Sort();
else
//No user sort is applied, so do default sorting by PurchaseDate, descending
//No user sort is applied, so do default sorting by DateAdded, descending
{
Comparer.PropertyName = nameof(GridEntry.DateAdded);
Comparer.Direction = ListSortDirection.Descending;
@ -148,5 +156,64 @@ namespace LibationWinForms
FilterString = null;
SearchResults = null;
}
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
Comparer.PropertyName = property.Name;
Comparer.Direction = direction;
Sort();
propertyDescriptor = property;
listSortDirection = direction;
isSorted = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
protected void Sort()
{
var itemsList = (List<GridEntry>)Items;
var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList();
var children = sortedItems.LibraryBooks().Where(i => i.Parent is not null).ToList();
itemsList.Clear();
//Only add parentless items at this stage. After these items are added in the
//correct sorting order, go back and add the children beneath their parents.
itemsList.AddRange(sortedItems.Except(children));
foreach (var parent in children.Select(c => c.Parent).Distinct())
{
var pIndex = itemsList.IndexOf(parent);
foreach (var c in children.Where(c => c.Parent == parent))
itemsList.Insert(++pIndex, c);
}
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (isSorted && e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor == SortPropertyCore)
{
var item = Items[e.NewIndex];
Sort();
var newIndex = Items.IndexOf(item);
base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex));
}
else
base.OnListChanged(e);
}
protected override void RemoveSortCore()
{
isSorted = false;
propertyDescriptor = base.SortPropertyCore;
listSortDirection = base.SortDirectionCore;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
}

View File

@ -0,0 +1,28 @@
using DataLayer;
using System;
namespace LibationWinForms
{
public class LiberateButtonStatus : IComparable
{
public LiberatedStatus BookStatus { get; set; }
public LiberatedStatus? PdfStatus { get; set; }
public bool IsSeries { get; init; }
public bool Expanded { get; set; }
/// <summary>
/// Defines the Liberate column's sorting behavior
/// </summary>
public int CompareTo(object obj)
{
if (obj is not LiberateButtonStatus second) return -1;
if (IsSeries && !second.IsSeries) return -1;
else if (!IsSeries && second.IsSeries) return 1;
else if (IsSeries && second.IsSeries) return 0;
else if (BookStatus == LiberatedStatus.Liberated && second.BookStatus != LiberatedStatus.Liberated) return -1;
else if (BookStatus != LiberatedStatus.Liberated && second.BookStatus == LiberatedStatus.Liberated) return 1;
else return BookStatus.CompareTo(second.BookStatus);
}
}
}

View File

@ -1,16 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using ApplicationServices;
using DataLayer;
using Dinah.Core.DataBinding;
using Dinah.Core;
using Dinah.Core.Drawing;
using LibationFileManager;
using System.Threading.Tasks;
namespace LibationWinForms
{
@ -30,7 +24,6 @@ namespace LibationWinForms
public string LongDescription { get; private set; }
#endregion
// alias
protected override Book Book => LibraryBook.Book;
#region Model properties exposed to the view
@ -69,9 +62,9 @@ namespace LibationWinForms
}
#endregion
public LibraryBookEntry(LibraryBook libraryBook) => setLibraryBook(libraryBook);
public SeriesEntry Parent { get; init; }
public void UpdateLibraryBook(LibraryBook libraryBook)
{
if (AudibleProductId != libraryBook.Book.AudibleProductId)

View File

@ -47,10 +47,8 @@
this.tagAndDetailsGVColumn = new LibationWinForms.EditTagsDataGridViewImageButtonColumn();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.syncBindingSource = new LibationWinForms.SyncBindingSource(this.components);
this.bindingSource = new System.Windows.Forms.BindingSource(this.components);
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.bindingSource)).BeginInit();
this.SuspendLayout();
//
// gridEntryDataGridView
@ -216,11 +214,7 @@
//
// syncBindingSource
//
this.syncBindingSource.DataSource = this.bindingSource;
//
// bindingSource
//
this.bindingSource.DataSource = typeof(LibationWinForms.GridEntry);
this.syncBindingSource.DataSource = typeof(LibationWinForms.GridEntry);
//
// ProductsGrid
//
@ -233,7 +227,6 @@
this.Load += new System.EventHandler(this.ProductsGrid_Load);
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.bindingSource)).EndInit();
this.ResumeLayout(false);
}
@ -257,6 +250,5 @@
private System.Windows.Forms.DataGridViewTextBoxColumn miscGVColumn;
private EditTagsDataGridViewImageButtonColumn tagAndDetailsGVColumn;
private SyncBindingSource syncBindingSource;
private System.Windows.Forms.BindingSource bindingSource;
}
}

View File

@ -23,7 +23,7 @@ namespace LibationWinForms.grid
public event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked;
public new event EventHandler<ScrollEventArgs> Scroll;
private FilterableSortableBindingList bindingList;
private GridEntryBindingList bindingList;
internal IEnumerable<LibraryBookEntry> GetVisible()
=> bindingList
.LibraryBooks();
@ -89,20 +89,15 @@ namespace LibationWinForms.grid
var episodes = dbBooks.Where(b => b.Book.ContentType is ContentType.Episode).ToList();
var series = episodes.Select(lb => lb.Book.SeriesLink.First()).DistinctBy(s => s.Series).ToList();
foreach (var s in series)
foreach (var series in episodes.Select(lb => lb.Book.SeriesLink.First()).DistinctBy(s => s.Series))
{
var seriesEntry = new SeriesEntry();
seriesEntry.Children = episodes.Where(lb => lb.Book.SeriesLink.First().Series == s.Book.SeriesLink.First().Series).Select(lb => new LibraryBookEntry(lb) { Parent = seriesEntry }).ToList();
seriesEntry.setSeriesBook(s);
var seriesEntry = new SeriesEntry(series, episodes.Where(lb => lb.Book.SeriesLink.First().Series == series.Book.SeriesLink.First().Series));
geList.Add(seriesEntry);
geList.AddRange(seriesEntry.Children);
}
bindingList = new FilterableSortableBindingList(geList.OrderByDescending(e => e.DateAdded));
bindingList = new GridEntryBindingList(geList.OrderByDescending(e => e.DateAdded));
bindingList.CollapseAll();
syncBindingSource.DataSource = bindingList;
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
@ -116,39 +111,36 @@ namespace LibationWinForms.grid
//Add absent books to grid, or update current books
var allItmes = bindingList.AllItems().LibraryBooks();
for (var i = dbBooks.Count - 1; i >= 0; i--)
foreach (var libraryBook in dbBooks)
{
var libraryBook = dbBooks[i];
var existingItem = allItmes.FirstOrDefault(i => i.AudibleProductId == libraryBook.Book.AudibleProductId);
var existingItem = allItmes.FindBookByAsin(libraryBook.Book.AudibleProductId);
// add new to top
if (existingItem is null)
{
var lb = new LibraryBookEntry(libraryBook);
if (libraryBook.Book.ContentType is ContentType.Episode)
{
LibraryBookEntry lbe;
//Find the series that libraryBook belongs to, if it exists
var series = bindingList.AllItems().Series().FirstOrDefault(i => libraryBook.Book.SeriesLink.Any(s => s.Series.Name == i.Series));
var series = bindingList.AllItems().FindBookSeriesEntry(libraryBook.Book.SeriesLink);
if (series is null)
{
//Series doesn't exist yet, so create and add it
var newSeries = new SeriesEntry { Children = new List<LibraryBookEntry> { lb } };
newSeries.setSeriesBook(libraryBook.Book.SeriesLink.First());
lb.Parent = newSeries;
var newSeries = new SeriesEntry(libraryBook.Book.SeriesLink.First(), libraryBook);
lbe = newSeries.Children[0];
newSeries.Liberate.Expanded = true;
bindingList.Insert(0, newSeries);
series = newSeries;
}
else
{
lb.Parent = series;
series.Children.Add(lb);
lbe = new(libraryBook) { Parent = series };
series.Children.Add(lbe);
}
//Add episode beneath the parent
int seriesIndex = bindingList.IndexOf(series);
bindingList.Insert(seriesIndex + 1, lb);
bindingList.Insert(seriesIndex + 1, lbe);
if (series.Liberate.Expanded)
bindingList.ExpandItem(series);
@ -159,7 +151,7 @@ namespace LibationWinForms.grid
}
else
//Add the new product
bindingList.Insert(0, lb);
bindingList.Insert(0, new LibraryBookEntry(libraryBook));
}
// update existing
else
@ -168,6 +160,9 @@ namespace LibationWinForms.grid
}
}
//Re-filter after updating existing / adding new books to capture any changes
Filter(existingFilter);
// remove deleted from grid.
// note: actual deletion from db must still occur via the RemoveBook feature. deleting from audible will not trigger this
var removedBooks =
@ -176,26 +171,23 @@ namespace LibationWinForms.grid
.LibraryBooks()
.ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId);
//Remove books in series from their parents' Children list
foreach (var removed in removedBooks.Where(b => b.Parent is not null))
{
var series = removed.Parent as SeriesEntry;
series.Children.Remove(removed);
series.NotifyPropertyChanged();
removed.Parent.Children.Remove(removed);
removed.Parent.NotifyPropertyChanged();
}
//Remove series that have no children
var removedSeries =
bindingList
.AllItems()
.Series()
.Where(i => i.Children.Count == 0);
.EmptySeries();
foreach (var removed in removedBooks.Cast<GridEntry>().Concat(removedSeries))
//no need to re-filter for removed books
bindingList.Remove(removed);
Filter(existingFilter);
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
}

View File

@ -6,9 +6,9 @@ using System.Linq;
namespace LibationWinForms
{
internal class SeriesEntry : GridEntry
public class SeriesEntry : GridEntry
{
public List<LibraryBookEntry> Children { get; set; }
public List<LibraryBookEntry> Children { get; init; }
public override DateTime DateAdded => Children.Max(c => c.DateAdded);
public override string ProductRating
{
@ -55,7 +55,18 @@ namespace LibationWinForms
private LiberateButtonStatus _liberate = new LiberateButtonStatus { IsSeries = true };
public void setSeriesBook(SeriesBook seriesBook)
public SeriesEntry(SeriesBook seriesBook, IEnumerable<LibraryBook> children)
{
Children = children.Select(c=>new LibraryBookEntry(c) { Parent = this }).ToList();
SetSeriesBook(seriesBook);
}
public SeriesEntry(SeriesBook seriesBook, LibraryBook child)
{
Children = new() { new LibraryBookEntry(child) { Parent = this } };
SetSeriesBook(seriesBook);
}
private void SetSeriesBook(SeriesBook seriesBook)
{
SeriesBook = seriesBook;
_memberValues = CreateMemberValueDictionary();

View File

@ -1,87 +0,0 @@
using Dinah.Core.DataBinding;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace LibationWinForms
{
internal class SortableBindingList1<T> : BindingList<T> where T : class, IMemberComparable, IHierarchical<T>
{
private bool isSorted;
private ListSortDirection listSortDirection;
private PropertyDescriptor propertyDescriptor;
public SortableBindingList1() : base(new List<T>()) { }
public SortableBindingList1(IEnumerable<T> enumeration) : base(new List<T>(enumeration)) { }
protected bool SuspendSorting { get; set; }
protected MemberComparer<T> Comparer { get; } = new();
protected override bool SupportsSortingCore => true;
protected override bool SupportsSearchingCore => true;
protected override bool IsSortedCore => isSorted;
protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
protected override ListSortDirection SortDirectionCore => listSortDirection;
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
{
Comparer.PropertyName = property.Name;
Comparer.Direction = direction;
Sort();
propertyDescriptor = property;
listSortDirection = direction;
isSorted = true;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
protected void Sort()
{
List<T> itemsList = (List<T>)Items;
var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList();
var children = sortedItems.Where(i => i.Parent is not null).ToList();
itemsList.Clear();
//Only add parentless items at this stage. After these items are added in the
//correct sorting order, go back and add the children beneath their parents.
itemsList.AddRange(sortedItems.Except(children));
foreach (var parent in children.Select(c => c.Parent).Distinct())
{
var pIndex = itemsList.IndexOf(parent);
foreach (var c in children.Where(c=> c.Parent == parent))
itemsList.Insert(++pIndex, c);
}
}
protected override void OnListChanged(ListChangedEventArgs e)
{
if (isSorted && !SuspendSorting &&
((e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor == SortPropertyCore) ||
e.ListChangedType == ListChangedType.ItemAdded))
{
var item = Items[e.NewIndex];
Sort();
var newIndex = Items.IndexOf(item);
base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex));
}
else
base.OnListChanged(e);
}
protected override void RemoveSortCore()
{
isSorted = false;
propertyDescriptor = base.SortPropertyCore;
listSortDirection = base.SortDirectionCore;
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
}