diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj
index db37ad59..37d7e6ca 100644
--- a/Source/AppScaffolding/AppScaffolding.csproj
+++ b/Source/AppScaffolding/AppScaffolding.csproj
@@ -3,7 +3,7 @@
net6.0-windows
- 7.7.0.14
+ 7.7.1.1
diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs
index 98a7b9f7..49c41b42 100644
--- a/Source/LibationWinForms/Form1.Designer.cs
+++ b/Source/LibationWinForms/Form1.Designer.cs
@@ -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(this.ProductsDisplay_LiberateClicked);
this.productsDisplay.VisibleCountChanged += new System.EventHandler(this.productsDisplay_VisibleCountChanged);
+ this.productsDisplay.LiberateClicked += new System.EventHandler(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;
diff --git a/Source/LibationWinForms/Form1.resx b/Source/LibationWinForms/Form1.resx
index 64da6d15..2505fa27 100644
--- a/Source/LibationWinForms/Form1.resx
+++ b/Source/LibationWinForms/Form1.resx
@@ -57,12 +57,48 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ True
+
+
+ True
+
+
+ True
+
17, 17
+
+ True
+
132, 17
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
+
+ True
+
diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
index 88c309a3..9acf1eb4 100644
--- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs
@@ -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;
diff --git a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
index 296e34bf..3c146703 100644
--- a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
+++ b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs
@@ -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);
diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs
index 5c53f7e9..d26ae575 100644
--- a/Source/LibationWinForms/grid/GridEntry.cs
+++ b/Source/LibationWinForms/grid/GridEntry.cs
@@ -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 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
+ 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 Series(this IEnumerable gridEntries)
=> gridEntries.Where(i => i is SeriesEntry).Cast();
public static IEnumerable LibraryBooks(this IEnumerable gridEntries)
=> gridEntries.Where(i => i is LibraryBookEntry).Cast();
+ public static LibraryBookEntry? FindBookByAsin(this IEnumerable gridEntries, string audibleProductID)
+ => gridEntries.FirstOrDefault(i => i.AudibleProductId == audibleProductID);
+ public static SeriesEntry? FindBookSeriesEntry(this IEnumerable gridEntries, IEnumerable matchSeries)
+ => gridEntries.Series().FirstOrDefault(i => matchSeries.Any(s => s.Series.Name == i.Series));
+ public static IEnumerable EmptySeries(this IEnumerable gridEntries)
+ => gridEntries.Series().Where(i => i.Children.Count == 0);
}
}
diff --git a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs b/Source/LibationWinForms/grid/GridEntryBindingList.cs
similarity index 56%
rename from Source/LibationWinForms/grid/FilterableSortableBindingList.cs
rename to Source/LibationWinForms/grid/GridEntryBindingList.cs
index dcb70524..fb6f7df7 100644
--- a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs
+++ b/Source/LibationWinForms/grid/GridEntryBindingList.cs
@@ -8,7 +8,7 @@ using System.Linq;
namespace LibationWinForms
{
/*
- * Allows filtering of the underlying SortableBindingList
+ * Allows filtering and sorting of the underlying BindingList
* 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, IBindingListView
+ internal class GridEntryBindingList : BindingList, IBindingListView
{
+ private bool isSorted;
+ private ListSortDirection listSortDirection;
+ private PropertyDescriptor propertyDescriptor;
+
+ public GridEntryBindingList() : base(new List()) { }
+ public GridEntryBindingList(IEnumerable enumeration) : base(new List(enumeration)) { }
+
+ /// All items in the list, including those filtered out.
+ public List AllItems() => Items.Concat(FilterRemoved).ToList();
+
+ protected MemberComparer 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); }
+
///
/// Items that were removed from the base list due to filtering
///
private readonly List FilterRemoved = new();
private string FilterString;
private LibationSearchEngine.SearchResultSet SearchResults;
- public FilterableSortableBindingList(IEnumerable enumeration) : base(enumeration) { }
- public FilterableSortableBindingList() : base(new List()) { }
- 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);
}
- /// All items in the list, including those filtered out.
- public List 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().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().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)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));
+ }
}
}
diff --git a/Source/LibationWinForms/grid/LiberateButtonStatus.cs b/Source/LibationWinForms/grid/LiberateButtonStatus.cs
new file mode 100644
index 00000000..8814eb24
--- /dev/null
+++ b/Source/LibationWinForms/grid/LiberateButtonStatus.cs
@@ -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; }
+
+ ///
+ /// Defines the Liberate column's sorting behavior
+ ///
+ 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);
+ }
+ }
+}
diff --git a/Source/LibationWinForms/grid/LibraryBookEntry.cs b/Source/LibationWinForms/grid/LibraryBookEntry.cs
index a02686eb..3834e502 100644
--- a/Source/LibationWinForms/grid/LibraryBookEntry.cs
+++ b/Source/LibationWinForms/grid/LibraryBookEntry.cs
@@ -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)
diff --git a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs
index cd312da0..fec72094 100644
--- a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs
+++ b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs
@@ -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;
}
}
diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs
index f0fc1497..8e3cfe8d 100644
--- a/Source/LibationWinForms/grid/ProductsGrid.cs
+++ b/Source/LibationWinForms/grid/ProductsGrid.cs
@@ -23,7 +23,7 @@ namespace LibationWinForms.grid
public event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked;
public new event EventHandler Scroll;
- private FilterableSortableBindingList bindingList;
+ private GridEntryBindingList bindingList;
internal IEnumerable 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 { 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().Concat(removedSeries))
//no need to re-filter for removed books
bindingList.Remove(removed);
- Filter(existingFilter);
-
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
}
diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/grid/SeriesEntry.cs
index bc74f147..9646c52f 100644
--- a/Source/LibationWinForms/grid/SeriesEntry.cs
+++ b/Source/LibationWinForms/grid/SeriesEntry.cs
@@ -6,9 +6,9 @@ using System.Linq;
namespace LibationWinForms
{
- internal class SeriesEntry : GridEntry
+ public class SeriesEntry : GridEntry
{
- public List Children { get; set; }
+ public List 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 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();
diff --git a/Source/LibationWinForms/grid/SortableBindingList1.cs b/Source/LibationWinForms/grid/SortableBindingList1.cs
deleted file mode 100644
index ec545340..00000000
--- a/Source/LibationWinForms/grid/SortableBindingList1.cs
+++ /dev/null
@@ -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 : BindingList where T : class, IMemberComparable, IHierarchical
- {
- private bool isSorted;
- private ListSortDirection listSortDirection;
- private PropertyDescriptor propertyDescriptor;
-
- public SortableBindingList1() : base(new List()) { }
- public SortableBindingList1(IEnumerable enumeration) : base(new List(enumeration)) { }
-
- protected bool SuspendSorting { get; set; }
- protected MemberComparer 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 itemsList = (List)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));
- }
- }
-}