From e26deb90923dda188a52dc6c1ebdabcfc5f5d67d Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 11:15:41 -0600 Subject: [PATCH] Address comments --- Source/LibationWinForms/GridView/GridEntry.cs | 14 ++++- .../GridView/GridEntryBindingList.cs | 62 +++++++++++-------- .../GridView/LiberateButtonStatus.cs | 2 +- .../LiberateDataGridViewImageButtonColumn.cs | 7 +++ .../GridView/LibraryBookEntry.cs | 18 +++--- .../LibationWinForms/GridView/SeriesEntry.cs | 28 ++++----- 6 files changed, 77 insertions(+), 54 deletions(-) diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index 6757ef60..6d7e9cd8 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -27,6 +27,7 @@ namespace LibationWinForms.GridView } public new bool InvokeRequired => base.InvokeRequired; public abstract DateTime DateAdded { get; } + public abstract float SeriesIndex { get; } public abstract string ProductRating { get; protected set; } public abstract string PurchaseDate { get; protected set; } public abstract string MyRating { get; protected set; } @@ -40,10 +41,21 @@ namespace LibationWinForms.GridView public abstract string Description { get; protected set; } public abstract string DisplayTags { get; } public abstract LiberateButtonStatus Liberate { get; } - public abstract object GetMemberValue(string memberName); #endregion + + #region Sorting + + public GridEntry() => _memberValues = CreateMemberValueDictionary(); + private Dictionary> _memberValues { get; set; } + protected abstract Dictionary> CreateMemberValueDictionary(); + + // These methods are implementation of Dinah.Core.DataBinding.IMemberComparable + // Used by GridEntryBindingList for all sorting + public virtual object GetMemberValue(string memberName) => _memberValues[memberName](); public IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType]; + #endregion + protected void LoadCover() { // Get cover art. If it's default, subscribe to PictureCached diff --git a/Source/LibationWinForms/GridView/GridEntryBindingList.cs b/Source/LibationWinForms/GridView/GridEntryBindingList.cs index 511e2ce6..b5ee0f8f 100644 --- a/Source/LibationWinForms/GridView/GridEntryBindingList.cs +++ b/Source/LibationWinForms/GridView/GridEntryBindingList.cs @@ -1,5 +1,6 @@ using ApplicationServices; using Dinah.Core.DataBinding; +using LibationSearchEngine; using System; using System.Collections.Generic; using System.ComponentModel; @@ -21,15 +22,13 @@ namespace LibationWinForms.GridView */ 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(); + public bool SupportsFiltering => true; + public string Filter { get => FilterString; set => ApplyFilter(value); } protected MemberComparer Comparer { get; } = new(); protected override bool SupportsSortingCore => true; @@ -37,15 +36,14 @@ namespace LibationWinForms.GridView 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 - /// + /// Items that were removed from the base list due to filtering private readonly List FilterRemoved = new(); private string FilterString; - private LibationSearchEngine.SearchResultSet SearchResults; + private SearchResultSet SearchResults; + private bool isSorted; + private ListSortDirection listSortDirection; + private PropertyDescriptor propertyDescriptor; #region Unused - Advanced Filtering @@ -118,11 +116,9 @@ namespace LibationWinForms.GridView if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId)) { FilterRemoved.Remove(episode); - Items.Insert(++sindex, episode); + InsertItem(++sindex, episode); } } - - OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); sEntry.Liberate.Expanded = true; } @@ -137,7 +133,7 @@ namespace LibationWinForms.GridView if (item is SeriesEntry || (item is LibraryBookEntry lbe && (lbe.Parent is null || lbe.Parent.Liberate.Expanded))) { FilterRemoved.Remove(item); - base.InsertItem(visibleCount++, item); + InsertItem(visibleCount++, item); } } @@ -175,36 +171,52 @@ namespace LibationWinForms.GridView { var itemsList = (List)Items; - var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList(); + var children = itemsList.LibraryBooks().Where(i => i.Parent is not null).ToList(); - var children = sortedItems.LibraryBooks().Where(i => i.Parent is not null).ToList(); + var sortedItems = itemsList.Except(children).OrderBy(ge => ge, Comparer).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)); + itemsList.AddRange(sortedItems); 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)) + + //children should always be sorted by series index. + foreach (var c in children.Where(c => c.Parent == parent).OrderBy(c => c.SeriesIndex)) itemsList.Insert(++pIndex, c); } } protected override void OnListChanged(ListChangedEventArgs e) { - if (isSorted && e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor == SortPropertyCore) + if (e.ListChangedType == ListChangedType.ItemChanged) { - var item = Items[e.NewIndex]; - Sort(); - var newIndex = Items.IndexOf(item); + if (Items[e.NewIndex] is LibraryBookEntry lbItem) + { + SearchResults = SearchEngineCommands.Search(FilterString); + if (!SearchResults.Docs.Any(d => d.ProductId == lbItem.AudibleProductId)) + { + FilterRemoved.Add(lbItem); + base.Remove(lbItem); + return; + } + } - base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex)); + if (isSorted && e.PropertyDescriptor == SortPropertyCore) + { + var item = Items[e.NewIndex]; + Sort(); + var newIndex = Items.IndexOf(item); + + base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemMoved, newIndex, e.NewIndex)); + return; + } } - else - base.OnListChanged(e); + base.OnListChanged(e); } protected override void RemoveSortCore() diff --git a/Source/LibationWinForms/GridView/LiberateButtonStatus.cs b/Source/LibationWinForms/GridView/LiberateButtonStatus.cs index 3ee577fd..21c207d0 100644 --- a/Source/LibationWinForms/GridView/LiberateButtonStatus.cs +++ b/Source/LibationWinForms/GridView/LiberateButtonStatus.cs @@ -7,8 +7,8 @@ namespace LibationWinForms.GridView { public LiberatedStatus BookStatus { get; set; } public LiberatedStatus? PdfStatus { get; set; } - public bool IsSeries { get; init; } public bool Expanded { get; set; } + public bool IsSeries { get; init; } /// /// Defines the Liberate column's sorting behavior diff --git a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index f7291962..b7f0eaa7 100644 --- a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -1,4 +1,5 @@ using DataLayer; +using Dinah.Core.Windows.Forms; using System; using System.Drawing; using System.Windows.Forms; @@ -15,12 +16,18 @@ namespace LibationWinForms.GridView internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell { + private static readonly Color SERIES_BG_COLOR = Color.LightSkyBlue; protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts) { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); if (value is LiberateButtonStatus status) { + if (rowIndex >= 0 && DataGridView.GetBoundItem(rowIndex) is LibraryBookEntry lbEntry && lbEntry.Parent is not null) + { + DataGridView.Rows[rowIndex].DefaultCellStyle.BackColor = SERIES_BG_COLOR; + } + if (status.IsSeries) { var imageName = status.Expanded ? "minus" : "plus"; diff --git a/Source/LibationWinForms/GridView/LibraryBookEntry.cs b/Source/LibationWinForms/GridView/LibraryBookEntry.cs index 8d93ebff..6a312ea1 100644 --- a/Source/LibationWinForms/GridView/LibraryBookEntry.cs +++ b/Source/LibationWinForms/GridView/LibraryBookEntry.cs @@ -32,6 +32,7 @@ namespace LibationWinForms.GridView private LiberatedStatus? _pdfStatus; public override DateTime DateAdded => LibraryBook.DateAdded; + public override float SeriesIndex => Book.SeriesLink.FirstOrDefault()?.Index ?? 0; public override string ProductRating { get; protected set; } public override string PurchaseDate { get; protected set; } public override string MyRating { get; protected set; } @@ -77,8 +78,7 @@ namespace LibationWinForms.GridView private void setLibraryBook(LibraryBook libraryBook) { - LibraryBook = libraryBook; - _memberValues = CreateMemberValueDictionary(); + LibraryBook = libraryBook; LoadCover(); @@ -119,16 +119,19 @@ namespace LibationWinForms.GridView { case nameof(udi.Tags): Book.UserDefinedItem.Tags = udi.Tags; + SearchEngineCommands.UpdateBookTags(Book); NotifyPropertyChanged(nameof(DisplayTags)); break; case nameof(udi.BookStatus): Book.UserDefinedItem.BookStatus = udi.BookStatus; _bookStatus = udi.BookStatus; + SearchEngineCommands.UpdateLiberatedStatus(Book); NotifyPropertyChanged(nameof(Liberate)); break; case nameof(udi.PdfStatus): Book.UserDefinedItem.PdfStatus = udi.PdfStatus; _pdfStatus = udi.PdfStatus; + SearchEngineCommands.UpdateLiberatedStatus(Book); NotifyPropertyChanged(nameof(Liberate)); break; } @@ -157,16 +160,9 @@ namespace LibationWinForms.GridView #endregion #region Data Sorting - // These methods are implementation of Dinah.Core.DataBinding.IMemberComparable - // Used by Dinah.Core.DataBinding.SortableBindingList for all sorting - public override object GetMemberValue(string memberName) => _memberValues[memberName](); - private Dictionary> _memberValues { get; set; } - - /// - /// Create getters for all member object values by name - /// - private Dictionary> CreateMemberValueDictionary() => new() + /// Create getters for all member object values by name + protected override Dictionary> CreateMemberValueDictionary() => new() { { nameof(Title), () => Book.TitleSortable() }, { nameof(Series), () => Book.SeriesSortable() }, diff --git a/Source/LibationWinForms/GridView/SeriesEntry.cs b/Source/LibationWinForms/GridView/SeriesEntry.cs index 65cb648e..96d40f49 100644 --- a/Source/LibationWinForms/GridView/SeriesEntry.cs +++ b/Source/LibationWinForms/GridView/SeriesEntry.cs @@ -10,6 +10,7 @@ namespace LibationWinForms.GridView { public List Children { get; init; } public override DateTime DateAdded => Children.Max(c => c.DateAdded); + public override float SeriesIndex { get; } public override string ProductRating { get @@ -47,20 +48,23 @@ namespace LibationWinForms.GridView public override string Description { get; protected set; } = string.Empty; public override string DisplayTags { get; } = string.Empty; - public override LiberateButtonStatus Liberate => _liberate; + public override LiberateButtonStatus Liberate { get; } protected override Book Book => SeriesBook.Book; private SeriesBook SeriesBook { get; set; } - private LiberateButtonStatus _liberate = new LiberateButtonStatus { IsSeries = true }; - - public SeriesEntry(SeriesBook seriesBook, IEnumerable children) + private SeriesEntry(SeriesBook seriesBook) { - Children = children.Select(c => new LibraryBookEntry(c) { Parent = this }).ToList(); + Liberate = new LiberateButtonStatus { IsSeries = true }; + SeriesIndex = seriesBook.Index; + } + public SeriesEntry(SeriesBook seriesBook, IEnumerable children) : this(seriesBook) + { + Children = children.Select(c => new LibraryBookEntry(c) { Parent = this }).OrderBy(c => c.SeriesIndex).ToList(); SetSeriesBook(seriesBook); } - public SeriesEntry(SeriesBook seriesBook, LibraryBook child) + public SeriesEntry(SeriesBook seriesBook, LibraryBook child) : this(seriesBook) { Children = new() { new LibraryBookEntry(child) { Parent = this } }; SetSeriesBook(seriesBook); @@ -69,7 +73,6 @@ namespace LibationWinForms.GridView private void SetSeriesBook(SeriesBook seriesBook) { SeriesBook = seriesBook; - _memberValues = CreateMemberValueDictionary(); LoadCover(); // Immutable properties @@ -83,16 +86,9 @@ namespace LibationWinForms.GridView } } - // These methods are implementation of Dinah.Core.DataBinding.IMemberComparable - // Used by Dinah.Core.DataBinding.SortableBindingList for all sorting - public override object GetMemberValue(string memberName) => _memberValues[memberName](); - private Dictionary> _memberValues { get; set; } - - /// - /// Create getters for all member object values by name - /// - private Dictionary> CreateMemberValueDictionary() => new() + /// Create getters for all member object values by name + protected override Dictionary> CreateMemberValueDictionary() => new() { { nameof(Title), () => Book.SeriesSortable() }, { nameof(Series), () => Book.SeriesSortable() },